Một test đăng nhập cần xác nhận trạng thái sau thao tác, không chỉ việc nút đã được bấm. Bài này phát triển từ hướng dẫn Playwright tổng quát thành hai ca cụ thể: đăng nhập đúng và từ chối thông tin sai.
A login test must verify the resulting state, not just that a button was clicked. This guide extends the general Playwright workflow into two concrete cases: successful login and rejected credentials.
Trong bài nàyOn this page
1. Chuẩn bị trước khi yêu cầu agent viết test1. Prepare before asking for tests
Ví dụ dành cho dự án Node.js/TypeScript đã cài @playwright/test và browser runtime phù hợp. Ứng dụng phải chạy ở http://127.0.0.1:3000, có tài khoản thử riêng và database thử có thể phục hồi. Nếu dự án dùng runner khác, giữ quy ước hiện có thay vì chép đè cấu hình.
Ứng dụng minh họa có nhãn Email, Password, nút Sign in, trang /dashboard và heading Dashboard. Agent phải quan sát DOM thật và thay các locator, URL, thông báo cho đúng ứng dụng. Các tên dưới đây không phải kết quả khám phá website của bạn.
Dùng webapp-testing để kiểm tra URL, công cụ và dữ liệu trước. Cấp thông tin tài khoản thử qua biến môi trường E2E_EMAIL, E2E_PASSWORD theo cơ chế secret của môi trường; không ghi giá trị vào test hoặc commit.
This example targets a Node.js/TypeScript project with @playwright/test and compatible browsers already installed. The application runs at http://127.0.0.1:3000, with a dedicated test account and restorable test database. Preserve existing runner conventions rather than replacing another setup.
The illustrative application has Email and Password labels, a Sign in button, a /dashboard route and a Dashboard heading. Inspect the actual DOM and adapt locators, URLs and messages. These names are assumptions for the example, not observations of your website.
Use webapp-testing to confirm the URL, tools and data first. Supply E2E_EMAIL and E2E_PASSWORD through the environment secret mechanism; never hard-code or commit their values.
2. Giữ bằng chứng khi thất bại2. Retain evidence on failure
Ghép các tùy chọn phù hợp vào playwright.config.ts hiện có. Ví dụ dùng một worker và không retry để việc điều tra ban đầu dễ theo dõi.
Merge suitable options into the existing playwright.config.ts. One worker and no retries make the initial investigation easier to follow.
import {defineConfig} from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
workers: 1,
retries: 0,
reporter: [['list'], ['html', {open: 'never'}]],
use: {
baseURL: 'http://127.0.0.1:3000',
trace: 'retain-on-failure',
screenshot: 'only-on-failure',
},
});
Thêm script gọi binary đã cài trong package.json; giữ các script khác của dự án:
Add scripts invoking the installed binary in package.json, preserving other project scripts:
{
"scripts": {
"test:e2e": "playwright test",
"test:report": "playwright show-report",
"test:trace": "playwright show-trace"
}
}
3. Kiểm tra thành công và từ chối đăng nhập3. Test successful and rejected login
Tạo tests/e2e/login.spec.ts trong dự án thử. Ca thành công kiểm tra cả điều hướng và nội dung sau reload. Ca thất bại dùng email minh họa phải được xác nhận là không tồn tại trong fixture, tránh gây khóa tài khoản người dùng.
Create tests/e2e/login.spec.ts in the test project. The success case checks navigation and content after reload. Confirm that the illustrative invalid email is absent from the fixture so the negative case cannot lock a real account.
import {test, expect} from '@playwright/test';
test('valid credentials create a usable session', async ({page}) => {
const email = process.env.E2E_EMAIL;
const password = process.env.E2E_PASSWORD;
if (!email || !password) {
throw new Error('Configure dedicated E2E_EMAIL and E2E_PASSWORD');
}
await page.goto('/login');
await page.getByLabel('Email', {exact: true}).fill(email);
await page.getByLabel('Password', {exact: true}).fill(password);
await page.getByRole('button', {name: 'Sign in', exact: true}).click();
await expect(page).toHaveURL(/\/dashboard(?:[/?#]|$)/);
await expect(page.getByRole('heading', {name: 'Dashboard', exact: true}))
.toBeVisible();
await page.reload();
await expect(page.getByRole('heading', {name: 'Dashboard', exact: true}))
.toBeVisible();
});
test('invalid credentials remain unauthenticated', async ({page}) => {
await page.goto('/login');
await page.getByLabel('Email', {exact: true}).fill('not-a-user@example.test');
await page.getByLabel('Password', {exact: true}).fill('deliberately-invalid');
await page.getByRole('button', {name: 'Sign in', exact: true}).click();
await expect(page.getByRole('alert'))
.toContainText('Invalid email or password');
await expect(page).toHaveURL(/\/login(?:[/?#]|$)/);
});
Mỗi test bắt đầu với browser context riêng. Nếu cấu hình chung đang nạp storageState đã đăng nhập, cần một project test sạch cho các ca này. Không dùng timeout cố định để chờ đăng nhập; assertion của Playwright sẽ chờ điều kiện mong đợi trong giới hạn cấu hình.
Each test starts with a separate browser context. If shared configuration loads an authenticated storageState, use a clean test project for these cases. Avoid fixed sleeps; Playwright assertions wait for the expected condition within configured limits.
4. Chạy ca nhỏ và đọc đúng loại lỗi4. Run the focused checks and classify failures
npm run test:e2e -- tests/e2e/login.spec.ts
npm run test:report
- Connection refused: kiểm tra server, cổng và môi trường trước khi sửa test.
- Không tìm thấy locator: đối chiếu DOM và nhãn accessible thật; đừng chỉ tăng timeout.
- Thông tin hợp lệ vẫn bị từ chối: kiểm tra dữ liệu seed, trạng thái tài khoản và request trong môi trường thử.
- Đã đổi URL nhưng chưa có nội dung: kiểm tra lỗi tải trang, session hoặc response tiếp theo.
Trace và ảnh có thể chứa dữ liệu nhạy cảm, cookie hoặc nội dung request. Lưu cục bộ, loại thông tin riêng trước khi chia sẻ và không commit trạng thái xác thực. Ghi lệnh chạy, phiên bản ứng dụng, bước lỗi và artifact tương ứng trong báo cáo.
- Connection refused: check the server, port and environment before editing tests.
- Locator not found: compare the real DOM and accessible label instead of merely increasing the timeout.
- Valid credentials rejected: inspect seed data, account state and the test-environment request.
- URL changed but content is absent: investigate page loading, session state or the next response.
Traces and screenshots can contain sensitive data, cookies or request contents. Keep them local, remove private information before sharing and never commit authentication state. Report the command, application version, failed step and corresponding artifact.
5. Mở rộng sau khi hai ca cơ bản ổn định5. Extend coverage after the basics are stable
Theo yêu cầu sản phẩm, bổ sung logout, truy cập trang được bảo vệ khi chưa đăng nhập, session hết hạn và điều hướng bàn phím. SSO hoặc MFA cần fixture và chính sách riêng; hai test minh họa này không kiểm chứng các luồng đó. Khi chia sẻ tài khoản giữa các test song song, kiểm tra xem chúng có thay đổi cùng trạng thái phía server không.
Add logout, unauthenticated access to protected pages, session expiry and keyboard navigation according to product requirements. SSO and MFA need their own fixtures and policies; the two examples do not verify them. Before sharing an account across parallel tests, check whether those tests mutate the same server-side state.