# Test Patterns — Quy ước chung cho automation-testing

Tài liệu này được skill `automation-testing` đọc ở Phase 1 và áp dụng khi sinh test.
Mục tiêu: test ổn định, dễ đọc, ít vỡ khi UI đổi.

## Nguyên tắc chọn locator (ưu tiên từ trên xuống)

1. `getByRole(role, { name })` — bám theo accessibility, bền nhất.
2. `getByLabel(text)` — cho input có `<label>`.
3. `getByPlaceholder(text)` — khi không có label.
4. `getByText(text)` — cho message, nội dung tĩnh.
5. `getByTestId(id)` — khi dev đã gắn `data-testid`.
6. CSS / XPath — **chỉ khi bất đắc dĩ**, kèm comment lý do.

> Locator phải lấy từ DOM thật (Phase 3 qua Playwright MCP), không tự bịa tên.

## Quy ước chung

- Mỗi test độc lập: tự `navigate` tới trang cần test, không phụ thuộc test trước.
- Page Object: 1 file / 1 màn hình; locator khai báo ở constructor; action là method.
- Spec import `{ test, expect }` từ `tests/e2e/fixtures/test.ts`.
- ID test (`TC01`, `TC02`...) khớp bảng test case đã chốt ở Phase 2.
- Tên test, comment viết tiếng Việt.
- Không hardcode tài khoản/dữ liệu thật; dùng dữ liệu test do tester cung cấp.

---

## Pattern 1 — Login

Steps điển hình: vào trang login → nhập email/password → submit.
- Thành công: `await expect(page).toHaveURL(/dashboard/)`.
- Sai thông tin: `await expect(errorMessage).toBeVisible()`.
- Validation rỗng: submit ngay → `await expect(page.getByText('... bắt buộc')).toBeVisible()`.

## Pattern 2 — Form validation

- Submit khi field rỗng → kiểm tra message bắt buộc của từng field.
- Nhập sai định dạng (email, số điện thoại) → kiểm tra message định dạng.
- Nhập hợp lệ → submit thành công (toast / redirect / record mới xuất hiện).

## Pattern 3 — CRUD (Create / Read / Update / Delete)

- **Create**: mở form → điền → submit → khẳng định record xuất hiện trong list.
- **Read**: tìm/lọc → khẳng định kết quả đúng.
- **Update**: mở record → sửa → lưu → khẳng định giá trị mới hiển thị.
- **Delete**: xóa → xác nhận → khẳng định record biến mất.
- Mẹo: tạo dữ liệu có hậu tố thời gian/ngẫu nhiên để tránh trùng giữa các lần chạy.

## Pattern 4 — Modal / Dialog

- Mở modal → `await expect(dialog).toBeVisible()`.
- Thao tác trong modal dùng locator scope trong dialog: `dialog.getByRole(...)`.
- Đóng/confirm → khẳng định modal đóng (`toBeHidden`) và side-effect đúng.

## Pattern 5 — Table / Pagination / Search

- Search: nhập từ khóa → khẳng định hàng khớp xuất hiện, hàng không khớp biến mất.
- Pagination: sang trang → khẳng định nội dung trang đổi.
- Dùng `getByRole('row')`, `getByRole('cell')` để bám bảng theo accessibility.

## Pattern 6 — Auth-gated pages (storageState)

Cơ chế có sẵn trong repo — **ưu tiên dùng**, đừng đăng nhập lại trong từng test:

- Đăng nhập **1 lần** ở `tests/e2e/auth.setup.ts`, lưu phiên vào `.auth/user.json`.
- Bật bằng env `E2E_USER` (+ `E2E_PASSWORD`, `E2E_LOGIN_PATH`, `E2E_POST_LOGIN_PATH`).
- Khi bật, mọi test chạy ở trạng thái **đã đăng nhập** sẵn — không cần `beforeEach` login.
- Test cần **chưa đăng nhập** (test login/logout): `test.use({ storageState: LOGGED_OUT })`
  ở đầu spec (`LOGGED_OUT` import từ `../../fixtures/test`).
- Selector trong `auth.setup.ts` phải là selector THẬT của trang login (lấy ở Phase 3).
