Twelve ready-to-paste Playwright scripts, each chosen to cover a distinct slice of real-world browser monitoring. Copy any of them into your Browser Check editor, adjust the URLs and selectors for your own application, and save.
These templates target the 2026.05 runtime (Playwright 1.59.1, Node 24) and pick up Web Vitals on every navigation. Runtime 2023.04 works for most of them, except the ones that rely on test.step reporting, toHaveScreenshot, or devices presets. See Browser Checks for runtime details.
process.env.NAME, then click Run to test before saving.Each template teaches a different Playwright primitive. Pick the one closest to what you actually need to watch, then mix techniques across scripts as your coverage grows.
The baseline. Navigate to a URL, confirm the page rendered, and exit. Use this as a cheap heartbeat when you only need to know that the origin is reachable and not serving a blank or error shell.
import { test, expect } from '@playwright/test';
test('homepage loads and renders', async ({ page }) => {
await page.goto('https://hyperping.io');
await expect(page).toHaveTitle(/Hyperping/);
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});Form authentication with credentials pulled from environment variables. The check fails the moment login breaks, whether the cause is a bad deploy, expired session cookies, or a misconfigured OAuth provider.
import { test, expect } from '@playwright/test';
test('user can log in', async ({ page }) => {
await page.goto('https://app.example.com/login');
await page.getByLabel('Email').fill(process.env.EMAIL);
await page.getByLabel('Password').fill(process.env.PASSWORD);
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL(/\/dashboard/);
await expect(page.getByTestId('user-menu')).toBeVisible();
});Build a unique email per run from a timestamp so the signup endpoint never collides with a previous run. Useful for verifying the full account-creation pipeline, including downstream welcome emails.
import { test, expect } from '@playwright/test';
test('new user can sign up', async ({ page }) => {
const stamp = Date.now();
const email = `qa+${stamp}@example.com`;
await page.goto('https://app.example.com/signup');
await page.getByLabel('Work email').fill(email);
await page.getByLabel('Password').fill(`Test-${stamp}!`);
await page.getByRole('button', { name: 'Create account' }).click();
await expect(page.getByText(/check your inbox/i)).toBeVisible();
});Keyboard input, <code>Enter</code> submission, and a count assertion on the result grid. Covers the common case where the failure mode is an empty result page rather than an HTTP error.
import { test, expect } from '@playwright/test';
test('search returns matching results', async ({ page }) => {
await page.goto('https://example-shop.com');
const box = page.getByPlaceholder('Search products');
await box.fill('running shoes');
await box.press('Enter');
await expect(page).toHaveURL(/q=running\+shoes/);
const results = page.getByTestId('product-card');
await expect(results.first()).toBeVisible();
expect(await results.count()).toBeGreaterThan(0);
});Wraps each stage in test.step so the Browser Check run view shows each step as it passes or fails. The step tree gives on-call engineers the exact breakpoint instead of a wall of logs.
import { test, expect } from '@playwright/test';
test('guest can complete a checkout', async ({ page }) => {
await test.step('open product', async () => {
await page.goto('https://example-shop.com/p/classic-tee');
await expect(page.getByRole('heading', { name: /classic tee/i })).toBeVisible();
});
await test.step('add to cart', async () => {
await page.getByRole('button', { name: 'Add to cart' }).click();
await expect(page.getByTestId('cart-count')).toHaveText('1');
});
await test.step('open cart and checkout', async () => {
await page.getByRole('link', { name: 'Cart' }).click();
await page.getByRole('button', { name: 'Checkout' }).click();
});
await test.step('enter shipping details', async () => {
await page.getByLabel('Email').fill('qa@example.com');
await page.getByLabel('Full name').fill('QA Runner');
await page.getByLabel('Address').fill('1 Test Street');
await page.getByRole('button', { name: 'Continue to payment' }).click();
});
await test.step('confirm order', async () => {
await expect(page.getByText(/order summary/i)).toBeVisible();
});
});Uses page.request to call a JSON endpoint directly, without rendering HTML. Faster than a UI test and useful for backends you want to check behind Hyperping’s probe regions.
import { test, expect } from '@playwright/test';
test('public status API is healthy', async ({ request }) => {
const res = await request.get('https://api.example.com/v1/status', {
headers: { Authorization: `Bearer ${process.env.API_TOKEN}` },
});
expect(res.status()).toBe(200);
expect(res.headers()['content-type']).toContain('application/json');
const body = await res.json();
expect(body).toMatchObject({
status: 'ok',
version: expect.any(String),
});
expect(body.uptime_seconds).toBeGreaterThan(0);
});Extracts every nav href, issues HEAD-style GETs, and fails on the first 4xx or 5xx. A lightweight way to catch dead blog links, rotated CDN paths, or accidental release of draft content.
import { test, expect } from '@playwright/test';
test('no broken links in the main navigation', async ({ page, request }) => {
await page.goto('https://example.com');
const hrefs = await page.locator('nav a').evaluateAll(
links => links.map(a => a.href).filter(h => h.startsWith('http'))
);
const broken = [];
for (const url of hrefs) {
const res = await request.get(url, { failOnStatusCode: false });
if (res.status() >= 400) broken.push(`${res.status()} ${url}`);
}
expect(broken, `broken links: \n${broken.join('\n')}`).toEqual([]);
});Reads PerformanceNavigationTiming from the page and fails when TTFB, DOMContentLoaded, or load exceed a budget. Logs the numbers on every run so you can watch them trend in the run logs.
import { test, expect } from '@playwright/test';
test('homepage meets performance budget', async ({ page }) => {
await page.goto('https://example.com', { waitUntil: 'load' });
const timing = await page.evaluate(() => {
const [nav] = performance.getEntriesByType('navigation');
return {
ttfb: nav.responseStart - nav.requestStart,
domContentLoaded: nav.domContentLoadedEventEnd - nav.startTime,
loadEvent: nav.loadEventEnd - nav.startTime,
};
});
console.log('perf', timing);
expect(timing.ttfb).toBeLessThan(800);
expect(timing.domContentLoaded).toBeLessThan(2500);
expect(timing.loadEvent).toBeLessThan(4000);
});Pixel-compares a single critical region against a golden snapshot. Waits for document.fonts.ready and disables animations so the diff does not flicker between runs.
import { test, expect } from '@playwright/test';
test('hero section matches golden snapshot', async ({ page }) => {
await page.goto('https://example.com');
await page.evaluate(() => document.fonts.ready);
const hero = page.getByTestId('hero');
await expect(hero).toHaveScreenshot('hero.png', {
maxDiffPixelRatio: 0.02,
animations: 'disabled',
});
});Uses Playwright’s devices preset to emulate an iPhone, including viewport, user agent, and touch events. Catches responsive regressions that desktop runs never see.
import { test, expect, devices } from '@playwright/test';
test.use({ ...devices['iPhone 14'] });
test('mobile nav opens and navigates', async ({ page }) => {
await page.goto('https://example.com');
await page.getByRole('button', { name: 'Open menu' }).tap();
await expect(page.getByRole('navigation')).toBeVisible();
await page.getByRole('link', { name: 'Pricing' }).tap();
await expect(page).toHaveURL(/\/pricing/);
});Injects a session cookie from an environment variable so the check never logs in. Faster, less brittle against auth provider changes, and easier to rotate than a scripted login.
import { test, expect } from '@playwright/test';
test.use({
storageState: {
cookies: [
{
name: 'session',
value: process.env.SESSION_COOKIE,
domain: 'app.example.com',
path: '/',
httpOnly: true,
secure: true,
sameSite: 'Lax',
expires: -1,
},
],
origins: [],
},
});
test('dashboard renders for an authenticated user', async ({ page }) => {
await page.goto('https://app.example.com/dashboard');
await expect(page.getByTestId('workspace-name')).toBeVisible();
await expect(page.getByRole('navigation')).toContainText('Settings');
});Waits for the download event, inspects the filename, and reads the stream to confirm the file is not empty. Covers invoice, report, and export flows that tend to silently break.
import { test, expect } from '@playwright/test';
test('invoice PDF downloads successfully', async ({ page }) => {
await page.goto('https://app.example.com/billing');
const [download] = await Promise.all([
page.waitForEvent('download'),
page.getByRole('button', { name: 'Download latest invoice' }).click(),
]);
expect(download.suggestedFilename()).toMatch(/invoice.*\.pdf$/i);
const stream = await download.createReadStream();
let bytes = 0;
for await (const chunk of stream) bytes += chunk.length;
expect(bytes).toBeGreaterThan(1024);
});getByRole('button', { name: 'Save' }) survives a stylesheet rewrite; .btn-primary.save does not.process.env.NAME so they never appear in the script body.test.setTimeout explicitly.await expect(locator).toBeVisible() already retries, so most waitForTimeout calls are unnecessary.console.log for latency, counts, or IDs shows up in run logs and makes post-mortem analysis far easier.