import { test, expect } from '@playwright/test'; import { LoginPage } from '../../page-objects/LoginPage.js'; import { testCredentials } from '../../fixtures/auth.js'; test.describe('Authentication - Login Flow', () => { let loginPage; test.beforeEach(async ({ page }) => { loginPage = new LoginPage(page); await loginPage.navigate(); }); test('should display login page correctly', async ({ page }) => { // Check page title and main elements await expect(page).toHaveTitle(/ROA Reports/); // Check login form elements are visible await expect(page.locator(loginPage.usernameInput)).toBeVisible(); await expect(page.locator(loginPage.passwordInput)).toBeVisible(); await expect(page.locator(loginPage.loginButton)).toBeVisible(); // Check page title const title = await loginPage.getPageTitle(); expect(title).toContain('ROA Reports'); }); test('should show validation errors for empty fields', async ({ page }) => { // Check initial state - button should be disabled expect(await loginPage.isLoginButtonDisabled()).toBe(true); // Clear any existing content and verify empty state await page.fill(loginPage.usernameInput, ''); await page.fill(loginPage.passwordInput, ''); await page.click(loginPage.loginCard); // Click outside to trigger validation // Wait for Vue reactivity await page.waitForTimeout(100); // Button should remain disabled with empty fields expect(await loginPage.isLoginButtonDisabled()).toBe(true); // Verify form validation classes are applied const hasInvalidFields = await loginPage.hasInvalidField(); // Note: validation might not show invalid state until user interaction }); test('should show validation error for empty username', async ({ page }) => { // Fill only password await loginPage.fillCredentials('', 'password123'); // Trigger validation by clicking outside await page.click(loginPage.loginCard); // Check that login button is disabled expect(await loginPage.isLoginButtonDisabled()).toBe(true); }); test('should show validation error for empty password', async ({ page }) => { // Fill only username await loginPage.fillCredentials('username', ''); // Trigger validation by clicking outside await page.click(loginPage.loginCard); // Check that login button is disabled expect(await loginPage.isLoginButtonDisabled()).toBe(true); }); test('should enable login button with valid input', async ({ page: _page }) => { // Fill both fields await loginPage.fillCredentials('testuser', 'testpass'); // Check that login button is enabled expect(await loginPage.isLoginButtonDisabled()).toBe(false); }); test('should handle invalid credentials', async ({ page }) => { // Mock the API response for invalid login await page.route('**/api/auth/login', async route => { await route.fulfill({ status: 401, contentType: 'application/json', body: JSON.stringify({ detail: 'Invalid credentials' }), }); }); // Attempt login with invalid credentials await loginPage.login(testCredentials.invalid.username, testCredentials.invalid.password); // Wait for response await loginPage.waitForLoginResult(); // Check that we're still on login page expect(await loginPage.isOnLoginPage()).toBe(true); // Check that error message appears (via toast or error div) // Note: Error might be shown via PrimeVue Toast, so we need to check for toast messages const toastError = page.locator('.p-toast-message-error'); if (await toastError.isVisible()) { const errorText = await toastError.textContent(); expect(errorText).toContain('Eroare'); } }); test('should handle successful login', async ({ page }) => { // Mock the API response for successful login await page.route('**/api/auth/login', async route => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ access_token: 'mock_access_token', refresh_token: 'mock_refresh_token', user: { id: 1, username: 'testuser', full_name: 'Test User' } }), }); }); // Mock companies endpoint for dashboard await page.route('**/api/companies', async route => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify([ { code: 'COMP1', name: 'Company 1' }, { code: 'COMP2', name: 'Company 2' } ]), }); }); // Attempt login with valid credentials await loginPage.login(testCredentials.valid.username, testCredentials.valid.password); // Wait for redirect to dashboard await page.waitForURL('/dashboard', { timeout: 10000 }); // Check that we're on dashboard page expect(page.url()).toContain('/dashboard'); }); test('should show loading state during login', async ({ page }) => { // Mock slow API response await page.route('**/api/auth/login', async route => { // Delay the response to see loading state await new Promise(resolve => setTimeout(resolve, 1000)); await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ access_token: 'mock_access_token', refresh_token: 'mock_refresh_token', user: { id: 1, username: 'testuser' } }), }); }); // Fill credentials and submit await loginPage.fillCredentials(testCredentials.valid.username, testCredentials.valid.password); await loginPage.clickLogin(); // Check loading state appears await expect(page.locator(loginPage.loadingSpinner)).toBeVisible(); // Wait for loading to finish await page.waitForLoadState('networkidle'); }); test('should handle network errors gracefully', async ({ page }) => { // Mock network error await page.route('**/api/auth/login', async route => { await route.abort('failed'); }); // Attempt login await loginPage.login(testCredentials.valid.username, testCredentials.valid.password); // Wait a bit for error handling await page.waitForTimeout(2000); // Should still be on login page expect(await loginPage.isOnLoginPage()).toBe(true); // Check for error message in toast summary or detail const toastSummary = page.locator('.p-toast-summary'); const toastDetail = page.locator('.p-toast-detail'); // Check if either summary or detail contains error text const summaryVisible = await toastSummary.isVisible(); const detailVisible = await toastDetail.isVisible(); if (summaryVisible || detailVisible) { let errorFound = false; if (summaryVisible) { const summaryText = await toastSummary.textContent(); if (summaryText && summaryText.toLowerCase().includes('eroare')) { errorFound = true; } } if (detailVisible) { const detailText = await toastDetail.textContent(); if (detailText && detailText.toLowerCase().includes('eroare')) { errorFound = true; } } expect(errorFound).toBe(true); } }); test('should focus username field on page load', async ({ page }) => { // Check that username field is focused const focusedElement = await page.locator(':focus'); await expect(focusedElement).toHaveAttribute('id', 'username'); }); });