Tantangan Determinisme pada Pengujian Visual Canvas
Komponen antarmuka berbasis waktu seperti jam analog (misalnya pada showcase clocks.dev) menggunakan loop requestAnimationFrame dan objek Date global untuk me-render posisi jarum secara kontinu. Menjalankan visual regression testing pada elemen ini sering menghasilkan kegagalan palsu (flaky tests) akibat ketidaksesuaian mikrodetik saat snapshot diambil.
Selain faktor waktu, pipeline CI (seperti GitHub Actions berbasis Linux) menggunakan engine rasterisasi font dan sub-pixel rendering GPU yang berbeda dibanding mesin pengembang lokal (macOS atau Windows). Dua masalah utama ini harus dieliminasi: non-determinisme waktu eksekusi dan variasi render grafis headless.
Mocking System Clock via Playwright
Playwright menyediakan API page.clock untuk mengontrol waktu browser secara deterministik. API ini menggantikan implementasi Date, setTimeout, setInterval, dan requestAnimationFrame bawaan.
import { test, expect } from '@playwright/test';
test.describe('Komponen Jam Canvas', () => {
test('harus merender posisi jarum jam 10:10:30 dengan presisi', async ({ page }) => {
// Inisialisasi waktu tetap sebelum memuat halaman
const fixedTime = new Date('2025-01-15T10:10:30.000Z');
await page.clock.install({ time: fixedTime });
await page.goto('/components/analog-clock');
const clockCanvas = page.locator('#clock-canvas');
await expect(clockCanvas).toBeVisible();
// Pastikan satu frame render canvas selesai dieksekusi
await page.clock.runFor(16);
// Ambil visual snapshot dari elemen canvas saja
await expect(clockCanvas).toHaveScreenshot('clock-10-10-30.png', {
maxDiffPixelRatio: 0.01,
animations: 'disabled',
});
});
});Penggunaan page.clock.install membekukan waktu JavaScript. Pemanggilan page.clock.runFor(16) memajukan siklus sebanyak satu frame (60 FPS) agar logika render internal canvas selesai diproses sebelum Playwright mengeksekusi buffer snapshot.
Konfigurasi Headless Worker untuk Hasil Konsisten
Perbedaan layer grafis antar platform dapat diselesaikan dengan memaksa browser headless menggunakan parameter rendering software konsisten via konfigurasi Playwright.
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
workers: process.env.CI ? 2 : undefined,
use: {
...devices['Desktop Chrome'],
deviceScaleFactor: 1,
launchOptions: {
args: [
'--font-render-hinting=none',
'--disable-skia-runtime-opts',
'--disable-font-subpixel-positioning',
'--disable-lcd-text',
],
},
},
expect: {
toHaveScreenshot: {
threshold: 0.2, // Sensitivitas perbedaan warna per piksel (0 - 1)
maxDiffPixelRatio: 0.005, // Toleransi total luas piksel berbeda (0.5%)
},
},
});Flag Chromium di atas mematikan optimasi hardware-specific antialiasing dan subpixel positioning font. Nilai deviceScaleFactor: 1 mengunci resolusi viewport agar rasio DPI tidak berubah di lingkungan server.
Integrasi CI Pipeline (GitHub Actions)
Snapshot visual harus dihasilkan dan divalidasi pada lingkungan sistem operasi yang identik. Jalankan test suite di dalam container Docker resmi Playwright pada CI runner untuk mengeliminasi variasi library font dan OS dependencies.
name: Visual Regression
on: [push, pull_request]
jobs:
visual-test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.49.0-jammy
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npx playwright test
- name: Upload Artifacts (Gagal Snapshot)
if: failure()
uses: actions/upload-artifact@v4
with:
name: visual-diff-results
path: test-results/
retention-days: 7Jika terjadi perbedaan rendering yang melampaui ambang batas maxDiffPixelRatio, artifact berupa gambar perbandingan (expected, actual, dan diff mask) diunggah otomatis untuk dianalisis oleh developer.
Trade-off dan Debugging
Bila canvas mengandalkan WebGL alih-alih 2D context standar, tambahkan flag --use-gl=swiftshader pada launchOptions untuk memastikan rasterisasi dieksekusi via software CPU di CI tanpa driver GPU fisik.
Hindari menaikkan maxDiffPixelRatio di atas 0.02. Toleransi yang terlalu longgar akan meloloskan regresi visual nyata seperti jarum detik yang melompat beberapa derajat.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!