119 lines
4.5 KiB
TypeScript
119 lines
4.5 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
||||
|
|
import { login } from './helpers/auth';
|
|||
|
|
import { testTasks } from './helpers/fixtures';
|
|||
|
|
|
|||
|
|
test.describe('Task Management', () => {
|
|||
|
|
test.beforeEach(async ({ page }) => {
|
|||
|
|
await login(page);
|
|||
|
|
await page.goto('/tasks');
|
|||
|
|
// Wait for the tasks page to load
|
|||
|
|
await expect(page.getByRole('heading', { name: /tasks/i })).toBeVisible();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test.describe('Tasks Kanban Board', () => {
|
|||
|
|
test('should display Kanban board with three columns', async ({ page }) => {
|
|||
|
|
// Should show the three Kanban columns
|
|||
|
|
await expect(page.getByRole('heading', { name: /to do/i })).toBeVisible();
|
|||
|
|
await expect(page.getByRole('heading', { name: /in progress/i })).toBeVisible();
|
|||
|
|
await expect(page.getByRole('heading', { name: /done/i })).toBeVisible();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('should switch between Board and List views', async ({ page }) => {
|
|||
|
|
// Default is Board (Kanban) view
|
|||
|
|
await expect(page.getByText('Board').first()).toHaveAttribute('data-state', 'active');
|
|||
|
|
|
|||
|
|
// Switch to List view
|
|||
|
|
await page.getByRole('tab', { name: /list/i }).click();
|
|||
|
|
await expect(page.getByText('List').first()).toHaveAttribute('data-state', 'active');
|
|||
|
|
|
|||
|
|
// Switch back to Board
|
|||
|
|
await page.getByRole('tab', { name: /board/i }).click();
|
|||
|
|
await expect(page.getByText('Board').first()).toHaveAttribute('data-state', 'active');
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test.describe('Task CRUD', () => {
|
|||
|
|
test('should create a new task via detail panel', async ({ page }) => {
|
|||
|
|
// Intercept the API call
|
|||
|
|
const createResponsePromise = page.waitForResponse(
|
|||
|
|
(resp) => resp.url().includes('/api/tasks') && resp.request().method() === 'POST',
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// Click on the "To Do" column area to open task creation
|
|||
|
|
// The app uses a TaskDetailPanel for creation
|
|||
|
|
// We'll look for an existing task or the create mechanism
|
|||
|
|
// The kanban view fetches tasks - let's verify it loaded
|
|||
|
|
await expect(page.getByRole('list', { name: /to do/i })).toBeVisible();
|
|||
|
|
|
|||
|
|
// Find and click a task if one exists, or trigger creation
|
|||
|
|
// The task detail panel opens when clicking a task
|
|||
|
|
// For creation, the API can be called directly - verify the flow works
|
|||
|
|
const taskCards = page.locator('[role="list"] [class*="cursor-grab"]');
|
|||
|
|
const count = await taskCards.count();
|
|||
|
|
|
|||
|
|
// If tasks exist, click one to open detail panel
|
|||
|
|
if (count > 0) {
|
|||
|
|
await taskCards.first().click();
|
|||
|
|
// Detail panel should open
|
|||
|
|
await expect(page.getByRole('dialog')).toBeVisible({ timeout: 5_000 });
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test('should open task detail panel when clicking a task', async ({ page }) => {
|
|||
|
|
// Look for any task card in the kanban board
|
|||
|
|
const taskCards = page.locator('[role="list"] [class*="cursor-grab"]');
|
|||
|
|
const count = await taskCards.count();
|
|||
|
|
|
|||
|
|
if (count > 0) {
|
|||
|
|
await taskCards.first().click();
|
|||
|
|
// Detail panel should appear as a dialog/sheet
|
|||
|
|
await expect(page.getByRole('dialog')).toBeVisible({ timeout: 5_000 });
|
|||
|
|
} else {
|
|||
|
|
// No tasks yet – skip gracefully
|
|||
|
|
test.skip();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test.describe('Task Drag and Drop', () => {
|
|||
|
|
test('should have draggable task cards', async ({ page }) => {
|
|||
|
|
// Verify draggable elements exist
|
|||
|
|
const draggableTasks = page.locator('[role="list"] [class*="cursor-grab"]');
|
|||
|
|
const count = await draggableTasks.count();
|
|||
|
|
|
|||
|
|
if (count > 0) {
|
|||
|
|
// Verify the first task has cursor-grab (indicating draggable)
|
|||
|
|
await expect(draggableTasks.first()).toHaveClass(/cursor-grab/);
|
|||
|
|
} else {
|
|||
|
|
test.skip();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test.describe('Task Filtering and Search', () => {
|
|||
|
|
test('should display domain badges on tasks', async ({ page }) => {
|
|||
|
|
// Tasks should show domain badges
|
|||
|
|
const domainBadges = page.locator('[role="list"] [class*="badge"]');
|
|||
|
|
const count = await domainBadges.count();
|
|||
|
|
|
|||
|
|
if (count > 0) {
|
|||
|
|
// At least one badge should be visible
|
|||
|
|
await expect(domainBadges.first()).toBeVisible();
|
|||
|
|
} else {
|
|||
|
|
test.skip();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
test.describe('Task List View', () => {
|
|||
|
|
test('should display tasks in list format', async ({ page }) => {
|
|||
|
|
// Switch to list view
|
|||
|
|
await page.getByRole('tab', { name: /list/i }).click();
|
|||
|
|
|
|||
|
|
// The list view should be rendered
|
|||
|
|
// TasksListView component renders tasks in a table or list format
|
|||
|
|
await page.waitForTimeout(500); // Wait for view transition
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|