Playwright Component Testing: E2E test cho React components
AAdmin
16 tháng 3, 2026
5 phút đọc
0 lượt xem
Hướng dẫn setup và viết Playwright component tests cho React — test components trong real browser thay vì jsdom.
Unit test React components bằng Jest + React Testing Library chạy trên jsdom — một DOM giả lập. Nó nhanh, nhưng không phản ánh đúng browser thật. Playwright Component Testing cho phép bạn test components trong real browser với API quen thuộc của Playwright.
Tại sao test trong real browser?
- jsdom không hỗ trợ CSS layout, animations, scroll behavior
- Nhiều bugs chỉ xuất hiện trong browser thật (focus management, clipboard API, resize observer)
- Playwright CT chạy component trong Chromium/Firefox/WebKit thật
- Vẫn nhanh vì không cần start full app server
Setup cho React + Vite
# Install
npm install -D @playwright/experimental-ct-react @playwright/test
# Config
npx playwright install
Tạo file playwright-ct.config.ts:
import { defineConfig, devices } from "@playwright/experimental-ct-react";
export default defineConfig({
testDir: "./src",
testMatch: "**/*.ct.tsx",
use: {
ctPort: 3100,
},
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
{
name: "mobile",
use: { ...devices["iPhone 14"] },
},
],
});
Viết component test đầu tiên
Giả sử có component SearchInput:
// src/components/search/SearchInput.ct.tsx
import { test, expect } from "@playwright/experimental-ct-react";
import { SearchInput } from "./SearchInput";
test("renders with placeholder", async ({ mount }) => {
const component = await mount(
<SearchInput placeholder="Tìm kiếm bài viết..." />
);
await expect(component.getByRole("textbox")).toHaveAttribute(
"placeholder",
"Tìm kiếm bài viết..."
);
});
test("calls onSearch after debounce", async ({ mount }) => {
const searches: string[] = [];
const component = await mount(
<SearchInput
placeholder="Search..."
onSearch={(q) => searches.push(q)}
/>
);
const input = component.getByRole("textbox");
await input.fill("react hooks");
// Đợi debounce (300ms)
await expect.poll(() => searches).toEqual(["react hooks"]);
});
test("shows clear button when has value", async ({ mount }) => {
const component = await mount(<SearchInput />);
const input = component.getByRole("textbox");
// Ban đầu không có clear button
await expect(component.getByLabel("Clear")).not.toBeVisible();
// Sau khi type
await input.fill("test");
await expect(component.getByLabel("Clear")).toBeVisible();
// Click clear
await component.getByLabel("Clear").click();
await expect(input).toHaveValue("");
});
test("keyboard navigation: Escape clears input", async ({ mount }) => {
const component = await mount(<SearchInput />);
const input = component.getByRole("textbox");
await input.fill("query");
await input.press("Escape");
await expect(input).toHaveValue("");
});
Test với context/providers
Components thường cần ThemeProvider, Router, etc. Tạo wrapper:
// playwright/index.tsx
import { beforeMount } from "@playwright/experimental-ct-react/hooks";
import { ThemeProvider } from "../src/components/theme-provider";
beforeMount(async ({ App }) => {
return (
<ThemeProvider defaultTheme="light">
<App />
</ThemeProvider>
);
});
So sánh với RTL (React Testing Library)
| Feature | RTL + Jest | Playwright CT |
|---|---|---|
| Environment | jsdom (fake) | Real browser |
| CSS/Layout | ❌ | ✅ |
| Speed | Nhanh hơn | Nhanh (không cần server) |
| Visual testing | ❌ | ✅ Screenshots |
| Cross-browser | ❌ | ✅ Chromium/FF/WebKit |
| Ecosystem | Mature | Experimental |
Recommendation
Không cần replace toàn bộ RTL tests. Dùng Playwright CT cho:
- Components phụ thuộc vào CSS layout (responsive, animations)
- Keyboard/focus management
- Components cần visual regression testing
- Cross-browser compatibility checks
Giữ RTL cho logic-heavy components không cần real browser. Hai tools bổ sung cho nhau.
A
Admin
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!