2.8 KiB
2.8 KiB
Playwright Component Testing
Status
Production-ready as of 2024. No longer experimental.
Setup
npm init playwright@latest -- --ct
Configuration
// playwright-ct.config.ts
import { defineConfig, devices } from '@playwright/experimental-ct-react';
export default defineConfig({
testDir: './src',
use: {
ctPort: 3100,
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
});
Basic Test
import { test, expect } from '@playwright/experimental-ct-react';
import { Button } from './Button';
test('renders button with text', async ({ mount }) => {
const component = await mount(<Button>Click me</Button>);
await expect(component).toContainText('Click me');
});
test('handles click event', async ({ mount }) => {
let clicked = false;
const component = await mount(
<Button onClick={() => clicked = true}>Click</Button>
);
await component.click();
expect(clicked).toBe(true);
});
With Props and State
test('counter increments', async ({ mount }) => {
const component = await mount(<Counter initial={0} />);
await expect(component.getByTestId('count')).toHaveText('0');
await component.getByRole('button', { name: 'Increment' }).click();
await expect(component.getByTestId('count')).toHaveText('1');
});
Visual Regression
test('button styles', async ({ mount }) => {
const component = await mount(<Button variant="primary">Submit</Button>);
await expect(component).toHaveScreenshot('button-primary.png');
});
Mocking
test('with mocked data', async ({ mount }) => {
const component = await mount(
<UserContext.Provider value={{ user: { name: 'Test' } }}>
<Profile />
</UserContext.Provider>
);
await expect(component).toContainText('Test');
});
When to Use CT vs E2E
| Use CT When | Use E2E When |
|---|---|
| Testing isolated components | Testing user flows |
| Visual regression on components | Navigation, routing |
| Component interactions | Full page behavior |
| Fast feedback during dev | Integration with backend |
When to Use CT vs Vitest
| Use CT When | Use Vitest When |
|---|---|
| Real browser needed | Speed is priority |
| Cross-browser testing | Unit testing logic |
| CSS/layout verification | Mocking is simpler |
| Complex DOM interactions | jsdom is sufficient |
Limitations
- Complex object passing requires serialization
- Slower than jsdom-based tests
- Watch mode less efficient than Vitest
Commands
npx playwright test -c playwright-ct.config.ts
npx playwright test -c playwright-ct.config.ts --ui