看下面这段代码:
context('Group Skipping - Checkout', () => { before(() => { cy.requireLoggedIn().then(() => { checkout.goToProductDetailsPage(); checkout.addProductToCart(); checkout.fillAddressForm(); cy.get('input[type=radio][formcontrolname=deliveryModeId]:not(:disabled)') .first() .focus() .click(); checkoutNextStep('/checkout/payment-details'); checkout.fillPaymentForm(); cy.get('.cx-review-summary-card'); cy.saveLocalStorage(); }); });
第39行会触发一个 HTTP Put 请求,其触发时机在这篇文章里可以看到。
下列文字描述了一种竟态情况:
- line 39 的代码触发了 HTTP put 请求,修改 delivery mode
- line 40 行的代码拿到了 radio input 控件实例。 执行完了 first 和 focus 方法。
- 此时 HTTP Put 请求使得 Spinner 控件替代了 radio input 按钮,所以后者 detached from DOM.
- 在一个 detached from DOM 状态的元素下执行 click 方法,就会出现本文描述的错误。
解决方案
等 HTTP put 操作成功执行之后,再执行下面代码即可:
cy.get('input[type=radio][formcontrolname=deliveryModeId]') .first() .focus() .click();
完整的解决方案:
cy.intercept({ method: 'PUT', path: `${Cypress.env('OCC_PREFIX')}/${Cypress.env( 'BASE_SITE' )}/${Cypress.env( 'OCC_PREFIX_USER_ENDPOINT' )}/current/carts/*/deliverymode?deliveryModeId=*`, }).as('setDeliveryMode'); checkout.fillAddressForm(); cy.wait('@setDeliveryMode'); cy.get('input[type=radio][formcontrolname=deliveryModeId]') .first() .focus() .click(); checkoutNextStep('/checkout/payment-details'); checkout.fillPaymentForm(); cy.get('.cx-review-summary-card'); cy.saveLocalStorage();```
思路就是首先用 cy.intercept 监控 path 参数指定的 HTTP put 请求的 endpoint,使用 as 存储成一个别名,然后调用 cy.wait, 传入别名,等待其执行完毕,再执行接下来的 cy.get().click() 即可。