Files
tfm_ainventory/frontend/e2e/fixtures/auth.ts

243 lines
6.1 KiB
TypeScript

import { Page, BrowserContext } from '@playwright/test';
/**
* Authentication fixture for E2E tests
* Handles login flows and session management
*/
export interface LoginCredentials {
username: string;
password: string;
}
export interface AuthSession {
token: string;
userId: string;
email: string;
isAdmin: boolean;
}
/**
* Perform LDAP authentication
*/
export async function loginWithLdap(
page: Page,
credentials: LoginCredentials,
baseUrl: string = 'http://localhost'
): Promise<void> {
await page.goto(`${baseUrl}`);
// Wait for login form to appear
await page.waitForSelector('[data-testid="login-form"]', { timeout: 5000 });
// Fill in username
await page.fill('[data-testid="username-input"]', credentials.username);
// Fill in password
await page.fill('[data-testid="password-input"]', credentials.password);
// Click login button
await page.click('[data-testid="login-submit"]');
// Wait for navigation to inventory page
await page.waitForURL(`${baseUrl}/*`, { waitUntil: 'networkidle' });
}
/**
* Perform local user authentication
*/
export async function loginWithLocalUser(
page: Page,
credentials: LoginCredentials,
baseUrl: string = 'http://localhost'
): Promise<void> {
await page.goto(`${baseUrl}`);
// Wait for identity check overlay
await page.waitForSelector('[data-testid="identity-check-overlay"]', { timeout: 5000 });
// Click "Local User" tab if it exists
const localUserTab = page.locator('[data-testid="local-user-tab"]');
if (await localUserTab.isVisible()) {
await localUserTab.click();
}
// Fill in username
await page.fill('[data-testid="local-username-input"]', credentials.username);
// Fill in password
await page.fill('[data-testid="local-password-input"]', credentials.password);
// Click login button
await page.click('[data-testid="local-login-submit"]');
// Wait for navigation to inventory page
await page.waitForURL(`${baseUrl}/*`, { waitUntil: 'networkidle' });
}
/**
* Logout from the application
*/
export async function logout(page: Page, baseUrl: string = 'http://localhost'): Promise<void> {
// Click logout button or menu
const logoutButton = page.locator('[data-testid="logout-button"]');
if (await logoutButton.isVisible()) {
await logoutButton.click();
} else {
// Try finding it in menu
const menu = page.locator('[data-testid="user-menu"]');
if (await menu.isVisible()) {
await menu.click();
await page.click('[data-testid="logout-menu-item"]');
}
}
// Wait for redirect to login
await page.waitForURL(`${baseUrl}/*`, { timeout: 5000 });
}
/**
* Get authentication token from local storage
*/
export async function getAuthToken(page: Page): Promise<string | null> {
const token = await page.evaluate(() => {
return localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token');
});
return token;
}
/**
* Set authentication token in local storage
*/
export async function setAuthToken(page: Page, token: string): Promise<void> {
await page.evaluate((t) => {
localStorage.setItem('auth_token', t);
}, token);
}
/**
* Clear authentication data
*/
export async function clearAuth(page: Page): Promise<void> {
await page.evaluate(() => {
localStorage.removeItem('auth_token');
sessionStorage.removeItem('auth_token');
localStorage.removeItem('user_data');
sessionStorage.removeItem('user_data');
});
}
/**
* Check if user is authenticated
*/
export async function isAuthenticated(page: Page): Promise<boolean> {
const token = await getAuthToken(page);
return !!token;
}
/**
* Get current user info from page
*/
export async function getCurrentUser(page: Page): Promise<{ username: string; email: string } | null> {
try {
const userData = await page.evaluate(() => {
const stored = localStorage.getItem('user_data') || sessionStorage.getItem('user_data');
return stored ? JSON.parse(stored) : null;
});
return userData;
} catch {
return null;
}
}
/**
* Wait for authentication to complete
*/
export async function waitForAuth(page: Page, timeout: number = 5000): Promise<void> {
// Wait for either:
// 1. Token to be in storage
// 2. Navigation to happen
// 3. Auth overlay to disappear
await page.waitForFunction(
() => {
const token =
typeof window !== 'undefined'
? localStorage.getItem('auth_token') || sessionStorage.getItem('auth_token')
: null;
return !!token;
},
{ timeout }
);
}
/**
* Verify login was successful by checking inventory page loaded
*/
export async function verifyLoginSuccess(page: Page, baseUrl: string = 'http://localhost'): Promise<boolean> {
try {
// Check if we're on the inventory page
await page.waitForURL(/inventory|dashboard/, { timeout: 5000 });
return true;
} catch {
return false;
}
}
/**
* Create test user via API (helper for setup)
*/
export async function createTestUserViaApi(
baseUrl: string,
token: string,
user: { username: string; password: string; email: string }
): Promise<{ id: string; username: string }> {
const response = await fetch(`${baseUrl}/api/admin/users`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
},
body: JSON.stringify(user),
});
if (!response.ok) {
throw new Error(`Failed to create user: ${response.statusText}`);
}
return response.json();
}
/**
* Delete test user via API (helper for cleanup)
*/
export async function deleteTestUserViaApi(
baseUrl: string,
token: string,
userId: string
): Promise<void> {
const response = await fetch(`${baseUrl}/api/admin/users/${userId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`,
},
});
if (!response.ok) {
throw new Error(`Failed to delete user: ${response.statusText}`);
}
}
/**
* Helper to setup admin context for tests
*/
export async function setupAdminContext(
context: BrowserContext,
baseUrl: string,
adminToken: string
): Promise<void> {
// Add auth headers to all API requests
await context.addInitScript((token) => {
(window as any).__testAuthToken = token;
}, adminToken);
}