veritas-kanban/e2e/task-status.spec.ts
Brad Groux de388dc377 feat: complete test coverage sprint - backend 53.5% + E2E 19/19 passing
Backend tests (9 new files):
- summary-service, metrics-helpers, template-service
- status-history-service, trace-service, diff-service
- conflict-service, preview-service, digest-service
- Coverage: 44.27% → 53.52% (897 tests passing)

E2E tests (fixed + hardened):
- Removed debug spec left by sub-agent
- API seed calls now hit backend directly (port 3001)
  to avoid IPv6/Vite proxy issues on macOS
- task-status tests use unique names (prevent strict mode)
- Added waitForResponse for status change assertions
- Enabled webServer auto-start in playwright.config.ts
- All 19 E2E tests passing
2026-01-28 22:42:16 -06:00

128 lines
4.4 KiB
TypeScript

import { test, expect } from '@playwright/test';
import { bypassAuth, seedTestTask, deleteTask, cleanupRoutes } from './helpers/auth';
test.describe('Task Status Change', () => {
let testTaskId: string | null = null;
test.beforeEach(async ({ page }) => {
await bypassAuth(page);
});
test.afterEach(async ({ page }) => {
if (testTaskId) {
await deleteTask(page, testTaskId).catch(() => {});
testTaskId = null;
}
await cleanupRoutes(page);
});
test('change task status via detail panel dropdown', async ({ page }) => {
const uniqueTitle = `E2E Status Change ${Date.now()}`;
const task = await seedTestTask(page, {
title: uniqueTitle,
status: 'todo',
priority: 'medium',
});
testTaskId = (task as { id: string }).id;
await page.goto('/');
// Verify the task is in the To Do column
const todoColumn = page.getByRole('region', { name: /To Do column/ });
await expect(todoColumn.locator(`text=${uniqueTitle}`)).toBeVisible({
timeout: 15_000,
});
// Click the task to open the detail panel
await todoColumn.locator(`text=${uniqueTitle}`).click();
const detailPanel = page.locator('[role="dialog"]');
await expect(detailPanel).toBeVisible({ timeout: 5_000 });
// The metadata section has a grid: Status | Type | Priority
// Status is the first Select in the grid
const statusSection = detailPanel.locator('label:has-text("Status")').locator('..');
const statusTrigger = statusSection.locator('button[role="combobox"]');
await expect(statusTrigger).toBeVisible();
await statusTrigger.click();
// Select "In Progress" and wait for the API PATCH response
const inProgressOption = page.getByRole('option', { name: 'In Progress' });
await expect(inProgressOption).toBeVisible();
const [patchResponse] = await Promise.all([
page.waitForResponse(
(resp) => resp.url().includes('/api/tasks/') && resp.request().method() === 'PATCH',
{ timeout: 10_000 }
),
inProgressOption.click(),
]);
// Ensure the PATCH succeeded
expect(patchResponse.status()).toBeLessThan(400);
// Close the detail panel
await page.keyboard.press('Escape');
await expect(detailPanel).not.toBeVisible({ timeout: 3_000 });
// Wait for the task to move to the In Progress column
const inProgressColumn = page.getByRole('region', { name: /In Progress column/ });
await expect(inProgressColumn.locator(`text=${uniqueTitle}`)).toBeVisible({
timeout: 10_000,
});
// Verify it's no longer in the To Do column
await expect(todoColumn.locator(`text=${uniqueTitle}`)).not.toBeVisible();
});
test('change task status to done via detail panel', async ({ page }) => {
const uniqueTitle = `E2E Done Task ${Date.now()}`;
const task = await seedTestTask(page, {
title: uniqueTitle,
status: 'in-progress',
priority: 'low',
});
testTaskId = (task as { id: string }).id;
await page.goto('/');
// Verify the task starts in In Progress
const inProgressCol = page.getByRole('region', { name: /In Progress column/ });
await expect(inProgressCol.locator(`text=${uniqueTitle}`)).toBeVisible({ timeout: 15_000 });
// Open the detail panel
await inProgressCol.locator(`text=${uniqueTitle}`).click();
const detailPanel = page.locator('[role="dialog"]');
await expect(detailPanel).toBeVisible({ timeout: 5_000 });
// Find the Status dropdown
const statusSection = detailPanel.locator('label:has-text("Status")').locator('..');
const statusTrigger = statusSection.locator('button[role="combobox"]');
await expect(statusTrigger).toBeVisible();
await statusTrigger.click();
// Select "Done" and wait for the API PATCH
const doneOption = page.getByRole('option', { name: 'Done' });
await expect(doneOption).toBeVisible();
const [patchResponse] = await Promise.all([
page.waitForResponse(
(resp) => resp.url().includes('/api/tasks/') && resp.request().method() === 'PATCH',
{ timeout: 10_000 }
),
doneOption.click(),
]);
expect(patchResponse.status()).toBeLessThan(400);
// Close panel
await page.keyboard.press('Escape');
// Verify the task moved to Done
const doneCol = page.getByRole('region', { name: /Done column/ });
await expect(doneCol.locator(`text=${uniqueTitle}`)).toBeVisible({ timeout: 10_000 });
});
});