feat: Add Playwright end-to-end testing setup and initial test suites for various application flows.
This commit is contained in:
parent
9bc24fbe8a
commit
a3b2e55443
50 changed files with 3321 additions and 101 deletions
122
Frontend-Learner/tests/e2e/login.spec.ts
Normal file
122
Frontend-Learner/tests/e2e/login.spec.ts
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import { test, expect, type Page, type Locator } from '@playwright/test';
|
||||
|
||||
const BASE_URL = process.env.E2E_BASE_URL ?? 'http://localhost:3000';
|
||||
|
||||
// ใช้ account ตามที่คุณให้มา
|
||||
const EMAIL = 'studentedtest@example.com';
|
||||
const PASSWORD = 'admin123';
|
||||
|
||||
async function waitAppSettled(page: Page) {
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await page.waitForLoadState('networkidle').catch(() => {});
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
|
||||
function emailLocator(page: Page): Locator {
|
||||
return page
|
||||
.locator('input[type="email"]')
|
||||
.or(page.getByRole('textbox', { name: /อีเมล|email/i }))
|
||||
.first();
|
||||
}
|
||||
|
||||
function passwordLocator(page: Page): Locator {
|
||||
return page
|
||||
.locator('input[type="password"]')
|
||||
.or(page.getByRole('textbox', { name: /รหัสผ่าน|password/i }))
|
||||
.first();
|
||||
}
|
||||
|
||||
function loginButtonLocator(page: Page): Locator {
|
||||
return page
|
||||
.getByRole('button', { name: /เข้าสู่ระบบ|login/i })
|
||||
.or(page.locator('button[type="submit"]'))
|
||||
.first();
|
||||
}
|
||||
|
||||
async function expectAnyVisible(page: Page, locators: Locator[], timeout = 20_000) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeout) {
|
||||
for (const loc of locators) {
|
||||
try {
|
||||
if (await loc.isVisible()) return;
|
||||
} catch {}
|
||||
}
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
throw new Error('None of the expected dashboard locators became visible.');
|
||||
}
|
||||
|
||||
test.describe('Login -> Dashboard', () => {
|
||||
test('Success Login แล้วเข้า /dashboard ได้', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/auth/login`, { waitUntil: 'domcontentloaded' });
|
||||
await waitAppSettled(page);
|
||||
|
||||
await emailLocator(page).fill(EMAIL);
|
||||
await passwordLocator(page).fill(PASSWORD);
|
||||
await loginButtonLocator(page).click();
|
||||
|
||||
await page.waitForURL('**/dashboard', { timeout: 25_000 });
|
||||
await waitAppSettled(page);
|
||||
|
||||
// ✅ ใช้ Locator ที่พบเจอแน่นอนใน Layout/Page โดยไม่ยึดติดกับภาษาปัจจุบัน (I18n)
|
||||
const dashboardEvidence = [
|
||||
// มองหา Layout container ฝั่ง Dashboard
|
||||
page.locator('.q-page-container').first(),
|
||||
page.locator('.q-drawer').first(),
|
||||
// มองหารูปโปรไฟล์ (UserAvatar)
|
||||
page.locator('img[src*="avataaars"]').first(),
|
||||
page.locator('img[alt],[alt="User Avatar"]').first()
|
||||
];
|
||||
|
||||
await expectAnyVisible(page, dashboardEvidence, 20_000);
|
||||
|
||||
await page.screenshot({ path: 'tests/e2e/screenshots/login-to-dashboard.png', fullPage: true });
|
||||
});
|
||||
|
||||
test('Invalid Email - Thai characters (พิมพ์ภาษาไทย)', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/auth/login`, { waitUntil: 'domcontentloaded' });
|
||||
await waitAppSettled(page);
|
||||
|
||||
await emailLocator(page).fill('ทดสอบภาษาไทย');
|
||||
await passwordLocator(page).fill(PASSWORD);
|
||||
|
||||
const errorHint = page.getByText('ห้ามใส่ภาษาไทย');
|
||||
|
||||
await expect(errorHint.first()).toBeVisible({ timeout: 12_000 });
|
||||
await page.screenshot({ path: 'tests/e2e/screenshots/login-thai-email.png', fullPage: true });
|
||||
});
|
||||
|
||||
test('Invalid Email Format (อีเมลผิดรูปแบบ)', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/auth/login`, { waitUntil: 'domcontentloaded' });
|
||||
await waitAppSettled(page);
|
||||
|
||||
// *สำคัญ*: HTML5 จะดักจับ invalid-email-format ตั้งแต่กด Submit (native validation)
|
||||
// ทำให้ Vue Form ไม่เริ่มทำงาน
|
||||
// ดังนั้นเพื่อให้ทดสอบเจอ Error จาก useFormValidation จริงๆ เราใช้ 'test@domain'
|
||||
// ซึ่ง HTML5 <input type="email"> ปล่อยผ่าน แต่ /regex/ ของระบบตรวจเจอว่าไม่มี .com
|
||||
await emailLocator(page).fill('test@domain');
|
||||
await passwordLocator(page).fill(PASSWORD);
|
||||
await loginButtonLocator(page).click();
|
||||
await waitAppSettled(page);
|
||||
|
||||
const errorHint = page.getByText('กรุณากรอกอีเมลให้ถูกต้อง (you@example.com)');
|
||||
|
||||
await expect(errorHint.first()).toBeVisible({ timeout: 12_000 });
|
||||
await page.screenshot({ path: 'tests/e2e/screenshots/login-invalid-email.png', fullPage: true });
|
||||
});
|
||||
|
||||
test('Wrong Password (รหัสผ่านผิด หรืออีเมลไม่ถูกต้องในระบบ)', async ({ page }) => {
|
||||
await page.goto(`${BASE_URL}/auth/login`, { waitUntil: 'domcontentloaded' });
|
||||
await waitAppSettled(page);
|
||||
|
||||
await emailLocator(page).fill(EMAIL);
|
||||
await passwordLocator(page).fill('wrong-password-123');
|
||||
await loginButtonLocator(page).click();
|
||||
await waitAppSettled(page);
|
||||
|
||||
const errorHint = page.getByText('กรุณาเช็ค Email หรือ รหัสผ่านใหม่อีกครั้ง');
|
||||
|
||||
await expect(errorHint.first()).toBeVisible({ timeout: 12_000 });
|
||||
await page.screenshot({ path: 'tests/e2e/screenshots/login-wrong-password.png', fullPage: true });
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue