Драматург не может выйти из системы и войти в нее как новый пользователь в течение одного теста

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

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

Добавление afterEach, которое полностью закрывает браузер между тестами, казалось решением этой проблемы для тестов с одним входом в систему, поскольку это очистило бы все файлы cookie, и я смог перейти ко второму тесту, войдя в систему как второй пользователь, однако это быстро снова не удалось, так как я не закрывал браузер между различными входами в систему между отдельными этапами тестирования.

Проблема в том, что я не могу найти способ управлять браузером/ссылаться на него или открывать несколько браузеров в тестовой модели, поскольку экземпляр браузера создается позже в тестовых перехватчиках. Я хотел бы найти способ закрыть и снова открыть окно браузера между шагами, в идеале включив это в мои функции входа/выхода из системы ИЛИ, возможно, я пропускаю какой-то шаг при выходе из системы, чтобы правильно очистить сеанс, а затем войдите снова под новым пользователем

Ниже приведены мои перехватчики, а также то, что xstate использует для запуска тестов и находится в нижней части моего скрипта.

test.beforeEach(async ({ browser }) => {
  page = await browser.newPage();
  await page.goto(URL);
  testNum++
});

test.afterEach(async ({browser}) => {
  for(let context of browser.contexts())
    await context.close()
});

test.describe('CE workflows', () => {
  const testPlans = testModel.getSimplePathPlans();

  testPlans.forEach((plan, i) => {
    plan.paths.forEach((path, i) => {
      test(path.description, async ({}) => {
        await path.test(page);
      });
    });
  });
})

Это функции входа/выхода, которыми я завершаю каждый этап теста.

async function login(email) {
  await page.goto(URL);
  await page.getByTestId('loginEmail').fill(email);
  await page.getByTestId('loginPassword').fill('password');
  await page.getByTestId('btnLogin').click();

  if (await page.getByRole('button', { name: 'Allow cookies' }).isVisible())
  await page.getByRole('button', { name: 'No thanks' }).click();
}

async function logout() {
  await page.getByTestId('btnOptions').click();
  await page.getByRole('button', { name: 'Logout' }).click();
}

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

