- 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
74 lines
2.1 KiB
TypeScript
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();
|
|
}
|