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

Визуальные сравнения

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

В этом уроке подготовим визуальное сравнение области main. Исходники находятся в lesson-16/expected архива, но PNG-эталона в архиве нет. Его предстоит создать и оценить отдельно в выбранном окружении. Тесты и снимки при подготовке не запускались; успешное визуальное совпадение не заявляется.

Эталон — принятое изображение

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

Наш эталон должен показывать начальный каталог: четыре курса, пустой запрос, все разделы, светлая тема, анонимная роль. Ширина 1280 и высота 800 заданы конфигурацией. Форму сообщения оставляем пустой. Изображение не относится к настоящему ProfessorWeb, поэтому оно не подтверждает сохранение его дизайна или старых страниц.

Рассмотрим источник нестабильности. Если один снимок сделан до загрузки карточек, а другой после, отличие отражает время, а не CSS. Если один контекст получил тёмную тему, изменится весь фон. Поэтому визуальная проверка должна сначала подтвердить смысловое состояние, а затем получать изображение.

Подготовка известной области

Фикстура catalogPage уже подставляет четыре записи и ждёт готовый каталог. В самом тесте добавим ожидание определения роли и проверку светлой темы:

await expect(page.locator('body'))
  .toHaveAttribute('data-session-ready', 'true');
await expect(page.getByRole('button', { name: 'Тёмная тема', exact: true }))
  .toHaveAttribute('aria-pressed', 'false');
await expect(page.getByRole('list', { name: 'Курсы' })
  .getByRole('listitem')).toHaveCount(4);

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

Не следует заменять подготовку большой паузой. Состояние нужно увидеть по договорённым признакам. Если через несколько секунд загрузка так и не закончилась, снимок ошибочной пустоты не должен становиться новым эталоном. Отдельные утверждения о состоянии помогают остановиться раньше и объяснить, почему визуальное сравнение ещё не имеет смысла.

Снимок главного содержимого

Основное утверждение выглядит так:

await expect(page.getByRole('main')).toHaveScreenshot('catalog-main.png', {
  animations: 'disabled',
  caret: 'hide',
  maxDiffPixels: 0
});

Playwright выбирает область элемента main, поэтому хедер и футер не входят в изображение. Имя catalog-main.png обозначает эталон этой области. В первом уроке визуального сравнения используем нулевой допуск, чтобы не скрывать неопределённые отличия числом. Это учебное решение, а не универсальная настройка любого приложения.

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

Метод и управление эталонами описаны в Visual comparisons. Для этой лаборатории важна последовательность: известная страница, подтверждённое состояние, снимок выбранной области, сравнение с отдельно одобренным изображением. Отсутствующий эталон не является автоматически успешной проверкой.

Первый будущий эталон

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

npx --no-install playwright test tests/lesson.spec.js --update-snapshots

Команда предназначена для будущего запуска в отдельной папке лаборатории. Она изменяет ожидаемые снимки, поэтому нельзя использовать её автоматически при любой неудаче. После создания необходимо открыть PNG и убедиться, что оно представляет выбранную договорённость. В текущем архиве команда не выполнялась, изображения и отчёт отсутствуют.

Следующий обычный запуск без --update-snapshots будет сравнивать фактическое изображение с этим эталоном. Если снимки отличаются, сначала смотрят характер различия: изменение данных, темы, размера, шрифта или CSS. Обновление ожидаемого файла принимается только после понимания причины, иначе регрессия превращается в новый стандарт одним флагом.

Окружение и осмысленный допуск

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

Закреплённый Playwright также связывает сценарий с версией браузерных бинарников, устанавливаемых им. Использование другого системного Chromium вместо учебного изменит условия. Смена версии инструмента — повод отдельно оценить новые снимки, а не предполагать, что старый эталон обязательно применим без разбора.

Для некоторых проектов небольшой допуск оправдан разницей пикселей без смыслового изменения. Тогда его выбирают после анализа конкретного источника. Число, позволяющее пропустить исчезновение карточки, уже не защищает выбранную задачу. Наш maxDiffPixels:0 сначала делает все различия видимыми; дальнейшая настройка требует объяснения, что именно считается допустимым.

Изображение и поведение дополняют друг друга

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

Обратная ситуация тоже важна: правильные данные не гарантируют читаемость. Четыре карточки могут присутствовать в DOM и совпадать по тексту, но оказаться визуально перекрытыми. Снимок выбранной области даст дополнительный сигнал, который нужно интерпретировать вместе с утверждениями о состоянии. Ни один тип проверки не следует выдавать за все свойства сайта.

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

Название эталона следует связывать с областью и состоянием. catalog-main.png показывает основной каталог, а не весь сайт и не все его страницы. Если позже добавится снимок пустого поиска, ему потребуется отдельное имя и подготовка. Перезаписывать исходный эталон изображением другого состояния означало бы потерять различие двух договорённостей.

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