const testModel = createModel(CEWorkflowMachine, {
  events: {
    CREATE_CE_CLIENT: async () => {
      await login()

      await page.goto(URL);
      await page.getByRole('link', { name: 'Create compensation event' }).click();

      await page.getByLabel('Title').fill(`Test for model based CE ${testNum}`);
      await page.getByLabel('Description').fill('Test Description');
      await page.getByLabel('Project manager\'s assumptions').fill('Test Assumption');
      await page.getByText('The Project Manager gives an').click();

      await page.getByText('Notify').click();

      await logout()
    },
page = await browser.newPage();. Где определяется страница? Есть ли у него декларация let? Если этого не происходит, это установка глобального значения, которое вызывает проблемы.
adsy 23.05.2024 17:36

Пожалуйста, удалите одиночные обратные кавычки из вашего кода.

ggorlen 23.05.2024 18:35
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
2
163
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Запутанная изоляция тестов

В настоящее время вы делаете это для управления страницами браузера:

test.beforeEach(async ({ browser }) => {
  page = await browser.newPage();
  await page.goto(URL);
  testNum++
})

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

Даже если он не глобальный, он требует проблем с сохранением изменяемой ссылки. У вас возникнут проблемы, если у вас есть распараллеливание в файле или вы хотите добавить его позже. Добавьте к этому, что вы можете случайно сослаться на эту переменную; или закрыть его застежкой; и учтите, что многие вещи асинхронны, и вы можете сделать вывод, что управлять этим очень рискованно и адски. Это очень необычно, и вы не увидите этого ни в одной официальной документации.

Хотя возможно иметь это и прекрасно управлять этим, вы открываете себя для целой категории ошибок, которые теперь возможны. page привязан к контексту браузера, и именно эту конструкцию предлагает драматург для изоляции тестов. Если у вас вообще есть шанс повторно использовать его между тестами, это проблема.

Я сильно подозреваю, что это вероятно в вашем случае. Конечные автоматы «закрывают» установленную вами переменную page. Они вполне могут по-прежнему ссылаться на эту старую ссылку на объект page, даже если вы переназначите page.

Вам следует полностью отказаться от этой широко распространенной page var. Вместо этого убедитесь, что вы используете page, передаваемый обработчикам/хукам тестов:

test.beforeEach(async ({ page }) => {
  await page.goto(URL);
});

test.describe('CE workflows', () => {
  const testPlans = testModel.getSimplePathPlans();

  testPlans.forEach((plan, i) => {
    plan.paths.forEach((path, i) => {
      test(path.description, async ({page}) => {
        await path.test(page);
      });
    });
  });
})`

Вам не потребуется закрытие браузера вручную, если ваши тесты правильно изолированы. Поэтому я удалил это. page, предоставленный методам test (и хукам), уже был автоматически включен в соответствующий тест. Убедитесь, что это передается во все общие методы, и вы нигде не используете глобальный или широкомасштабный метод page .

Это означает, что ваши конечные автоматы должны быть предоставлены page. Вам действительно нужно использовать page, который был передан машине (которому теперь передается правильный из test()). Обратите внимание: это сделано в официальной документации.

const testModel = createModel(CEWorkflowMachine, {
  events: {
    CREATE_CE_CLIENT: async (page) => {
      await login(page)

      await page.goto(URL);
      await page.getByRole('link', { name: 'Create compensation event' }).click();

      await page.getByLabel('Title').fill(`Test for model based CE ${testNum}`);
      await page.getByLabel('Description').fill('Test Description');
      await page.getByLabel('Project manager\'s assumptions').fill('Test Assumption');
      await page.getByText('The Project Manager gives an').click();

      await page.getByText('Notify').click();

      await logout(page);
    }

И, конечно же, эти методы входа/выхода должны принимать page в качестве параметра и использовать его.

Звонить browser.newPage не нужно, так как предоставленный на тесты/хуки уже свежий.

Выход из системы не ждет

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

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

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

Вы можете исправить это, попросив Драматурга подождать.

await Promise.all([
  page.waitForNavigation(),
  page.getByRole('button', { name: 'Logout' }).click()
])

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

Спасибо за ваш ответ, похоже, это правильное решение моей проблемы, я исправил функцию выхода из системы, чтобы без проблем ждать навигации. Я настроил каждый из них так, как вы (думаю, я изначально скопировал это из сообщения на форуме) и изменил свою модель так, чтобы она была точно такой же, как в документации, однако все мои ссылки на страницу в пределах шага выдают ошибку, что это имеет тип НЕИЗВЕСТНО, у меня есть объявление let page: Page вверху страницы, но это неправильно, как вы упомянули, мне следует избегать объявления его глобально, где мне следует его определить вместо этого

sam evans 24.05.2024 09:49

Да, очень важно избавиться от этой переменной. В этом примере xstate.js.org/docs/packages/xstate-test (кукловод, а не драматург, но это не имеет значения) перейдите к шагам 3/4. Xstate предоставляет тот, который был передан в path.test, для обратных вызовов повсюду. Жалоба unknown вызвана тем, что вам нужно будет передать тип arg. На самом деле это будет там, и я думаю, это будет правильно, это просто проблема с набором текста. Возможно, попробуйте createMachine<Page> где Пейдж импортирует тип TS из драматурга?

adsy 24.05.2024 11:15

Или вы можете просто попробовать ввести его, назначив тип прямо в параметре обратного вызова. Честно говоря, я менее уверен в этой части. Я не уверен, как это работает с тестовой библиотекой xstate. Требует большего чтения. Может ошибаться насчет типа параметра.

adsy 24.05.2024 11:17

Хорошо, это подробно здесь xstate.js.org/docs/packages/xstate-test/#api. См. «ТестКонтекст». Хотя примеров TS нет. Думаю, вы могли бы просто ввести его, назначив тип непосредственно параметру.

adsy 24.05.2024 11:24

Да, вы правы, при запуске он просто игнорирует ошибки типа и все работает как положено. Интересно, что после некоторого расследования выяснилось, что на самом деле и функция входа, и функция выхода из системы все портят: с помощью шагов «Вход -> перейти к URL-адресу» новый URL-адрес загружается до того, как вход в систему сможет завершить аутентификацию нового пользователя и поэтому по умолчанию используется предыдущий, и выход из системы, как вы сказали, делал то же самое. Спасибо за вашу помощь!

sam evans 24.05.2024 16:27

Однако этого не должно быть. Это признак того, что что-то не так. Предполагается, что все отдельные тесты имеют собственную страницу, которая, как гарантирует драматург, имеет собственный контекст браузера. Контексты браузера не используют файлы cookie и т. д. Это необходимо для обеспечения изоляции тестов. Тот факт, что тест может получить сеанс «предыдущего» пользователя, предположительно, который вошел в систему во время предыдущего теста, доказывает, что ваши тесты не изолированы должным образом. Вы можете исправить код выхода/входа в систему для ложной изоляции, но это не устранит основную причину, которая будет преследовать вас. Ошибка, которую вы видите, не должна быть возможна даже в условиях изоляции.

adsy 25.05.2024 23:19

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

sam evans 28.05.2024 09:32

Ах! Да, это имеет смысл. Честно говоря, вам следует использовать отдельные тесты и сохранять изоляцию. В E2E также сохраняется ситуация, когда взаимозависимые утверждения, в которых некоторая концептуальная группировка зависит от предыдущего, подвержены ошибкам. По сути, такой подход приводит к высокой сложности и нестабильности. Вы больше не получите хороших отзывов, когда они терпят неудачу, потому что многие терпят неудачу одновременно. Возьмите это у кого-то, кто создал слишком много корпоративных решений e2e (мне скучно, лол). Эта концепция сложнее для e2e, но достижима, и вам следует ее потребовать. В противном случае он не масштабируется.

adsy 09.06.2024 12:37

Вот почему разделение тестов в драматурге также сопровождается всеми этими песочницами и контекстом. Это задумано, и этого нельзя избегать. Для блага вашего здравомыслия через несколько лет вам следует принять его. Если необходимо, используйте API-интерфейсы драматурга, чтобы явно делиться определенными вещами, например, файлами cookie аутентификации (это важно, в отличие от подразумеваемых двусмысленных отношений). Когда он растет органично и неконтролируемо, без изоляции, вы получаете пакет, который проходит только тогда, когда выполнение попадает на очень узкий путь кода, а в противном случае просто без разбора брызгает красным.

adsy 09.06.2024 12:39

Тогда вы в конечном итоге потратите больше времени на их обслуживание, чем на получение от них реальной пользы.

adsy 09.06.2024 12:44

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