使用 Playwright 测试 UI 状态和动画
如何使用 Playwright 为动画 UI 编写稳定的测试:使用 page.clock 控制时间、模拟简化运动和黑暗模式以及禁用动画的屏幕截图。
核心要点
使动画测试具有确定性:使用 page.clock 控制时间,涵盖计时器、requestAnimationFrame 和性能;使用 page.emulateMedia({ returnedMotion, colorScheme }) 模拟用户设置;对状态而不是动画中像素进行断言;并在禁用动画的情况下截取屏幕截图。将简化运动路径作为一流案例进行测试。
为什么动画测试不稳定
动画取决于时间,而实际时间因运行和机器而异。早或晚 10 毫秒拍摄的屏幕截图会显示不同的帧。解决方法是控制时间和设置,并测试动画在各个帧之间移动的状态,而不是单独的帧。
控制时间
Playwright 的时钟 API 允许测试安装假时钟,然后在设定的持续时间内暂停、快进或运行时间。它涵盖了 setTimeout、setInterval、requestAnimationFrame 和性能,因此您可以快进经过加载延迟并检查是否出现成功状态。
模拟用户设置
page.emulateMedia()可以将reducedMotion设置为“reduce”,将colorScheme设置为“dark”或“light”。以减少的动作运行关键流程,以确认没有任何重要的事情取决于动画,并在两个主题中捕获对比度问题。
稳定的截图
Playwright Test可以将屏幕截图与expect(page).toHaveScreenshot()进行比较,屏幕截图选项包括动画:“disabled”,它会停止CSS动画和捕获的过渡。对最终状态使用视觉比较,对行为使用 DOM 或可访问性断言。
测试基于画布的组件
画布动画(例如由 Lottie、Rive 或 ExodeUI 运行时绘制的动画)在 DOM 中不可见。测试代码发送的输入及其接收的事件,将真正可访问的元素保留在画布旁边,并在控制时间后使用屏幕截图进行视觉检查。
生产实施片段
import { test, expect } from '@playwright/test';
test('save button shows success, also with reduced motion', async ({ page }) => {
await page.clock.install(); // fake timers + rAF
await page.emulateMedia({ reducedMotion: 'reduce' }); // test the calm path
await page.goto('/settings');
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByRole('status')).toHaveText('Saving…');
await page.clock.runFor(2000); // skip the wait deterministically
await expect(page.getByRole('status')).toHaveText('Saved');
await expect(page).toHaveScreenshot('saved.png', { animations: 'disabled' });
});要记住的关键 Principle
- 使用 page.clock 控制时间(计时器、requestAnimationFrame、性能)
- 使用 page.emulateMedia 模拟 returnedMotion 和 colorScheme
- 对状态和可访问的文本进行断言,而不是对动画中的像素进行断言
- 带动画的屏幕截图:“已禁用”
- 将简化运动路径作为一流案例进行测试
常见问题解答
来源
- Clock — Playwright 文档
- Page.emulateMedia and screenshot options — Playwright 文档
- Visual comparisons — Playwright 文档