如何使用cypress将数据输入iframe中的表单输入?

时间:2017-11-16 08:51:24

标签: javascript testing cypress

我一直在尝试使用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。基本上,表单不会注册更改输入触发器。

有没有人知道如何使用cypress将数据输入到iframe中的表单中?

7 个答案:

答案 0 :(得分:9)

以下代码段应该对您有用。我从@izaacdb在this thread中的帖子中复制/粘贴了它。

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#   和#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;
    }
  });
};

答案 1 :(得分:3)

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

  1. 在cypress.json中将chromeWebSecurity设置为false
  2. 添加cypress命令以在command.js中获取iframe
  3. 在脚本中使用iframe命令

答案 2 :(得分:1)

iframe工作流程仍然非常笨重(直到this feature实施)。现在,您可以尝试强制几乎每个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 });
});

答案 3 :(得分:1)

我花了太长时间试图使它正常工作,我发现的答案都无法完全起作用。我将解决方案添加到了针对iframe的赛普拉斯github issue上(那里还有更多内容),也希望将其放到这里以节省其他时间。

我从this stackoverflow answer窃取了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")

您可以在调用.iframe()后再.alias()进行其余输入或.get() iframe多次引用,我将由您自己决定。

答案 4 :(得分:1)

@ 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); });

merge = concatenate([sent_encoder, USE], axis=-1)

答案 5 :(得分:0)

这并没有直接回答你的问题,但经过几天尝试使用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 中的操作,因此您无法返回太容易了。

它可能不适用于您的用例,但如果/当它用,它是我找到的最简单的解决方法。

答案 6 :(得分:0)

为了避免使用:

cy.wait(5000)

我找到了一种更好的方法,可以按照cypress在本教程中提供的有关how to work with iframes的说明进行操作

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