Playwright Tests
We use Playwright with Stencil's Playwright integration for testing our web components. Each component has a dedicated test file that covers functional behavior, visual regression, and accessibility.
For implementation examples, consult the existing test files.
Setup
Install Playwright browsers:
bashnpm run test:e2e-installRun tests:
bashnpm run test:e2eRun tests for a specific component (from
libraries/ui-library):bashcd libraries/ui-library npx playwright test six-buttonRun tests with UI for debugging (from
libraries/ui-library):bashnpx playwright test --ui
Run Tests Faster
Run npm start first. Tests run faster when the dev server is already running because it is reused across all tests instead of a new server being started for each test file.
Test File Structure
Every component test file has three test.describe blocks with clear separation of concerns:
import { test } from '../../test-utils/fixtures';
import { expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
// 1. Functional tests - behavior, events, keyboard interaction
test.describe('six-{component}', () => {
test('should show and hide via open prop', async ({ page }) => {
/* ... */
});
test('should emit events (focus, change, blur)', async ({ page }) => {
/* ... */
});
test('should handle keyboard navigation', async ({ page }) => {
/* ... */
});
});
// 2. Screenshot tests - visual regression
test.describe('six-{component} screenshots', () => {
test('should match screenshot for default', async ({ page }) => {
/* ... */
});
test('should match screenshot for disabled', async ({ page }) => {
/* ... */
});
});
// 3. Accessibility tests - ARIA attributes and axe-core
test.describe('six-{component} accessibility', () => {
test('should have correct ARIA attributes', async ({ page }) => {
/* ... */
});
test('should have no a11y violations', async ({ page }) => {
/* ... */
});
});Why separate blocks?
A test failure clearly indicates whether it's a visual bug, a behavioral bug, or an accessibility issue. This separation makes tests more maintainable and easier to debug.
What to Test
Functional Tests
- Behavior: State transitions, show/hide logic, error handling
- Disabled state: Verify
click({ force: true })doesn't change state - Events: Test focus, change, blur in one realistic flow using
spyOnEvent() - Keyboard navigation: Follow ARIA APG patterns
- Programmatic changes: Setting values via JavaScript
See six-checkbox.e2e.ts for a complete example.
Screenshot Tests
Capture visual states that functional tests cannot verify:
- Default and active states
- Disabled states
- All variants (types, sizes)
- Focus and hover states (for focusable components)
WARNING
Always visually verify screenshots after generating them. Open the PNG files and check that content is fully visible and not cropped.
Accessibility Tests
- ARIA attributes: role, aria-hidden, aria-checked, aria-label, etc.
- axe-core validation: Use
AxeBuilderto check WCAG compliance
Document any disabled axe rules with TODO comments explaining why.
Key Patterns
Selectors
- Use
page.locator('six-component')for interactions (clicks the host element) - Use
page.getByRole()for assertions (queries the accessibility tree)
Testing Events
Use spyOnEvent() to test custom events:
const changeSpy = await page.spyOnEvent('six-checkbox-change');
await page.locator('six-checkbox').click();
expect(changeSpy).toHaveReceivedEvent();Setting Properties
Use evaluate() to set web component properties:
await page.locator('six-input').evaluate((el) => {
el.value = 'test';
});Animations
The test fixture disables animations by default. Use { disableAnimations: false } when testing after-show or after-hide events.
ARIA Authoring Practices
Components should follow the ARIA Authoring Practices Guide (APG):
six-alert→ Alertsix-checkbox→ Checkboxsix-dialog→ Dialog (Modal)six-menu→ Menusix-radio→ Radio Groupsix-switch→ Switchsix-tab-group→ Tabs
Commands Reference
From the repository root:
npm run test:e2e # run all tests
npm run test:e2e-install # install Playwright browsers
npm run test:e2e-update-screenshots # update all screenshotsFrom libraries/ui-library for more options:
npx playwright test six-button # run specific component
npx playwright test six-button --update-snapshots # update component screenshots
npx playwright test --ui # run with UI debugger
npx playwright test --headed # run with visible browser