From f50a2efdbf0a4f726e383c62a99a30c9a7397388 Mon Sep 17 00:00:00 2001 From: zhi Date: Mon, 16 Mar 2026 16:32:13 +0000 Subject: [PATCH 1/2] test: cover shared task creation modal flow --- tests/task.spec.ts | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/task.spec.ts b/tests/task.spec.ts index b8627d4..684123e 100644 --- a/tests/task.spec.ts +++ b/tests/task.spec.ts @@ -98,17 +98,17 @@ test.describe('Task & Comment Flow', () => { // Click "+ Create Task" await page.click('button:has-text("+ Create Task")'); - await page.waitForTimeout(500); + await page.waitForSelector('.modal.task-create-modal', { timeout: 10000 }); const taskTitle = `Test Task ${TS}`; console.log(`Task title: ${taskTitle}`); - await page.fill('input[placeholder="Title"]', taskTitle); - await page.fill('textarea[placeholder="Description (optional)"]', 'Task created by automated test'); + await page.getByTestId('task-title-input').fill(taskTitle); + await page.getByTestId('task-description-input').fill('Task created by automated test'); await page.waitForTimeout(300); - // Click Create button in the card form - await page.click('.card button.btn-primary'); - await page.waitForTimeout(2000); + await page.getByTestId('create-task-button').click(); + await page.waitForSelector('.modal.task-create-modal', { state: 'detached', timeout: 10000 }); + await page.waitForTimeout(1500); // Verify task appears in the Tasks tab await page.click('.tab:has-text("Tasks")'); @@ -119,10 +119,12 @@ test.describe('Task & Comment Flow', () => { // ── Step 5: Create a Task item (for comment testing) ─────────────── // Milestone tasks don't have a detail page with comments, so we create - // a Task item via the /tasks/new form to test the comment flow. + // a Task item from the shared Tasks-page modal to test the comment flow. console.log('📋 Creating task item for comment testing...'); - await page.goto(`${BASE_URL}/tasks/new`); + await page.goto(`${BASE_URL}/tasks`); await page.waitForLoadState('networkidle'); + await page.click('button:has-text("+ Create Task")'); + await page.waitForSelector('.modal.task-create-modal', { timeout: 10000 }); const taskItemTitle = `Test Task for Comment ${TS}`; await page.getByTestId('task-title-input').fill(taskItemTitle); @@ -157,11 +159,14 @@ test.describe('Task & Comment Flow', () => { await page.waitForURL(`${BASE_URL}/tasks`, { timeout: 10000 }); await page.waitForLoadState('networkidle'); - console.log('✅ Task item created'); + await expect(page.locator('.tasks-table')).toBeVisible({ timeout: 10000 }); + await expect(page.locator(`.tasks-table tr:has-text("${taskItemTitle}")`)).toBeVisible({ timeout: 10000 }); + console.log('✅ Task item created and visible in list'); // ── Step 6: Open Task → Add Comment ──────────────────────────────── console.log('💬 Opening task to add comment...'); - await page.goto(`${BASE_URL}/tasks/${createdTask.id}`); + await page.click(`.tasks-table tr:has-text("${taskItemTitle}")`); + await page.waitForURL(`${BASE_URL}/tasks/${createdTask.id}`, { timeout: 10000 }); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); From 82e9dc2c86c218af96efe568df4ccf8130e997f3 Mon Sep 17 00:00:00 2001 From: zhi Date: Mon, 16 Mar 2026 18:13:54 +0000 Subject: [PATCH 2/2] test: cover project milestone and task modal editors --- tests/milestone.spec.ts | 31 +++++++++++++++++++------------ tests/project-editor.spec.ts | 30 +++++++++++++++++++----------- tests/task.spec.ts | 21 +++++++++++++++++---- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/tests/milestone.spec.ts b/tests/milestone.spec.ts index 5cd8ee0..f971f28 100644 --- a/tests/milestone.spec.ts +++ b/tests/milestone.spec.ts @@ -6,7 +6,7 @@ const ADMIN_USERNAME = 'admin'; const ADMIN_PASSWORD = 'admin123'; test.describe('Milestone Editor', () => { - test('login -> create project -> create milestone through frontend form', async ({ page }) => { + test('login -> create project -> create and edit milestone through modal', async ({ page }) => { // Step 1: Login (with retry — backend may still be warming up) console.log('🔐 Logging in...'); @@ -66,14 +66,14 @@ test.describe('Milestone Editor', () => { await page.waitForSelector('button:has-text("+ New")', { timeout: 10000 }); await page.click('button:has-text("+ New")'); - await page.waitForSelector('input[placeholder="Project name"]', { timeout: 10000 }); + await page.waitForSelector('.modal', { timeout: 10000 }); const projectName = 'Milestone Test Project ' + Date.now(); console.log('Creating project with name:', projectName); - await page.fill('input[placeholder="Project name"]', projectName); - await page.fill('input[placeholder="Description (optional)"]', 'Project for milestone testing'); + await page.getByTestId('project-name-input').fill(projectName); + await page.getByTestId('project-description-input').fill('Project for milestone testing'); - await page.click('button:has-text("Create")'); + await page.click('.modal button.btn-primary:has-text("Create")'); await page.waitForTimeout(2000); // Wait for our specific project card to appear @@ -122,20 +122,27 @@ test.describe('Milestone Editor', () => { // Step 5: Verify milestone was created console.log('🔍 Verifying milestone...'); - - // Get the heading text const headingText = await page.locator('h3').filter({ hasText: /Milestones/i }).textContent(); console.log('Heading text:', headingText); - - // Check if milestone count > 0 const hasMilestone = headingText && /\(1?\d+\)/.test(headingText) && !headingText.includes('(0)'); console.log('Has milestone:', hasMilestone); - - // Verify milestone exists in UI expect(hasMilestone).toBe(true); console.log('✅ Milestone verified in UI'); - // Note: intentionally NOT deleting the project/milestone — leave for inspection + // Step 6: Open milestone and edit it via modal + console.log('✏️ Editing milestone...'); + await page.click(`.milestone-item:has-text("${milestoneName}")`); + await page.waitForLoadState('networkidle'); + await page.click('button:has-text("Edit Milestone")'); + await page.waitForSelector('.modal', { timeout: 10000 }); + + const updatedMilestoneName = `${milestoneName} Updated`; + await page.getByTestId('milestone-title-input').fill(updatedMilestoneName); + await page.click('.modal button.btn-primary:has-text("Save")'); + await page.waitForSelector('.modal', { state: 'detached', timeout: 10000 }); + await expect(page.locator(`h2:has-text("${updatedMilestoneName}")`)).toBeVisible({ timeout: 10000 }); + console.log('✅ Milestone edited'); + console.log('🎉 All milestone tests passed!'); }); }); diff --git a/tests/project-editor.spec.ts b/tests/project-editor.spec.ts index 539cb89..ab3c743 100644 --- a/tests/project-editor.spec.ts +++ b/tests/project-editor.spec.ts @@ -7,7 +7,7 @@ const ADMIN_USERNAME = 'admin'; const ADMIN_PASSWORD = 'admin123'; test.describe('Project Editor', () => { - test('login -> create project -> delete project -> logout', async ({ page }) => { + test('login -> create project -> edit project via modal -> delete project -> logout', async ({ page }) => { // Step 1: Login console.log('🔐 Logging in...'); await page.goto(`${BASE_URL}/login`); @@ -57,17 +57,14 @@ test.describe('Project Editor', () => { // Click create project button - it's "+ New" await page.waitForSelector('button:has-text("+ New")', { timeout: 10000 }); await page.click('button:has-text("+ New")'); - // Wait for the form to appear (look for the project name input) - await page.waitForSelector('input[placeholder="Project name"]', { timeout: 10000 }); + await page.waitForSelector('.modal', { timeout: 10000 }); - // Fill project form with a unique, identifiable name const projectName = 'ProjEditor Test ' + Date.now(); console.log('Creating project with name:', projectName); - await page.fill('input[placeholder="Project name"]', projectName); - await page.fill('input[placeholder="Description (optional)"]', 'Project for E2E testing'); + await page.getByTestId('project-name-input').fill(projectName); + await page.getByTestId('project-description-input').fill('Project for E2E testing'); - // Submit project form - await page.click('button:has-text("Create")'); + await page.click('.modal button.btn-primary:has-text("Create")'); // Wait a bit for submission await page.waitForTimeout(2000); @@ -76,12 +73,23 @@ test.describe('Project Editor', () => { await page.waitForSelector(`.project-card:has-text("${projectName}")`, { timeout: 10000 }); console.log('✅ Project created'); - // Step 3: Delete the created project — click OUR project card by name - console.log('🗑️ Deleting project...'); - + // Step 3: Edit the created project via modal + console.log('✏️ Editing project via modal...'); await page.click(`.project-card:has-text("${projectName}")`); await page.waitForLoadState('networkidle'); await page.waitForTimeout(1000); + + await page.click('button:has-text("Edit")'); + await page.waitForSelector('.modal', { timeout: 10000 }); + const updatedDescription = `Updated project description ${Date.now()}`; + await page.getByTestId('project-description-input').fill(updatedDescription); + await page.click('.modal button.btn-primary:has-text("Save")'); + await page.waitForSelector('.modal', { state: 'detached', timeout: 10000 }); + await expect(page.locator(`text=${updatedDescription}`)).toBeVisible({ timeout: 10000 }); + console.log('✅ Project edited'); + + // Step 4: Delete the created project — click OUR project card by name + console.log('🗑️ Deleting project...'); // Look for delete button const deleteBtn = page.locator('button:has-text("Delete")'); diff --git a/tests/task.spec.ts b/tests/task.spec.ts index 684123e..236686b 100644 --- a/tests/task.spec.ts +++ b/tests/task.spec.ts @@ -52,10 +52,10 @@ test.describe('Task & Comment Flow', () => { await page.waitForSelector('button:has-text("+ New")', { timeout: 10000 }); await page.click('button:has-text("+ New")'); - await page.waitForSelector('input[placeholder="Project name"]', { timeout: 10000 }); - await page.fill('input[placeholder="Project name"]', projectName); - await page.fill('input[placeholder="Description (optional)"]', 'Project for task & comment testing'); - await page.click('button:has-text("Create")'); + await page.waitForSelector('.modal', { timeout: 10000 }); + await page.getByTestId('project-name-input').fill(projectName); + await page.getByTestId('project-description-input').fill('Project for task & comment testing'); + await page.click('.modal button.btn-primary:has-text("Create")'); await page.waitForTimeout(2000); await page.waitForSelector(`.project-card:has-text("${projectName}")`, { timeout: 10000 }); @@ -117,6 +117,19 @@ test.describe('Task & Comment Flow', () => { await expect(taskRow).toBeVisible({ timeout: 10000 }); console.log('✅ Task created and verified'); + // Edit the created task via task detail modal + console.log('✏️ Editing created task...'); + await taskRow.click(); + await page.waitForURL(/\/tasks\/\d+$/, { timeout: 10000 }); + await page.click('button:has-text("Edit Task")'); + await page.waitForSelector('.modal.task-create-modal', { timeout: 10000 }); + const updatedTaskTitle = `${taskTitle} Updated`; + await page.getByTestId('task-title-input').fill(updatedTaskTitle); + await page.getByTestId('create-task-button').click(); + await page.waitForSelector('.modal.task-create-modal', { state: 'detached', timeout: 10000 }); + await expect(page.locator(`h2:has-text("${updatedTaskTitle}")`)).toBeVisible({ timeout: 10000 }); + console.log('✅ Task edited and verified'); + // ── Step 5: Create a Task item (for comment testing) ─────────────── // Milestone tasks don't have a detail page with comments, so we create // a Task item from the shared Tasks-page modal to test the comment flow.