import { test, expect } from '@playwright/test'; test.describe('Login Page', () => { test.beforeEach(async ({ page }) => { await page.goto('/login'); }); test('renders login form with all elements', async ({ page }) => { await expect(page.getByRole('heading', { name: 'Đăng nhập' })).toBeVisible(); await expect(page.getByText('Nhập số điện thoại và mật khẩu để đăng nhập')).toBeVisible(); await expect(page.getByLabel('Số điện thoại')).toBeVisible(); await expect(page.getByLabel('Mật khẩu')).toBeVisible(); await expect(page.getByRole('button', { name: 'Đăng nhập' })).toBeVisible(); // OAuth buttons await expect(page.getByRole('button', { name: /Google/i })).toBeVisible(); await expect(page.getByRole('button', { name: /Zalo/i })).toBeVisible(); // Register link await expect(page.getByText('Chưa có tài khoản?')).toBeVisible(); await expect(page.getByRole('link', { name: 'Đăng ký' })).toBeVisible(); }); test('shows validation errors for empty submission', async ({ page }) => { await page.getByRole('button', { name: 'Đăng nhập' }).click(); // Form validation should show error messages const alerts = page.locator('[role="alert"]'); await expect(alerts.first()).toBeVisible(); }); test('validates phone number format', async ({ page }) => { await page.getByLabel('Số điện thoại').fill('123'); await page.getByLabel('Mật khẩu').fill('Test@1234!'); await page.getByRole('button', { name: 'Đăng nhập' }).click(); const alerts = page.locator('[role="alert"]'); await expect(alerts.first()).toBeVisible(); }); test('toggles password visibility', async ({ page }) => { const passwordInput = page.getByLabel('Mật khẩu'); await expect(passwordInput).toHaveAttribute('type', 'password'); // Click "Hiện" button to show password await page.getByRole('button', { name: 'Hiện' }).click(); await expect(passwordInput).toHaveAttribute('type', 'text'); // Click "Ẩn" button to hide password await page.getByRole('button', { name: 'Ẩn' }).click(); await expect(passwordInput).toHaveAttribute('type', 'password'); }); test('navigates to register page', async ({ page }) => { await page.getByRole('link', { name: 'Đăng ký' }).click(); await expect(page).toHaveURL(/\/register/); }); test('shows OAuth error message from query params', async ({ page }) => { await page.goto('/login?error=oauth_failed'); await expect( page.getByText('Đăng nhập bằng mạng xã hội thất bại'), ).toBeVisible(); }); test('shows access denied OAuth error', async ({ page }) => { await page.goto('/login?error=access_denied'); await expect( page.getByText('Bạn đã từ chối quyền truy cập'), ).toBeVisible(); }); test('submit button shows loading state during submission', async ({ page }) => { // Fill valid-looking data await page.getByLabel('Số điện thoại').fill('0912345678'); await page.getByLabel('Mật khẩu').fill('Test@1234!'); // Intercept the API call to delay response await page.route('**/auth/login', async (route) => { await new Promise((r) => setTimeout(r, 1000)); await route.fulfill({ status: 401, body: JSON.stringify({ message: 'Invalid credentials' }) }); }); await page.getByRole('button', { name: 'Đăng nhập' }).click(); // Button should be disabled during loading await expect(page.getByRole('button', { name: 'Đăng nhập' })).toBeDisabled(); }); test('displays server error on failed login', async ({ page }) => { await page.route('**/auth/login', (route) => route.fulfill({ status: 401, contentType: 'application/json', body: JSON.stringify({ message: 'Sai số điện thoại hoặc mật khẩu' }), }), ); await page.getByLabel('Số điện thoại').fill('0912345678'); await page.getByLabel('Mật khẩu').fill('WrongPass1!'); await page.getByRole('button', { name: 'Đăng nhập' }).click(); const errorAlert = page.locator('[role="alert"]').filter({ hasNotText: /Số điện thoại|Mật khẩu/ }); await expect(errorAlert.first()).toBeVisible({ timeout: 5000 }); }); });