簡體   English   中英

如何將 cypress 與預取或預加載 webpack 的組件一起使用?

[英]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
        })
    )
})

以下是我采用的解決方案的優缺點:

  • 優點:當用戶可能永遠不會遇到這個問題時,無需增加包大小或修改客戶端代碼
  • 缺點:技術上仍然可能存在競爭條件,即點擊事件發生在資源下載之后,但在它們都可以執行和呈現其內容之前,但不太可能,不如等待 UI 本身指示何時有效准備好了

這是我選擇的解決方法,但以下解決方案也有效:

  • 創建輕量級占位符組件以在下載時代替異步組件,並讓 cypress 等待實際組件呈現(例如,當實際模式在后台下載時,只顯示一個微調器的默認模式)
    • 優點:不必等待網絡資源,如果實施得當,可以避免所有競爭條件
    • 缺點:必須創建一個用戶可能永遠不會看到的組件,增加了包的大小
  • 使用cy.wait(...) “睡眠”任意數量(盡管這很脆弱cy.wait(...)
    • 優點:易於實施
    • 缺點:脆弱,不建議 Cypress 直接使用它,如果使用eslint-plugin-cypress會導致linter問題(你可以在你使用它的線路上禁用 eslint,但它對我來說“感覺很丑”(不討厭)任何以這種方式編程的人)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM