2026-07-23 22:35:03 +02:00
|
|
|
import { test, expect } from '@playwright/test';
|
|
|
|
|
import { setupApiMocks, login, logout, TEST_USER } from './helpers';
|
|
|
|
|
|
|
|
|
|
test.describe('Authentication E2E', () => {
|
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
|
|
|
await setupApiMocks(page);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('login form is rendered correctly', async ({ page }) => {
|
|
|
|
|
await page.goto('/login');
|
|
|
|
|
|
|
|
|
|
// Login page container visible
|
|
|
|
|
await expect(page.locator('[data-testid="login-page"]')).toBeVisible();
|
|
|
|
|
|
|
|
|
|
// Email and password inputs present
|
|
|
|
|
await expect(page.locator('input[type="email"]')).toBeVisible();
|
|
|
|
|
await expect(page.locator('input[type="password"]')).toBeVisible();
|
|
|
|
|
|
|
|
|
|
// Submit button present
|
|
|
|
|
await expect(page.locator('button[type="submit"]')).toBeVisible();
|
|
|
|
|
|
|
|
|
|
// Logo / title visible
|
|
|
|
|
await expect(page.locator('h1')).toContainText(/leocrm/i);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('successful login redirects to dashboard', async ({ page }) => {
|
|
|
|
|
await login(page);
|
|
|
|
|
|
2026-08-04 20:53:51 +02:00
|
|
|
// Should be on start page (Login.tsx navigates to /start, not /dashboard)
|
|
|
|
|
await expect(page).toHaveURL(/\/start/);
|
2026-07-23 22:35:03 +02:00
|
|
|
await expect(page.locator('[data-testid="topbar"]')).toBeVisible();
|
2026-08-04 20:53:51 +02:00
|
|
|
await expect(page.locator('[data-testid="start-layout"]')).toBeVisible();
|
2026-07-23 22:35:03 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('login with invalid credentials shows error', async ({ page }) => {
|
|
|
|
|
// Override login route to return 401
|
|
|
|
|
await page.route('**/api/v1/auth/login', (route) => {
|
|
|
|
|
route.fulfill({
|
|
|
|
|
status: 401,
|
|
|
|
|
contentType: 'application/json',
|
|
|
|
|
body: JSON.stringify({ detail: 'Invalid credentials' }),
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
await page.goto('/login');
|
|
|
|
|
await page.locator('input[type="email"]').fill('wrong@test.test');
|
|
|
|
|
await page.locator('input[type="password"]').fill('wrongpassword');
|
|
|
|
|
await page.locator('button[type="submit"]').click();
|
|
|
|
|
|
|
|
|
|
// Should stay on login page and show error
|
|
|
|
|
await expect(page).toHaveURL(/\/login/);
|
|
|
|
|
await expect(page.locator('[role="alert"]')).toBeVisible();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('logout button works and redirects to login', async ({ page }) => {
|
|
|
|
|
await login(page);
|
|
|
|
|
|
2026-08-04 20:53:51 +02:00
|
|
|
// Open user menu (use aria-label to distinguish from notification button)
|
|
|
|
|
await page.locator('[data-testid="topbar"] button[aria-label="Benutzermenü"]').click();
|
2026-07-23 22:35:03 +02:00
|
|
|
|
|
|
|
|
// Click logout
|
|
|
|
|
await page.locator('[role="menuitem"]').filter({ hasText: /logout|abmelden/i }).click();
|
|
|
|
|
|
|
|
|
|
// Should redirect to login
|
|
|
|
|
await expect(page).toHaveURL(/\/login/);
|
|
|
|
|
await expect(page.locator('[data-testid="login-page"]')).toBeVisible();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('protected route redirects to login when not authenticated', async ({ page }) => {
|
|
|
|
|
// Try to access dashboard without login
|
|
|
|
|
await page.goto('/dashboard');
|
|
|
|
|
|
|
|
|
|
// Should redirect to login
|
|
|
|
|
await expect(page).toHaveURL(/\/login/);
|
|
|
|
|
await expect(page.locator('[data-testid="login-page"]')).toBeVisible();
|
|
|
|
|
});
|
|
|
|
|
});
|