Перейти к содержанию

Проверка переходов

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

В предыдущем уроке фикстура подготовила каталог с четырьмя известными курсами. Теперь нажмём «Основы HTML» и проверим результат перехода. Полный сценарий находится в lesson-06/expected/lesson.spec.js архива. Он не обращается к опубликованному сайту и пока не выполнялся.

Ссылка и её назначение

Запись c001 содержит адрес /courses/html/. На странице этот адрес становится атрибутом href ссылки «Основы HTML». Проверка атрибута отвечает на вопрос, какой маршрут обещает документ. Но один атрибут не доказывает, что маршрут доступен и возвращает правильный курс. Поэтому мы связали его с последующим нажатием и наблюдением.

const link = catalogPage.getByRole('list', { name: 'Курсы' })
  .getByRole('link', { name: 'Основы HTML', exact: true });
await expect(link).toHaveAttribute('href', '/courses/html/');
await link.click();
await expect(catalogPage).toHaveURL(/\/courses\/html\/$/);
await expect(catalogPage.getByRole('heading', { level: 1 }))
  .toHaveText('Основы HTML');

Это основная часть теста с catalogPage из нашей фикстуры. Область списка исключает одноимённые ссылки вне результата каталога. Утверждение toHaveAttribute проверяет именно относительный путь, записанный приложением. Затем действие инициирует навигацию; ожидание URL и заголовка подтверждает состояние конечной страницы.

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

Почему заголовок проверяется отдельно

Представим, что сервер отдаёт по всем путям один и тот же документ с каталогом. URL после нажатия окажется верным, но урок HTML не откроется. Проверка одного адреса пройдёт, хотя пользовательская задача не выполнена. Заголовок страницы «Основы HTML» различает нужный документ и общую оболочку, которая могла появиться при ошибочной маршрутизации.

Обратная ситуация также возможна. Приложение показывает нужный заголовок, но остаётся на /. В одностраничном интерфейсе это может быть намеренным поведением, однако в нашей лаборатории курс имеет самостоятельный постоянный маршрут. Адрес важен для повторного открытия, передачи ссылки и кнопки возврата. Проверка сообщает, что новая реализация изменила этот контракт.

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

Механизм переходов и ожидания конечного URL описан в Playwright Navigations. Для каталога мы наблюдаем конечное состояние после действия. Не требуется добавлять произвольный sleep или предполагать, что конкретный сетевой ответ уже отрисовал страницу. Заголовок получает отдельное ожидание, потому что готовность адреса и готовность содержимого — разные сигналы.

Возврат в оглавление

На странице курса есть ссылка «Вернуться в каталог». Продолжим тот же пользовательский путь:

await catalogPage.getByRole('link', {
  name: 'Вернуться в каталог'
}).click();
await expect(catalogPage.getByTestId('results-status'))
  .toHaveText('Найдено курсов: 4');

Возврат открывает новый документ каталога. Страничный маршрут, установленный фикстурой, остаётся связан с той же страницей, поэтому повторный запрос /api/courses получает те же учебные данные. Это не новый тест и не общий контекст другого сценария. Мы продолжаем один путь и проверяем, что читатель может снова выбрать курс.

Ожидаемый результат возврата — четыре карточки. Приложение не сохраняет текст фильтра в URL или хранилище; состояние новой формы начинается с пустого запроса и всех разделов. Если позже появится сохранение выбора, потребуется изменить договорённость и добавить соответствующие проверки. Нельзя одновременно ожидать восстановленный фильтр и исходный общий каталог без уточнения продукта.

Нажатие ссылки возврата отличается от браузерной команды «Назад». При работе истории браузер может восстановить страницу и её состояние иным способом. Текущий сценарий проверяет предоставленную самим документом ссылку. Он не заявляет, что обработаны все виды возврата или сохранения страницы в истории.

Ошибки, которые различает сценарий

Неправильный href обнаружится до нажатия. Отсутствующий маршрут обнаружится по конечному заголовку или ответу, если добавить наблюдение статуса. Правильный маршрут с неверной страницей даст другой H1. Отсутствующая ссылка возврата не позволит завершить путь. Такие границы делают неудачу более конкретной, чем общее сообщение «навигация не работает».

Наш сервер выдаёт страницам курсов минимальный HTML. Это сделано намеренно: здесь важна связь карточки, URL и заголовка, а не полнота настоящего урока. При проверке ProfessorWeb потребовались бы содержание, метаданные, прежние .php-адреса и переходы учебной последовательности. Перенос того же приёма возможен, но перечень гарантий определяется отдельно.

Не следует превращать тест переходов в перебор всех ссылок с каждым возможным действием. Сначала выделяют важные маршруты и их характерные результаты. Затем для большого набора адресов удобно проверять HTTP-ответы отдельно, а несколько пользовательских путей оставлять в браузере. У браузерного сценария есть стоимость подготовки и диагностики, поэтому его применяют к смысловой цепочке взаимодействия.

Для самостоятельного изменения выберите «HTTP и API». Данные содержат /courses/http/, а конечный H1 должен совпасть с названием. Не меняйте только регулярное выражение: обновите и локатор, и ожидаемый заголовок, чтобы все части описывали один курс. Это упражнение показывает, что проверка состоит из согласованной договорённости, а не независимых строк.

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

Адрес и служебная страница

Наш сервер возвращает JSON с ошибкой для неизвестного пути. Поэтому случайная опечатка в адресе не подменяется красивой страницей курса. В настоящем проекте обработчик отсутствующего маршрута может иметь собственный H1, статус и навигацию; тогда браузерный сценарий должен отличать эту страницу от материала.

Для сохранённых URL особенно опасен успешный HTTP-статус с неправильным содержимым. Поисковый робот и человек получили документ, но прежнего урока в нём нет. Поэтому при будущем переносе приёма на большую библиотеку связывайте маршрут с характерным содержанием и принятым статусом. Наш маленький пример вводит эту связь, не заявляя, что перебрал настоящие адреса ProfessorWeb.