[英]How do I use cypress with components that are prefetched or preloaded with webpack?
我正在使用Cypress
7.7.0
(也在8.0.0
進行了測試),並且遇到了一個有趣的競爭條件。 我正在測試一個頁面,其中Cypress
執行的第一個交互之一是單擊按鈕以打開模態。 為了保持包的大小較小,我將模態拆分為自己的預取webpack
塊。 我的Cypress
測試從cy.get('#modal-button').click()
但這不會加載模態,因為模態尚未完成下載/加載。 它什么都不做(甚至不向控制台拋出任何錯誤)。 換句話說, Cypress
與頁面交互的速度太快。 這也是通過手動測試重現的(我在頁面加載后超快地點擊了按鈕)。 我曾嘗試將模態設置為預加載,但這也不起作用。
我能夠通過在頁面加載和按鈕交互之間引入更多延遲來解決這個問題。 例如,在單擊按鈕之前插入任何Cypress
命令(甚至是cy.wait(0)
)可修復解決方案。 然而, Cypress
以不需要插入這些脆弱的解決方案而聞名。 有沒有好辦法解決這個問題? 我想將模態保留在自己的塊中。
僅供參考:我使用Vue
作為我的前端庫,並使用一個簡單的defineAsyncComponent(() => import(/* webpackPrefetch: true */ './my-modal.vue'))
來加載模態組件。 我認為這個問題對Cypress
來說是普遍的。
cy.wait(0)
沒有任何問題。
您所做的就是將控制權從測試傳遞到 JS 隊列中的下一個進程,在這種情況下,它是應用程序的啟動腳本,它可能正在等待將單擊處理程序添加到按鈕。
我最近發現 React 鈎子應用程序也需要這樣做,以允許鈎子完成它的過程。 您可能還會在 Vue 3 中遇到這種情況,因為它們引入了類似鈎子的功能。
如果您想憑經驗測試事件處理程序是否已到達,您可以使用這里給出的方法(修改為click()
) -測試何時開始?
let appHasStarted
function spyOnAddEventListener (win) {
const addListener = win.EventTarget.prototype.addEventListener
win.EventTarget.prototype.addEventListener = function (name) {
if (name === 'click') {
appHasStarted = true
win.EventTarget.prototype.addEventListener = addListener // restore original listener
}
return addListener.apply(this, arguments)
}
}
function waitForAppStart() {
return new Cypress.Promise((resolve, reject) => {
const isReady = () => {
if (appHasStarted) {
return resolve()
}
setTimeout(isReady, 0) // recheck "appHasStarted" variable
}
isReady()
})
}
it('greets', () => {
cy.visit('app.html', {
onBeforeLoad: spyOnAddEventListener
}).then(waitForAppStart)
cy.get('#modal-button').click()
})
但請注意, setTimeout(isReady, 0)
可能只會在您的應用程序中實現與cy.wait(0)
相同的cy.wait(0)
,即您實際上並不需要輪詢事件處理程序,您只需要讓應用程序喘口氣。
您的問題似乎是在加載支持按鈕的代碼之前已經渲染了按鈕。 正如您所注意到的,這不僅是快速自動化機器人的問題,甚至是“普通”用戶的問題。
簡而言之,解決方案是不提前顯示按鈕,而是顯示加載對話框。 賽普拉斯允許等待 DOM 元素可見,甚至帶有超時選項。 這比脆弱的隨機等待更健壯。
我最終還是等待網絡空閑,盡管我有多種選擇。
我用來執行此操作的 cypress 函數如下,它受此等待網絡的解決方案的影響很大:
Cypress.Commands.add('waitForIdleNetwork', () => {
const idleTimesInit = 3
let idleTimes = idleTimesInit
let resourcesLengthPrevious
cy.window().then(win =>
cy.waitUntil(() => {
const resourcesLoaded = win.performance.getEntriesByType('resource')
if (resourcesLoaded.length === resourcesLengthPrevious) {
idleTimes--
} else {
idleTimes = idleTimesInit
resourcesLengthPrevious = resourcesLoaded.length
}
return !idleTimes
})
)
})
以下是我采用的解決方案的優缺點:
這是我選擇的解決方法,但以下解決方案也有效:
cy.wait(...)
“睡眠”任意數量(盡管這很脆弱cy.wait(...)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.