Files
jdescopingtool/TestScripts/playwright/fixtures/test.fixture.ts
T
Joseph Doherty ee044d03e0 feat: add health check endpoint, file upload result handling, and Playwright E2E tests
- Add /health endpoint with anonymous access for monitoring
- Add FileUploadResult<T> model and PostMultipartForFileResultAsync for proper upload response handling
- Add ApiResult.Success() factory method for interface types
- Refactor Login.razor for cleaner code
- Add comprehensive Playwright E2E test suite with fixtures and helpers
2026-01-30 07:12:20 -05:00

136 lines
3.6 KiB
TypeScript

import { test as base, expect, Page } from '@playwright/test';
import { login, ensureLoggedIn } from '../helpers/auth.helper';
import { waitForBlazorReady } from '../helpers/navigation.helper';
/**
* Extended test fixture with authentication state and common utilities
*/
export const test = base.extend<{
/** Page that is already logged in */
authenticatedPage: Page;
}>({
authenticatedPage: async ({ page }, use) => {
// Navigate to the app and login
await page.goto('/');
await page.waitForLoadState('networkidle', { timeout: 60000 });
await login(page);
await waitForBlazorReady(page);
// Provide the authenticated page to the test
await use(page);
},
});
export { expect };
/**
* Test configuration options
*/
export const testConfig = {
/** Default timeout for waiting operations */
defaultTimeout: 30000,
/** Timeout for Blazor WASM initialization */
blazorTimeout: 120000,
/** Timeout for network operations */
networkTimeout: 60000,
/** Short wait for UI updates */
shortWait: 500,
/** Medium wait for async operations */
mediumWait: 2000,
/** Long wait for file operations */
longWait: 5000,
};
/**
* Common test setup function
* @param page - Playwright page object
* @param route - Route to navigate to after login (default: '/search')
*/
export async function setupTest(page: Page, route: string = '/search'): Promise<void> {
await page.goto(route);
await page.waitForLoadState('networkidle', { timeout: testConfig.networkTimeout });
await ensureLoggedIn(page);
await waitForBlazorReady(page);
}
/**
* Test data factory for generating unique test names
*/
export function generateTestName(prefix: string): string {
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
return `${prefix}-${timestamp}`;
}
/**
* Test utilities for common assertions
*/
export const testUtils = {
/**
* Wait for page to stabilize after navigation
*/
async waitForStable(page: Page): Promise<void> {
await page.waitForLoadState('networkidle', { timeout: testConfig.networkTimeout });
await page.waitForTimeout(testConfig.shortWait);
},
/**
* Assert page URL contains expected path
*/
async assertUrlContains(page: Page, expectedPath: string): Promise<void> {
await expect(page).toHaveURL(new RegExp(expectedPath));
},
/**
* Assert element is visible with custom timeout
*/
async assertVisible(page: Page, selector: string, timeout?: number): Promise<void> {
await expect(page.locator(selector)).toBeVisible({
timeout: timeout ?? testConfig.defaultTimeout,
});
},
/**
* Assert element is not visible
*/
async assertNotVisible(page: Page, selector: string, timeout?: number): Promise<void> {
await expect(page.locator(selector)).not.toBeVisible({
timeout: timeout ?? testConfig.defaultTimeout,
});
},
/**
* Assert text is present on page
*/
async assertTextPresent(page: Page, text: string): Promise<void> {
await expect(page.locator(`text=${text}`)).toBeVisible();
},
/**
* Assert text is not present on page
*/
async assertTextNotPresent(page: Page, text: string): Promise<void> {
await expect(page.locator(`text=${text}`)).not.toBeVisible();
},
};
/**
* Skip test if condition is met
*/
export function skipIf(condition: boolean, reason: string): void {
if (condition) {
test.skip(true, reason);
}
}
/**
* Mark test as slow (extends timeout)
*/
export function markSlow(): void {
test.slow();
}