Testing Strategy
Overview
Aliz projects use three layers of testing, each with a distinct tool and purpose:
| Layer | Tool | What it covers |
|---|---|---|
| Unit & integration | Vitest | Isolated logic, components, hooks |
| Visual regression | Vitest v4 browser mode | Unintended UI changes caught by screenshot comparison |
| End-to-end | Playwright | Full user flows in real browsers |
Each layer has a different scope and speed trade-off. Run unit tests constantly during development, visual regression tests on PRs, and E2E tests at the integration stage or in CI.
You don't need all three layers on every project. Start with unit tests. Add E2E tests for critical user flows. Introduce visual regression tests when UI stability becomes a priority.
Unit tests with Vitest
What they cover
Unit tests verify isolated pieces of logic — pure functions, custom hooks, and individual components — without a real browser or network. They are fast, deterministic, and the first line of defence against regressions.
How to write them
Vitest provides a Jest-compatible API, so the patterns are familiar:
- Use
describeto group related tests. - Use
it(ortest) for individual assertions. - Use
expectmatchers for assertions. - Pair with React Testing Library for component tests — render, query, and interact with components the way a user would.
import { describe, it, expect } from 'vitest';
import { formatCurrency } from './format';
describe('formatCurrency', () => {
it('formats a positive amount with the correct symbol', () => {
expect(formatCurrency(1234.5, 'USD')).toBe('$1,234.50');
});
it('returns an empty string for invalid input', () => {
expect(formatCurrency(NaN, 'USD')).toBe('');
});
});
Vitest shares your vite.config.ts, so TypeScript, JSX, and path aliases work out of the box — no separate Babel or Jest config needed.
See Vitest for setup details and configuration options.
Visual regression tests with Vitest v4
What they cover
Visual regression tests catch unintended UI changes by comparing screenshots of components or pages against a stored baseline. A pixel diff that exceeds a threshold fails the test, surfacing broken layouts, colour shifts, or missing elements that functional assertions would miss.
How they work in Vitest v4
Vitest v4 introduced a browser mode that runs tests inside real browsers instead of a simulated DOM. This makes screenshot-based assertions reliable and consistent.
- Install
@vitest/browserandvitest-browser-react, then configure a browser provider (playwrightrecommended). - Use the built-in
toMatchScreenshot()matcher to capture and compare renders against a stored baseline. - Tests run against real browser engines — Chromium, Firefox, and WebKit — via the Playwright provider, without needing a separate E2E harness.
import { render } from 'vitest-browser-react';
import { expect, test } from 'vitest';
import { Badge } from './Badge';
test('Badge renders correctly', async () => {
const screen = render(<Badge label="New" variant="success" />);
await expect(screen.baseElement).toMatchScreenshot();
});
Screenshot baselines are committed to the repository alongside the test files. Update them intentionally with the --update (-u) flag when a UI change is deliberate.
Playwright also supports screenshot assertions and is the better choice for full-page or flow-level visual checks within an E2E test. Vitest browser mode visual regression is best suited for isolated component snapshots during development.
See Vitest for browser mode configuration details.
E2E tests with Playwright
What they cover
End-to-end tests exercise complete user flows — login, form submission, navigation, checkout — in a real browser against a running application. They validate that all layers of the stack (UI, API, auth) work together correctly.
What Playwright brings
- Cross-browser — a single test suite runs against Chromium, Firefox, and WebKit.
- Auto-wait — locators wait for elements to be visible and actionable before interacting, eliminating manual sleeps and reducing flakiness.
- Trace viewer — captures a full timeline (DOM snapshots, network, console) for post-mortem debugging of failed CI runs.
- Codegen — records user interactions and generates test code, making it fast to bootstrap new test scenarios.
- Parallel execution — tests run in isolated browser contexts by default and can be sharded across CI machines.
import { test, expect } from '@playwright/test';
test('user can log in and reach the dashboard', async ({ page }) => {
await page.goto('/login');
await page.getByLabel('Email').fill('user@example.com');
await page.getByLabel('Password').fill('secret');
await page.getByRole('button', { name: 'Sign in' }).click();
await expect(page).toHaveURL('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
See Playwright for installation, configuration, and CI setup.
Resources
- Vitest — unit, integration, and visual regression testing
- Playwright — end-to-end and cross-browser testing
- Vite — the build tool Vitest runs on
- Official Vitest docs
- Official Playwright docs