Cypress.ioを使用してストライプチェックアウトフォームをテストしようとしています
誰かがこれをうまく動作させた場合は、私に知らせてください。私はここで問題に関するスレッドを見つけました https://github.com/cypress-io/cypress/issues/136 これに基づいて私は思いつきました:
cy.get('iframe.stripe_checkout_app')
.wait(10000)
.then($iframe => {
const iframe = $iframe.contents()
const myInput0 = iframe.find('input:eq(0)')
const myInput1 = iframe.find('input:eq(1)')
const myInput2 = iframe.find('input:eq(2)')
const myButton = iframe.find('button')
cy
.wrap(myInput0)
.invoke('val', 4000056655665556)
.trigger('change')
cy
.wrap(myInput1)
.invoke('val', 112019)
.trigger('change')
cy
.wrap(myInput2)
.invoke('val', 424)
.trigger('change')
cy.wrap(myButton).click({ force: true })
})
しかし問題は、ストライプフォームがまだ入力値を登録しないことです。これが何が起きるかの小さなGIFです http://www.giphy.com/gifs/xT0xeEZ8CmCTVMwOU8 。基本的に、フォームは変更入力トリガーを登録しません。
サイプレスを使用してiframeのフォームにデータを入力する方法を知っている人はいますか?
次のスニペットでうまくいくはずです。 this thread の@izaacdbの投稿からコピー/貼り付けました。
cy.wait(5000)
cy.get('.__PrivateStripeElement > iframe').then($element => {
const $body = $element.contents().find('body')
let stripe = cy.wrap($body)
stripe.find('.Input .InputElement').eq(0).click().type('4242424242424242')
stripe = cy.wrap($body)
stripe.find('.Input .InputElement').eq(1).click().type('4242')
stripe = cy.wrap($body)
stripe.find('.Input .InputElement').eq(2).click().type('424')
})
ただし、上記が機能するためには、以下を実行する必要があります(上記のリンクと同じスレッドから@nerdmaxの投稿からコピー/貼り付け):
@Vedelopment @ brian-mannに感謝します!
私はreact-stripe-checkoutコンポーネントでテストしましたが、動作します。
ソリューションの詳細を追加するだけで、他の人の時間を節約できます。
chromeWebSecurity disable
:// cypress.json { "chromeWebSecurity": false }
--disable-site-isolation-trials
:チェック: https://docs.cypress.io/api/plugins/browser-launch-api.html# AND#1951
// /plugins/index.js module.exports = (on, config) => { on("before:browser:launch", (browser = {}, args) => { if (browser.name === "chrome") { args.Push("--disable-site-isolation-trials"); return args; } }); };
私はこれを機能させるためにあまりにも長い時間を費やしましたが、私が見つけた答えはどれも完全には機能しません。私のソリューションをサイプレスに追加しました github issue for iframes(そこにもう少しコンテキストがあります)。また、ここに置いて、他の人の時間を節約することを願っています。
このスタックオーバーフローの回答 からonIframeReady()関数を盗みました。
基本的には、iframeがロードされているかどうかを確認し、iframeがロードされている場合は$iframe.contents().find("body");
を実行してコンテンツに切り替えます。ロードされていない場合は、同じコードをload
イベントにフックして、iframeがロードされるとすぐに実行されるようにします。
これは、iframeに切り替えた後にサイプレスチェーンを使用できるようにするカスタムコマンドとして記述されているため、_support/commands.js
_ファイルに次のように記述します。
_Cypress.Commands.add("iframe", { prevSubject: "element" }, $iframe => {
Cypress.log({
name: "iframe",
consoleProps() {
return {
iframe: $iframe,
};
},
});
return new Cypress.Promise(resolve => {
onIframeReady(
$iframe,
() => {
resolve($iframe.contents().find("body"));
},
() => {
$iframe.on("load", () => {
resolve($iframe.contents().find("body"));
});
}
);
});
});
function onIframeReady($iframe, successFn, errorFn) {
try {
const iCon = $iframe.first()[0].contentWindow,
bl = "about:blank",
compl = "complete";
const callCallback = () => {
try {
const $con = $iframe.contents();
if ($con.length === 0) {
// https://git.io/vV8yU
throw new Error("iframe inaccessible");
}
successFn($con);
} catch (e) {
// accessing contents failed
errorFn();
}
};
const observeOnload = () => {
$iframe.on("load.jqueryMark", () => {
try {
const src = $iframe.attr("src").trim(),
href = iCon.location.href;
if (href !== bl || src === bl || src === "") {
$iframe.off("load.jqueryMark");
callCallback();
}
} catch (e) {
errorFn();
}
});
};
if (iCon.document.readyState === compl) {
const src = $iframe.attr("src").trim(),
href = iCon.location.href;
if (href === bl && src !== bl && src !== "") {
observeOnload();
} else {
callCallback();
}
} else {
observeOnload();
}
} catch (e) {
// accessing contentWindow failed
errorFn();
}
}
_
次に、テストから次のように呼び出します。
_cy.get('iframe.stripe_checkout_app')
.iframe()
.find('input:eq(0)')
.type("4000056655665556")
_
。alias() を呼び出した後、.iframe()
を呼び出して残りの入力を参照したり、.get()
iframeを何度か参照したりできます。理解するのはあなた次第です。
Iframeワークフローはまだかなり不格好です(- この機能 が実装されるまで)。今のところ、ほとんどすべてのDOMインタラクションを強制することができます。
cy.visit("https://jsfiddle.net/1w9jpnxo/1/");
cy.get("iframe").then( $iframe => {
const $doc = $iframe.contents();
cy.wrap( $doc.find("#input") ).type( "test", { force: true });
cy.wrap( $doc.find("#submit") ).click({ force: true });
});
'.Input .InputElement'
@ user8888のセレクターが機能しませんでした。代わりに、input
属性を使用して各name
にアクセスしています。
cy.get(".__PrivateStripeElement > iframe").then(($element) => {
const $body = $element.contents().find("body");
let stripe = cy.wrap($body);
stripe
.find('[name="cardnumber"]')
.click()
.type(MOCK_CC_NUMBER);
stripe = cy.wrap($body);
stripe
.find('[name="exp-date"]')
.click()
.type(MOCK_CC_EXP);
stripe = cy.wrap($body);
stripe
.find('[name="cvc"]')
.click()
.type(MOCK_CC_CVC);
stripe = cy.wrap($body);
stripe
.find('[name="postal"]')
.click()
.type(MOCK_CC_Zip);
});
この link の解決策は私のために働いています。基本的に、手順は次のとおりです。
これは直接質問に答えませんが、jQueryを使用してiframe内の要素を操作しようと数日間試行した後、サイプレスがすでに行っていた一連の処理を再実装して、私は自分自身を叩き始めましたこの:
Cypress.Commands.add('openiframe', () => {
return cy.get("iframe[src^='/']").then(iframe => {
cy.visit(Cypress.$(iframe).attr('src'), { timeout: Cypress.config("pageLoadTimeout") });
});
});
これにより、cy.openiframe()。then(()=> {});私がテストしていたサイトが最初からiframeにたくさんの機能を配置していないかのように進めます。
欠点は、iframeで何かを行う前に、実行中の作業を完了しなければならないということですiframeではありません。
それはあなたのユースケースではうまくいかないかもしれませんが、もしそうなら、私が見つけた最も簡単な回避策です。