Разделы портала

Онлайн-тренинги

.
Ожидание загрузки страницы в Cypress
27.12.2023 00:00

Автор: Филип Рик (Filip Hric)
Оригинал статьи: https://filiphric.com/how-to-wait-for-page-to-load-in-cypress
Перевод: Ольга Алифанова

Cypress-тест может быть очень быстрым – иногда даже быстрее тестируемого приложения. Если вы оказались в ситуации, когда Cypress работает быстрее, чем загружается ваше приложение, то эта статья для вас.

Событие загрузки страницы

Если вы нашли эту статью через Google, то, возможно, приступили к поиску, так как увидели сообщение «Завершено по таймауту после ожидания загрузки вашей удаленной страницы в течение 60000 мс».

 

Это сообщение появляется, когда страница не вызывает событие загрузки. Но что же это такое?

Согласно документации MDN, событие загрузки – это нечто, вызываемое браузером после того, как он загрузил все ресурсы страницы.

Что же это значит?

Когда вы вводите URL в браузер и нажимаете Enter, браузер выполняет GET-запрос API к этому адресу. Это похоже на тестирование API через Postman или Cypress. Вместо получения JSON-структурированного объекта вам вернется HTML-документ.

В упрощенном виде возвращенный документ будет выглядеть как-то так:

1  <html>
2  <head>
3    <title>My page</title>
4    <link rel="stylesheet" href="/style.css">
5    <script src="/app.js" defer></script>
6  </head>
7  <body>
8    Hello world!
9  </body>
10  </html>

Браузер посмотрит на этот документ и запросит все связанные с ним файлы. Заметьте, что у нас есть файлы style.css и app.js. Браузер загрузит их, и как только это сделано, вызовет событие загрузки, которого ожидает Cypress.

Это в целом наилучший момент для определения готовности приложения. В реальности у многих сайтов есть файлы dynamic.js, которые начинают вызывать API загрузки ресурсов с сервера или анимировать элементы на странице. В результате страница все еще находится «в процессе загрузки». Невозможно предсказать, сколько ресурсов загрузят эти .js-файлы, и сколько времени это займет. Событие загрузки – хорошая опорная точка, но в случае с современными веб-приложениями она редко бывает последним, что со страницей произошло.

Если вы получаете ошибку Cypress о невозможности загрузки страницы, то с шансами скачивание не состоялось, или же что-то его заблокировало. Возможно, проблема на вашей стороне – тогда рассматривайте ее как баг, нуждающийся в исправлении.

Недавно я видел вышеупомянутую ошибку на демо-сайте Sauce Labs. Ее вызывала конфигурация вспомогательного сервиса, и в результате событие загрузки не происходило вообще. Так бывает не со всеми вспомогательными сервисами, но мне пришлось повозиться. В результате пришлось сделать заглушку для API-вызова от этого сервиса, по сути отключая его. Я добавил прерывание и перезагрузил браузер.

1  cy.intercept('/service-worker.js', {
2   body: undefined
3  })

Ожидание сетевых вызовов

Еще один способ убедиться, что страница загружена – это дождаться сетевого вызова. К примеру, ваше приложение ведет список дел и загружает все задачи после открытия. Можно использовать перехват сетевого вызова, который будет ожидать осуществления этого вызова:

1  it('testing todos', () => {
2    cy.intercept('/todos').as('todos')
3    cy.visit('/')
4    cy.wait('@todos')
5    // page is loaded, continue with the test
6  })

Так как cy.wait() будет ждать не только осуществления запроса, но и ответа, поэтому это надежный способ убедиться, что страница правильно загружена.

Если у вас несколько вызовов API, можно перехватить все и заставить тест ожидать их осуществления, передавая массив псевдонимов в команду cy.wait():

1  it('testing todos', () => {
2    cy.intercept('/todos').as('todos')
3    cy.intercept('/profile').as('profile')
4    cy.visit('/')
5    cy.wait(['@todos', '@profile'])
6    // page is loaded, continue with the test
7  })

Другой способ ожидания нескольких запросов – применить синтаксис cy.get('@alias.all'). Так можно дожидаться всех запросов.

1  it('testing todos', () => {
2    cy.intercept('**').as('requests')
3    cy.visit('/')
4    cy.get('@requests.all')
5      .should('have.length', 10)
6    // all 10 requests have been sent
7  })

Тут, однако, есть две маленьких хитрости. cy.get() будет ожидать запроса лишь в течение 4000 мс, так как он слушает опцию defaultCommandTimeout, а не responseTimeout, как в случае с cy.wait(). Если ваши запросы занимают больше времени, вам нужно изменить таймаут для команды cy.get(). К тому же cy.get() будет ожидать только срабатывания запроса, а ответа ждать не будет. С этим нам может помочь встроенный в Cypress механизм повторных попыток – мы можем проверять код статуса последнего запроса, ссылаясь на него:

1  it('testing todos', () => {
2    cy.intercept('**').as('requests')
3    cy.visit('/')
4    // set longer timeout
5    cy.get('@requests.all', { timeout: 30000 })
6      .should('have.length', 10)
7      .its('9.response.statusCode') // check last request
8      .should('eq', 200)
9  })

Ожидание DOM

Еще один способ убедиться, что приложение полностью загружено – это взглянуть на DOM. Таким образом мы проверим, что не делаем ничего такого, чего не ожидаем от настоящего пользователя.

К примеру, в реальности вы, скорее всего, не будете пытаться взаимодействовать с приложением, если анимация загрузки все еще перекрывает страницу. Можно добавить в тест охранное условие, проверяющее, что анимация исчезла, до начала взаимодействия со страницей.

1  it('testing todos', () => {
2    cy.visit('/')
3    // page loading, wait for loader to disappear
4    cy.get('.loader')
5      .should('not.exist')
6
7    // continue with our test
8  })

Негативные проверки могут быть мудреными, и в зависимости от скорости страницы или специфики работы аниматора .loader может понадобиться добавить .should('be.visible') до проверки отсутствия существования элемента.

Перевернем с ног на голову – можно просто воспользоваться cy.get() для первого элемента, с которым мы собираемя взаимодействовать. Если он появляется дольше, можно добавить таймаут. Про таймауты и ожидания можно прочитать в моих прошлых статьях.

Таймаут по умолчанию

Иногда простота – лучшее ожидание. Каждый cy.visit() будет ждать события загрузки в течение 60 секунд. Это довольно долго, потому что у реальных пользователей терпение закончится раньше. Если команда cy.visit() упадет, возможно, стоит исправлять само приложение, используя этот таймаут в качестве метрики производительности. Как вариант, можно уменьшить его, изменив опцию pageLoadTimeout в настройках. О различных таймаутах Cypress можно прочитать в его документации.

Обсудить в форуме