diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c8a2abf53..41d8fc00e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -378,6 +378,9 @@ jobs: with: name: frontend_dist path: ./frontend/dist + - name: Inject testing flag into index.html + run: | + sed -i 's/
/ diff --git a/frontend/tests/e2e/task/overview.spec.ts b/frontend/tests/e2e/task/overview.spec.ts index 4b9ce944a..b8a458bd3 100644 --- a/frontend/tests/e2e/task/overview.spec.ts +++ b/frontend/tests/e2e/task/overview.spec.ts @@ -61,7 +61,7 @@ test.describe('Home Page Task Overview', () => { } }) - test.skip('Should show a new task with a very soon due date at the top', async ({authenticatedPage: page, apiContext}) => { + test('Should show a new task with a very soon due date at the top', async ({authenticatedPage: page, apiContext}) => { const {tasks, project} = await seedTasks(apiContext, 49) const newTaskTitle = 'New Task' @@ -84,7 +84,7 @@ test.describe('Home Page Task Overview', () => { await expect(page.locator('[data-cy="showTasks"] .card .task').first()).toContainText(newTaskTitle) }) - test.skip('Should not show a new task without a date at the bottom when there are > 50 tasks', async ({authenticatedPage: page, apiContext}) => { + test('Should not show a new task without a date at the bottom when there are > 50 tasks', async ({authenticatedPage: page, apiContext}) => { // We're not using the api here to create the task in order to verify the flow const {tasks} = await seedTasks(apiContext, 100) const newTaskTitle = 'New Task' @@ -103,7 +103,7 @@ test.describe('Home Page Task Overview', () => { await expect(page.locator('[data-cy="showTasks"]')).not.toContainText(newTaskTitle) }) - test.skip('Should show a new task without a date at the bottom when there are < 50 tasks', async ({authenticatedPage: page, apiContext}) => { + test('Should show a new task without a date at the bottom when there are < 50 tasks', async ({authenticatedPage: page, apiContext}) => { const {project} = await seedTasks(apiContext, 40) const newTaskTitle = 'New Task' await TaskFactory.create(1, { @@ -117,7 +117,7 @@ test.describe('Home Page Task Overview', () => { await expect(page.locator('[data-cy="showTasks"]')).toContainText(newTaskTitle) }) - test.skip('Should show a task without a due date added via default project at the bottom', async ({authenticatedPage: page, apiContext}) => { + test('Should show a task without a due date added via default project at the bottom', async ({authenticatedPage: page, apiContext}) => { const {project} = await seedTasks(apiContext, 40) // Navigate first to get access to localStorage diff --git a/frontend/tests/e2e/task/task.spec.ts b/frontend/tests/e2e/task/task.spec.ts index 113d53f6a..a8d67c537 100644 --- a/frontend/tests/e2e/task/task.spec.ts +++ b/frontend/tests/e2e/task/task.spec.ts @@ -217,11 +217,20 @@ test.describe('Task', () => { await expect(page).toHaveURL(/\/projects\/1\/\d+/) }) - test.skip('provides back navigation to the project in the kanban view on mobile', async ({authenticatedPage: page}) => { + test('provides back navigation to the project in the kanban view on mobile', async ({authenticatedPage: page}) => { await page.setViewportSize({width: 375, height: 667}) // iphone-8 - const tasks = await TaskFactory.create(1) - await page.goto('/projects/1/4') + const tasks = await TaskFactory.create(1, { + id: 1, + project_id: projects[0].id, + }) + // Task must be in a bucket to appear in kanban view + await TaskBucketFactory.create(1, { + task_id: tasks[0].id, + bucket_id: buckets[0].id, + project_view_id: buckets[0].project_view_id, + }) + await page.goto(`/projects/${projects[0].id}/4`) await page.waitForLoadState('networkidle') // Wait for kanban view and task to be visible @@ -230,14 +239,23 @@ test.describe('Task', () => { await taskLocator.click() await expect(page.locator('.task-view .back-button')).toBeVisible() await page.locator('.task-view .back-button').click() - await expect(page).toHaveURL(/\/projects\/1\/\d+/) + await expect(page).toHaveURL(/\/projects\/\d+\/\d+/) }) - test.skip('does not provide back navigation to the project in the kanban view on desktop', async ({authenticatedPage: page}) => { + test('does not provide back navigation to the project in the kanban view on desktop', async ({authenticatedPage: page}) => { await page.setViewportSize({width: 1440, height: 900}) // macbook-15 - const tasks = await TaskFactory.create(1) - await page.goto('/projects/1/4') + const tasks = await TaskFactory.create(1, { + id: 1, + project_id: projects[0].id, + }) + // Task must be in a bucket to appear in kanban view + await TaskBucketFactory.create(1, { + task_id: tasks[0].id, + bucket_id: buckets[0].id, + project_view_id: buckets[0].project_view_id, + }) + await page.goto(`/projects/${projects[0].id}/4`) await page.waitForLoadState('networkidle') // Wait for kanban view and task to be visible @@ -314,7 +332,7 @@ test.describe('Task', () => { await expect(page.locator('.task-view h1.title.task-id')).toContainText(`${projects[0].identifier}-${tasks[0].index}`) }) - test.skip('Can edit the description', async ({authenticatedPage: page}) => { + test('Can edit the description', async ({authenticatedPage: page}) => { const tasks = await TaskFactory.create(1, { id: 1, description: 'Lorem ipsum dolor sit amet.', @@ -446,7 +464,7 @@ test.describe('Task', () => { await expect(page).toHaveURL(new RegExp(`/projects/${tasks[0].project_id}/`)) }) - test.skip('Can add an assignee to a task', async ({authenticatedPage: page}) => { + test('Can add an assignee to a task', async ({authenticatedPage: page}) => { await TaskAssigneeFactory.truncate() // Create users with IDs starting at 100 to avoid conflict with logged-in user (ID 1) @@ -566,7 +584,7 @@ test.describe('Task', () => { await expect(page.locator('.bucket .task')).toContainText(labels[0].title) }) - test.skip('Can remove a label from a task', async ({authenticatedPage: page}) => { + test('Can remove a label from a task', async ({authenticatedPage: page}) => { const tasks = await TaskFactory.create(1, { id: 1, project_id: 1, @@ -595,7 +613,7 @@ test.describe('Task', () => { await expect(labelWrapper).not.toContainText(labels[0].title) }) - test.skip('Can set a due date for a task', async ({authenticatedPage: page}) => { + test('Can set a due date for a task', async ({authenticatedPage: page}) => { const tasks = await TaskFactory.create(1, { id: 1, done: false, @@ -623,7 +641,7 @@ test.describe('Task', () => { await expect(page.locator('.global-notification')).toContainText('Success') }) - test.skip('Can set a due date to a specific date for a task', async ({authenticatedPage: page}) => { + test('Can set a due date to a specific date for a task', async ({authenticatedPage: page}) => { const tasks = await TaskFactory.create(1, { id: 1, done: false, @@ -656,7 +674,7 @@ test.describe('Task', () => { await expect(page.locator('.global-notification')).toContainText('Success') }) - test.skip('Can change a due date to a specific date for a task', async ({authenticatedPage: page}) => { + test('Can change a due date to a specific date for a task', async ({authenticatedPage: page}) => { const dueDate = new Date(2025, 2, 20) dueDate.setHours(12) dueDate.setMinutes(0) diff --git a/frontend/tests/e2e/user/login.spec.ts b/frontend/tests/e2e/user/login.spec.ts index 7a83389ad..52c563878 100644 --- a/frontend/tests/e2e/user/login.spec.ts +++ b/frontend/tests/e2e/user/login.spec.ts @@ -48,8 +48,7 @@ test.describe('Login', () => { await expect(page.locator('main h2')).toContainText(`Hi ${credentials.username}!`) }) - // FIXME: request timeout for the request that's awaited - test.skip('Should fail with a bad password', async ({page}) => { + test('Should fail with a bad password', async ({page}) => { const fixture = { username: 'test', password: '123456', @@ -72,15 +71,18 @@ test.describe('Login', () => { await expect(page).toHaveURL(/\/login/) }) - // FIXME: request timeout - test.skip('Should redirect to the previous route after logging in', async ({page}) => { + test('Should redirect to the previous route after logging in', async ({page}) => { const projects = await ProjectFactory.create(1) await page.goto(`/projects/${projects[0].id}/1`) await expect(page).toHaveURL(/\/login/) - await login(page) + // Login without expecting redirect to / + await page.locator('input[id=username]').fill(credentials.username) + await page.locator('input[id=password]').fill(credentials.password) + await page.locator('.button').filter({hasText: 'Login'}).click() + // Should redirect back to the project route await expect(page).toHaveURL(new RegExp(`/projects/${projects[0].id}/1`)) }) }) diff --git a/frontend/tests/e2e/user/settings.spec.ts b/frontend/tests/e2e/user/settings.spec.ts index 4176db5af..4e8749ea2 100644 --- a/frontend/tests/e2e/user/settings.spec.ts +++ b/frontend/tests/e2e/user/settings.spec.ts @@ -1,9 +1,7 @@ import {test, expect} from '../../support/fixtures' test.describe('User Settings', () => { - // TODO: This test is flaky - the cropper's canvas.toBlob returns null intermittently - // The vue-advanced-cropper component seems to not properly initialize in the test environment - test.skip('Changes the user avatar', async ({authenticatedPage: page}) => { + test('Changes the user avatar', async ({authenticatedPage: page}) => { await page.goto('/user/settings/avatar') await page.waitForLoadState('networkidle') @@ -25,6 +23,9 @@ test.describe('User Settings', () => { const uploadButton = page.locator('[data-cy="uploadAvatar"]') await expect(uploadButton).toBeVisible() + // Wait for the cropper to be ready (button becomes enabled when canvas is ready) + await expect(uploadButton).toBeEnabled({timeout: 10000}) + // Set up response waiter before clicking const avatarUploadPromise = page.waitForResponse(response => response.url().includes('avatar') && response.request().method() === 'PUT', @@ -39,7 +40,7 @@ test.describe('User Settings', () => { await expect(page.locator('.global-notification')).toContainText('Success', {timeout: 10000}) }) - test.skip('Updates the name', async ({authenticatedPage: page}) => { + test('Updates the name', async ({authenticatedPage: page}) => { await page.goto('/user/settings/general') await page.waitForLoadState('networkidle')