test(e2e): restore all test files from 4781b6ec with updated import paths

This commit is contained in:
Johannes Millan 2025-08-02 16:57:12 +02:00
parent fed010b6d3
commit 613ccfa3da
30 changed files with 1551 additions and 42 deletions

View file

@ -0,0 +1,49 @@
import { test } from '../fixtures/test.fixture';
const CANCEL_BTN = 'mat-dialog-actions button:first-child';
test.describe('All Basic Routes Without Error', () => {
test('should open all basic routes from menu without error', async ({
page,
workViewPage,
}) => {
// Load app and wait for work view
await workViewPage.waitForTaskList();
// Navigate to schedule
await page.goto('/#/tag/TODAY/schedule');
// Click main side nav item
await page.click('side-nav section.main > side-nav-item > button');
await page.locator('side-nav section.main > button').nth(0).click();
await page.waitForSelector(CANCEL_BTN, { state: 'visible' });
await page.click(CANCEL_BTN);
await page.locator('side-nav section.main > button').nth(1).click();
await page.click('side-nav section.projects button');
await page.click('side-nav section.tags button');
await page.locator('side-nav section.app > button').nth(0).click();
await page.click('button.tour-settingsMenuBtn');
// Navigate to different routes
await page.goto('/#/tag/TODAY/quick-history');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/worklog');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/metrics');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/planner');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/daily-summary');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/settings');
await page.waitForTimeout(500);
// Send 'n' key to open notes dialog
await page.keyboard.press('n');
// Verify no errors in console (implicit with test passing)
});
});

View file

@ -0,0 +1,26 @@
import { expect, test } from '../../fixtures/test.fixture';
const CONFIRM_CREATE_TAG_BTN = `dialog-confirm button[e2e="confirmBtn"]`;
const BASIC_TAG_TITLE = 'task tag-list tag:last-of-type .tag-title';
test.describe('Autocomplete Dropdown', () => {
test('should create a simple tag', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Add task with tag syntax, skipClose=true to keep input open
await workViewPage.addTask('some task <3 #basicTag', true);
// Wait for and click the confirm create tag button
await page.waitForSelector(CONFIRM_CREATE_TAG_BTN, { state: 'visible' });
await page.click(CONFIRM_CREATE_TAG_BTN);
// Wait for tag to be created
await page.waitForSelector(BASIC_TAG_TITLE, { state: 'visible' });
// Assert tag is present and has correct text
const tagTitle = page.locator(BASIC_TAG_TITLE);
await expect(tagTitle).toBeVisible();
await expect(tagTitle).toContainText('basicTag');
});
});

View file

@ -0,0 +1,36 @@
import { expect, test } from '../../fixtures/test.fixture';
const SUMMARY_TABLE_TASK_EL = '.task-title .value-wrapper';
test.describe('Daily Summary', () => {
test('Daily summary message', async ({ page }) => {
// Navigate directly to daily summary page
await page.goto('/#/tag/TODAY/daily-summary');
// Wait for done headline to be visible
await page.waitForSelector('.done-headline', { state: 'visible' });
// Assert the text content
const doneHeadline = page.locator('.done-headline');
await expect(doneHeadline).toContainText('Take a moment to celebrate');
});
test('show any added task in table', async ({ page, workViewPage }) => {
// First navigate to work view to add task
await page.goto('/');
await workViewPage.waitForTaskList();
// Add task
await workViewPage.addTask('test task hohoho 1h/1h');
// Navigate to daily summary
await page.goto('/#/tag/TODAY/daily-summary');
// Wait for task element in summary table
await page.waitForSelector(SUMMARY_TABLE_TASK_EL, { state: 'visible' });
// Assert task appears in summary
const taskElement = page.locator(SUMMARY_TABLE_TASK_EL);
await expect(taskElement).toContainText('test task hohoho');
});
});

View file

@ -0,0 +1,54 @@
import { test } from '../../fixtures/test.fixture';
const PANEL_BTN = '.e2e-toggle-issue-provider-panel';
const CANCEL_BTN = 'mat-dialog-actions button:first-child';
test.describe('Issue Provider Panel', () => {
test('should open all dialogs without error', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
await page.waitForSelector(PANEL_BTN, { state: 'visible' });
await page.click(PANEL_BTN);
await page.waitForSelector('mat-tab-group', { state: 'visible' });
// Click on the last tab (add tab) which contains the issue-provider-setup-overview
await page.click('mat-tab-group .mat-mdc-tab:last-child');
await page.waitForSelector('issue-provider-setup-overview', { state: 'visible' });
// Wait for the setup overview to be fully loaded
await page.waitForTimeout(1000);
// Get all buttons in the issue provider setup overview
const setupButtons = page.locator('issue-provider-setup-overview button');
const buttonCount = await setupButtons.count();
// Click each button and close the dialog
for (let i = 0; i < buttonCount; i++) {
const button = setupButtons.nth(i);
// Skip if button is not visible or enabled
const isVisible = await button.isVisible().catch(() => false);
const isEnabled = await button.isEnabled().catch(() => false);
if (isVisible && isEnabled) {
await button.click();
// Wait for dialog to open
const dialogOpened = await page
.waitForSelector(CANCEL_BTN, {
state: 'visible',
timeout: 5000,
})
.catch(() => null);
if (dialogOpened) {
await page.click(CANCEL_BTN);
// Wait for dialog to close
await page.waitForTimeout(500);
}
}
}
// No error check is implicit - test will fail if any error occurs
});
});

View file

@ -0,0 +1,77 @@
import { test, expect } from '../../fixtures/test.fixture';
test.describe('Basic Navigation', () => {
test('should navigate between main views', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Verify we're on work view
await expect(page).toHaveURL(/\/#\/tag\/TODAY/);
await expect(page.locator('task-list').first()).toBeVisible();
// Navigate to schedule view
await page.goto('/#/schedule');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/schedule/);
await expect(page.locator('.route-wrapper')).toBeVisible();
// Navigate to quick history
await page.goto('/#/tag/TODAY/quick-history');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/tag\/TODAY\/quick-history/);
await expect(page.locator('quick-history')).toBeVisible();
// Navigate to worklog
await page.goto('/#/tag/TODAY/worklog');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/tag\/TODAY\/worklog/);
await expect(page.locator('.route-wrapper')).toBeVisible();
// Navigate to metrics
await page.goto('/#/tag/TODAY/metrics');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/tag\/TODAY\/metrics/);
await expect(page.locator('.route-wrapper')).toBeVisible();
// Navigate to planner
await page.goto('/#/planner');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/planner/);
await expect(page.locator('.route-wrapper')).toBeVisible();
// Navigate to daily summary
await page.goto('/#/tag/TODAY/daily-summary');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/tag\/TODAY\/daily-summary/);
await expect(page.locator('daily-summary')).toBeVisible();
// Navigate to settings
await page.goto('/#/config');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/config/);
await expect(page.locator('.page-settings')).toBeVisible();
// Navigate back to work view
await page.goto('/#/tag/TODAY');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/tag\/TODAY/);
await expect(page.locator('task-list').first()).toBeVisible();
});
test('should navigate using side nav buttons', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Click settings button
await page.click('side-nav .tour-settingsMenuBtn');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/config/);
await expect(page.locator('.page-settings')).toBeVisible();
// Click on work context to go back
await page.click('.current-work-context-title');
await page.waitForLoadState('networkidle');
await expect(page).toHaveURL(/\/#\/tag\/TODAY/);
await expect(page.locator('task-list').first()).toBeVisible();
});
});

View file

@ -0,0 +1,35 @@
import { expect, test } from '../../fixtures/test.fixture';
test.describe.serial('Performance Tests - Adding Multiple Tasks', () => {
test('performance: adding 20 tasks sequentially', async ({ page, workViewPage }) => {
// Set a longer timeout for this performance test
test.setTimeout(20000);
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Start performance measurement
const startTime = performance.now();
// Add 20 tasks sequentially
for (let i = 1; i <= 20; i++) {
// Keep the add task dialog open for all tasks except the last one
const isLastTask = i === 20;
await workViewPage.addTask(`${i} test task koko`, !isLastTask);
}
// Calculate total time
const totalTime = performance.now() - startTime;
// Log performance metrics (only if test fails or in verbose mode)
// console.log(`Time to create 20 tasks: ${totalTime.toFixed(2)}ms`);
// console.log(`Average time per task: ${(totalTime / 20).toFixed(2)}ms`);
// Verify all tasks were created
const tasks = page.locator('task');
await expect(tasks).toHaveCount(20);
// Performance assertion - 20 tasks should be created in under 60 seconds
expect(totalTime).toBeLessThan(20000);
});
});

View file

@ -0,0 +1,57 @@
import { test, expect } from '../../fixtures/test.fixture';
const NOTES_WRAPPER = 'notes';
const NOTE = 'notes note';
const FIRST_NOTE = `${NOTE}:first-of-type`;
const TOGGLE_NOTES_BTN = '.e2e-toggle-notes-btn';
test.describe('Project Note', () => {
test('create a note', async ({ page, projectPage }) => {
// Create and navigate to default project
await projectPage.createAndGoToTestProject();
// Add a note
await projectPage.addNote('Some new Note');
// Move to notes wrapper area and verify note is visible
const notesWrapper = page.locator(NOTES_WRAPPER);
await notesWrapper.hover({ position: { x: 10, y: 50 } });
const firstNote = page.locator(FIRST_NOTE);
await firstNote.waitFor({ state: 'visible' });
await expect(firstNote).toContainText('Some new Note');
});
test('new note should be still available after reload', async ({
page,
projectPage,
}) => {
// Create and navigate to default project
await projectPage.createAndGoToTestProject();
// Add a note
await projectPage.addNote('Some new Note');
// Wait for save
await page.waitForLoadState('networkidle');
// Reload the page
await page.reload();
// Click toggle notes button
const toggleNotesBtn = page.locator(TOGGLE_NOTES_BTN);
await toggleNotesBtn.waitFor({ state: 'visible' });
await toggleNotesBtn.click();
// Verify notes wrapper is present
const notesWrapper = page.locator(NOTES_WRAPPER);
await notesWrapper.waitFor({ state: 'visible' });
await notesWrapper.hover({ position: { x: 10, y: 50 } });
// Verify note is still there
const firstNote = page.locator(FIRST_NOTE);
await firstNote.waitFor({ state: 'visible' });
await expect(firstNote).toBeVisible();
await expect(firstNote).toContainText('Some new Note');
});
});

View file

@ -0,0 +1,94 @@
import { expect, test } from '../../fixtures/test.fixture';
import { ProjectPage } from '../../pages/project.page';
import { WorkViewPage } from '../../pages/work-view.page';
test.describe('Project', () => {
let projectPage: ProjectPage;
let workViewPage: WorkViewPage;
test.beforeEach(async ({ page, testPrefix }) => {
projectPage = new ProjectPage(page, testPrefix);
workViewPage = new WorkViewPage(page, testPrefix);
// Wait for app to be ready
await workViewPage.waitForTaskList();
// Additional wait for stability in parallel execution
await page.waitForTimeout(50);
});
test('move done tasks to archive without error', async ({ page }) => {
// First navigate to Inbox project (not Today view) since archive button only shows in project views
const inboxMenuItem = page.locator('[role="menuitem"]:has-text("Inbox")');
await inboxMenuItem.click();
// Add tasks using the page object method
await workViewPage.addTask('Test task 1', true); // skipClose=true to keep input open
await workViewPage.addTask('Test task 2');
// Mark first task as done
const firstTask = page.locator('task').first();
await firstTask.hover();
const doneBtn = firstTask.locator('.task-done-btn');
await doneBtn.waitFor({ state: 'visible' });
await doneBtn.click();
// Archive button should be visible in the done tasks section
const archiveBtn = page.locator('.e2e-move-done-to-archive');
await archiveBtn.waitFor({ state: 'visible' });
await archiveBtn.click();
// Verify one task remains and no error
const tasks = page.locator('task');
await expect(tasks).toHaveCount(1);
await expect(projectPage.globalErrorAlert).not.toBeVisible();
});
test('create second project', async ({ page, testPrefix }) => {
// First click on Projects menu item to expand it
await projectPage.projectAccordion.click();
// Create a new project
await projectPage.createProject('Cool Test Project');
// Find the newly created project directly (with test prefix)
const expectedProjectName = testPrefix
? `${testPrefix}-Cool Test Project`
: 'Cool Test Project';
const newProject = page.locator(
`[role="menuitem"]:has-text("${expectedProjectName}")`,
);
await expect(newProject).toBeVisible();
// Click on the new project
await newProject.click();
// Verify we're in the new project
await expect(projectPage.workCtxTitle).toContainText(expectedProjectName);
});
test('navigate to project settings', async ({ page }) => {
// Navigate to Inbox project
const inboxMenuItem = page.locator('[role="menuitem"]:has-text("Inbox")');
await inboxMenuItem.click();
// Navigate directly to settings via the Settings menu item
const settingsMenuItem = page.locator('[role="menuitem"]:has-text("Settings")');
await settingsMenuItem.click();
// Navigate to project settings tab/section if needed
const projectSettingsTab = page
.locator('button:has-text("Project"), [role="tab"]:has-text("Project")')
.first();
if (await projectSettingsTab.isVisible()) {
await projectSettingsTab.click();
}
// Verify we're on the settings page - look for any settings-related content
const settingsIndicator = page
.locator(
'h1:has-text("Settings"), h2:has-text("Settings"), .settings-section, mat-tab-group',
)
.first();
await expect(settingsIndicator).toBeVisible();
});
});

View file

@ -0,0 +1,138 @@
import { test, expect } from '../../fixtures/test.fixture';
const TASK = 'task';
const TASK_SCHEDULE_BTN = '.ico-btn.schedule-btn';
const SCHEDULE_ROUTE_BTN = 'button[routerlink="scheduled-list"]';
const SCHEDULE_PAGE_CMP = 'scheduled-list-page';
const SCHEDULE_PAGE_TASKS = `${SCHEDULE_PAGE_CMP} .tasks planner-task`;
const SCHEDULE_PAGE_TASK_1 = `${SCHEDULE_PAGE_TASKS}:first-of-type`;
// Note: not sure why this is the second child, but it is
const SCHEDULE_PAGE_TASK_2 = `${SCHEDULE_PAGE_TASKS}:nth-of-type(2)`;
const SCHEDULE_PAGE_TASK_1_TITLE_EL = `${SCHEDULE_PAGE_TASK_1} .title`;
// Note: not sure why this is the second child, but it is
const SCHEDULE_PAGE_TASK_2_TITLE_EL = `${SCHEDULE_PAGE_TASK_2} .title`;
test.describe('Reminders Schedule Page', () => {
test('should add a scheduled tasks', async ({ page, workViewPage, testPrefix }) => {
await workViewPage.waitForTaskList();
// Add task with reminder (manually implementing addTaskWithReminder)
const title = `${testPrefix}-0 test task koko`;
const scheduleTime = Date.now() + 10000; // Add 10 seconds buffer
// Add task
await workViewPage.addTask(title);
await page.waitForSelector(TASK, { state: 'visible' });
// Schedule task - use first() to avoid ambiguity
const firstTask = page.locator(TASK).first();
await firstTask.hover();
const scheduleBtn = firstTask.locator(TASK_SCHEDULE_BTN);
await scheduleBtn.waitFor({ state: 'visible' });
await scheduleBtn.click();
// Set schedule time in dialog
const dialog = page.locator('dialog-schedule-task');
await expect(dialog).toBeVisible();
// Set time (convert timestamp to time string)
const date = new Date(scheduleTime);
const hours = date.getHours().toString().padStart(2, '0');
const minutes = date.getMinutes().toString().padStart(2, '0');
await page.fill('input[type="time"]', `${hours}:${minutes}`);
// Confirm
await page.click('mat-dialog-actions button:last-of-type');
// Verify schedule button is present
await expect(firstTask.locator(TASK_SCHEDULE_BTN)).toBeVisible();
// Navigate to scheduled page and check if entry is there
await page.click(SCHEDULE_ROUTE_BTN);
await expect(page.locator(SCHEDULE_PAGE_CMP)).toBeVisible();
await expect(page.locator(SCHEDULE_PAGE_TASK_1)).toBeVisible();
await expect(page.locator(SCHEDULE_PAGE_TASK_1_TITLE_EL)).toBeVisible();
await expect(page.locator(SCHEDULE_PAGE_TASK_1_TITLE_EL)).toContainText(title);
});
test('should add multiple scheduled tasks', async ({
page,
workViewPage,
testPrefix,
}) => {
await workViewPage.waitForTaskList();
// First add the first task from previous test (needed for continuity)
const title1 = `${testPrefix}-0 test task koko`;
const scheduleTime1 = Date.now() + 10000;
await workViewPage.addTask(title1);
await page.waitForSelector(TASK, { state: 'visible' });
// Schedule first task
const firstTask = page.locator(TASK).first();
await firstTask.hover();
const scheduleBtn1 = firstTask.locator(TASK_SCHEDULE_BTN);
await scheduleBtn1.waitFor({ state: 'visible' });
await scheduleBtn1.click();
const dialog1 = page.locator('dialog-schedule-task');
await expect(dialog1).toBeVisible();
const date1 = new Date(scheduleTime1);
const hours1 = date1.getHours().toString().padStart(2, '0');
const minutes1 = date1.getMinutes().toString().padStart(2, '0');
await page.fill('input[type="time"]', `${hours1}:${minutes1}`);
await page.click('mat-dialog-actions button:last-of-type');
await dialog1.waitFor({ state: 'hidden' });
// Click to go back to work context
await page.click('.current-work-context-title');
await workViewPage.waitForTaskList();
// Add second task with reminder
const title2 = `${testPrefix}-2 hihihi`;
const scheduleTime2 = Date.now() + 10000;
await workViewPage.addTask(title2);
// Wait for both tasks to be visible
await page.waitForFunction(() => {
const tasks = document.querySelectorAll('task');
return tasks.length >= 2;
});
// Schedule the second task (which will be the first in the list due to newest first)
const allTasks = page.locator(TASK);
const newestTask = allTasks.first();
await newestTask.hover();
const scheduleBtn2 = newestTask.locator(TASK_SCHEDULE_BTN);
await scheduleBtn2.waitFor({ state: 'visible' });
await scheduleBtn2.click();
const dialog2 = page.locator('dialog-schedule-task');
await expect(dialog2).toBeVisible();
const date2 = new Date(scheduleTime2);
const hours2 = date2.getHours().toString().padStart(2, '0');
const minutes2 = date2.getMinutes().toString().padStart(2, '0');
await page.fill('input[type="time"]', `${hours2}:${minutes2}`);
await page.click('mat-dialog-actions button:last-of-type');
await dialog2.waitFor({ state: 'hidden' });
// Verify both tasks have schedule buttons
const task1 = page.locator(TASK).filter({ hasText: title1 });
const task2 = page.locator(TASK).filter({ hasText: title2 });
await expect(task1.locator(TASK_SCHEDULE_BTN)).toBeVisible();
await expect(task2.locator(TASK_SCHEDULE_BTN)).toBeVisible();
// Navigate to scheduled page and check if entries are there
await page.click(SCHEDULE_ROUTE_BTN);
await expect(page.locator(SCHEDULE_PAGE_CMP)).toBeVisible();
await expect(page.locator(SCHEDULE_PAGE_TASK_1)).toBeVisible();
await expect(page.locator(SCHEDULE_PAGE_TASK_1_TITLE_EL)).toBeVisible();
await expect(page.locator(SCHEDULE_PAGE_TASK_1_TITLE_EL)).toContainText(title1);
await expect(page.locator(SCHEDULE_PAGE_TASK_2_TITLE_EL)).toContainText(title2);
});
});

View file

@ -0,0 +1,108 @@
import { expect, test } from '../../fixtures/test.fixture';
const DIALOG = 'dialog-view-task-reminder';
const DIALOG_TASK = `${DIALOG} .task`;
const DIALOG_TASK1 = `${DIALOG_TASK}:first-of-type`;
const SCHEDULE_MAX_WAIT_TIME = 180000;
// Helper selectors from addTaskWithReminder
const TASK = 'task';
const SCHEDULE_TASK_ITEM = 'task-detail-item:nth-child(2)';
const DIALOG_CONTAINER = 'mat-dialog-container';
const DIALOG_SUBMIT = `${DIALOG_CONTAINER} mat-dialog-actions button:last-of-type`;
const TIME_INP = 'input[type="time"]';
const getTimeVal = (d: Date): string => {
const hours = d.getHours().toString().padStart(2, '0');
const minutes = d.getMinutes().toString().padStart(2, '0');
return `${hours}:${minutes}`;
};
test.describe('Reminders View Task', () => {
test('should display a modal with a scheduled task if due', async ({
page,
workViewPage,
testPrefix,
}) => {
test.setTimeout(SCHEDULE_MAX_WAIT_TIME + 30000); // Add extra time for test setup
// Wait for work view to be ready
await workViewPage.waitForTaskList();
const taskTitle = `${testPrefix}-0 A task`;
const scheduleTime = Date.now() + 10000; // Add 10 seconds buffer
const d = new Date(scheduleTime);
const timeValue = getTimeVal(d);
// Add task
await workViewPage.addTask(taskTitle);
// Open panel for task
const taskEl = page.locator(TASK).first();
await taskEl.hover();
const detailPanelBtn = page.locator('.show-additional-info-btn').first();
await detailPanelBtn.waitFor({ state: 'visible' });
await detailPanelBtn.click();
// Wait for and click schedule task item
await page.waitForSelector(SCHEDULE_TASK_ITEM, { state: 'visible' });
await page.click(SCHEDULE_TASK_ITEM);
// Wait for dialog
await page.waitForSelector(DIALOG_CONTAINER, { state: 'visible' });
await page.waitForTimeout(100);
// Set time
await page.waitForSelector(TIME_INP, { state: 'visible' });
await page.waitForTimeout(150);
// Focus and set time value
await page.click(TIME_INP);
await page.waitForTimeout(150);
// Clear and set value
await page.fill(TIME_INP, '');
await page.waitForTimeout(100);
// Set the time value
await page.evaluate(
({ selector, value }) => {
const el = document.querySelector(selector) as HTMLInputElement;
if (el) {
el.value = value;
el.dispatchEvent(new Event('input', { bubbles: true }));
el.dispatchEvent(new Event('change', { bubbles: true }));
}
},
{ selector: TIME_INP, value: timeValue },
);
await page.waitForTimeout(200);
// Also set value normally
await page.fill(TIME_INP, timeValue);
await page.waitForTimeout(200);
// Tab to commit value
await page.keyboard.press('Tab');
await page.waitForTimeout(200);
// Submit dialog
await page.waitForSelector(DIALOG_SUBMIT, { state: 'visible' });
await page.click(DIALOG_SUBMIT);
await page.waitForSelector(DIALOG_CONTAINER, { state: 'hidden' });
// Wait for reminder dialog to appear
await page.waitForSelector(DIALOG, {
state: 'visible',
timeout: SCHEDULE_MAX_WAIT_TIME,
});
// Assert dialog and task are present
await expect(page.locator(DIALOG)).toBeVisible();
await page.waitForSelector(DIALOG_TASK1, { state: 'visible' });
await expect(page.locator(DIALOG_TASK1)).toBeVisible();
await expect(page.locator(DIALOG_TASK1)).toContainText(taskTitle);
});
});

View file

@ -0,0 +1,85 @@
import { expect, test } from '../../fixtures/test.fixture';
const DIALOG = 'dialog-view-task-reminder';
const DIALOG_TASKS_WRAPPER = `${DIALOG} .tasks`;
const DIALOG_TASK = `${DIALOG} .task`;
const DIALOG_TASK1 = `${DIALOG_TASK}:first-of-type`;
const DIALOG_TASK2 = `${DIALOG_TASK}:nth-of-type(2)`;
const SCHEDULE_MAX_WAIT_TIME = 180000;
// Helper selectors for task scheduling
const TASK = 'task';
const SCHEDULE_TASK_ITEM = 'task-detail-item:nth-child(2)';
const SCHEDULE_DIALOG = 'mat-dialog-container';
const DIALOG_SUBMIT = `${SCHEDULE_DIALOG} mat-dialog-actions button:last-of-type`;
const TIME_INP = 'input[type="time"]';
const SIDE_INNER = '.right-panel';
const DEFAULT_DELTA = 5000; // 5 seconds instead of 1.2 minutes
test.describe.serial('Reminders View Task 2', () => {
const addTaskWithReminder = async (
page: any,
workViewPage: any,
title: string,
scheduleTime: number = Date.now() + DEFAULT_DELTA,
): Promise<void> => {
// Add task
await workViewPage.addTask(title);
// Open task panel by hovering and clicking the detail button
const taskSel = page.locator(TASK).first();
await taskSel.waitFor({ state: 'visible' });
await taskSel.hover();
const detailPanelBtn = page.locator('.show-additional-info-btn').first();
await detailPanelBtn.waitFor({ state: 'visible' });
await detailPanelBtn.click();
await page.waitForSelector(SIDE_INNER, { state: 'visible' });
// Click schedule item
await page.click(SCHEDULE_TASK_ITEM);
await page.waitForSelector(SCHEDULE_DIALOG, { state: 'visible' });
// Set time
const d = new Date(scheduleTime);
const timeValue = `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
const timeInput = page.locator(TIME_INP);
await timeInput.click();
await timeInput.clear();
await timeInput.fill(timeValue);
await page.keyboard.press('Tab');
// Submit
await page.click(DIALOG_SUBMIT);
await page.waitForSelector(SCHEDULE_DIALOG, { state: 'hidden' });
};
test('should display a modal with 2 scheduled task if due', async ({
page,
workViewPage,
testPrefix,
}) => {
test.setTimeout(SCHEDULE_MAX_WAIT_TIME + 60000); // Add extra buffer
await workViewPage.waitForTaskList();
// Add two tasks with reminders using test prefix
const task1Name = `${testPrefix}-0 B task`;
const task2Name = `${testPrefix}-1 B task`;
await addTaskWithReminder(page, workViewPage, task1Name);
await addTaskWithReminder(page, workViewPage, task2Name, Date.now() + 5000);
// Wait for reminder dialog
await page.waitForSelector(DIALOG, {
state: 'visible',
timeout: SCHEDULE_MAX_WAIT_TIME,
});
// Verify both tasks are shown
await expect(page.locator(DIALOG_TASK1)).toBeVisible();
await expect(page.locator(DIALOG_TASK2)).toBeVisible();
await expect(page.locator(DIALOG_TASKS_WRAPPER)).toContainText(task1Name);
await expect(page.locator(DIALOG_TASKS_WRAPPER)).toContainText(task2Name);
});
});

View file

@ -0,0 +1,102 @@
import { expect, test } from '../../fixtures/test.fixture';
const DIALOG = 'dialog-view-task-reminder';
const DIALOG_TASKS_WRAPPER = `${DIALOG} .tasks`;
const DIALOG_TASK = `${DIALOG} .task`;
const DIALOG_TASK1 = `${DIALOG_TASK}:first-of-type`;
const DIALOG_TASK2 = `${DIALOG_TASK}:nth-of-type(2)`;
const DIALOG_TASK3 = `${DIALOG_TASK}:nth-of-type(3)`;
const TO_TODAY_SUF = ' .actions button:last-of-type';
const SCHEDULE_MAX_WAIT_TIME = 180000;
// Helper selectors for task scheduling
const TASK = 'task';
const SCHEDULE_TASK_ITEM = 'task-detail-item:nth-child(2)';
const SCHEDULE_DIALOG = 'mat-dialog-container';
const DIALOG_SUBMIT = `${SCHEDULE_DIALOG} mat-dialog-actions button:last-of-type`;
const TIME_INP = 'input[type="time"]';
const RIGHT_PANEL = '.right-panel';
const DEFAULT_DELTA = 5000; // 5 seconds instead of 1.2 minutes
test.describe.serial('Reminders View Task 4', () => {
const addTaskWithReminder = async (
page: any,
workViewPage: any,
title: string,
scheduleTime: number = Date.now() + DEFAULT_DELTA,
): Promise<void> => {
// Add task
await workViewPage.addTask(title);
// Open task panel by hovering and clicking the detail button
const taskSel = page.locator(TASK).first();
await taskSel.waitFor({ state: 'visible' });
await taskSel.hover();
const detailPanelBtn = page.locator('.show-additional-info-btn').first();
await detailPanelBtn.waitFor({ state: 'visible' });
await detailPanelBtn.click();
await page.waitForSelector(RIGHT_PANEL, { state: 'visible' });
// Click schedule item
await page.click(SCHEDULE_TASK_ITEM);
await page.waitForSelector(SCHEDULE_DIALOG, { state: 'visible' });
// Set time
const d = new Date(scheduleTime);
const timeValue = `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
const timeInput = page.locator(TIME_INP);
await timeInput.click();
await timeInput.clear();
await timeInput.fill(timeValue);
await page.keyboard.press('Tab');
// Submit
await page.click(DIALOG_SUBMIT);
await page.waitForSelector(SCHEDULE_DIALOG, { state: 'hidden' });
};
test('should manually empty list via add to today', async ({
page,
workViewPage,
testPrefix,
}) => {
test.setTimeout(SCHEDULE_MAX_WAIT_TIME + 120000);
await workViewPage.waitForTaskList();
const start = Date.now() + 10000; // Reduce from 100 seconds to 10 seconds
// Add three tasks with reminders using test prefix
const task1Name = `${testPrefix}-0 D task xyz`;
const task2Name = `${testPrefix}-1 D task xyz`;
const task3Name = `${testPrefix}-2 D task xyz`;
await addTaskWithReminder(page, workViewPage, task1Name, start);
await addTaskWithReminder(page, workViewPage, task2Name, start);
await addTaskWithReminder(page, workViewPage, task3Name, Date.now() + 5000);
// Wait for reminder dialog
await page.waitForSelector(DIALOG, {
state: 'visible',
timeout: SCHEDULE_MAX_WAIT_TIME + 120000,
});
// Wait for all tasks to be present
await page.waitForSelector(DIALOG_TASK1, { state: 'visible' });
await page.waitForSelector(DIALOG_TASK2, { state: 'visible' });
await page.waitForSelector(DIALOG_TASK3, { state: 'visible' });
// Verify all tasks are shown
await expect(page.locator(DIALOG_TASKS_WRAPPER)).toContainText(task1Name);
await expect(page.locator(DIALOG_TASKS_WRAPPER)).toContainText(task2Name);
await expect(page.locator(DIALOG_TASKS_WRAPPER)).toContainText(task3Name);
// Click "add to today" buttons
await page.click(DIALOG_TASK1 + TO_TODAY_SUF);
await page.click(DIALOG_TASK2 + TO_TODAY_SUF);
// Verify remaining task contains 'D task xyz'
await expect(page.locator(DIALOG_TASK1)).toContainText('D task xyz');
});
});

View file

@ -0,0 +1,22 @@
import { expect, test } from '../../fixtures/test.fixture';
test.describe('Short Syntax', () => {
test('should add task with project via short syntax', async ({
page,
workViewPage,
}) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Add a task with project short syntax
await workViewPage.addTask('0 test task koko +i');
// Verify task is visible
const task = page.locator('task').first();
await expect(task).toBeVisible();
// Verify the task has the Inbox tag
const taskTags = task.locator('tag');
await expect(taskTags).toContainText('Inbox');
});
});

View file

@ -0,0 +1,63 @@
import { test, expect } from '../../fixtures/test.fixture';
import { SyncPage } from '../../pages/sync.page';
test.describe('WebDAV Sync', () => {
let syncPage: SyncPage;
test.beforeEach(async ({ page, workViewPage }) => {
syncPage = new SyncPage(page);
await workViewPage.waitForTaskList();
});
test('should configure WebDAV sync', async ({ page, workViewPage }) => {
// Configure WebDAV sync
await syncPage.setupWebdavSync({
baseUrl: 'http://localhost:2345/',
username: 'admin',
password: 'admin',
syncFolderPath: '/super-productivity-test',
});
// Wait for dialog to close
await page.waitForTimeout(1000);
// The sync button should exist after configuration
await expect(syncPage.syncBtn).toBeVisible();
// Create a test task to ensure app is working
await workViewPage.addTask('Test task for WebDAV sync');
await page.waitForTimeout(500);
// Verify task was created
await expect(page.locator('task')).toHaveCount(1);
});
test('should create and sync tasks', async ({ page, workViewPage }) => {
// Configure WebDAV sync
await syncPage.setupWebdavSync({
baseUrl: 'http://localhost:2345/',
username: 'admin',
password: 'admin',
syncFolderPath: '/super-productivity-test-2',
});
await page.waitForTimeout(1000);
// Create multiple test tasks
await workViewPage.addTask('First sync task');
await workViewPage.addTask('Second sync task');
await page.waitForTimeout(500);
// Verify tasks are present
await expect(page.locator('task')).toHaveCount(2);
// Trigger sync
await syncPage.triggerSync();
// Wait a reasonable time for sync
await page.waitForTimeout(5000);
// Verify sync button is still visible (basic check)
await expect(syncPage.syncBtn).toBeVisible();
});
});

View file

@ -0,0 +1,65 @@
import { test, expect } from '../../fixtures/test.fixture';
const TASK = 'task';
const TASK_TEXTAREA = 'task textarea';
const FIRST_TASK = 'task:first-child';
const SECOND_TASK = 'task:nth-child(2)';
const TASK_DONE_BTN = '.task-done-btn';
test.describe('Task CRUD Operations', () => {
test('should create, edit and delete tasks', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Create first task
await workViewPage.addTask('First task');
await page.waitForSelector(TASK, { state: 'visible' });
await expect(page.locator(TASK_TEXTAREA).first()).toHaveValue(/First task/);
// Create second task
await workViewPage.addTask('Second task');
await expect(page.locator(`${FIRST_TASK} textarea`)).toHaveValue(/Second task/);
await expect(page.locator(`${SECOND_TASK} textarea`)).toHaveValue(/First task/);
// Edit first task (newest)
await page.click(`${FIRST_TASK} textarea`);
await page.fill(`${FIRST_TASK} textarea`, 'Edited second task');
await page.keyboard.press('Tab'); // Blur to save
await expect(page.locator(`${FIRST_TASK} textarea`)).toHaveValue(
/Edited second task/,
);
// Mark first task as done
await page.hover(FIRST_TASK);
await page.waitForSelector(`${FIRST_TASK} ${TASK_DONE_BTN}`, { state: 'visible' });
await page.click(`${FIRST_TASK} ${TASK_DONE_BTN}`);
// Verify task is marked as done
await expect(page.locator(`${FIRST_TASK}.isDone`)).toBeVisible();
// Verify we have one done task and one undone task
await expect(page.locator(`${TASK}.isDone`)).toHaveCount(1);
await expect(page.locator(`${TASK}:not(.isDone)`)).toHaveCount(1);
});
test('should handle task title updates', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Create a task
await workViewPage.addTask('Original title');
await page.waitForSelector(TASK, { state: 'visible' });
// Update the task title multiple times
await page.click(`${FIRST_TASK} textarea`);
await page.fill(`${FIRST_TASK} textarea`, 'Updated title 1');
await page.keyboard.press('Tab');
await expect(page.locator(`${FIRST_TASK} textarea`)).toHaveValue(/Updated title 1/);
// Update again
await page.click(`${FIRST_TASK} textarea`);
await page.fill(`${FIRST_TASK} textarea`, 'Final title');
await page.keyboard.press('Tab');
await expect(page.locator(`${FIRST_TASK} textarea`)).toHaveValue(/Final title/);
});
});

View file

@ -0,0 +1,113 @@
import { test, expect } from '../../fixtures/test.fixture';
const TASK_SEL = 'task';
const TASK_TEXTAREA = 'task textarea';
const TASK_DONE_BTN = '.task-done-btn';
const FINISH_DAY_BTN = '.e2e-finish-day';
const FIRST_TASK = 'task:nth-child(1)';
const SECOND_TASK = 'task:nth-child(2)';
const THIRD_TASK = 'task:nth-child(3)';
const SAVE_AND_GO_HOME_BTN = 'button[mat-flat-button][color="primary"]:last-of-type';
const TABLE_CAPTION = 'quick-history h3';
const TABLE_ROWS = 'table tr';
test.describe('Finish Day Quick History With Subtasks', () => {
test('should complete full finish day flow with subtasks', async ({
page,
workViewPage,
}) => {
test.setTimeout(60000); // Increase timeout for this long flow
// Wait for work view to be ready
await workViewPage.waitForTaskList();
await workViewPage.addTask('Main Task with Subtasks');
await page.waitForSelector(TASK_SEL, { state: 'visible' });
await expect(page.locator(TASK_TEXTAREA).first()).toHaveValue(
/Main Task with Subtasks/,
);
// Add tasks that would be subtasks as top-level tasks
await workViewPage.addTask('First Subtask');
await workViewPage.addTask('Second Subtask');
// Verify we have three tasks (newest first)
await expect(page.locator(FIRST_TASK)).toBeVisible();
await expect(page.locator(SECOND_TASK)).toBeVisible();
await expect(page.locator(THIRD_TASK)).toBeVisible();
await expect(page.locator(`${FIRST_TASK} textarea`)).toHaveValue(/Second Subtask/);
await expect(page.locator(`${SECOND_TASK} textarea`)).toHaveValue(/First Subtask/);
await expect(page.locator(`${THIRD_TASK} textarea`)).toHaveValue(
/Main Task with Subtasks/,
);
// Step 2: Mark all tasks as done
// Mark all three tasks as done - always mark the first visible task
// as done tasks might be hidden or moved
await page.hover(FIRST_TASK);
await page.waitForSelector(`${FIRST_TASK} ${TASK_DONE_BTN}`, { state: 'visible' });
await page.click(`${FIRST_TASK} ${TASK_DONE_BTN}`);
// Mark the (new) first task
await page.hover(FIRST_TASK);
await page.waitForSelector(`${FIRST_TASK} ${TASK_DONE_BTN}`, { state: 'visible' });
await page.click(`${FIRST_TASK} ${TASK_DONE_BTN}`);
// Mark the (new) first task again
await page.hover(FIRST_TASK);
await page.waitForSelector(`${FIRST_TASK} ${TASK_DONE_BTN}`, { state: 'visible' });
await page.click(`${FIRST_TASK} ${TASK_DONE_BTN}`);
// Verify no undone tasks remain
await expect(page.locator('task:not(.isDone)')).toHaveCount(0);
// Step 3: Click Finish Day button
await page.waitForSelector(FINISH_DAY_BTN, { state: 'visible' });
await page.click(FINISH_DAY_BTN);
// Step 4: Wait for route change and click Save and go home
await page.waitForSelector('daily-summary', { state: 'visible' });
await page.waitForSelector(SAVE_AND_GO_HOME_BTN, { state: 'visible' });
await page.click(SAVE_AND_GO_HOME_BTN);
// Wait for navigation back to work view
await page.waitForSelector('task-list', { state: 'visible' });
// Step 5: Navigate to quick history via left-hand menu
await page.click('side-nav > section.main > side-nav-item.g-multi-btn-wrapper', {
button: 'right',
});
await page.waitForSelector('work-context-menu > button:nth-child(1)', {
state: 'visible',
});
await page.click('work-context-menu > button:nth-child(1)');
await page.waitForSelector('quick-history', { state: 'visible' });
// Step 6: Click on table caption
await page.waitForSelector(TABLE_CAPTION, { state: 'visible' });
await page.click(TABLE_CAPTION);
// Step 7: Confirm quick history page loads
await page.waitForSelector('quick-history', { state: 'visible' });
// Verify we're on the quick history page without specific task checks
// Tasks created with 'a' shortcut may not be properly nested/archived
await expect(page.locator('quick-history')).toBeVisible();
await expect(page.locator('table')).toBeVisible();
// Step 8: Confirm tasks are in the table
await page.waitForSelector(TABLE_ROWS, { state: 'visible', timeout: 5000 });
await expect(page.locator(TABLE_ROWS).first()).toBeVisible();
await page.waitForSelector('table > tr:nth-child(1) > td.title > span', {
state: 'visible',
});
// Verify the task title is present in the table
await expect(page.locator('table > tr:nth-child(1) > td.title > span')).toContainText(
'Main Task with Subtasks',
);
await expect(page.locator('table > tr:nth-child(2) > td.title > span')).toContainText(
'First Subtask',
);
await expect(page.locator('table > tr:nth-child(3) > td.title > span')).toContainText(
'Second Subtask',
);
});
});

View file

@ -0,0 +1,89 @@
import { expect, test } from '../../fixtures/test.fixture';
const TASK_SEL = 'task';
const TASK_TEXTAREA = 'task textarea';
const FINISH_DAY_BTN = '.e2e-finish-day';
const SAVE_AND_GO_HOME_BTN =
'daily-summary button[mat-flat-button][color="primary"]:last-of-type';
const TABLE_CAPTION = 'quick-history h3';
const TABLE_ROWS = 'table tr';
test.describe.serial('Finish Day Quick History', () => {
test('should create task, mark as done, finish day and view in quick history', async ({
page,
workViewPage,
testPrefix,
}) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Create a task with unique prefix
const taskName = `${testPrefix}-Task for Quick History`;
await workViewPage.addTask(taskName);
await page.waitForSelector(TASK_SEL, { state: 'visible' });
const taskTextarea = page.locator(TASK_TEXTAREA);
await expect(taskTextarea).toHaveValue(new RegExp(taskName));
// Mark task as done
await page.waitForSelector(TASK_SEL, { state: 'visible' });
const task = page.locator(TASK_SEL).first();
await task.hover();
const doneBtn = page.locator(`${TASK_SEL} .task-done-btn`).first();
await doneBtn.waitFor({ state: 'visible' });
await doneBtn.click();
// Wait for task to be marked as done
await page.waitForFunction(() => {
const tasks = document.querySelectorAll('task');
return Array.from(tasks).some((t) => t.classList.contains('isDone'));
});
// Click Finish Day button
const finishDayBtn = page.locator(FINISH_DAY_BTN);
await finishDayBtn.waitFor({ state: 'visible' });
await finishDayBtn.click();
// Wait for route change to daily summary
await page.waitForURL(/#\/tag\/TODAY\/daily-summary/);
await page.waitForSelector('daily-summary', { state: 'visible' });
// Click Save and go home
const saveBtn = page.locator(SAVE_AND_GO_HOME_BTN);
await saveBtn.waitFor({ state: 'visible' });
await saveBtn.click();
// Wait for navigation back to work view
await page.waitForURL(/#\/tag\/TODAY/);
// Navigate to quick history via left-hand menu
const contextBtn = page
.locator('side-nav > section.main > side-nav-item.g-multi-btn-wrapper')
.first();
await contextBtn.waitFor({ state: 'visible' });
await contextBtn.click({ button: 'right' });
const quickHistoryBtn = page.locator('work-context-menu > button:nth-child(1)');
await quickHistoryBtn.waitFor({ state: 'visible' });
await quickHistoryBtn.click();
// Wait for quick history page
await page.waitForURL(/#\/tag\/TODAY\/quick-history/);
await page.waitForSelector('quick-history', { state: 'visible' });
// Click on table caption
const tableCaption = page.locator(TABLE_CAPTION);
await tableCaption.waitFor({ state: 'visible' });
await tableCaption.click();
// Confirm quick history page loads
await expect(page.locator('quick-history')).toBeVisible();
// Confirm task is in the table
await page.waitForSelector(TABLE_ROWS, { state: 'visible' });
const taskTitle = page.locator('table > tr:nth-child(1) > td.title > span');
await taskTitle.waitFor({ state: 'visible' });
// Verify the task title is present in the table
await expect(taskTitle).toContainText(taskName);
});
});

View file

@ -0,0 +1,19 @@
import { expect, test } from '../../fixtures/test.fixture';
test.describe('Simple Subtask', () => {
test('should create subtask with keyboard shortcut', async ({ page, workViewPage }) => {
// Add parent task
await workViewPage.addTask('Parent Task');
const task = page.locator('task');
await workViewPage.addSubTask(task, 'SubTask 1');
const subTask = task.locator('.sub-tasks task');
await subTask.waitFor({ state: 'visible' });
// Verify subtask was created with correct content
const subtaskTextarea = subTask.locator('textarea');
await expect(subtaskTextarea).toHaveValue('SubTask 1');
});
});

View file

@ -0,0 +1,40 @@
import { expect, test } from '../../fixtures/test.fixture';
test.describe('Task List - Start/Stop', () => {
test('should start and stop single task', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Add a task
await workViewPage.addTask('0 C task');
// Get the first task
const firstTask = page.locator('task').first();
// Hover over the task to show the play button
await firstTask.hover();
// Wait for play button to be visible and click it
const playBtn = page.locator('.play-btn.tour-playBtn').first();
await playBtn.waitFor({ state: 'visible' });
await playBtn.click();
// Wait a moment for the class to be applied
await page.waitForTimeout(200);
// Verify the task has the 'isCurrent' class
await expect(firstTask).toHaveClass(/isCurrent/);
// Hover again to ensure button is visible
await firstTask.hover();
// Click the play button again to stop the task
await playBtn.click();
// Wait a moment for the class to be removed
await page.waitForTimeout(200);
// Verify the task no longer has the 'isCurrent' class
await expect(firstTask).not.toHaveClass(/isCurrent/);
});
});

View file

@ -0,0 +1,131 @@
import { test, expect } from '../../fixtures/test.fixture';
const TASK = 'task';
const TASK_TEXTAREA = 'task textarea';
const FIRST_TASK = 'task:first-of-type';
const UNDONE_TASK_LIST = 'task-list[listmodelid="UNDONE"]';
const DONE_TASK_LIST = 'task-list[listmodelid="DONE"]';
const DONE_TASKS_SECTION = '.tour-doneList';
const TOGGLE_DONE_TASKS_BTN = '.tour-doneList .mat-expansion-indicator';
test.describe('Work View Features', () => {
test('should show undone and done task lists', async ({
page,
workViewPage,
testPrefix,
}) => {
test.setTimeout(30000); // Increase timeout
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Wait for any dialogs to be dismissed
await page.waitForTimeout(2000);
// Verify undone task list is visible
await expect(page.locator(UNDONE_TASK_LIST)).toBeVisible({ timeout: 10000 });
// Create tasks
await workViewPage.addTask('Task 1');
await page.waitForSelector(TASK, { state: 'visible', timeout: 5000 });
await page.waitForTimeout(500);
await workViewPage.addTask('Task 2');
await page.waitForTimeout(1000);
// Verify we have 2 tasks
await expect(page.locator(TASK)).toHaveCount(2);
// Mark first task as done
const firstTask = page.locator(FIRST_TASK);
await firstTask.waitFor({ state: 'visible' });
// Hover over the task to show the done button
await firstTask.hover();
// Click the done button
const doneBtn = firstTask.locator('.task-done-btn');
await doneBtn.waitFor({ state: 'visible' });
await doneBtn.click();
// Wait a bit for the transition
await page.waitForTimeout(2000);
// Check if done section exists (it might not show if there are no done tasks)
const doneSectionExists = await page
.locator(DONE_TASKS_SECTION)
.isVisible({ timeout: 5000 })
.catch(() => false);
if (doneSectionExists) {
// Toggle done tasks visibility if needed
const toggleBtn = page.locator(TOGGLE_DONE_TASKS_BTN);
if (await toggleBtn.isVisible({ timeout: 1000 }).catch(() => false)) {
await toggleBtn.click();
await page.waitForTimeout(1000);
}
// Verify done task list is visible
await expect(page.locator(DONE_TASK_LIST)).toBeVisible({ timeout: 5000 });
// Verify tasks are in correct lists
await expect(page.locator(`${UNDONE_TASK_LIST} ${TASK}`)).toHaveCount(1);
await expect(page.locator(`${DONE_TASK_LIST} ${TASK}`)).toHaveCount(1);
} else {
// If no done section, just verify we have one less undone task
await expect(page.locator(`${UNDONE_TASK_LIST} ${TASK}`)).toHaveCount(1);
}
});
test('should handle task order correctly', async ({ page, workViewPage }) => {
test.setTimeout(20000);
// Wait for work view to be ready
await workViewPage.waitForTaskList();
await page.waitForTimeout(1000);
// Create multiple tasks
await workViewPage.addTask('First created');
await page.waitForTimeout(500);
await workViewPage.addTask('Second created');
await page.waitForTimeout(500);
await workViewPage.addTask('Third created');
await page.waitForTimeout(500);
await workViewPage.addTask('Fourth created');
await page.waitForTimeout(500);
// Verify order (newest first)
await expect(page.locator('task:nth-of-type(1) textarea')).toHaveValue(
/Fourth created/,
);
await expect(page.locator('task:nth-of-type(2) textarea')).toHaveValue(
/Third created/,
);
await expect(page.locator('task:nth-of-type(3) textarea')).toHaveValue(
/Second created/,
);
await expect(page.locator('task:nth-of-type(4) textarea')).toHaveValue(
/First created/,
);
});
test('should persist tasks after navigation', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Create a task
await workViewPage.addTask('Persistent task');
await page.waitForSelector(TASK, { state: 'visible' });
// Navigate to settings
await page.goto('/#/config');
await expect(page.locator('.page-settings')).toBeVisible();
// Navigate back
await page.goto('/#/tag/TODAY');
await expect(page.locator('task-list').first()).toBeVisible();
// Verify task is still there
await expect(page.locator(TASK_TEXTAREA).first()).toHaveValue(/Persistent task/);
});
});

View file

@ -0,0 +1,100 @@
import { expect, test } from '../../fixtures/test.fixture';
test.describe('Work View', () => {
test('should add task via key combo', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Add a task
await workViewPage.addTask('0 test task koko');
// Verify task is visible
const task = page.locator('task').first();
await expect(task).toBeVisible();
// Verify task content (accounting for test prefix)
const taskTextarea = task.locator('textarea');
await expect(taskTextarea).toHaveValue(/.*0 test task koko/);
});
test('should still show created task after reload', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Add a task
await workViewPage.addTask('0 test task lolo');
// Verify task is visible
const task = page.locator('task').first();
await expect(task).toBeVisible();
// Reload the page
await page.reload();
// Wait for work view to be ready again
await workViewPage.waitForTaskList();
// Verify task is still visible after reload
await expect(task).toBeVisible();
const taskTextarea = task.locator('textarea');
await expect(taskTextarea).toHaveValue(/.*0 test task lolo/);
});
test('should add multiple tasks from header button', async ({ page, workViewPage }) => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
// Click the add button in the header to open global add task input
const headerAddBtn = page.locator('.tour-addBtn');
await headerAddBtn.click();
// Wait for global input to be visible
await workViewPage.addTaskGlobalInput.waitFor({ state: 'visible' });
// Add first task
await workViewPage.addTaskGlobalInput.fill('4 test task hohoho');
await page.keyboard.press('Enter');
// Add second task
await workViewPage.addTaskGlobalInput.fill('5 some other task xoxo');
await page.keyboard.press('Enter');
// Close the input by clicking backdrop
if (await workViewPage.backdrop.isVisible()) {
await workViewPage.backdrop.click();
}
// Verify both tasks are visible
const tasks = page.locator('task');
await expect(tasks).toHaveCount(2);
// NOTE: global adds to top rather than bottom
await expect(tasks.nth(0).locator('textarea')).toHaveValue(
/.*5 some other task xoxo/,
);
await expect(tasks.nth(1).locator('textarea')).toHaveValue(/.*4 test task hohoho/);
});
test('should add 2 tasks from initial bar', async ({ page, workViewPage }) => {
test.setTimeout(20000);
// Wait for work view to be ready
await workViewPage.waitForTaskList();
await page.waitForTimeout(2000); // Wait for UI to stabilize
// Simply add two tasks using the standard method
await workViewPage.addTask('2 test task hihi');
await page.waitForTimeout(500);
await workViewPage.addTask('3 some other task');
await page.waitForTimeout(500);
// Verify both tasks are visible
const tasks = page.locator('task');
await expect(tasks).toHaveCount(2);
// Verify task order (most recent first due to global add)
await expect(tasks.nth(0).locator('textarea')).toHaveValue(/.*3 some other task/);
await expect(tasks.nth(1).locator('textarea')).toHaveValue(/.*2 test task hihi/);
});
});

View file

@ -29,17 +29,17 @@ test.describe('All Basic Routes Without Error', () => {
// Navigate to different routes
await page.goto('/#/tag/TODAY/quick-history');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/worklog');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/metrics');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/planner');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/daily-summary');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await page.goto('/#/tag/TODAY/settings');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
// Send 'n' key to open notes dialog
await page.keyboard.press('n');

View file

@ -15,8 +15,8 @@ test.describe('Issue Provider Panel', () => {
await page.click('mat-tab-group .mat-mdc-tab:last-child');
await page.waitForSelector('issue-provider-setup-overview', { state: 'visible' });
// Wait for the setup overview content to be fully loaded
await page.waitForLoadState('networkidle');
// Wait for the setup overview to be fully loaded
await page.waitForTimeout(1000);
// Get all buttons in the issue provider setup overview
const setupButtons = page.locator('issue-provider-setup-overview button');
@ -43,10 +43,8 @@ test.describe('Issue Provider Panel', () => {
if (dialogOpened) {
await page.click(CANCEL_BTN);
// Wait for dialog to close by waiting for cancel button to be hidden
await page
.waitForSelector(CANCEL_BTN, { state: 'hidden', timeout: 2000 })
.catch(() => {});
// Wait for dialog to close
await page.waitForTimeout(500);
}
}
}

View file

@ -3,7 +3,7 @@ import { expect, test } from '../../fixtures/test.fixture';
test.describe.serial('Performance Tests - Adding Multiple Tasks', () => {
test('performance: adding 20 tasks sequentially', async ({ page, workViewPage }) => {
// Set a longer timeout for this performance test
test.setTimeout(60000);
test.setTimeout(20000);
// Wait for work view to be ready
await workViewPage.waitForTaskList();
@ -22,12 +22,14 @@ test.describe.serial('Performance Tests - Adding Multiple Tasks', () => {
const totalTime = performance.now() - startTime;
// Log performance metrics (only if test fails or in verbose mode)
// console.log(`Time to create 20 tasks: ${totalTime.toFixed(2)}ms`);
// console.log(`Average time per task: ${(totalTime / 20).toFixed(2)}ms`);
// Verify all tasks were created
const tasks = page.locator('task');
await expect(tasks).toHaveCount(20);
// Performance assertion - 20 tasks should be created in under 60 seconds
expect(totalTime).toBeLessThan(60000);
expect(totalTime).toBeLessThan(20000);
});
});

View file

@ -12,6 +12,8 @@ test.describe('Project', () => {
// Wait for app to be ready
await workViewPage.waitForTaskList();
// Additional wait for stability in parallel execution
await page.waitForTimeout(50);
});
test('move done tasks to archive without error', async ({ page }) => {

View file

@ -51,19 +51,19 @@ test.describe('Reminders View Task', () => {
// Wait for dialog
await page.waitForSelector(DIALOG_CONTAINER, { state: 'visible' });
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(100);
// Set time
await page.waitForSelector(TIME_INP, { state: 'visible' });
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(150);
// Focus and set time value
await page.click(TIME_INP);
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(150);
// Clear and set value
await page.fill(TIME_INP, '');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(100);
// Set the time value
await page.evaluate(
@ -78,15 +78,15 @@ test.describe('Reminders View Task', () => {
{ selector: TIME_INP, value: timeValue },
);
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(200);
// Also set value normally
await page.fill(TIME_INP, timeValue);
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(200);
// Tab to commit value
await page.keyboard.press('Tab');
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(200);
// Submit dialog
await page.waitForSelector(DIALOG_SUBMIT, { state: 'visible' });

View file

@ -18,11 +18,15 @@ test.describe('WebDAV Sync', () => {
syncFolderPath: '/super-productivity-test',
});
// Wait for sync dialog to close and sync button to be visible
await expect(syncPage.syncBtn).toBeVisible({ timeout: 3000 });
// Wait for dialog to close
await page.waitForTimeout(1000);
// The sync button should exist after configuration
await expect(syncPage.syncBtn).toBeVisible();
// Create a test task to ensure app is working
await workViewPage.addTask('Test task for WebDAV sync');
await page.waitForTimeout(500);
// Verify task was created
await expect(page.locator('task')).toHaveCount(1);
@ -37,12 +41,12 @@ test.describe('WebDAV Sync', () => {
syncFolderPath: '/super-productivity-test-2',
});
// Wait for sync dialog to close
await expect(syncPage.syncBtn).toBeVisible({ timeout: 3000 });
await page.waitForTimeout(1000);
// Create multiple test tasks
await workViewPage.addTask('First sync task');
await workViewPage.addTask('Second sync task');
await page.waitForTimeout(500);
// Verify tasks are present
await expect(page.locator('task')).toHaveCount(2);
@ -50,8 +54,8 @@ test.describe('WebDAV Sync', () => {
// Trigger sync
await syncPage.triggerSync();
// Wait for sync to complete (wait for spinner to disappear or check icon to appear)
await syncPage.waitForSyncComplete();
// Wait a reasonable time for sync
await page.waitForTimeout(5000);
// Verify sync button is still visible (basic check)
await expect(syncPage.syncBtn).toBeVisible();

View file

@ -20,7 +20,7 @@ test.describe('Task List - Start/Stop', () => {
await playBtn.click();
// Wait a moment for the class to be applied
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(200);
// Verify the task has the 'isCurrent' class
await expect(firstTask).toHaveClass(/isCurrent/);
@ -32,7 +32,7 @@ test.describe('Task List - Start/Stop', () => {
await playBtn.click();
// Wait a moment for the class to be removed
await page.waitForLoadState('domcontentloaded');
await page.waitForTimeout(200);
// Verify the task no longer has the 'isCurrent' class
await expect(firstTask).not.toHaveClass(/isCurrent/);

View file

@ -20,7 +20,7 @@ test.describe('Work View Features', () => {
await workViewPage.waitForTaskList();
// Wait for any dialogs to be dismissed
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2000);
// Verify undone task list is visible
await expect(page.locator(UNDONE_TASK_LIST)).toBeVisible({ timeout: 10000 });
@ -28,10 +28,10 @@ test.describe('Work View Features', () => {
// Create tasks
await workViewPage.addTask('Task 1');
await page.waitForSelector(TASK, { state: 'visible', timeout: 5000 });
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await workViewPage.addTask('Task 2');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
// Verify we have 2 tasks
await expect(page.locator(TASK)).toHaveCount(2);
@ -49,7 +49,7 @@ test.describe('Work View Features', () => {
await doneBtn.click();
// Wait a bit for the transition
await page.waitForLoadState('networkidle');
await page.waitForTimeout(2000);
// Check if done section exists (it might not show if there are no done tasks)
const doneSectionExists = await page
@ -62,7 +62,7 @@ test.describe('Work View Features', () => {
const toggleBtn = page.locator(TOGGLE_DONE_TASKS_BTN);
if (await toggleBtn.isVisible({ timeout: 1000 }).catch(() => false)) {
await toggleBtn.click();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
}
// Verify done task list is visible
@ -82,17 +82,17 @@ test.describe('Work View Features', () => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
await page.waitForLoadState('networkidle');
await page.waitForTimeout(1000);
// Create multiple tasks
await workViewPage.addTask('First created');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await workViewPage.addTask('Second created');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await workViewPage.addTask('Third created');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await workViewPage.addTask('Fourth created');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
// Verify order (newest first)
await expect(page.locator('task:nth-of-type(1) textarea')).toHaveValue(

View file

@ -80,14 +80,14 @@ test.describe('Work View', () => {
// Wait for work view to be ready
await workViewPage.waitForTaskList();
await page.waitForLoadState('networkidle'); // Wait for UI to stabilize
await page.waitForTimeout(2000); // Wait for UI to stabilize
// Simply add two tasks using the standard method
await workViewPage.addTask('2 test task hihi');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
await workViewPage.addTask('3 some other task');
await page.waitForLoadState('networkidle');
await page.waitForTimeout(500);
// Verify both tasks are visible
const tasks = page.locator('task');