17

我一直在尝试使用 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 })
      })

但问题是条带形式仍然没有注册输入值。这是http://www.giphy.com/gifs/xT0xeEZ8CmCTVMwOU8发生的事情的一个小 gif 。基本上,表单不会注册更改输入触发器。

有谁知道如何使用柏树将数据输入到 iframe 中的表单中?

4

8 回答 8

22

以下代码段应该适合您。我从@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;
    }
  });
};
于 2018-11-10T09:30:45.090 回答
4

@user8888的'.Input .InputElement'选择器对我不起作用。相反,我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);
            });
于 2019-08-28T21:15:00.767 回答
3

链接中的解决方案对我有用。基本上,步骤如下:

  1. 在 cypress.json 中将 chromeWebSecurity 设置为 false
  2. 在 command.js 中添加 cypress 命令以获取 iframe
  3. 在脚本中使用 iframe 命令
于 2020-01-03T03:21:55.090 回答
3

我只是花了太长时间试图让这个工作,我找到的答案都不能完全工作。我将我的解决方案添加到 iframe 的 cypress github 问题(那里有更多的上下文),也将它放在这里希望能节省其他人一些时间。

我从这个 stackoverflow answer偷了 onIframeReady() 函数。

基本上它所做的是检查 iframe 是否已加载,如果 iframe 已加载,它将$iframe.contents().find("body");切换到内容。如果它没有加载,它会将相同的代码挂接到load事件中,这样它就会在 iframe 加载后立即运行。

这是作为自定义命令编写的,以允许在切换到 iframe 后使用 cypress 链接,因此将以下内容放入您的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,我将由您自己解决。

于 2018-08-29T19:09:05.833 回答
2

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 });
});
于 2017-11-16T10:45:47.300 回答
2

这并不能直接回答您的问题,但是在尝试使用 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中做任何事情,所以你不能太容易来回走动。

它可能不适用于您的用例,但如果/当它起作用时,它是我发现的最简单的解决方法。

于 2018-05-14T21:49:42.967 回答
0

我昨天发布了一个插件,它添加了一个简单的 Cypress API 来填充 Stripe Elements:

cy.fillElementsInput('cardNumber', '4242424242424242');

这个插件避免了cy.wait()调用、<iframe>手动查看 s 和其他尴尬的选择器。

于 2020-12-26T18:37:06.037 回答
0

为了避免使用:

cy.wait(5000)

按照 cypress 在本教程中提供的有关如何使用 iframe的说明,我找到了一种更好的方法

   cy.get('iframe[name="__privateStripeFrame5"]')
    .its("0.contentDocument.body")
    .should("not.be.empty")
    .then((body) => {
      cy.wrap(body)
        .find("[name=cardnumber]")
        .type("6011111111111117", { force: true });
   });
于 2020-09-07T19:46:54.880 回答