javascript 如何使用Cypress将数据输入到iframe中的表单输入中?

yuvru6vn  于 2023-01-01  发布在  Java
关注(0)|答案(9)|浏览(336)

我一直在尝试使用www.example.com测试一个条带结帐表单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 })
      })

但问题是stripe表单仍然没有注册输入值,下面是http://www.giphy.com/gifs/xT0xeEZ8CmCTVMwOU8的一个小gif,基本上,表单没有注册更改输入触发器。
有人知道如何使用Cypress在iframe中向表单输入数据吗?

wpcxdonn

wpcxdonn1#

下面的代码片段应该对你有用。我从@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的文章从同一线程链接以上):

非常感谢“发展”brian-mann!
我测试了React条纹检测组件,它的工作。
只需添加一些解决方案的详细信息,这样可以保存其他人的时间。
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;
    }
  });
};
nkoocmlb

nkoocmlb2#

@user8888中的'.Input .InputElement'选择器对我不起作用,而是通过name属性访问每个input

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

yr9zkbsy3#

我只是花了太多的时间试图让这个工作,没有一个答案,我发现将完全工作。我添加了我的解决方案到Cypress github问题的iframes(有一点更多的上下文),也把它放在这里,希望保存别人一些时间。
我从这个stackoverflow答案中窃取了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")

您可以在调用.iframe()之后使用.alias()来引用它,以便在您的其余输入中引用它,或者多次调用.get()来引用iframe,我将把这个问题留给您来解决。

yrwegjxp

yrwegjxp4#

这个link中的解决方案对我来说很有效。基本上,步骤如下:
1.在cypress.json中将chromeWebSecurity设置为假
1.添加cypress命令以获取command.js中的iframe
1.在脚本中使用iframe命令

x6h2sr28

x6h2sr285#

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

piv4azn76#

这并没有直接回答你的问题,但是在尝试使用jQuery在iframe中操作元素,重新实现Cypress已经做过的一系列事情几天之后,我打了自己一巴掌,开始这样做:

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中,所以你不能太容易地来回。
它可能不适用于您的用例,但如果/当它适用时,它是我发现的最简单的变通方法。

ejk8hzay

ejk8hzay7#

为了避免使用:

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

lawou6xi8#

我昨天发布了一个plugin,它添加了一个简单的Cypress API来填充Stripe元素:

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

这个插件避免了cy.wait()调用、手动窥视<iframe>以及其他笨拙的选择器。

xoshrz7s

xoshrz7s9#

对我来说,这个iframe有一个随机行为,所以我使用了其中的一些代码,但最后还是用了force:true因为最后一个值失败了。这个名称对我来说无效,我不得不使用一个更具体的不同属性。

cy.get(".__PrivateStripeElement > iframe").then(($element) => {
        const $body = $element.contents().find("body");

    cy.wrap($body)
        .find('[data-elements-stable-field-name="cardNumber"]')
        .click()
        .type('value')

    cy.wrap($body)
        .find('[data-elements-stable-field-name="cardExpiry"]')
        .click()
        .type('value')

    cy.wrap($body)
        .find('[data-elements-stable-field-name="cardCvc"]')
        .click()
        .type('value', { force: true });

相关问题