feat: Establish Playwright testing infrastructure with initial tests for authentication, admin, and instructor modules, and fix instructor video and quiz lesson management pages.
All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 1m17s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 8s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 2s
All checks were successful
Build and Deploy Frontend Management to Dev Server / Build Frontend Management Docker Image (push) Successful in 1m17s
Build and Deploy Frontend Management to Dev Server / Deploy E-learning Frontend Management to Dev Server (push) Successful in 8s
Build and Deploy Frontend Management to Dev Server / Notify Deployment Status (push) Successful in 2s
This commit is contained in:
parent
734d922393
commit
9bc24fbe8a
18 changed files with 1344 additions and 7 deletions
124
frontend_management/tests/auth/login.spec.ts
Normal file
124
frontend_management/tests/auth/login.spec.ts
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
import { TEST_ADMIN, TEST_INSTRUCTOR, TEST_URLS } from '../fixtures/test-data';
|
||||
|
||||
test.describe('Login Page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(TEST_URLS.login);
|
||||
});
|
||||
|
||||
test('should display login form', async ({ page }) => {
|
||||
// Title
|
||||
await expect(page.locator('h1')).toContainText('E-Learning');
|
||||
|
||||
// Email & password fields
|
||||
await expect(page.locator('input[type="email"]')).toBeVisible();
|
||||
await expect(page.locator('input[type="password"]')).toBeVisible();
|
||||
|
||||
// Submit button
|
||||
await expect(page.locator('button[type="submit"]')).toBeVisible();
|
||||
|
||||
// Register link
|
||||
await expect(page.locator('a[href="/register"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show validation errors for empty fields', async ({ page }) => {
|
||||
// Click submit without filling fields
|
||||
await page.locator('button[type="submit"]').click();
|
||||
|
||||
// Expect validation messages in Thai
|
||||
await expect(page.getByText('กรุณากรอกอีเมล')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show validation errors for empty fields password', async ({ page }) => {
|
||||
// Click submit without filling fields
|
||||
await page.locator('input[type="email"]').fill('test@email.com');
|
||||
await page.locator('button[type="submit"]').click();
|
||||
|
||||
// Expect validation messages in Thai
|
||||
await expect(page.getByText('กรุณากรอกรหัสผ่าน')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should toggle password visibility', async ({ page }) => {
|
||||
const passwordInput = page.locator('input[type="password"]');
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
await passwordInput.fill('test123');
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
// Click the visibility toggle icon
|
||||
await page.locator('//i[normalize-space(text())="visibility"]').click();
|
||||
|
||||
// Password field should now be text type
|
||||
await expect(page.locator('input[type="text"]').last()).toHaveValue('test123');
|
||||
});
|
||||
|
||||
test('should show error for invalid credentials', async ({ page }) => {
|
||||
await page.waitForTimeout(1000);
|
||||
await page.locator('input[type="email"]').fill('wrong@email.com');
|
||||
await page.waitForTimeout(500);
|
||||
await page.locator('input[type="password"]').fill('wrongpassword');
|
||||
await page.locator('button[type="submit"]').click();
|
||||
|
||||
// Should show error notification (Quasar Notify)
|
||||
await expect(page.locator('.q-notification')).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('should login as admin and redirect to /admin', async ({ page }) => {
|
||||
await page.waitForTimeout(1000);
|
||||
await page.locator('input[type="email"]').fill(TEST_ADMIN.email);
|
||||
await page.waitForTimeout(500);
|
||||
await page.locator('input[type="password"]').fill(TEST_ADMIN.password);
|
||||
await page.locator('button[type="submit"]').click();
|
||||
|
||||
// Should redirect to admin dashboard
|
||||
await page.waitForURL('**/admin**', { timeout: 15_000 });
|
||||
await expect(page).toHaveURL(/\/admin/);
|
||||
});
|
||||
|
||||
test('should login as instructor and redirect to /instructor', async ({ page }) => {
|
||||
await page.locator('input[type="email"]').fill(TEST_INSTRUCTOR.email);
|
||||
await page.waitForTimeout(1000);
|
||||
await page.locator('input[type="password"]').fill(TEST_INSTRUCTOR.password);
|
||||
await page.waitForTimeout(1000);
|
||||
await page.locator('button[type="submit"]').click();
|
||||
|
||||
// Should redirect to instructor dashboard
|
||||
await page.waitForURL('**/instructor**', { timeout: 15_000 });
|
||||
await expect(page).toHaveURL(/\/instructor/);
|
||||
});
|
||||
|
||||
test('should open forgot password modal', async ({ page }) => {
|
||||
await page.getByText('ลืมรหัสผ่าน?').click();
|
||||
|
||||
// Modal should be visible
|
||||
await expect(page.getByText('ลืมรหัสผ่าน').nth(1)).toBeVisible();
|
||||
await expect(page.locator('.q-dialog input[type="email"]')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should remember email after login with remember me checked', async ({ page }) => {
|
||||
// 1. Fill credentials
|
||||
await page.waitForTimeout(500);
|
||||
await page.locator('input[type="email"]').fill(TEST_ADMIN.email);
|
||||
await page.waitForTimeout(500);
|
||||
await page.locator('input[type="password"]').fill(TEST_ADMIN.password);
|
||||
await page.waitForTimeout(500);
|
||||
// 2. Check "remember me"
|
||||
await page.getByText('จดจำฉันไว้').click();
|
||||
|
||||
// 3. Login
|
||||
await page.locator('button[type="submit"]').click();
|
||||
await page.waitForURL('**/admin**', { timeout: 15_000 });
|
||||
|
||||
// 4. Logout - click avatar menu then logout
|
||||
await page.getByText('ออกจากระบบ').click();
|
||||
await page.waitForTimeout(1000);
|
||||
// 5. Confirm logout dialog
|
||||
await page.locator("(//button[@type='button'])[2]").click();
|
||||
|
||||
// 6. Should redirect back to login page
|
||||
await page.waitForURL('**/login**', { timeout: 15_000 });
|
||||
|
||||
// 7. Verify the email is still pre-filled
|
||||
await expect(page.locator('input[type="email"]')).toHaveValue(TEST_ADMIN.email);
|
||||
});
|
||||
});
|
||||
146
frontend_management/tests/auth/register.spec.ts
Normal file
146
frontend_management/tests/auth/register.spec.ts
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
import { TEST_URLS } from '../fixtures/test-data';
|
||||
import { faker } from '@faker-js/faker';
|
||||
|
||||
/**
|
||||
* Register Page Tests
|
||||
* Test instructor registration flow
|
||||
*/
|
||||
test.describe('Register Page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto(TEST_URLS.register);
|
||||
});
|
||||
|
||||
test('check display register form', async ({ page }) => {
|
||||
// Header
|
||||
await expect(page.getByText('ลงทะเบียนเป็นผู้สอน')).toBeVisible();
|
||||
await expect(page.getByText('สร้างบัญชีเพื่อเริ่มสร้างหลักสูตร')).toBeVisible();
|
||||
|
||||
// Form fields
|
||||
await expect(page.locator('label').filter({ hasText: 'ชื่อผู้ใช้ (Username)' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'อีเมล' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'รหัสผ่าน *' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'ยืนยันรหัสผ่าน' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'คำนำหน้า' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'ชื่อจริง' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'นามสกุล' })).toBeVisible();
|
||||
await expect(page.locator('label').filter({ hasText: 'เบอร์โทรศัพท์' })).toBeVisible();
|
||||
|
||||
// Submit button
|
||||
await expect(page.getByRole('button', { name: 'ลงทะเบียน' })).toBeVisible();
|
||||
|
||||
// Login link
|
||||
await expect(page.getByText('มีบัญชีอยู่แล้ว?')).toBeVisible();
|
||||
await expect(page.getByText('เข้าสู่ระบบ')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show validation errors for empty fields', async ({ page }) => {
|
||||
await page.getByRole('button', { name: 'ลงทะเบียน' }).click();
|
||||
|
||||
await expect(page.getByText('กรุณากรอก username')).toBeVisible();
|
||||
await expect(page.getByText('กรุณากรอกอีเมล')).toBeVisible();
|
||||
await expect(page.getByText('กรุณากรอกรหัสผ่าน')).toBeVisible();
|
||||
await expect(page.getByText('กรุณายืนยันรหัสผ่าน')).toBeVisible();
|
||||
await expect(page.getByText('กรุณากรอกชื่อ')).toBeVisible();
|
||||
await expect(page.getByText('กรุณากรอกนามสกุล')).toBeVisible();
|
||||
await expect(page.getByText('กรุณากรอกเบอร์โทร')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show username min length validation', async ({ page }) => {
|
||||
const usernameInput = page.locator('label').filter({ hasText: 'ชื่อผู้ใช้ (Username)' }).locator('input');
|
||||
await usernameInput.fill('ab');
|
||||
await page.getByRole('button', { name: 'ลงทะเบียน' }).click();
|
||||
|
||||
await expect(page.getByText('อย่างน้อย 4 ตัวอักษร')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show email format validation', async ({ page }) => {
|
||||
const emailInput = page.locator('input[type="email"]');
|
||||
await emailInput.fill('invalid-email');
|
||||
await page.getByRole('button', { name: 'ลงทะเบียน' }).click();
|
||||
|
||||
await expect(page.getByText('รูปแบบอีเมลไม่ถูกต้อง')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show password min length validation', async ({ page }) => {
|
||||
const passwordInput = page.locator('label').filter({ hasText: 'รหัสผ่าน *' }).locator('input');
|
||||
await passwordInput.fill('1234');
|
||||
await page.getByRole('button', { name: 'ลงทะเบียน' }).click();
|
||||
|
||||
await expect(page.getByText('อย่างน้อย 8 ตัวอักษร')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should show password mismatch validation', async ({ page }) => {
|
||||
const passwordInput = page.locator('label').filter({ hasText: 'รหัสผ่าน *' }).locator('input');
|
||||
const confirmInput = page.locator('label').filter({ hasText: 'ยืนยันรหัสผ่าน' }).locator('input');
|
||||
|
||||
await passwordInput.fill('password123');
|
||||
await confirmInput.fill('differentpass');
|
||||
await page.getByRole('button', { name: 'ลงทะเบียน' }).click();
|
||||
|
||||
await expect(page.getByText('รหัสผ่านไม่ตรงกัน')).toBeVisible();
|
||||
});
|
||||
|
||||
test('should toggle password visibility', async ({ page }) => {
|
||||
const passwordInput = page.locator('label').filter({ hasText: 'รหัสผ่าน *' }).locator('input');
|
||||
await passwordInput.fill('test1234');
|
||||
|
||||
// Click visibility icon
|
||||
await page.locator('label').filter({ hasText: 'รหัสผ่าน *' }).locator('.q-icon').filter({ hasText: 'visibility' }).click();
|
||||
|
||||
// Both password fields should now be text type
|
||||
await expect(passwordInput).toHaveAttribute('type', 'text');
|
||||
});
|
||||
|
||||
test('should navigate to login page', async ({ page }) => {
|
||||
await page.getByText('เข้าสู่ระบบ').click();
|
||||
await page.waitForURL('**/login**', { timeout: 10_000 });
|
||||
await expect(page).toHaveURL(/\/login/);
|
||||
});
|
||||
|
||||
test('should register successfully with valid data', async ({ page }) => {
|
||||
const username = faker.internet.username().toLowerCase().replace(/[^a-z0-9]/g, '') + faker.string.alpha({ length: 4, casing: 'lower' });
|
||||
const email = faker.internet.email().toLowerCase();
|
||||
const password = 'Test@1234';
|
||||
const firstName = faker.person.firstName();
|
||||
const lastName = faker.person.lastName();
|
||||
const phone = `0${faker.string.numeric(9)}`;
|
||||
|
||||
// Fill form
|
||||
const usernameInput = page.locator('label').filter({ hasText: 'ชื่อผู้ใช้ (Username)' }).locator('input');
|
||||
await usernameInput.fill(username);
|
||||
|
||||
await page.locator('input[type="email"]').fill(email);
|
||||
|
||||
const passwordInput = page.locator('label').filter({ hasText: 'รหัสผ่าน *' }).locator('input');
|
||||
await passwordInput.fill(password);
|
||||
|
||||
const confirmInput = page.locator('label').filter({ hasText: 'ยืนยันรหัสผ่าน' }).locator('input');
|
||||
await confirmInput.fill(password);
|
||||
|
||||
// Select prefix
|
||||
const prefixSelect = page.locator('label').filter({ hasText: 'คำนำหน้า' });
|
||||
await prefixSelect.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.locator('.q-item__label').filter({ hasText: 'นาย / Mr.' }).click();
|
||||
|
||||
// Fill name
|
||||
const firstNameInput = page.locator('label').filter({ hasText: 'ชื่อจริง' }).locator('input');
|
||||
await firstNameInput.fill(firstName);
|
||||
|
||||
const lastNameInput = page.locator('label').filter({ hasText: 'นามสกุล' }).locator('input');
|
||||
await lastNameInput.fill(lastName);
|
||||
|
||||
// Fill phone
|
||||
const phoneInput = page.locator('label').filter({ hasText: 'เบอร์โทรศัพท์' }).locator('input');
|
||||
await phoneInput.fill(phone);
|
||||
|
||||
// Submit
|
||||
await page.getByRole('button', { name: 'ลงทะเบียน' }).click();
|
||||
|
||||
// Should show success notification and redirect to login
|
||||
await expect(page.locator('.q-notification')).toBeVisible({ timeout: 10_000 });
|
||||
await page.waitForURL('**/login**', { timeout: 15_000 });
|
||||
await expect(page).toHaveURL(/\/login/);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue