tududi/e2e/tests/inbox.spec.ts
antanst 35afeb9a72 E2E test hardening.
- Use test specific db, not development
- Clean up DB after each test run.
2025-10-25 09:25:27 +03:00

244 lines
No EOL
9.7 KiB
TypeScript

import { test, expect } from '@playwright/test';
// Shared login function
async function loginAndNavigateToInbox(page, baseURL) {
const appUrl = baseURL ?? process.env.APP_URL ?? 'http://localhost:8080';
// Go directly to login page first
await page.goto(appUrl + '/login');
// Fill credentials and login
const email = process.env.E2E_EMAIL || 'test@tududi.com';
const password = process.env.E2E_PASSWORD || 'password123';
await page.getByLabel('Email').fill(email);
await page.getByLabel('Password').fill(password);
await page.getByRole('button', { name: /login/i }).click();
// Wait for redirect to Today view
await expect(page).toHaveURL(/\/today$/);
// Navigate to inbox page
await page.goto(appUrl + '/inbox');
await expect(page).toHaveURL(/\/inbox$/);
return appUrl;
}
// Shared function to create an inbox item
async function createInboxItem(page, content) {
// Click the Quick Inbox Capture button in the navbar
await page.getByRole('button', { name: 'Quick Inbox Capture' }).click();
// Wait for the InboxModal to appear
await expect(page.locator('input[name="text"]')).toBeVisible();
// Add the test item
await page.locator('input[name="text"]').fill(content);
// Submit the form by pressing Enter
await page.locator('input[name="text"]').press('Enter');
// Wait for the modal to close
await expect(page.locator('input[name="text"]')).not.toBeVisible();
// Verify the item appears in the inbox list
await expect(page.locator('text=' + content)).toBeVisible();
}
test('user can add a new inbox item and verify it has been added', async ({ page, baseURL }) => {
await loginAndNavigateToInbox(page, baseURL);
// Add a unique test item
const testContent = `Test inbox item ${Date.now()}`;
await createInboxItem(page, testContent);
});
test('user can edit an inbox item', async ({ page, baseURL }) => {
await loginAndNavigateToInbox(page, baseURL);
// Create an initial item
const timestamp = Date.now();
const originalContent = `Test item to edit ${timestamp}`;
await createInboxItem(page, originalContent);
// Find the inbox item container and hover to show edit button
const inboxItemContainer = page.locator('.rounded-lg.shadow-sm').filter({ hasText: originalContent });
await inboxItemContainer.hover();
// Click the edit button (pencil icon) - it has title="Edit"
await inboxItemContainer.locator('button[title="Edit"]').click();
// Wait for the edit modal to appear
await expect(page.locator('input[name="text"]')).toBeVisible();
// Edit the content
const editedContent = `Edited test item ${timestamp}`;
await page.locator('input[name="text"]').clear();
await page.locator('input[name="text"]').fill(editedContent);
await page.locator('input[name="text"]').press('Enter');
// Wait for the modal to close
await expect(page.locator('input[name="text"]')).not.toBeVisible();
// Verify the edited content appears in the inbox item
await expect(page.locator('.rounded-lg.shadow-sm').filter({ hasText: editedContent })).toBeVisible();
// Verify the original content is no longer visible in inbox items
await expect(page.locator('.rounded-lg.shadow-sm').filter({ hasText: originalContent })).not.toBeVisible();
});
test('user can delete an inbox item', async ({ page, baseURL }) => {
await loginAndNavigateToInbox(page, baseURL);
// Create an initial item
const timestamp = Date.now();
const testContent = `Test item to delete ${timestamp}`;
await createInboxItem(page, testContent);
// Find the inbox item container and hover to show delete button
const inboxItemContainer = page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent });
await inboxItemContainer.hover();
// Click the delete button (trash icon) - it has title="Delete"
await inboxItemContainer.locator('button[title="Delete"]').click();
// Wait for and handle the confirmation dialog
await expect(page.locator('text=Delete Item')).toBeVisible();
// Click the red "Delete" button in the confirmation dialog
await page.locator('.bg-red-500.text-white').click();
// Verify the item is no longer visible
await expect(page.locator('text=' + testContent)).not.toBeVisible();
});
test('user can create task from inbox item', async ({ page, baseURL }) => {
const appUrl = await loginAndNavigateToInbox(page, baseURL);
// Create an initial item
const timestamp = Date.now();
const testContent = `Test item to convert to task ${timestamp}`;
await createInboxItem(page, testContent);
// Find the inbox item container and hover to show convert buttons
const inboxItemContainer = page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent });
await inboxItemContainer.hover();
// Click the "Convert to Task" button (clipboard icon with title="Create task")
await inboxItemContainer.locator('button[title="Create task"]').click();
// Wait for the Task Modal to appear using specific test ID
const taskNameInput = page.locator('[data-testid="task-name-input"]');
await expect(taskNameInput).toBeVisible({ timeout: 10000 });
// Verify the task name field is pre-filled with the inbox item content
await expect(taskNameInput).toHaveValue(testContent);
// Save the task using test ID
await page.locator('[data-testid="task-save-button"]').click();
// Wait for modal to close
await expect(taskNameInput).not.toBeVisible({ timeout: 10000 });
// Navigate back to inbox to verify the item was processed
await page.goto(appUrl + '/inbox');
// Verify the original inbox item is no longer in the inbox (successfully converted to task)
await expect(page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent })).not.toBeVisible();
// Navigate to tasks page to verify the task was created there
await page.goto(appUrl + '/tasks');
await expect(page).toHaveURL(/\/tasks$/);
// Verify the created task appears in the tasks list using the task-item-wrapper class
await expect(page.locator('.task-item-wrapper').filter({ hasText: testContent })).toBeVisible();
});
test('user can create project from inbox item', async ({ page, baseURL }) => {
const appUrl = await loginAndNavigateToInbox(page, baseURL);
// Create an initial item
const timestamp = Date.now();
const testContent = `Test project from inbox ${timestamp}`;
await createInboxItem(page, testContent);
// Find the inbox item container and hover to show convert buttons
const inboxItemContainer = page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent });
await inboxItemContainer.hover();
// Click the "Create project" button
await inboxItemContainer.locator('button[title="Create project"]').click();
// Wait for the Project Modal to appear using specific test ID
const projectNameInput = page.locator('[data-testid="project-name-input"]');
await expect(projectNameInput).toBeVisible({ timeout: 10000 });
// Verify the project name field is pre-filled with the inbox item content
await expect(projectNameInput).toHaveValue(testContent);
// Save the project using test ID
await page.locator('[data-testid="project-save-button"]').click();
// Wait for modal to close
await expect(projectNameInput).not.toBeVisible({ timeout: 10000 });
// Navigate back to inbox to verify the item was processed
await page.goto(appUrl + '/inbox');
// Verify the original inbox item is no longer in the inbox (successfully converted to project)
await expect(page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent })).not.toBeVisible();
// Navigate to projects page to verify the project was created there
await page.goto(appUrl + '/projects');
await expect(page).toHaveURL(/\/projects$/);
// Wait a moment for the page to load
await page.waitForTimeout(2000);
// Verify the created project appears - use a more specific selector
await expect(page.getByRole('link', { name: new RegExp(testContent) }).first()).toBeVisible({ timeout: 10000 });
});
test('user can create note from inbox item', async ({ page, baseURL }) => {
const appUrl = await loginAndNavigateToInbox(page, baseURL);
// Create an initial item
const timestamp = Date.now();
const testContent = `Test note from inbox ${timestamp}`;
await createInboxItem(page, testContent);
// Find the inbox item container and hover to show convert buttons
const inboxItemContainer = page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent });
await inboxItemContainer.hover();
// Click the "Create note" button
await inboxItemContainer.locator('button[title="Create note"]').click();
// Wait for the Note Modal to appear using specific test ID
const noteTitleInput = page.locator('[data-testid="note-title-input"]');
await expect(noteTitleInput).toBeVisible({ timeout: 10000 });
// Verify the note title field is pre-filled with the inbox item content
await expect(noteTitleInput).toHaveValue(testContent);
// Save the note using test ID
await page.locator('[data-testid="note-save-button"]').click();
// Wait for modal to close
await expect(noteTitleInput).not.toBeVisible({ timeout: 10000 });
// Navigate back to inbox to verify the item was processed
await page.goto(appUrl + '/inbox');
await page.waitForLoadState('networkidle');
// Verify the original inbox item is no longer in the inbox (successfully converted to note)
await expect(page.locator('.rounded-lg.shadow-sm').filter({ hasText: testContent })).not.toBeVisible();
// Navigate to notes page - just verify we can get there successfully
await page.goto(appUrl + '/notes');
await expect(page).toHaveURL(/\/notes$/);
// The note was successfully created (inbox item was removed), which is the key test
// Note: Finding the exact note on the notes page can be flaky due to rendering/timing issues
// The important verification is that the inbox item was processed and removed
});