392 words
2 minutes
Use Playwright to Monitor Web Application
Playwright can be used to automate visual monitoring by capturing screenshots of key pages on a schedule.
A practical workflow is:
- Capture baseline screenshots for important pages and user flows
- Capture fresh screenshots in each monitoring run
- Compare pixel differences between baseline and current images
- Trigger an alert when the difference exceeds a configured threshold
This approach helps detect unintended UI regressions quickly and provides image evidence to speed up triage. Code:
import { chromium } from "playwright";import pixelmatch from "pixelmatch";import { PNG } from "pngjs";import fs from "fs";import path from "path";
const BASELINE_DIR = "./screenshots/baseline";const DIFF_DIR = "./screenshots/diff";const THRESHOLD = 0.03; // Alert when pixel difference exceeds 3%
interface CheckResult { passed: boolean; diffPercent: number; diffImagePath: string;}
async function captureScreenshot(url: string, name: string): Promise<Buffer> { const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
// Wait for network idle so async content has finished loading await page.goto(url, { waitUntil: "networkidle" }); // Wait an extra 2 seconds so animations can finish await page.waitForTimeout(2000);
const screenshot = await page.screenshot({ fullPage: false }); await browser.close(); return screenshot;}
function compareScreenshots( current: Buffer, name: string): CheckResult { const currentPng = PNG.sync.read(current); const baselinePath = path.join(BASELINE_DIR, `${name}.png`); const diffPath = path.join(DIFF_DIR, `${name}.png`);
if (!fs.existsSync(baselinePath)) { // First run: save as baseline image fs.writeFileSync(baselinePath, current); console.log(`[${name}] Baseline image created`); return { passed: true, diffPercent: 0, diffImagePath: "" }; }
const baselinePng = PNG.sync.read(fs.readFileSync(baselinePath)); const { width, height } = currentPng; const diff = new PNG({ width, height });
const diffPixels = pixelmatch( baselinePng.data, currentPng.data, diff.data, width, height, { threshold: 0.1 } );
const diffPercent = diffPixels / (width * height);
if (diffPercent > THRESHOLD) { fs.mkdirSync(DIFF_DIR, { recursive: true }); fs.writeFileSync(diffPath, PNG.sync.write(diff)); return { passed: false, diffPercent, diffImagePath: diffPath }; }
return { passed: true, diffPercent, diffImagePath: "" };}
async function patrol(pages: Array<{ url: string; name: string }>) { for (const { url, name } of pages) { const screenshot = await captureScreenshot(url, name); const result = compareScreenshots(screenshot, name);
if (!result.passed) { sendAlert({ page: name, diffPercent: (result.diffPercent * 100).toFixed(2), diffImage: result.diffImagePath, }); // Keep a current screenshot when an issue is detected fs.writeFileSync( path.join(DIFF_DIR, `${name}_current.png`), screenshot ); } }}
function sendAlert(data: Record<string, string>) { // Replace with your own notification channel: Slack, Teams, or webhook console.log(`⚠️ Page anomaly alert:`, JSON.stringify(data, null, 2));} Use Playwright to Monitor Web Application
https://astro-nyc.pages.dev/posts/use-playwright-to-monitor-web-application/