Files
michaelschiemer/tests/e2e/support/test-helpers.ts
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- Add comprehensive health check system with multiple endpoints
- Add Prometheus metrics endpoint
- Add production logging configurations (5 strategies)
- Add complete deployment documentation suite:
  * QUICKSTART.md - 30-minute deployment guide
  * DEPLOYMENT_CHECKLIST.md - Printable verification checklist
  * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle
  * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference
  * production-logging.md - Logging configuration guide
  * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation
  * README.md - Navigation hub
  * DEPLOYMENT_SUMMARY.md - Executive summary
- Add deployment scripts and automation
- Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment
- Update README with production-ready features

All production infrastructure is now complete and ready for deployment.
2025-10-25 19:18:37 +02:00

149 lines
3.6 KiB
TypeScript

import { Page, expect } from '@playwright/test';
/**
* Helper utilities for Playwright E2E tests
*/
/**
* Wait for LiveComponent to be ready
*/
export async function waitForLiveComponent(page: Page, componentId: string) {
await page.waitForSelector(`[data-live-component="${componentId}"]`, {
state: 'visible',
timeout: 5000
});
}
/**
* Wait for HTMX request to complete
*/
export async function waitForHtmxRequest(page: Page) {
await page.waitForEvent('htmx:afterRequest', { timeout: 5000 });
}
/**
* Fill form and submit with LiveComponent validation
*/
export async function fillAndSubmitForm(
page: Page,
formSelector: string,
data: Record<string, string>
) {
const form = page.locator(formSelector);
for (const [name, value] of Object.entries(data)) {
await form.locator(`[name="${name}"]`).fill(value);
}
await form.locator('button[type="submit"]').click();
}
/**
* Assert validation error is shown
*/
export async function assertValidationError(
page: Page,
fieldName: string,
expectedError?: string
) {
const errorSelector = `[data-field="${fieldName}"] .error-message, .error-${fieldName}`;
await expect(page.locator(errorSelector)).toBeVisible();
if (expectedError) {
await expect(page.locator(errorSelector)).toContainText(expectedError);
}
}
/**
* Assert no validation errors
*/
export async function assertNoValidationErrors(page: Page) {
const errors = page.locator('.error-message, [class*="error-"]');
await expect(errors).toHaveCount(0);
}
/**
* Wait for success message
*/
export async function waitForSuccessMessage(page: Page, message?: string) {
const successSelector = '.flash-success, .alert-success, [role="alert"][data-type="success"]';
await expect(page.locator(successSelector)).toBeVisible();
if (message) {
await expect(page.locator(successSelector)).toContainText(message);
}
}
/**
* Login helper for authenticated tests
*/
export async function login(page: Page, email: string, password: string) {
await page.goto('/login');
await page.fill('[name="email"]', email);
await page.fill('[name="password"]', password);
await page.click('button[type="submit"]');
// Wait for redirect after login
await page.waitForURL('**/dashboard', { timeout: 5000 });
}
/**
* Logout helper
*/
export async function logout(page: Page) {
await page.click('[data-action="logout"]');
await page.waitForURL('**/login', { timeout: 5000 });
}
/**
* Check if element is visible in viewport
*/
export async function isInViewport(page: Page, selector: string): Promise<boolean> {
return await page.locator(selector).evaluate((element) => {
const rect = element.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
});
}
/**
* Scroll element into view
*/
export async function scrollIntoView(page: Page, selector: string) {
await page.locator(selector).scrollIntoViewIfNeeded();
}
/**
* Upload file helper
*/
export async function uploadFile(
page: Page,
inputSelector: string,
filePath: string
) {
const fileInput = page.locator(inputSelector);
await fileInput.setInputFiles(filePath);
}
/**
* Wait for network idle
*/
export async function waitForNetworkIdle(page: Page) {
await page.waitForLoadState('networkidle', { timeout: 10000 });
}
/**
* Clear browser storage
*/
export async function clearStorage(page: Page) {
await page.context().clearCookies();
await page.evaluate(() => {
localStorage.clear();
sessionStorage.clear();
});
}