Визуальная регрессия: дрейф эталона или ошибка?
Проверьте сбой визуального теста на управляемых изменениях шрифта, окна и масштаба. Разделяйте допуск цвета и бюджет отличающихся пикселей до замены эталона.
BotBrowser Team
Нужна структурированная документация по теме Документация?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
Сбой теста визуальной регрессии означает, что сравнение не прошло настроенные правила. Причину он не определяет. Изменение файла шрифта, версии браузера, области просмотра или графического механизма может сдвинуть пиксели без изменения кода приложения. BotBrowser предоставляет профильные наборы шрифтов и средства управления рендерингом для согласованности между платформами, но не гарантирует одинаковые пиксели глифов на разных механизмах, не определяет правильность CSS и не выбирает допуски снимков.
Кратко
- Сравните размеры изображений и параметры рендеринга до подсчёта или принятия различий.
- Допуск цвета определяет, какие пиксели считать разными. Допустимое количество или доля определяет, сколько уже классифицированных различий можно принять.
- Управляемый запуск обычного Chrome разделил изменения шрифта, окна, масштаба и CSS. Как большой бюджет, так и большой допуск цвета приняли намеренное изменение CSS.
- Повторное совпадение показывает воспроизводимость при записанных параметрах. Оно не подтверждает правильность интерфейса, шрифта или эталона.
Содержание
- Отделить неудачное сравнение от причины
- Проверить четыре источника дрейфа
- Воспроизвести одно изменение за раз
- Прочитать результаты управляемых снимков
- Выбрать отдельно допуск цвета и бюджет
- Решить восстановить исследовать или обновить
- Вопросы о визуальной регрессии
Отделить неудачное сравнение от причины
Начните с размеров двух декодированных изображений. Снимок с удвоенной шириной и высотой содержит в четыре раза больше позиций пикселей. Прямое сравнение с маленьким изображением не даёт обычного количества различий между соответствующими позициями. Уменьшение добавляет повторную дискретизацию и новый источник отличий. Сначала запишите несовпадение размеров; вводите явную нормализацию только тогда, когда она входит в предполагаемый контракт проверки.
Затем сравните описание захвата: версию исполняемого файла, ОС, режим рендеринга, viewport в CSS-пикселях, deviceScaleFactor, доступные шрифты и ревизию приложения. Метка контейнера полезна, но изменяемая метка сама по себе не доказывает неизменность этих параметров. Храните фактическую версию браузера и идентичность файлов шрифтов рядом с эталоном. Сравнение с разными входными данными требует другого расследования, чем сравнение с одинаковыми.
Посмотрите, где расположены различия. Контуры текста указывают на возможный вопрос шрифта или текстовой раскладки; отдельный прямоугольник предполагает локальное изменение стиля или содержимого; широкие отличия могут относиться к геометрии, масштабу или общему параметру рендеринга. Это подсказки, не диагнозы. Новая надпись тоже меняет текстовые пиксели, а подмена шрифта может переместить элементы далеко за пределами глифов.
В проверках подготовки снимков уже разобраны готовность и метаданные захвата. Указывайте эти условия явно, а не считайте проверку изображения детектором готовности приложения. Playwright ждёт совпадения двух последовательных снимков и затем сравнивает последний с эталоном. Два стабильных кадра всё ещё могут содержать пустую таблицу, запасной шрифт или ошибку загрузки. Условие готовности приложения должно называть состояние, которое требуется зафиксировать.
Разделяйте в отчёте три вопроса: использованы ли сопоставимые параметры, какие области изменились и приемлем ли новый интерфейс. Первые два относятся к измерениям. Третий требует контракта приложения или проверки дизайна. Автоматическая замена эталона не отвечает ни на один: она только меняет изображение для будущих сравнений.
Проверить четыре источника дрейфа
Версия браузера и режим захвата
Закрепляйте исполняемый файл, создавший эталон, а не только версию библиотеки автоматизации. Обновление библиотеки может выбрать другой поставляемый браузер, а системный браузер способен обновляться независимо. Записывайте реальную версию во время захвата. Если эталон создан другой версией, запустите старую ревизию приложения с новым браузером до отнесения разницы к коду приложения.
Режим с окном или без окна также включайте в описание. Документация визуальных сравнений Playwright называет ОС, её версию, настройки, оборудование, источник питания и безоконный режим среди влияющих факторов. Такое предупреждение обосновывает контроль среды, но не доказывает причину конкретного сбоя. Для проверки обновления нужен парный эксперимент со старой и новой версиями. Описанный ниже управляемый запуск версии браузера не меняет.
Файлы шрифтов и состояние загрузки
Название семейства CSS является запросом, а не доказательством файла, нарисовавшего каждый глиф. Нужный шрифт может отсутствовать, ещё загружаться или не содержать определённого символа. Другой запасной шрифт меняет форму, ширину и переносы строк. Записывайте переданные тесту файлы и проверяйте характерный текст после завершения соответствующих обещаний загрузки. Ожидание шрифта не создаёт отсутствующий в нём глиф.
document.fonts.ready относится к загрузке шрифтов и раскладке документа. Оно не обещает, что всё будущее содержимое и все будущие запросы уже появились. Если приложение позднее добавляет область, дождитесь её собственного условия готовности. При намеренной смене шрифта дайте раскладке пересчитаться перед следующим снимком. Иначе сравнение старого изображения со снимком до применения нового начертания смешивает задержку с выбором шрифта.
В управлении шрифтами BotBrowser описаны профильные наборы и рендеринг без обещания одинакового результата для каждого глифа. Системные шрифты остаются важны, когда выбранный режим разрешает подстановку из среды или использует реальные шрифты ОС. Поэтому объявленный набор и политика сравнения изображений решают разные задачи; ни один из этих механизмов не отменяет другой.
Область просмотра и коэффициент масштаба
Ширина области просмотра меняет адаптивную раскладку и перенос текста. Масштаб меняет соотношение CSS-координат и пикселей изображения. Записывайте оба параметра вместо подмены ширины окна шириной снимка. Полностраничный снимок также может вырасти из-за роста документа, хотя область просмотра остаётся прежней.
Проверяйте наблюдаемые значения внутри страницы вместе с запрошенными настройками автоматизации. Эталон и текущий снимок при разной ширине CSS сначала должны получить классификацию геометрического несовпадения. Увеличение допустимого числа отличий не восстанавливает исходные условия раскладки. Для намеренно разных проектов с разными размерами сохраняйте отдельные эталоны и идентификаторы проектов.
Графический механизм и рендеринг среды
Объявленное значение GPU не доказывает, что изображение нарисовано тем же растеризатором. Программные растеризаторы и графические пути системы могут создавать разные пиксели для эквивалентных операций. В документации графического механизма Linux BotBrowser явно разделены такие механизмы и их результаты. Одной строки идентификации рендерера недостаточно для происхождения точного эталона.
Сохраняйте настройки графики, если среда их раскрывает, и не смешивайте эталоны от разных типов исполнителей. При подозрении на драйвер или механизм выполните парный эксперимент с неизменными приложением, шрифтами и геометрией. Следующий пример не меняет драйвер и не доказывает причину GPU. Его задача: показать классификацию известного изменения до расширения допуска.
Воспроизвести одно изменение за раз
Используйте собственную страницу с постоянным содержимым, двумя локальными файлами шрифтов и без внешних запросов. Создайте эталон, затем отдельные группы, меняющие только выбранный шрифт, ширину окна, коэффициент масштаба или известный цвет CSS. Каждую группу повторите в новых процессах браузера. Изменение CSS служит положительным контролем: оно показывает, что сравнение обнаруживает намеренно внесённое изменение приложения.
В запуске было пять групп по три независимых старта, всего 15 задач. Единый глобальный пул имел запрошенный предел 20 и фактический 15, поскольку задач было только 15. Перед запуском Ubuntu имела 32 ядра и достаточную свободную память; использовался существующий сервис дисплея :88. Пиковая одновременная работа составила 15 браузеров. Это ограниченный эксперимент, а не совет запускать 15 браузеров на любой машине.
Загрузите самостоятельный сценарий воспроизведения и JSON записанного запуска. Нужны Linux, Node 20 или новее, обычный Chrome, Playwright Core, Python с Pillow, два локальных TTF и существующий xvfb-88.service. Сценарий ничего не устанавливает. Укажите зависимости в четырёх переменных путей и выполните:
DISPLAY=:88 node visual-regression-baseline-drift.mjs \
--browser "$CHROME_BIN" --playwright-root "$PLAYWRIGHT_ROOT" \
--font-a "$FONT_A" --font-b "$FONT_B" \
--workers 20 --out /tmp/visual-regression-result.json
Сценарий обслуживает постоянную страницу и файлы шрифтов через локальный интерфейс, записывает группы и проверяет завершение браузерных процессов. Изображения и временные каталоги сохраняются для исследования. Режим --self-test проверяет граничные случаи сравнения до запуска браузеров. Числа ниже принадлежат переданному запуску; другие файлы шрифтов могут дать другие количества без опровержения метода.
Шаблон захвата отделяет готовность страницы от сравнения. Замените селектор приложения состоянием, которое действительно реализовано на собственной странице:
import { chromium } from 'playwright';
import { writeFile } from 'node:fs/promises';
const browser = await chromium.launch({ headless: false });
try {
const context = await browser.newContext({
viewport: { width: 1280, height: 900 },
deviceScaleFactor: 1,
});
const page = await context.newPage();
await page.goto('http://127.0.0.1:4173/fixture');
await page.locator('[data-ready="true"]').waitFor();
await page.evaluate(async () => {
await document.fonts.ready;
await new Promise(resolve =>
requestAnimationFrame(() => {
requestAnimationFrame(resolve);
})
);
});
const manifest = await page.evaluate(() => ({
viewport: [innerWidth, innerHeight],
devicePixelRatio,
fontStatus: document.fonts.status,
}));
manifest.browserVersion = browser.version();
await page.screenshot({ path: 'capture.png', animations: 'disabled' });
await writeFile('capture.json', JSON.stringify(manifest, null, 2));
} finally {
await browser.close();
}
Пример фиксирует свидетельства, но не сравнивает картинки и не определяет сервер тестовой страницы. Один снимок не восстанавливает версию браузера, переданные файлы или условие функциональной готовности. Добавляйте эти данные к эталону, а не выводите их из пикселей. Отключение анимаций устраняет названный источник движения, но не останавливает живые данные, часы и таймеры приложения.
В шрифтовой группе сохраняйте текст, ширины и браузер, меняя только выбранное начертание из переданных файлов. В геометрической группе сохраняйте содержимое и шрифты, меняя окно или масштаб. В контрольной группе CSS меняйте только известное объявление цвета. Если меняется несколько условий сразу, укажите это и назовите результат сравнением конфигураций; не представляйте его изолированным причинным эффектом.
Прочитать результаты управляемых снимков
Обычный Chrome сообщил версию 154.0.8037.57 во всех 15 задачах. Эталон имел размер 1280 × 900; три запуска каждой группы дали согласованные результаты. PNG декодировались в RGBA. Пиксель считался изменённым, когда наибольшая абсолютная разность каналов превышала выбранный целочисленный допуск. Это иллюстративное точное сравнение байтов, а не перцептивное правило pixelmatch в Playwright.
| Группа | Размер изображения | Изменённые пиксели при точном сравнении | Значение |
|---|---|---|---|
| Повторы эталона | 1280 × 900 | 0 | Управляемые параметры повторились в этом запуске. |
| Смена выбранного шрифта | 1280 × 900 | 26,962, примерно 2.34% | Отличия остались в области, связанной с текстом. |
| Смена ширины окна | 1024 × 900 | Не вычислялись | Сначала классифицировано несовпадение размеров. |
| Смена коэффициента масштаба | 2560 × 1800 | Не вычислялись | Размеры дискретизации изображения отличались. |
| Известная смена цвета CSS | 1280 × 900 | 9,600, примерно 0.83% | Отличия остались в предусмотренном цветном блоке. |
Блок CSS занимал 120 × 80 пикселей, то есть площадь 9,600 пикселей. Намеренно изменённый канал отличался на 8. При допуске канала 0 эти пиксели считались разными. Бюджет 10,000 принимал изображение несмотря на известное изменение. Увеличение иллюстративного допуска до 16 обнуляло количество различий. Числа демонстрируют два разных способа принять известное изменение, а не рекомендуемые производственные настройки.
Шрифтовая группа изменила 26,962 пикселя внутри записанной области текста, без изменений снаружи. Это поддерживает атрибуцию на намеренно ограниченной странице. Реальное приложение может существенно перестроиться при замене шрифта, поэтому ограничение областью текста не является универсальным детектором шрифтов. Контроль CSS также специально прост: изменение свойства раскладки способно затронуть почти всю страницу, а не один прямоугольник.
Каждая задача загружала только собственную страницу и локальный шрифт. Запись сохраняет наблюдаемую геометрию, готовность шрифтов, происхождение браузера и результаты. После завершения не осталось браузеров или рабочих процессов задач. Это подтверждает окончание ограниченного запуска, но не наличие тех же шрифтов или графической среды на машине читателя. Воспроизводимый сценарий и его вывод сохранены вместе с доказательствами эксперимента.
Выбрать отдельно допуск цвета и бюджет
Документированные параметры сравнения Playwright разделяют допустимую воспринимаемую разницу цвета соответствующих пикселей и общий разрешённый объём различий. maxDiffPixels задаёт допустимое количество уже классифицированных разных пикселей, а maxDiffPixelRatio задаёт долю площади изображения. Параметры действуют на разные части сравнения. Изменение одного не равно изменению другого; значения другого алгоритма также не взаимозаменяемы.
| Настройка или действие | На какой вопрос отвечает | Какую ошибку может скрыть |
|---|---|---|
| Допуск цвета одного пикселя | Достаточно ли отличается цвет для учёта пикселя? | Небольшую намеренную смену цвета важного компонента. |
| Максимальное число разных пикселей | Сколько классифицированных различий разрешено? | Пропавший значок или изменённый элемент площадью меньше бюджета. |
| Максимальная доля различий | Какая часть изображения может отличаться? | Постоянный дефект, ставший меньшей долей более длинной страницы. |
| Новое эталонное изображение | С чем сравнивать следующие запуски? | Любое случайное изменение, принятое без просмотра картинки. |
Доля зависит от площади. Одна и та же отсутствующая кнопка занимает меньшую долю длинного полностраничного снимка, чем обрезанного компонента. У численного бюджета нет такого знаменателя, но он всё равно не понимает значение области. Ни одна метрика не знает, принадлежат пиксели декоративной тени, цене или отключённой кнопке оплаты. Область интереса выбирайте в контракте теста, а не после сбоя ради его устранения.
Формируйте политику допуска по записанным повторам в требуемой среде и по известным изменениям приложения, которые должны обнаруживаться. Сохраняйте оба вида примеров. Если предложенная настройка принимает известный дефект CSS, способность подавлять безвредный шум не делает её подходящей. Изучите различия и решите, следует ли сравнивать меньший стабильный компонент, стабилизировать ещё один параметр или оставить строгую настройку.
Не переносите целочисленный допуск канала 16 в параметр перцептивной дистанции Playwright: шкала и смысл различаются. Не используйте бюджет 10,000 как образец настройки: его зафиксировали до запуска, чтобы показать поглощение известного контроля площадью 9,600 пикселей. Обоснованный бюджет связан с размерами, областью и конкретными изменениями, которые проверка обязана отклонять.
Решить восстановить исследовать или обновить
При неожиданном несовпадении размеров сначала восстановите требуемые viewport, deviceScaleFactor и область захвата. Если изменились шрифты или браузер, определите намеренность перемены среды. Исправьте случайный дрейф; при запланированном обновлении сравните прежнюю ревизию приложения в двух средах до принятия нового эталона. По возможности отделяйте миграцию среды от функционального изменения.
Если параметры совпали, но стабильная область всё ещё отличается, исследуйте ревизию приложения и зафиксированное состояние: стили, текст, данные, результаты загрузки. Разделение конфигурации и пиксельного расхождения Grid относится к атрибуции распределённых узлов. Локальный тест должен сохранять это разделение, не выдавая совпавшее описание среды за доказательство совпавшего рендеринга.
Обновляйте эталон после принятия изменённой области и записи среды генерации. Сохраняйте прежнюю картинку в истории версий, текущую разницу, причину принятия и параметры нового изображения. Команда обновления записывает результат, а не проверяет продукт. Исключение этой команды из автоматического восстановления после сбоя не даёт постоянной ошибке стать завтрашним принятым эталоном.
При намеренном обновлении браузера рассматривайте характерный текст, элементы управления и раскладку вместе с общей разницей. Маленький процент может скрывать важный локальный дефект, а большой может отражать безвредные края букв. Допуск выражает выбранную политику после понимания этих различий. Он не решает, должен ли продукт сохранять изменение.
Вопросы о визуальной регрессии
Одинаковый хеш снимка доказывает отсутствие ошибок?
Нет. Одинаковые байты доказывают равенство данного захвата. Обе картинки могут содержать один неверный шрифт, пропущенное содержимое или старые данные. Добавляйте смысловые проверки, например требуемое состояние элемента или условие загрузки данных приложения.
Все исполнители должны использовать один эталон?
Только если это задуманный контракт рендеринга и нужные параметры контролируются. Разные проекты браузеров, графические пути платформ или размеры окна могут требовать отдельных эталонов. Создание эталона для каждой случайной разницы скрывает дрейф: сначала классифицируйте отличие, затем добавляйте семейство.
Профиль заменяет записи о шрифтах и GPU?
Нет. Профильные средства описывают выбранные поверхности браузера, а изображение также зависит от реального текста и графического пути. Указывайте режим шрифтов и предположения о механизме; имя профиля не гарантирует идентичность пикселей между машинами.
Источники
- Playwright: Visual comparisons, зависимость от среды и параметры сравнения.
- Playwright: SnapshotAssertions, допуск цвета, количество и доля пикселей.
- Playwright: PageAssertions.toHaveScreenshot, последовательные снимки и анимации.
- MDN: Document.fonts, загрузка и готовность шрифтов документа.
- BotBrowser: Cross-platform profiles, объявленные параметры и границы среды.
- BotBrowser: Управление шрифтами, наборы, режимы и ограничения рендеринга.
- BotBrowser: Linux GPU backend, программные графические пути и различия пикселей.
Практический вывод
Сбой снимка требует сопоставимых размеров, записанных параметров и просмотра разницы раньше, чем расширения допуска. Разделите допуск классификации пикселя и общий бюджет; проверьте оба на известном изменении, которое должно отклоняться. Принимайте новый эталон только после проверки интерфейса и среды, создавшей изображение.
Похожие статьи
Надёжные скриншоты в headless-браузере
Начало работыКак написать воспроизводимый отчёт о браузерной ошибке
ОтпечаткиПроверка приватности в разных графических backend
СравнениеЧто меняет антидетект-браузер?
Начало работыРазбор регрессии после обновления браузера
ОтпечаткиСвойства экрана и viewport для адаптивного дизайна
Выполните ту же проверку в BotBrowser
Загрузите профиль, сохраните внутри браузера те сигналы, о которых только что прочитали, и подтвердите результат на своей машине до раскатки.