ИИ-визуальное регрессионное тестирование
CSS-обновление в компоненте Button сдвигает отступы на 2px — этого достаточно, чтобы сломать раскладку таблицы на странице отчётов, которую никто не проверял вручную перед деплоем. Классический pixel-diff (Percy, Chromatic) поймает это изменение, но даст 200 ложных алармов на каждый реальный баг из-за антиалиасинга, шрифтовых хинтов и динамического контента. ИИ-подход отделяет значимые регрессии от шума.
Почему pixel-diff недостаточно
Попиксельное сравнение скриншотов — наивный baseline. Проблемы:
- Антиалиасинг — один пиксель на краях текста и иконок отличается между рендерами, даже если визуально идентично
- Динамический контент — дата, счётчики, аватары — всегда разные
- Субпиксельные сдвиги — шрифтовые хинты на разных OS дают 1–2px сдвиг текста
- Анимации — скриншот в разный момент анимации = разный пиксельный результат
Результат: specificity pixel-diff — около 30–40% в реальных проектах. Команда устаёт от ложных алармов и начинает игнорировать алерты. Реальная регрессия проходит незамеченной.
ИИ-подход: perception-based сравнение
Суть: сравниваем не пиксели, а семантические features — как это воспринимает человек. Embedding скриншотов через vision transformer (CLIP ViT-B/16 или DinoV2) + cosine distance вместо pixel difference.
import torch
import clip
from PIL import Image
import numpy as np
model, preprocess = clip.load("ViT-B/16", device="cuda")
def get_screenshot_embedding(screenshot_path):
image = preprocess(Image.open(screenshot_path)).unsqueeze(0).cuda()
with torch.no_grad():
embedding = model.encode_image(image)
return embedding / embedding.norm(dim=-1, keepdim=True)
def visual_regression_score(baseline, current):
emb_base = get_screenshot_embedding(baseline)
emb_curr = get_screenshot_embedding(current)
# 1.0 = идентично, < 0.95 = заметное отличие
return (emb_base * emb_curr).sum().item()
Но global embedding теряет локальные изменения — маленький баг в footer'е при большом идентичном header'е не виден в общем скоре. Решение: patch-based сравнение — делим скриншот на патчи 64×64, сравниваем embeddings попатчно, получаем heatmap изменений.
Локализация регрессий через saliency heatmap
Пайплайн:
- Разбивка скриншотов baseline и current на N×M патчей
- DinoV2-embeddings каждого патча (patch size 14×14, ViT-S/14)
- Cosine distance между соответствующими патчами → матрица различий
- Threshold + морфологические операции → bounding boxes регрессий
- Классификатор «значимая регрессия / шум» на признаках: размер зоны, глубина изменения, тип компонента
Classifier обучен на 2 000 аннотированных парах скриншотов (реальные баги + шум). XGBoost на признаках патча: precision 0.87, recall 0.91 для значимых регрессий.
Integration в CI/CD
# GitHub Actions пример
- name: Visual Regression Tests
run: |
# Playwright делает скриншоты
npx playwright test --project=chromium visual-regression.spec.ts
# Python скрипт запускает ИИ-сравнение
python ai_visual_diff.py \
--baseline ./baseline-screenshots \
--current ./current-screenshots \
--threshold 0.93 \
--output ./regression-report.json
При обнаружении регрессии: автоматическая аннотация PR с heatmap-изображениями изменённых зон и confidence score. Разработчик видит конкретные компоненты, которые изменились визуально.
Baseline management
Базовые скриншоты — не просто файлы, это версионированные артефакты. Стратегия хранения: S3 с версионированием, автоматическое обновление baseline после approved merge в main. Storybook integration: скриншоты компонентов во всех состояниях (hover, focus, disabled, error).
Сроки
Базовая система (скриншоты + ИИ-сравнение + CI-интеграция): 3–5 недель. Полная платформа с компонентным тестированием и baseline management: 6–10 недель. Стоимость рассчитывается индивидуально.







