我已经坚持了很长时间了,不知道如何让Cypress 12.8.1与条纹元素一起输入信用卡详细信息并创建付款.

我搜索了互联网,但似乎没有一个解决方案奏效.

Any help is greatly appreciated.

我试过了:

  1. Using xpath https://www.browserstack.com/guide/frames-and-iframes-in-cypress this does nto work for me. See error: https://github.com/cypress-io/cypress/issues/24764#issuecomment-1489438851 enter image description here

  2. 我试过这个插件,但它不再工作了.https://stackoverflow.com/a/70024952/10222449https://github.com/dbalatero/cypress-plugin-stripe-elements

  3. try 此操作,但出现以下错误.

    const $body = $element.contents().find('body')
    let stripe = cy.wrap($body)
    stripe.find('input[name="number"]').click().type('4242424242424242')
    stripe = cy.wrap($body)
    stripe.find('input[name="expiry"]').click().type('4242')
    stripe = cy.wrap($body)
    stripe.find('input[name="cvc"]').click().type('424')
})

enter image description here

  1. try 了几个版本的添加自定义Cypress命令"iframeLoaded",但我想不出如何在新的Cypress 12打字脚本格式中添加这些命令,并且只收到错误. https://medium.com/@michabahr/testing-stripe-elements-with-cypress-5a2fc17ab27bhttps://bionicjulia.com/blog/cypress-testing-stripe-elements

我在Support/Commands.ts中的代码

// ***********************************************
// This example namespace declaration will help
// with Intellisense and code completion in your
// IDE or Text Editor.
// ***********************************************
declare namespace Cypress {
  interface Chainable<Subject = any> {
    iframeLoaded($iframe: any): typeof iframeLoaded;
  }
}

function iframeLoaded($iframe: any): Promise<any> {
  const contentWindow = $iframe.prop('contentWindow')
  return new Promise(resolve => {
    if (contentWindow && contentWindow.document.readyState === 'complete') {
      resolve(contentWindow)
    } else {
      $iframe.on('load', () => {
        resolve(contentWindow)
      })
    }
  })
}

Cypress.Commands.add('iframeLoaded', {prevSubject: 'element'}, iframeLoaded);

enter image description here

Update:

我想我用福迪的回答就知道了.我做了3次更改. 我不得不这样改变它:

    function getCardField(selector: any, attempts = 0) {
          Cypress.log({displayName: 'getCardField', message: `${selector}: ${attempts}`})
          
          if (attempts > 50) throw new Error('too many attempts')
        
          return cy.get('iframe', {timeout:10_000, log:false})
// CHANGE: .eq(1 to .eq(0
            .eq(0, {log:false})
            .its('0.contentDocument', {log:false})
            .find('body', {log:false})
            .then(body => {
              const cardField = body.find(selector)
              if (!cardField.length) {
                return cy.wait(300, {log:false})
                  .then(() => {
                    getCardField(selector, ++attempts)
                  })
              } else {
                return cy.wrap(cardField)
              }
            })
        }
        
// CHANGE: "div.CardField" to "div.CardNumberField input"
        getCardField('div.CardNumberField input')
          .type('4242424242424242')

// CHANGE: "div.CardField" to "div.CardNumberField-input-wrapper"
getCardField('div.CardNumberField-input-wrapper')
      .find('input').eq(0)
      .should('have.value', '4242 4242 4242 4242')   // passes

推荐答案

简短的答案是,Strike iframe需要时间来加载和显示您需要访问的字段,因此您需要添加一个retry.

通常,您使用.should()个断言来重试,直到DOM中出现您想要的内容.

不幸的是,页面中有一个<iframe>,您不能使用.should(),因为它不会重试返回到contentDocument的链中的所有步骤.

因此,您需要使用递归函数自行重试.

下面是一个使用样例条纹页面的工作示例:

cy.intercept({ resourceType: /xhr|fetch/ }, { log: false })  // suppress fetch logs 
cy.viewport(1500, 1000)
cy.visit('https://stripe-payments-demo.appspot.com');  

function getCardField(selector, attempts = 0) {
  Cypress.log({displayName: 'getCardField', message: `${selector}: ${attempts}`})
  
  if (attempts > 50) throw new Error('too many attempts')

  return cy.get('iframe', {timeout:10_000, log:false})
    .eq(1, {log:false})
    .its('0.contentDocument', {log:false}) 
    .find('body', {log:false})
    .then(body => {
      const cardField = body.find(selector)
      if (!cardField.length) {
        return cy.wait(300, {log:false})
          .then(() => {
            getCardField(selector, ++attempts)
          })
      } else {
        return cy.wrap(cardField)
      }
    })
}

getCardField('div.CardField')
  .type('4242424242424242')

getCardField('div.CardField')
  .find('input').eq(0)
  .should('have.value', '4242 4242 4242 4242')   // ✅ passes

一个更一般的递归函数

function getStripeField({iframeSelector, fieldSelector}, attempts = 0) {
  Cypress.log({displayName: 'getCardField', message: `${fieldSelector}: ${attempts}`})

  if (attempts > 50) throw new Error('too many attempts')

  return cy.get(iframeSelector, {timeout:10_000, log:false})
    .eq(0, {log:false})
    .its('0.contentDocument', {log:false}) 
    .find('body', {log:false})
    .then(body => {
      const stripeField = body.find(fieldSelector)
      if (!stripeField.length) {
        return cy.wait(300, {log:false})
          .then(() => {
            getStripeField({iframeSelector, fieldSelector}, ++attempts)
          })
      } else {
        return cy.wrap(stripeField)
      }
    })
}
cy.visit('https://hivepass.app/temp-stripe-example.html')

getStripeField({
  iframeSelector: 'iframe[title="Secure card number input frame"]', 
  fieldSelector: 'div.CardNumberField-input-wrapper'
})
.type('4242424242424242')

getStripeField({
  iframeSelector: 'iframe[title="Secure card number input frame"]', 
  fieldSelector: 'div.CardNumberField-input-wrapper input'
})
.should('have.value', '4242 4242 4242 4242')

getStripeField({
  iframeSelector: '[title="Secure expiration date input frame"]', 
  fieldSelector: '[name="exp-date"]'
})
.type('0323')

getStripeField({
  iframeSelector: '[title="Secure expiration date input frame"]', 
  fieldSelector: '[name="exp-date"]'
})
.should('have.value', '03 / 23')

Note,在确认条带字段的新值时,在更新后重新查询它似乎很重要.

Angular相关问答推荐

Angular:浏览器中未显示一些Google Content图标

Angular Signal只指一种类型,但这里用作值.ts(2693)''

Angular 单元测试组件s @ Input.'变化值检测

父母和子元素在17号角不保持同步

大型 Angular 12 应用程序 - 我如何管理 js 文件

显示带有数组子列的标题表 - Angular

无法在 Angular 中使用 CryptoJS 正确加密

如何更改 Angular material 按钮中的内部 RippleColor 和 UnboundedRipple 效果?

如何以Angular 显示动态视图模型

关闭 Dynamsoft Web Twain 弹出窗口

Angular2:更新数组时*ngFor不更新

如何在 Angular 2 模板中使用枚举

无法获得 angular-material md-sidenav 的 100% 高度

由router-outlet 创建时如何将css类应用于组件元素?

Angular 2 单元测试 - @ViewChild 未定义

Angular 2 http 没有得到

Angular 5 中 value 和 ngValue 的区别

从 angular2 模板调用静态函数

Angular2中是否有像window.onbeforeunload这样的生命周期钩子?

路由 getCurrentNavigation 始终返回 null