web-dev-qa-db-ja.com

サイプレスを使用してiframeのフォーム入力にデータを入力するにはどうすればよいですか?

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のフォームにデータを入力する方法を知っている人はいますか?

11
Josh Pittman

次のスニペットでうまくいくはずです。 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;
    }
  });
};
17
user8888

私はこれを機能させるためにあまりにも長い時間を費やしましたが、私が見つけた答えはどれも完全には機能しません。私のソリューションをサイプレスに追加しました 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を何度か参照したりできます。理解するのはあなた次第です。

2
Brendan

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 });
});
2
dwelle

'.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);
            });
1
theUtherSide

この link の解決策は私のために働いています。基本的に、手順は次のとおりです。

  1. Cypress.jsonでchromeWebSecurityをfalseに設定します
  2. Cypressコマンドを追加して、command.jsのiframeを取得します
  3. スクリプトでiframeコマンドを使用する
1
Juniada

これは直接質問に答えませんが、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ではありません

それはあなたのユースケースではうまくいかないかもしれませんが、もしそうなら、私が見つけた最も簡単な回避策です。

1
DrShaffopolis