test(e2e): repair 26 Playwright smoke-test failures
Failures were mostly stale selectors, not product regressions:
- .or() traps matching the topbar "+ New" button → use specific names
(Add Webhook, New Field, New Template)
- broad /create|add|new/ patterns → same fix
- [role="dialog"] overlay matched before content → getByRole('dialog').last()
- locator('input') picked hidden Radix Select inputs → getByPlaceholder /
getByRole('combobox', { name })
- 11-global-search rewritten for the inline topbar search (the cmdk
CommandDialog the old tests targeted was replaced)
- missing .first() causing strict-mode failures on notifications heading,
version history text, nav links
- dashboard landing test: no h1 exists, target KPI text instead
- activity-feed: items aren't anchors; match action badge text
- monitoring data-leak check scoped to <main> (sidebar has Email/Documents)
- admin API without port context returns 400 (not 403) for non-admins —
accept 400 as a valid "blocked" status in the sales-agent test
Also dropped dead imports and unused locals surfaced by lint-staged.
Full suite: 124 passed (11.2m).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,12 +15,8 @@ test.describe('Admin Features', () => {
|
||||
const heading = page.getByText(/webhook/i).first();
|
||||
await expect(heading).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// "Add Webhook" / "Create" / "New Webhook" button
|
||||
const addBtn = page
|
||||
.getByRole('button', { name: /add webhook|create|new webhook/i })
|
||||
.first()
|
||||
.or(page.getByRole('button', { name: /add|new|create/i }).first());
|
||||
|
||||
// "Add Webhook" button on the page (not the topbar "+ New")
|
||||
const addBtn = page.getByRole('button', { name: 'Add Webhook' }).first();
|
||||
await expect(addBtn).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
@@ -28,14 +24,24 @@ test.describe('Admin Features', () => {
|
||||
await navigateTo(page, '/admin/webhooks');
|
||||
await page.waitForTimeout(2_000);
|
||||
|
||||
// Should show either a table of existing webhooks or an empty-state message
|
||||
const hasTable = await page.locator('table').isVisible({ timeout: 5_000 }).catch(() => false);
|
||||
// Should show either existing webhooks (table or cards) or an empty-state message
|
||||
const hasTable = await page
|
||||
.locator('table')
|
||||
.first()
|
||||
.isVisible({ timeout: 3_000 })
|
||||
.catch(() => false);
|
||||
const hasWebhookUrl = await page
|
||||
.getByText(/^https?:\/\//i)
|
||||
.first()
|
||||
.isVisible({ timeout: 3_000 })
|
||||
.catch(() => false);
|
||||
const hasEmptyState = await page
|
||||
.getByText(/no webhooks|add your first|get started/i)
|
||||
.isVisible({ timeout: 5_000 })
|
||||
.first()
|
||||
.isVisible({ timeout: 3_000 })
|
||||
.catch(() => false);
|
||||
|
||||
expect(hasTable || hasEmptyState).toBeTruthy();
|
||||
expect(hasTable || hasWebhookUrl || hasEmptyState).toBeTruthy();
|
||||
});
|
||||
|
||||
// ── Custom Fields ─────────────────────────────────────────────────────────
|
||||
@@ -44,26 +50,15 @@ test.describe('Admin Features', () => {
|
||||
await navigateTo(page, '/admin/custom-fields');
|
||||
await page.waitForTimeout(2_000);
|
||||
|
||||
const heading = page.getByText(/custom field/i).first();
|
||||
const heading = page.getByRole('heading', { name: /custom fields/i }).first();
|
||||
await expect(heading).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// Should have tabs for each entity type
|
||||
const clientsTab = page
|
||||
.getByRole('tab', { name: /client/i })
|
||||
.first()
|
||||
.or(page.getByText(/clients/i).first());
|
||||
|
||||
const clientsTab = page.getByRole('tab', { name: /client/i }).first();
|
||||
await expect(clientsTab).toBeVisible({ timeout: 5_000 });
|
||||
|
||||
const interestsTab = page
|
||||
.getByRole('tab', { name: /interest/i })
|
||||
.first()
|
||||
.or(page.getByText(/interests/i).first());
|
||||
|
||||
const berthsTab = page
|
||||
.getByRole('tab', { name: /berth/i })
|
||||
.first()
|
||||
.or(page.getByText(/berths/i).first());
|
||||
const interestsTab = page.getByRole('tab', { name: /interest/i }).first();
|
||||
const berthsTab = page.getByRole('tab', { name: /berth/i }).first();
|
||||
|
||||
const hasInterests = await interestsTab.isVisible({ timeout: 3_000 }).catch(() => false);
|
||||
const hasBerths = await berthsTab.isVisible({ timeout: 3_000 }).catch(() => false);
|
||||
@@ -77,11 +72,7 @@ test.describe('Admin Features', () => {
|
||||
await navigateTo(page, '/admin/custom-fields');
|
||||
await page.waitForTimeout(2_000);
|
||||
|
||||
const newFieldBtn = page
|
||||
.getByRole('button', { name: /new field|add field|create field/i })
|
||||
.first()
|
||||
.or(page.getByRole('button', { name: /add|new|create/i }).first());
|
||||
|
||||
const newFieldBtn = page.getByRole('button', { name: 'New Field' }).first();
|
||||
await expect(newFieldBtn).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
@@ -99,7 +90,10 @@ test.describe('Admin Features', () => {
|
||||
await page.waitForTimeout(1_000);
|
||||
|
||||
// Page should not crash
|
||||
const body = await page.locator('body').textContent().catch(() => '');
|
||||
const body = await page
|
||||
.locator('body')
|
||||
.textContent()
|
||||
.catch(() => '');
|
||||
expect(body && body.length > 10).toBeTruthy();
|
||||
|
||||
// Click back to first tab
|
||||
@@ -124,8 +118,15 @@ test.describe('Admin Features', () => {
|
||||
await navigateTo(page, '/admin/templates');
|
||||
await page.waitForTimeout(2_000);
|
||||
|
||||
const hasTable = await page.locator('table').isVisible({ timeout: 5_000 }).catch(() => false);
|
||||
const hasCards = await page.locator('[class*="card"], [class*="template"]').first().isVisible({ timeout: 3_000 }).catch(() => false);
|
||||
const hasTable = await page
|
||||
.locator('table')
|
||||
.isVisible({ timeout: 5_000 })
|
||||
.catch(() => false);
|
||||
const hasCards = await page
|
||||
.locator('[class*="card"], [class*="template"]')
|
||||
.first()
|
||||
.isVisible({ timeout: 3_000 })
|
||||
.catch(() => false);
|
||||
const hasEmptyState = await page
|
||||
.getByText(/no templates|create your first|get started/i)
|
||||
.isVisible({ timeout: 5_000 })
|
||||
@@ -138,11 +139,7 @@ test.describe('Admin Features', () => {
|
||||
await navigateTo(page, '/admin/templates');
|
||||
await page.waitForTimeout(2_000);
|
||||
|
||||
const createBtn = page
|
||||
.getByRole('button', { name: /create|add|new template/i })
|
||||
.first()
|
||||
.or(page.getByRole('button', { name: /add|new|create/i }).first());
|
||||
|
||||
const createBtn = page.getByRole('button', { name: 'New Template' }).first();
|
||||
await expect(createBtn).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
@@ -197,13 +194,9 @@ test.describe('Admin Features', () => {
|
||||
await page.waitForTimeout(3_000);
|
||||
|
||||
// Look for a refresh button or auto-refresh indicator
|
||||
const refreshBtn = page
|
||||
.getByRole('button', { name: /refresh|reload/i })
|
||||
.first();
|
||||
const refreshBtn = page.getByRole('button', { name: /refresh|reload/i }).first();
|
||||
|
||||
const autoRefreshToggle = page
|
||||
.getByText(/auto.?refresh|live|polling/i)
|
||||
.first();
|
||||
const autoRefreshToggle = page.getByText(/auto.?refresh|live|polling/i).first();
|
||||
|
||||
const hasRefresh = await refreshBtn.isVisible({ timeout: 3_000 }).catch(() => false);
|
||||
const hasAutoRefresh = await autoRefreshToggle.isVisible({ timeout: 3_000 }).catch(() => false);
|
||||
@@ -223,7 +216,10 @@ test.describe('Admin Features', () => {
|
||||
|
||||
if (!hasStats) {
|
||||
// Broader search: any element containing just a number
|
||||
const anyNumber = page.locator('[class*="count"], [class*="stat"], [class*="badge"]').filter({ hasText: /^\d+$/ }).first();
|
||||
const anyNumber = page
|
||||
.locator('[class*="count"], [class*="stat"], [class*="badge"]')
|
||||
.filter({ hasText: /^\d+$/ })
|
||||
.first();
|
||||
const hasAnyNumber = await anyNumber.isVisible({ timeout: 3_000 }).catch(() => false);
|
||||
expect(hasAnyNumber || true).toBeTruthy();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user