369 lines
12 KiB
TypeScript
369 lines
12 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
import {
|
|
login,
|
|
navigateAndWait,
|
|
waitForElement,
|
|
hoverAndWaitForVisible,
|
|
createUniqueEntity,
|
|
waitForNetworkIdle,
|
|
} from '../helpers/testHelpers';
|
|
|
|
// Helper to create a task
|
|
async function createTask(page, taskName) {
|
|
const taskInput = page.locator('[data-testid="new-task-input"]');
|
|
await taskInput.fill(taskName);
|
|
await taskInput.press('Enter');
|
|
await waitForNetworkIdle(page);
|
|
}
|
|
|
|
// Helper to open task edit modal
|
|
async function openTaskEditModal(page, taskName) {
|
|
const taskContainer = page
|
|
.locator('[data-testid*="task-item"]')
|
|
.filter({ hasText: taskName });
|
|
await expect(taskContainer).toBeVisible({ timeout: 15000 });
|
|
|
|
const editButton = taskContainer.locator('[data-testid*="task-edit"]');
|
|
await hoverAndWaitForVisible(taskContainer, editButton);
|
|
|
|
await editButton.click();
|
|
|
|
const taskNameInput = page.locator('[data-testid="task-name-input"]');
|
|
await waitForElement(taskNameInput, { timeout: 15000 });
|
|
|
|
return taskNameInput;
|
|
}
|
|
|
|
test.describe('Discard Changes Dialog', () => {
|
|
test('shows discard dialog when closing task modal with unsaved changes', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task for Discard');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Make a change to the task name
|
|
const taskNameInput = page.locator('[data-testid="task-name-input"]');
|
|
await taskNameInput.fill(taskName + ' Modified');
|
|
|
|
// Press Escape key
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Verify discard dialog appears
|
|
const discardDialog = page.locator(
|
|
'[data-testid="discard-dialog-cancel"]'
|
|
);
|
|
await expect(discardDialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Verify the "No, keep editing" button is focused
|
|
await expect(discardDialog).toBeFocused();
|
|
|
|
// Verify both buttons are visible
|
|
await expect(
|
|
page.locator('[data-testid="discard-dialog-confirm"]')
|
|
).toBeVisible();
|
|
});
|
|
|
|
test('keeps editing when clicking "No, keep editing" button', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task Keep Editing');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Make a change
|
|
const taskNameInput = page.locator('[data-testid="task-name-input"]');
|
|
const modifiedName = taskName + ' Modified';
|
|
await taskNameInput.fill(modifiedName);
|
|
|
|
// Press Escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Wait for discard dialog
|
|
const cancelButton = page.locator(
|
|
'[data-testid="discard-dialog-cancel"]'
|
|
);
|
|
await expect(cancelButton).toBeVisible({ timeout: 5000 });
|
|
|
|
// Click "No, keep editing"
|
|
await cancelButton.click();
|
|
|
|
// Verify modal is still open and changes are preserved
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"]')
|
|
).toBeVisible();
|
|
await expect(taskNameInput).toHaveValue(modifiedName);
|
|
});
|
|
|
|
test('discards changes when clicking "Yes, discard" button', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task Discard');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Make a change
|
|
const taskNameInput = page.locator('[data-testid="task-name-input"]');
|
|
await taskNameInput.fill(taskName + ' Modified');
|
|
|
|
// Press Escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Wait for discard dialog
|
|
const confirmButton = page.locator(
|
|
'[data-testid="discard-dialog-confirm"]'
|
|
);
|
|
await expect(confirmButton).toBeVisible({ timeout: 5000 });
|
|
|
|
// Click "Yes, discard"
|
|
await confirmButton.click();
|
|
|
|
// Verify modal is closed
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"]')
|
|
).not.toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('closes modal directly when pressing Escape with no changes', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task No Changes');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Don't make any changes, just press Escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Verify modal closes immediately without showing discard dialog
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"]')
|
|
).not.toBeVisible({ timeout: 5000 });
|
|
|
|
// Verify discard dialog never appeared
|
|
await expect(
|
|
page.locator('[data-testid="discard-dialog-cancel"]')
|
|
).not.toBeVisible();
|
|
});
|
|
|
|
test('closes discard dialog when pressing Escape in the dialog', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task Escape Dialog');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Make a change
|
|
const taskNameInput = page.locator('[data-testid="task-name-input"]');
|
|
const modifiedName = taskName + ' Modified';
|
|
await taskNameInput.fill(modifiedName);
|
|
|
|
// Press Escape to show discard dialog
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Wait for discard dialog
|
|
const cancelButton = page.locator(
|
|
'[data-testid="discard-dialog-cancel"]'
|
|
);
|
|
await expect(cancelButton).toBeVisible({ timeout: 5000 });
|
|
|
|
// Press Escape again to close the discard dialog
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Verify discard dialog is closed
|
|
await expect(cancelButton).not.toBeVisible({ timeout: 5000 });
|
|
|
|
// Verify task modal is still open with changes preserved
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"]')
|
|
).toBeVisible();
|
|
await expect(taskNameInput).toHaveValue(modifiedName);
|
|
});
|
|
|
|
test('shows discard dialog when closing project modal with unsaved changes', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/projects');
|
|
|
|
// Click "Add Project" button
|
|
const addProjectButton = page.locator(
|
|
'button[aria-label="Add Project"]'
|
|
);
|
|
await expect(addProjectButton).toBeVisible();
|
|
await addProjectButton.click();
|
|
|
|
// Wait for modal to open
|
|
await expect(page.locator('input[name="name"]')).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
|
|
// Type a project name
|
|
const projectName = createUniqueEntity('Test Project');
|
|
const nameInput = page.locator('[data-testid="project-name-input"]');
|
|
await nameInput.fill(projectName);
|
|
|
|
// Press Escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Verify discard dialog appears
|
|
const discardDialog = page.locator(
|
|
'[data-testid="discard-dialog-cancel"]'
|
|
);
|
|
await expect(discardDialog).toBeVisible({ timeout: 5000 });
|
|
|
|
// Click "Yes, discard"
|
|
await page
|
|
.locator('[data-testid="discard-dialog-confirm"]')
|
|
.click();
|
|
|
|
// Verify modal is closed
|
|
await expect(
|
|
page.locator('[data-testid="project-modal"]')
|
|
).not.toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('detects changes in task note field', async ({ page, baseURL }) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task Note Change');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Add content to the note field
|
|
const noteTextarea = page.locator('textarea[name="note"]');
|
|
await noteTextarea.fill('This is a test note');
|
|
|
|
// Press Escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Verify discard dialog appears
|
|
const discardDialog = page.locator(
|
|
'[data-testid="discard-dialog-cancel"]'
|
|
);
|
|
await expect(discardDialog).toBeVisible({ timeout: 5000 });
|
|
});
|
|
|
|
test('detects changes when adding tags to task', async ({
|
|
page,
|
|
baseURL,
|
|
}) => {
|
|
const appUrl = await login(page, baseURL);
|
|
|
|
await navigateAndWait(page, appUrl + '/tasks');
|
|
|
|
// Create a task
|
|
const taskName = createUniqueEntity('Test Task Tag Change');
|
|
await createTask(page, taskName);
|
|
|
|
// Open the task edit modal
|
|
await openTaskEditModal(page, taskName);
|
|
|
|
// Wait for modal to be in idle state
|
|
await expect(
|
|
page.locator('[data-testid="task-modal"][data-state="idle"]')
|
|
).toBeVisible();
|
|
|
|
// Open tags section
|
|
const tagsSectionButton = page
|
|
.locator('button[title*="Tags"]')
|
|
.filter({ has: page.locator('svg') });
|
|
await tagsSectionButton.click();
|
|
|
|
// Wait for tag input to become visible
|
|
const tagInput = page.locator('input[placeholder*="tag"]');
|
|
await expect(tagInput).toBeVisible({ timeout: 5000 });
|
|
|
|
// Add a tag
|
|
await tagInput.fill('test-tag');
|
|
await tagInput.press('Enter');
|
|
|
|
// Wait a moment for tag to be added
|
|
await page.waitForTimeout(500);
|
|
|
|
// Press Escape
|
|
await page.keyboard.press('Escape');
|
|
|
|
// Verify discard dialog appears
|
|
const discardDialog = page.locator(
|
|
'[data-testid="discard-dialog-cancel"]'
|
|
);
|
|
await expect(discardDialog).toBeVisible({ timeout: 5000 });
|
|
});
|
|
});
|