towerops/e2e/tests/helpers.ts
Graham McIntire 17bf1f83e8
feat: add Playwright e2e test suite
- Set up Playwright in dedicated e2e directory
- Multi-environment support (local, staging)
- TOTP authentication handling with otplib
- Test coverage for organizations, devices, alerts, status indicators
- Helper utilities for common test operations
- Comprehensive README with setup and usage instructions
- Setup script for quick initialization
2026-03-06 15:02:56 -06:00

74 lines
2.1 KiB
TypeScript

import { Page, expect } from '@playwright/test';
/**
* Navigate to organizations and select the first one
*/
export async function selectFirstOrganization(page: Page) {
await page.goto('/orgs');
const firstOrg = page.locator('[data-test="organization-card"]').first();
await firstOrg.waitFor({ timeout: 10000 });
await firstOrg.click();
await expect(page).toHaveURL(/\/orgs\/[^\/]+/);
}
/**
* Wait for LiveView to be fully connected
*/
export async function waitForLiveView(page: Page) {
// Wait for Phoenix LiveView to connect
await page.waitForFunction(() => {
// @ts-ignore
return window.liveSocket && window.liveSocket.isConnected();
}, { timeout: 10000 }).catch(() => {
// Fallback to network idle if LiveView check doesn't work
return page.waitForLoadState('networkidle');
});
}
/**
* Check if an element exists on the page
*/
export async function elementExists(page: Page, selector: string): Promise<boolean> {
return (await page.locator(selector).count()) > 0;
}
/**
* Get status emoji from page title
*/
export async function getStatusEmoji(page: Page): Promise<string | null> {
const title = await page.title();
const match = title.match(/^([🔴🟡🟢])/);
return match ? match[1] : null;
}
/**
* Wait for page title to update with specific emoji
*/
export async function waitForStatusEmoji(page: Page, expectedEmoji: string, timeout = 5000) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const emoji = await getStatusEmoji(page);
if (emoji === expectedEmoji) {
return true;
}
await page.waitForTimeout(100);
}
throw new Error(`Status emoji did not update to ${expectedEmoji} within ${timeout}ms`);
}
/**
* Fill a form and submit it
*/
export async function fillAndSubmitForm(
page: Page,
formSelector: string,
fields: Record<string, string>
) {
for (const [label, value] of Object.entries(fields)) {
await page.getByLabel(new RegExp(label, 'i')).fill(value);
}
await page.locator(formSelector).getByRole('button', { name: /submit|save|create/i }).click();
}