diff --git a/tests/e2e/access-lists.spec.ts b/tests/e2e/access-lists.spec.ts index 6abc9503..5f8d34bd 100644 --- a/tests/e2e/access-lists.spec.ts +++ b/tests/e2e/access-lists.spec.ts @@ -7,23 +7,20 @@ test.describe('Access Lists', () => { await expect(page.locator('body')).toBeVisible(); }); - test('page has an Add button', async ({ page }) => { + test('page has a Create Access List button', async ({ page }) => { await page.goto('/access-lists'); - await expect(page.getByRole('button', { name: /add/i })).toBeVisible(); + await expect(page.getByRole('button', { name: /create access list/i })).toBeVisible(); }); test('create access list — appears in the list', async ({ page }) => { await page.goto('/access-lists'); - await page.getByRole('button', { name: /add/i }).click(); + await page.getByRole('button', { name: /create access list/i }).click(); - // Fill in the name - const nameInput = page.getByLabel(/name/i).first(); - await nameInput.fill('E2E Test List'); + await expect(page.getByRole('dialog')).toBeVisible(); + await page.getByLabel('Name').fill('E2E Test List'); + await page.getByRole('button', { name: /^save$/i }).click(); - // Save - await page.getByRole('button', { name: /save|create|add/i }).last().click(); - - // Should appear in the list + await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 }); await expect(page.getByText('E2E Test List')).toBeVisible({ timeout: 10000 }); }); @@ -31,17 +28,20 @@ test.describe('Access Lists', () => { await page.goto('/access-lists'); // Create one to delete - await page.getByRole('button', { name: /add/i }).click(); - const nameInput = page.getByLabel(/name/i).first(); - await nameInput.fill('Delete This List'); - await page.getByRole('button', { name: /save|create|add/i }).last().click(); + await page.getByRole('button', { name: /create access list/i }).click(); + await expect(page.getByRole('dialog')).toBeVisible(); + await page.getByLabel('Name').fill('Delete This List'); + await page.getByRole('button', { name: /^save$/i }).click(); + + await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 }); await expect(page.getByText('Delete This List')).toBeVisible({ timeout: 10000 }); - // Delete it - const row = page.locator('tr', { hasText: 'Delete This List' }); - await row.getByRole('button', { name: /delete/i }).click(); + // Open the list and delete it + await page.getByText('Delete This List').click(); + await page.getByRole('button', { name: /delete list/i }).click(); - const confirmBtn = page.getByRole('button', { name: /confirm|yes|delete/i }); + // Confirm + const confirmBtn = page.getByRole('button', { name: /^delete$/i }); if (await confirmBtn.isVisible({ timeout: 2000 }).catch(() => false)) { await confirmBtn.click(); } diff --git a/tests/e2e/analytics.spec.ts b/tests/e2e/analytics.spec.ts index fbf8fe35..46ac113f 100644 --- a/tests/e2e/analytics.spec.ts +++ b/tests/e2e/analytics.spec.ts @@ -9,24 +9,26 @@ test.describe('Analytics', () => { test('analytics page renders content', async ({ page }) => { await page.goto('/analytics'); - // Should have analytics-related content const hasContent = await page.locator('text=/analytics|traffic|requests|blocked/i').count() > 0; expect(hasContent).toBe(true); }); - test('analytics page shows summary stats section', async ({ page }) => { + test('analytics page shows summary stat cards', async ({ page }) => { await page.goto('/analytics'); - // Stats or metrics are visible - await expect(page.locator('body')).toBeVisible(); - // The page should have some numeric or stat display - const hasStats = await page.locator('[class*="stat"], [class*="metric"], [class*="card"], [class*="summary"]').count() > 0; - // Just verify it doesn't error out — the content may vary - expect(await page.title()).toBeTruthy(); + // These card headers are rendered by AnalyticsClient + await expect(page.getByText('Total Requests')).toBeVisible({ timeout: 10000 }); + await expect(page.getByText('Unique IPs')).toBeVisible({ timeout: 10000 }); + await expect(page.getByText('Blocked Requests')).toBeVisible({ timeout: 10000 }); + }); + + test('analytics page has time range toggle buttons', async ({ page }) => { + await page.goto('/analytics'); + await expect(page.getByRole('button', { name: '24h' })).toBeVisible(); + await expect(page.getByRole('button', { name: '7d' })).toBeVisible(); }); test('analytics page does not show error content', async ({ page }) => { await page.goto('/analytics'); - // Should not show error states await expect(page.locator('text=/500|internal server error/i')).not.toBeVisible(); }); }); diff --git a/tests/e2e/audit-log.spec.ts b/tests/e2e/audit-log.spec.ts index 23df956d..415b2a83 100644 --- a/tests/e2e/audit-log.spec.ts +++ b/tests/e2e/audit-log.spec.ts @@ -19,17 +19,16 @@ test.describe('Audit Log', () => { test('creating a proxy host creates audit log entry', async ({ page }) => { // Create a proxy host await page.goto('/proxy-hosts'); - await page.getByRole('button', { name: /add/i }).click(); + await page.getByRole('button', { name: /create host/i }).click(); await expect(page.getByRole('dialog')).toBeVisible(); - const domainInput = page.getByLabel(/domain/i).first(); - await domainInput.fill('audit-test.local'); + await page.getByLabel('Name').fill('Audit Test Host'); + await page.getByLabel(/domains/i).fill('audit-test.local'); + await page.getByLabel(/target/i).fill('localhost:8888'); - const upstreamInput = page.getByLabel(/upstream/i).first(); - await upstreamInput.fill('localhost:8888'); - - await page.getByRole('button', { name: /save|create|add/i }).last().click(); - await expect(page.getByText('audit-test.local')).toBeVisible({ timeout: 10000 }); + await page.getByRole('button', { name: /^create$/i }).click(); + await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 }); + await expect(page.getByText('Audit Test Host')).toBeVisible({ timeout: 10000 }); // Check audit log await page.goto('/audit-log'); diff --git a/tests/e2e/profile.spec.ts b/tests/e2e/profile.spec.ts index 37652792..adc2fb36 100644 --- a/tests/e2e/profile.spec.ts +++ b/tests/e2e/profile.spec.ts @@ -9,44 +9,42 @@ test.describe('Profile', () => { test('profile page shows username or email', async ({ page }) => { await page.goto('/profile'); - // Should show the user's email or username (testadmin) - await expect(page.locator('text=/testadmin|testadmin@/i')).toBeVisible({ timeout: 5000 }); + // Use first() since username appears in sidebar + profile body + await expect(page.locator('text=/testadmin|testadmin@/i').first()).toBeVisible({ timeout: 5000 }); + }); + + test('Change Password button is visible', async ({ page }) => { + await page.goto('/profile'); + await expect(page.getByRole('button', { name: /change password|set password/i })).toBeVisible(); }); test('change password: wrong current password shows error', async ({ page }) => { await page.goto('/profile'); - const currentPasswordInput = page.getByLabel(/current password/i).first(); - if (await currentPasswordInput.isVisible({ timeout: 3000 }).catch(() => false)) { - await currentPasswordInput.fill('WrongCurrentPassword!'); + await page.getByRole('button', { name: /change password|set password/i }).click(); + await expect(page.getByRole('dialog')).toBeVisible(); - const newPasswordInput = page.getByLabel(/new password/i).first(); - await newPasswordInput.fill('NewPassword2026!'); + await page.getByLabel('Current Password').fill('WrongCurrentPassword!'); + await page.getByLabel('New Password').fill('NewPassword2026!'); + await page.getByLabel('Confirm New Password').fill('NewPassword2026!'); - const confirmInput = page.getByLabel(/confirm/i).first(); - if (await confirmInput.isVisible({ timeout: 1000 }).catch(() => false)) { - await confirmInput.fill('NewPassword2026!'); - } + await page.getByRole('button', { name: /change password|set password/i }).last().click(); - await page.getByRole('button', { name: /change|update|save.*password/i }).click(); - await expect(page.locator('text=/incorrect|wrong|invalid|error/i')).toBeVisible({ timeout: 5000 }); - } else { - // UI may be different - test.skip(); - } + // Should show an error alert + await expect(page.locator('[role="alert"]')).toBeVisible({ timeout: 10000 }); }); test('change password: new password too short shows validation error', async ({ page }) => { await page.goto('/profile'); - const newPasswordInput = page.getByLabel(/new password/i).first(); - if (await newPasswordInput.isVisible({ timeout: 3000 }).catch(() => false)) { - await newPasswordInput.fill('short'); - await newPasswordInput.blur(); - // Should show validation error about length - await expect(page.locator('text=/least.*char|minimum|too short/i')).toBeVisible({ timeout: 3000 }); - } else { - test.skip(); - } + await page.getByRole('button', { name: /change password|set password/i }).click(); + await expect(page.getByRole('dialog')).toBeVisible(); + + await page.getByLabel('New Password').fill('short'); + await page.getByLabel('Confirm New Password').fill('short'); + + await page.getByRole('button', { name: /change password|set password/i }).last().click(); + + await expect(page.locator('text=/at least 12 characters/i')).toBeVisible({ timeout: 5000 }); }); }); diff --git a/tests/e2e/proxy-hosts.spec.ts b/tests/e2e/proxy-hosts.spec.ts index 9d77f3a9..a6cda0b7 100644 --- a/tests/e2e/proxy-hosts.spec.ts +++ b/tests/e2e/proxy-hosts.spec.ts @@ -5,60 +5,53 @@ test.describe('Proxy Hosts', () => { await page.goto('/proxy-hosts'); }); - test('page loads with Add button visible', async ({ page }) => { - await expect(page.getByRole('button', { name: /add/i })).toBeVisible(); + test('page loads with Create Host button visible', async ({ page }) => { + await expect(page.getByRole('button', { name: /create host/i })).toBeVisible(); }); - test('clicking Add opens a dialog with form fields', async ({ page }) => { - await page.getByRole('button', { name: /add/i }).click(); - // Dialog should open with domain and upstream fields + test('clicking Create Host opens a dialog with form fields', async ({ page }) => { + await page.getByRole('button', { name: /create host/i }).click(); await expect(page.getByRole('dialog')).toBeVisible(); - await expect(page.getByLabel(/domain/i)).toBeVisible(); + await expect(page.getByLabel(/domains/i)).toBeVisible(); }); test('create a proxy host — appears in the table', async ({ page }) => { - await page.getByRole('button', { name: /add/i }).click(); + await page.getByRole('button', { name: /create host/i }).click(); await expect(page.getByRole('dialog')).toBeVisible(); - // Fill in the domain field - const domainInput = page.getByLabel(/domain/i).first(); - await domainInput.fill('e2etest.local'); + await page.getByLabel('Name').fill('E2E Test Host'); + await page.getByLabel(/domains/i).fill('e2etest.local'); + // Target is the upstream field + await page.getByLabel(/target/i).fill('localhost:9999'); - // Fill upstream - const upstreamInput = page.getByLabel(/upstream/i).first(); - await upstreamInput.fill('localhost:9999'); + await page.getByRole('button', { name: /^create$/i }).click(); - // Submit - await page.getByRole('button', { name: /save|create|add/i }).last().click(); - - // Should appear in the table - await expect(page.getByText('e2etest.local')).toBeVisible({ timeout: 10000 }); + // Dialog should close and host appear in table + await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 }); + await expect(page.getByText('E2E Test Host')).toBeVisible({ timeout: 10000 }); }); test('delete proxy host removes it from table', async ({ page }) => { - // First create one - await page.getByRole('button', { name: /add/i }).click(); + // Create one to delete + await page.getByRole('button', { name: /create host/i }).click(); await expect(page.getByRole('dialog')).toBeVisible(); - const domainInput = page.getByLabel(/domain/i).first(); - await domainInput.fill('delete-me.local'); + await page.getByLabel('Name').fill('Host To Delete'); + await page.getByLabel(/domains/i).fill('delete-me.local'); + await page.getByLabel(/target/i).fill('localhost:7777'); + await page.getByRole('button', { name: /^create$/i }).click(); - const upstreamInput = page.getByLabel(/upstream/i).first(); - await upstreamInput.fill('localhost:7777'); + await expect(page.getByRole('dialog')).not.toBeVisible({ timeout: 10000 }); + await expect(page.getByText('Host To Delete')).toBeVisible({ timeout: 10000 }); - await page.getByRole('button', { name: /save|create|add/i }).last().click(); - await expect(page.getByText('delete-me.local')).toBeVisible({ timeout: 10000 }); + // Click the Delete icon button for that row + const row = page.locator('tr', { hasText: 'Host To Delete' }); + await row.getByTitle('Delete').click(); - // Find and click the delete button for this row - const row = page.locator('tr', { hasText: 'delete-me.local' }); - await row.getByRole('button', { name: /delete/i }).click(); + // Confirm dialog + await expect(page.getByRole('dialog')).toBeVisible(); + await page.getByRole('button', { name: /^delete$/i }).click(); - // Confirm dialog if present - const confirmBtn = page.getByRole('button', { name: /confirm|yes|delete/i }); - if (await confirmBtn.isVisible({ timeout: 2000 }).catch(() => false)) { - await confirmBtn.click(); - } - - await expect(page.getByText('delete-me.local')).not.toBeVisible({ timeout: 10000 }); + await expect(page.getByText('Host To Delete')).not.toBeVisible({ timeout: 10000 }); }); }); diff --git a/tests/e2e/settings.spec.ts b/tests/e2e/settings.spec.ts index 6dd7dfc9..88ddc21f 100644 --- a/tests/e2e/settings.spec.ts +++ b/tests/e2e/settings.spec.ts @@ -9,33 +9,30 @@ test.describe('Settings', () => { test('settings page renders content', async ({ page }) => { await page.goto('/settings'); - // Settings page should have some sections - await expect(page.locator('body')).toBeVisible(); - // Check for settings-related text const hasContent = await page.locator('text=/settings|general|cloudflare|dns|logging/i').count() > 0; expect(hasContent).toBe(true); }); - test('settings page has save buttons', async ({ page }) => { + test('settings page has named save buttons', async ({ page }) => { await page.goto('/settings'); - const saveButtons = page.getByRole('button', { name: /save/i }); - await expect(saveButtons.first()).toBeVisible(); + await expect(page.getByRole('button', { name: /save general settings/i })).toBeVisible(); }); - test('general settings section: can fill and save primary domain', async ({ page }) => { + test('general settings: fill primary domain and save', async ({ page }) => { await page.goto('/settings'); - // Look for the primary domain or general settings input - const domainInput = page.getByLabel(/primary domain/i).first(); - if (await domainInput.isVisible({ timeout: 3000 }).catch(() => false)) { - await domainInput.fill('test.local'); - const saveBtn = page.getByRole('button', { name: /save/i }).first(); - await saveBtn.click(); - // Toast or success indicator should appear - await expect(page.locator('text=/saved|success/i')).toBeVisible({ timeout: 5000 }); - } else { - // If the UI is different, just verify the page loaded - test.skip(); - } + const domainInput = page.getByLabel('Primary domain'); + await domainInput.fill('test.local'); + + await page.getByRole('button', { name: /save general settings/i }).click(); + + // Wait for the button to re-enable (save completes) or any success indicator + await expect(page.getByRole('button', { name: /save general settings/i })).toBeEnabled({ timeout: 10000 }); + }); + + test('settings page has Cloudflare and DNS sections', async ({ page }) => { + await page.goto('/settings'); + await expect(page.getByRole('button', { name: /save cloudflare settings/i })).toBeVisible(); + await expect(page.getByRole('button', { name: /save dns settings/i })).toBeVisible(); }); }); diff --git a/tests/e2e/waf.spec.ts b/tests/e2e/waf.spec.ts index 41e01a16..6d2b6ed5 100644 --- a/tests/e2e/waf.spec.ts +++ b/tests/e2e/waf.spec.ts @@ -9,15 +9,19 @@ test.describe('WAF', () => { test('WAF page has global settings visible', async ({ page }) => { await page.goto('/waf'); - // Should have some WAF-related content - await expect(page.locator('body')).toBeVisible(); - // Look for WAF, mode, or enable controls const hasWafContent = await page.locator('text=/waf|mode|enabled|owasp/i').count() > 0; expect(hasWafContent).toBe(true); }); - test('WAF page has save button', async ({ page }) => { + test('WAF page has Save WAF settings button', async ({ page }) => { await page.goto('/waf'); - await expect(page.getByRole('button', { name: /save/i })).toBeVisible(); + await expect(page.getByRole('button', { name: /save waf settings/i })).toBeVisible(); + }); + + test('WAF page has tabs', async ({ page }) => { + await page.goto('/waf'); + await expect(page.getByRole('tab', { name: /events/i })).toBeVisible(); + await expect(page.getByRole('tab', { name: /suppressed rules/i })).toBeVisible(); + await expect(page.getByRole('tab', { name: /settings/i })).toBeVisible(); }); });