test(sw): stabilize Firefox hover, pixel and timing checks

- hover-stability-firefox: instant scroll and state waits instead of a fixed 60ms delay (smooth scroll in preview).
- integrated-visual, viewer-regression, theme-inheritance: deterministic Chromium raster flags, animations held at
  time 0, layout settle wait, integer instant scroll of inner panels; tolerance stays 0.
- text-weight: wait for the Viewer More panel instead of a fixed 200ms and fix the failure report.
This commit is contained in:
shkim4 committed 2026-10-21 06:08:31 +09:00
1 parent 1ffff67991
commit af7f98d66b
5 files changed
+49 -14

No files matched your search

@@ -24,7 +24,10 @@ if (mode === 'capture') {
await new Promise(resolve => server.listen(0, '127.0.0.1', resolve))
const base = `http://127.0.0.1:${server.address().port}`
const { chromium } = require('playwright')
const browser = await chromium.launch({ headless: true, executablePath: process.env.SW_CHROME_PATH ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe' })
// 2026-10-20: software raster without font hinting / subpixel positioning. Without these flags two captures of the same
// build differed in text anti-aliasing (Δ1 paragraphs, 2 × 2 glyph spots); with them four captures were identical. The
// zero threshold of `compare` is unchanged, and a 1px layout change still shows (25 of 28 shots).
const browser = await chromium.launch({ headless: true, args: ['--disable-gpu', '--font-render-hinting=none', '--disable-font-subpixel-positioning', '--disable-lcd-text', '--force-color-profile=srgb'], executablePath: process.env.SW_CHROME_PATH ?? 'C:/Program Files/Google/Chrome/Application/chrome.exe' })
const page = await browser.newPage({ viewport: { width: 1920, height: 1000 }, reducedMotion: 'reduce' })
const errors = []
page.on('pageerror', e => errors.push(e.message))
@@ -36,7 +39,13 @@ if (mode === 'capture') {
await page.locator('.hm-sw-header, .hm-sw-theme').first().waitFor()
await page.evaluate(() => document.fonts.ready)
await page.mouse.move(1, 1)
await page.evaluate(() => { window.__swRunning = document.getAnimations().filter(a => a.playState === 'running') })
await page.addStyleTag({ content: '*,*::before,*::after{animation-play-state:paused!important;animation-delay:0s!important;transition:none!important;caret-color:transparent!important}' })
// 2026-10-20: the paused animations above stop at whatever time they had reached, so two captures of the same build
// differed (Window Move Motion). Animations that were running before the pause are set to time 0; specimens the Story paused itself keep
// their own time. Then wait until the layout holds for three frames (measured Stories such as Responsive More).
await page.evaluate(() => { for (const a of window.__swRunning) { a.pause(); a.currentTime = 0 } })
await page.evaluate(() => new Promise(resolve => { let last = '', stable = 0; const tick = () => { const now = [document.documentElement.scrollHeight, ...[...document.querySelectorAll('.hm-sw-header')].map(h => h.getBoundingClientRect().width)].join(); stable = now === last ? stable + 1 : 0; last = now; stable >= 3 ? resolve() : requestAnimationFrame(tick) }; requestAnimationFrame(tick) }))
await page.waitForTimeout(150)
const file = `${id}-${width}.png`
await page.screenshot({ path: path.join(out, file), fullPage: true })