towerops/e2e/tests/alerts.spec.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

117 lines
3.7 KiB
TypeScript

import { test, expect } from '@playwright/test';
test.describe('Alerts', () => {
test.beforeEach(async ({ page }) => {
// Navigate to organizations and select the first one
await page.goto('/orgs');
await page.locator('[data-test="organization-card"]').first().click();
await expect(page).toHaveURL(/\/orgs\/[^\/]+/);
});
test('can view alerts page', async ({ page }) => {
// Navigate to alerts
await page.getByRole('link', { name: /alerts/i }).click();
// Check URL
await expect(page).toHaveURL(/\/alerts/);
// Check for page heading
await expect(page.getByRole('heading', { name: /alerts/i })).toBeVisible();
});
test('alerts page shows alert list', async ({ page }) => {
await page.goto('/alerts');
await page.waitForLoadState('networkidle');
// Check for alerts list or empty state
const hasAlerts = await page.locator('[data-test="alert-item"], table tbody tr').count() > 0;
const hasEmptyState = await page.locator('text=/no alerts|no active alerts/i').count() > 0;
// Should show either alerts or empty state
expect(hasAlerts || hasEmptyState).toBeTruthy();
});
test('can filter alerts by status', async ({ page }) => {
await page.goto('/alerts');
// Look for status filter buttons/tabs
const activeFilter = page.getByRole('button', { name: /active|open/i });
const resolvedFilter = page.getByRole('button', { name: /resolved|closed/i });
if (await activeFilter.count() > 0) {
// Click active filter
await activeFilter.click();
await page.waitForLoadState('networkidle');
// URL or UI should reflect active filter
// Adjust based on your implementation
}
if (await resolvedFilter.count() > 0) {
// Click resolved filter
await resolvedFilter.click();
await page.waitForLoadState('networkidle');
// URL or UI should reflect resolved filter
}
});
test('alert items show severity indicators', async ({ page }) => {
await page.goto('/alerts');
await page.waitForLoadState('networkidle');
// Check for severity indicators (critical/warning)
const alertItems = page.locator('[data-test="alert-item"], table tbody tr');
if (await alertItems.count() > 0) {
const firstAlert = alertItems.first();
await expect(firstAlert).toBeVisible();
// Should have severity indicator (color, icon, badge)
// Adjust based on your alert item markup
}
});
test('can view alert details', async ({ page }) => {
await page.goto('/alerts');
await page.waitForLoadState('networkidle');
// Click first alert if any exist
const firstAlert = page.locator('[data-test="alert-item"], table tbody tr a').first();
if (await firstAlert.count() > 0) {
await firstAlert.click();
// Should show alert details (either modal or detail page)
await page.waitForTimeout(500);
// Check for alert detail content
// Adjust based on your implementation (modal vs page)
const hasModal = await page.locator('[role="dialog"], .modal').count() > 0;
const hasDetailPage = page.url().includes('/alerts/');
expect(hasModal || hasDetailPage).toBeTruthy();
}
});
test('can acknowledge/resolve an alert', async ({ page }) => {
await page.goto('/alerts');
await page.waitForLoadState('networkidle');
// Look for resolve/acknowledge button
const resolveButton = page.getByRole('button', { name: /resolve|acknowledge/i }).first();
if (await resolveButton.count() > 0) {
await resolveButton.click();
// Should show confirmation or success message
await expect(
page.locator('text=/resolved|acknowledged/i')
).toBeVisible({ timeout: 3000 });
}
});
});