11.2 Todo 핵심 Journey
Page object는 의미 있는 action만 노출한다
import { expect, test, type Page } from '@playwright/test';
export class TodoPage {
constructor(private readonly page: Page) {}
async goto() {
await this.page.goto('/');
await expect(this.page.getByRole('heading', { name: 'Todo Lab' })).toBeVisible();
}
async add(title: string) {
await test.step(`Todo 추가: ${title}`, async () => {
await this.page.getByLabel('할 일', { exact: true }).fill(title);
await this.page.getByRole('button', { name: '추가' }).click();
}, { box: true });
}
item(title: string) {
return this.page.getByRole('listitem').filter({ hasText: title });
}
}
clickAddButton()처럼 UI detail을 그대로 노출하면 test가 business story를 설명하지 못합니다.
Journey test
test('CAP-TODO-001 Todo를 만들고 완료한다 @smoke', async ({ page }) => {
const todos = new TodoPage(page);
await todos.goto();
await todos.add('Playwright capstone');
const item = todos.item('Playwright capstone');
await expect(item).toBeVisible();
await item.getByRole('checkbox').check();
await expect(item.getByRole('checkbox')).toBeChecked();
});
Boundary cases
- 공백 title
- 매우 긴 title
- 같은 title 중복
- Unicode와 emoji
- 빠른 double submit
- Re-render 중 checkbox action
E2E에는 대표 경계만 두고 나머지 data 조합은 component/unit test로 내립니다.
Fixture로 기본 상태 제공
export const test = base.extend<{ todoPage: TodoPage }>({
todoPage: async ({ page }, use) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await use(todoPage);
},
});
Fixture가 test가 필요하지 않은 data를 무조건 생성하지 않게 lazy하고 composable하게 유지합니다.
진단 연습
- Button label을 바꾸어 locator failure를 만듭니다.
- Summary API를 2초 늦추고 manual sleep 없이 통과하는지 확인합니다.
- Checkbox 위에 overlay를 표시해 actionability log를 읽습니다.
- 실패 trace에서 before/after DOM snapshot을 비교합니다.
실행 파일: todo.spec.ts, todo-page.ts