Cypress - перезагрузить страницу, пока элемент не будет найден

Я пытаюсь использовать cypress-wait-until для простого случая. https://github.com/NoriSte/cypress-wait-until

  1. Посетите страницу
  2. Проверить, есть ли здесь элемент
  3. Если нет, перезагрузите страницу, пока этот элемент не будет найден.
  4. После того, как найдено, подтвердите, что элемент существует

Рабочий код (кипарис-ждите-пока не используется)

before(() => {
    cy.visit('http://localhost:8080/en/registration');
});

describe('Foo', () => {
  it('should check that registration button is displayed', () => {
    const selector = 'button[data-test=startRegistration-individual-button]';
    cy.get(selector).should('exist');
  });
});

Не работает, время повторной попытки истекло

before(() => {
    cy.visit('http://localhost:8080/en/registration');
});

describe('Foo', () => {
  it('should check that registration button is displayed', () => {
    const options = { timeout: 8000, interval: 4000 };
    const selector = 'button[data-test=startRegistration-individual-button]';
    cy.waitUntil(() => cy.reload().then(() => Cypress.$(selector).length), options);
    cy.get(selector).should('exist');
  });
});

Не работает, см. ошибку ниже

before(() => {
    cy.visit('http://localhost:8080/en/registration');
});

describe('Foo', () => {
  it('should check that registration button is displayed', () => {
    const options = { timeout: 8000, interval: 4000 };
    const selector = 'button[data-test=startRegistration-individual-button]';
    cy.waitUntil(() => {
        cy.reload();
        return Cypress.$(selector).length;
      }, options);

    cy.get(selector).should('exist');
});

Для двух версий не работает, как только я удаляю cy.reload(), он начинает работать.

Вопрос

Что я могу сделать, чтобы он работал с перезагрузкой?

РЕДАКТИРОВАТЬ

Эта команда, которую я написал, работает правильно.

Cypress.Commands.add('refreshUntil', (selector: string, opts?: { retries: number; waitAfterRefresh: number }) => {
  const defaultOptions = {
    retries: 10,
    waitAfterRefresh: 2500,
  };

  const options = { ...defaultOptions, ...opts };

  function check(selector: string): any {
    if (Cypress.$(selector).length) { // Element is there
      return true;
    }
    if (options.retries === 0) {
      throw Error(`${selector} not found`);
    }
    options.retries -= 1;
    cy.log(`Element ${selector} not found. Remaining attempts: ${options.retries}`);
    cy.reload();
    // Wait a some time for the server to respond
    return cy.wait(options.waitAfterRefresh).then(() => check(selector));
  }

  check(parsedSelector);
});

Я мог видеть две разности потенциалов с waitUntil от cypress-wait-until

  1. Cypress.$(selector).length будет новым при каждой попытке
  2. После перезагрузки проходит wait время, прежде чем снова проверить, есть ли элемент.

РЕДАКТИРОВАТЬ 2

Вот рабочее решение с использованием cypress-wait-until

cy.waitUntil(() => cy.reload().wait(2500).then(() => Cypress.$(selector).length), options);
cypress-wait-until работает у меня с example.com. Тот же обратный вызов, но изменение селектора с h1 (успешно) на h2 (неудачно). Такой ошибки не возникает.
A.A.Qadosh 04.02.2023 05:08

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

Léo Coco 04.02.2023 06:03

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

DJSDev 04.02.2023 06:35

Первоначальная загрузка приложения может занять ~5 секунд, затем отображается таблица с элементами из базы данных. Если элемент уже есть в базе данных, он будет отображаться, иначе нет. Затем мне нужно обновить страницу и дождаться повторной загрузки данных, прежде чем проверять, есть ли они. и т.д.

Léo Coco 04.02.2023 06:47

Я детерминирован, чтобы добраться до сути - можете ли вы заглушить вызов API, чтобы издеваться над базой данных?

A.A.Qadosh 04.02.2023 08:02

По крайней мере, формат cy.waitUntil() выглядит нормально — предположим, что ошибка только для последнего неработающего блока кода?

A.A.Qadosh 04.02.2023 08:04

Это правильно, только пример дает ошибку. Я также отредактирую свой вопрос, поскольку я написал свою собственную функцию, которая работает для меня.

Léo Coco 04.02.2023 16:15

@A.A.Qadosh, нашел... Ключом было ожидание после перезагрузки. Спасибо

Léo Coco 04.02.2023 17:05
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
8
97
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вот рабочее решение с использованием кипарисового ожидания

cy.waitUntil(() => cy.reload().wait(2500).then(() => Cypress.$(selector).length), options);

Правила Cypress применяются как внутри cy.waitUntil(), так и снаружи, поэтому .wait(2500) было бы плохой практикой.

Было бы лучше изменить вашу неповторную команду Cypress.$(selector).length на правильную команду повторной попытки Cypress. Таким образом, вы получаете 4 секунды (по умолчанию) на повторную попытку, но ждете столько, сколько необходимо.

В частности, поскольку cy.waitUntil() повторяется n раз, вы ждете (тратите впустую) много секунд.

cy.waitUntil(() => { cy.reload(); cy.get(selector) }, options)  
// NB retry period for `cy.get()` is > 2.5 seconds

Я не уверен, что то, что вы предлагаете, сработает. diogonunes.com/blog/cypress-tips-tricks/#waits

Léo Coco 06.02.2023 21:41

Если мистер Гекко прав, да, я согласен. Но его пример также указывает на то, что ваш исходный код должен был работать, и по логике .wait(2500) не должен быть нужен, так как cy.waitUntil() имеет встроенное ожидание — и имеет опции для тайм-аута и увеличения интервала.

Mordecai.S 06.02.2023 22:29
Ответ принят как подходящий

В итоге я написал свой собственный метод (вдохновленный cypress-wait-until) без необходимости долго ждать.

/**
 * Run a check, and then refresh wait until an element is displayed.
 * Retries for a specified amount of time.
 *
 * @function
 * @param {function} firstCheckFunction - The function to run before checking if the element is displayed.
 * @param {string|{ selector: string, type: string }} selector - The selector to search for. Can be a string or an object with selector and type properties.
 * @param {WaitUntilOptions} [opts = {timeout: 5000, interval: 500}] - The options object, with timeout and interval properties.
 * @throws {Error} if the firstWaitFunction parameter is not a function.
 * @throws {Error} if the specified element is not found after all retries.
 * @example
 * cy.refreshUntilDisplayed('#element-id', () => {...});
 * cy.refreshUntilDisplayed({ selector: 'element-id', type: 'div' }, () => {...});
 */
Cypress.Commands.add('waitFirstRefreshUntilDisplayed', (firstCheckFunction, selector: string | { selector: string, type: string }, opts = {}) => {
  if (!(firstCheckFunction instanceof Function)) {
    throw new Error(`\`firstCheckFunction\` parameter should be a function. Found: ${firstCheckFunction}`);
  }

  let parsedSelector = '';

  // Define the default options for the underlying `cy.wait` command
  const defaultOptions = {
    timeout: 5000,
    interval: 500,
  };

  const options = { ...defaultOptions, ...opts };

  // Calculate the number of retries to wait for the element to be displayed
  let retries = Math.floor(options.timeout / options.interval);
  const totalRetries = retries;

  if (typeof selector === 'string') {
    parsedSelector = selector;
  }

  if (typeof selector !== 'string' && selector.selector && selector.type) {
    parsedSelector = `${selector.type}[data-test=${selector.selector}]`;
  }

  // Define the check function that will be called recursively until the element is displayed
  function check(selector: string): boolean {
    if (Cypress.$(selector).length) { // Element exists
      return true;
    }
    if (retries < 1) {
      throw Error(`${selector} not found`);
    }

    if (totalRetries !== retries) { // we don't reload first time
      cy.log(`Element ${parsedSelector} not found. ${retries} left`);
      cy.reload();
    }

    // Waits for the firstCheckFunction to return true,
    // then pause for the time define in options.interval
    // and call recursively the check function
    cy.waitUntil(firstCheckFunction, options).then(() => { // We first for firstCheckFunction to be true
      cy.wait(options.interval).then(() => { // Then we loop until the selector is displayed
        retries -= 1;
        return check(selector);
      });
    });
    return false;
  }

  check(parsedSelector);
});

Другие вопросы по теме