Skip to content

Снапшоты вычисленных стилей в test-runner - #129

Merged
ichiesov merged 9 commits into
storybook-test-runnerfrom
storybook-style-snapshots
Aug 27, 2026
Merged

Снапшоты вычисленных стилей в test-runner#129
ichiesov merged 9 commits into
storybook-test-runnerfrom
storybook-style-snapshots

Conversation

@khaliulin

@khaliulin khaliulin commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Стекнут на #127 — база storybook-test-runner, без неё раннера в проекте нет.

Что

Хук postVisit в .storybook/test-runner.ts снимает у каждой story вычисленные стили и сверяет с baseline в .storybook/style-baselines/<storyId>.json.

Слепок берётся с элементов, размеченных PrimeNG (data-pc-section) — это стабильная семантическая карта компонента, покрывающая и внутренние части (inputtext/root, pcfilter/root, button/label). Отслеживается 21 свойство: паддинги, height/min-height, типографика, радиусы, толщины границ, gap, color/background-color/border-top-color.

Пиксельных скриншотов нет намеренно: дифф читается как pcfilter/root · height: 40px → 44px, а не «13 пикселей отличаются», и baseline не привязан к шрифтам, сглаживанию и ОС — переносится между macOS и CI без docker.

Как пользоваться

npm run storybook
npm run test-storybook                              # проверка
UPDATE_STYLE_BASELINES=1 npm run test-storybook     # принять изменения токенов

Baseline в репозиторий не кладётся — как пиксельные снимки (#131) и долг axe (#130): каталог под игнором, первый прогон на чистой копии снимает слепок сам, дальше сверяется с ним. В Files changed четыре файла: .gitignore, .storybook/test-runner.ts, package.json, package-lock.json.

Стабилизация

Первый прогон ловил ложные срабатывания на гонках загрузки:

  • galleria/thumbnails: height: 0px → 97.59px — картинки превью;
  • listbox/filter: height: 40px → 44px — иконочный веб-шрифт.

Поэтому перед съёмкой хук ждёт document.fonts.ready, догрузку всех <img> и два кадра requestAnimationFrame.

Проверка

Baseline сгенерирован (262 story), затем два прогона подряд без расхождений:

Test Suites: 1 failed, 51 passed · Tests: 1 failed, 263 passed · 40 s

Единственное падение — password.stories.ts, тот самый баг шаблона; исправляется в #128. После его вмержа baseline для Password добавится обычным прогоном.

Прогон подорожал с 26 с до 40 с.

Что это ловит

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

Проверка после выноса baseline: прогон components/tag на чистой копии — 4 passed, 4 written, содержимое совпало с прежними файлами из репозитория; повторный — 4 passed; подменил padding-top в снимке — 1 failed с [light] tag/root · padding-top: 99px → 4px.

@khaliulin

khaliulin commented Aug 26, 2026

Copy link
Copy Markdown
Contributor Author

Добавил два варианта съёмки поверх исходного — коммит 269a68e (снимать стили в вариантах light, dark и mobile).

Что изменилось

Каждая story снимается трижды в рамках одного визита: light (десктоп), dark (класс dark на <html> — тот же селектор, что у withThemeByClassName и darkModeSelector в preview.ts) и mobile (вьюпорт 390×844). Отдельных прогонов не нужно.

light хранится целиком, dark и mobileтолько отличия от него:

вариант файлов объём
light 261 2.14 МБ
dark 261 0.31 МБ
mobile 261 0.02 МБ

Тёмная тема меняет только цвета, мобильный вьюпорт — единицы свойств; три полные копии заняли бы ~6.5 МБ вместо 2.47 МБ. Дифф при этом остаётся читаемым: [dark] inputtext/root · background-color: rgb(255,255,255) → rgb(30,32,36).

Если вариант меняет не стили, а разметку, снапшот не пишется — тест падает с явным сообщением «набор элементов не совпадает с light».

Ожидание раскладки

Прежнее ожидание (waitForPageReady + два кадра) не ловило галерею: превью лежат в контейнере фиксированной высоты, размеры страницы не менялись, и снимок заставал height: 0px. Теперь подпись для ожидания собирается по тем же элементам, что попадут в снимок, — их высоты плюс число недогруженных картинок, — и опрашивается по кадрам, пока не совпадёт дважды подряд (потолок 2 с.)

Опт-аут для story

Появился parameters.styleSnapshot.skip:

export const Thumbnails: Story = {
  parameters: { styleSnapshot: { skip: true } }
};

Пока он нужен ровно одной story — Galleria / Thumbnails. Лента миниатюр перерисовывается вместе с позицией карусели, часть элементов остаётся свёрнутой в нулевую высоту, и какие именно — от прогона к прогону разное. Снимок адресует повторы по порядку в DOM, поэтому там он нестабилен по своей природе; геометрию галереи покрывают остальные её stories.

Проверка

Два прогона подряд после генерации baseline:

Test Suites: 1 failed, 51 passed · Tests: 1 failed, 263 passed · 34 с / 37 с

Падает по-прежнему только Password (#128). Прогон подорожал с 25–31 с до 34–37 с — три варианта вместо одного.

@khaliulin
khaliulin force-pushed the storybook-style-snapshots branch from 269a68e to d27a905 Compare August 26, 2026 12:41
@khaliulin

Copy link
Copy Markdown
Contributor Author

Переписал коммит: конфиг тест-раннера больше не протекает в stories. Опт-ины переехали в .storybook/test-runner.tsSTYLE_SNAPSHOT_SKIP (по id story) и PIXEL_SNAPSHOT_TITLES (по title компонента) вместо parameters.styleSnapshot.skip и parameters.pixelSnapshot в файлах stories.

Во всём стеке теперь ноль изменённых stories — только .storybook/, зависимости и .gitignore. Ветки перезалиты force-push, базы PR не менялись.

Проверка после переписывания: полный прогон 1 failed, 263 passed (падает только Password из #128), 39 пиксельных снимков на месте — поведение не изменилось.

@ichiesov
ichiesov merged commit 8aae420 into storybook-test-runner Aug 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants