Playwright Component Testing: Test Your React Components in a Real Browser
Playwright Component Testing mounts real components in a real browser for testing. Learn how it differs from jsdom-based testing, when to use it, and how to write effective component tests.
Why Component Testing in a Real Browser?
Jest + React Testing Library uses jsdom — a JavaScript implementation of the DOM that runs in Node.js. It's fast but not a real browser. CSS doesn't apply correctly, browser APIs are faked, and layout-dependent behavior can't be tested. Playwright Component Testing runs your components in actual Chromium/Firefox/WebKit — what you see in tests is what users see.
Setting Up Playwright CT
npm install --save-dev @playwright/experimental-ct-react
npx playwright install
// playwright-ct.config.ts
import { defineConfig, devices } from '@playwright/experimental-ct-react';
export default defineConfig({
testDir: './tests/components',
snapshotDir: './tests/__snapshots__',
timeout: 10 * 1000,
use: {
ctPort: 3100,
ctViteConfig: {
// Import your actual project CSS
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname }
}
}
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'mobile', use: { ...devices['iPhone 14'] } },
],
});
Writing Your First Component Test
// tests/components/Button.spec.tsx
import { test, expect } from '@playwright/experimental-ct-react';
import { Button } from '../../src/components/Button';
test.describe('Button component', () => {
test('renders with correct text', async ({ mount }) => {
const component = await mount(
<Button variant="primary">Click Me</Button>
);
await expect(component).toContainText('Click Me');
});
test('calls onClick when clicked', async ({ mount }) => {
let clicked = false;
const component = await mount(
<Button onClick={() => { clicked = true; }}>Click</Button>
);
await component.click();
expect(clicked).toBe(true);
});
test('is disabled when disabled prop set', async ({ mount }) => {
const component = await mount(
<Button disabled>Disabled</Button>
);
await expect(component).toBeDisabled();
});
test('visual snapshot matches', async ({ mount }) => {
const component = await mount(
<Button variant="primary" size="lg">Submit</Button>
);
await expect(component).toHaveScreenshot('button-primary-lg.png');
});
});
Testing Interactive Components
// tests/components/Dropdown.spec.tsx
test('dropdown shows options on click and selects correctly', async ({ mount }) => {
const values: string[] = [];
const component = await mount(
<Dropdown
options={['Option 1', 'Option 2', 'Option 3']}
onSelect={(val) => values.push(val)}
/>
);
// Verify closed initially
await expect(component.getByRole('listbox')).not.toBeVisible();
// Open the dropdown
await component.getByRole('button').click();
await expect(component.getByRole('listbox')).toBeVisible();
// Select an option
await component.getByText('Option 2').click();
expect(values).toEqual(['Option 2']);
// Verify closed after selection
await expect(component.getByRole('listbox')).not.toBeVisible();
});
When to Use Playwright CT vs RTL
- Use RTL (React Testing Library) for: unit-level behavior tests, hook testing, fast feedback loops
- Use Playwright CT for: visual regression, CSS-dependent behavior, cross-browser testing, complex interaction sequences
Running and CI Integration
# Run component tests
npx playwright test --config playwright-ct.config.ts
# Update visual snapshots
npx playwright test --update-snapshots --config playwright-ct.config.ts
# Run specific component
npx playwright test Button.spec.tsx --config playwright-ct.config.ts
Playwright CT and RTL are complementary. Use RTL as your default for speed; add Playwright CT for the tests that actually need a real browser environment.
Admin
Cal.com
Open source scheduling — tự host booking system, thay thế Calendly. Free & privacy-first.
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!