mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
test(e2e): restore all test files from 4781b6ec with updated import paths
This commit is contained in:
parent
fed010b6d3
commit
613ccfa3da
30 changed files with 1551 additions and 42 deletions
49
e2e-playwright/tests/all-basic-routes-without-error.spec.ts
Normal file
49
e2e-playwright/tests/all-basic-routes-without-error.spec.ts
Normal 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)
|
||||
});
|
||||
});
|
||||
|
|
@ -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');
|
||||
});
|
||||
});
|
||||
36
e2e-playwright/tests/daily-summary/daily-summary.spec.ts
Normal file
36
e2e-playwright/tests/daily-summary/daily-summary.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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
|
||||
});
|
||||
});
|
||||
77
e2e-playwright/tests/navigation/basic-navigation.spec.ts
Normal file
77
e2e-playwright/tests/navigation/basic-navigation.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
35
e2e-playwright/tests/performance/perf2.spec.ts
Normal file
35
e2e-playwright/tests/performance/perf2.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
57
e2e-playwright/tests/project-note/project-note.spec.ts
Normal file
57
e2e-playwright/tests/project-note/project-note.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
94
e2e-playwright/tests/project/project.spec.ts
Normal file
94
e2e-playwright/tests/project/project.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
138
e2e-playwright/tests/reminders/reminders-schedule-page.spec.ts
Normal file
138
e2e-playwright/tests/reminders/reminders-schedule-page.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
108
e2e-playwright/tests/reminders/reminders-view-task.spec.ts
Normal file
108
e2e-playwright/tests/reminders/reminders-view-task.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
85
e2e-playwright/tests/reminders/reminders-view-task2.spec.ts
Normal file
85
e2e-playwright/tests/reminders/reminders-view-task2.spec.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
102
e2e-playwright/tests/reminders/reminders-view-task4.spec.ts
Normal file
102
e2e-playwright/tests/reminders/reminders-view-task4.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
22
e2e-playwright/tests/short-syntax/short-syntax.spec.ts
Normal file
22
e2e-playwright/tests/short-syntax/short-syntax.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
63
e2e-playwright/tests/sync/webdav-sync.spec.ts
Normal file
63
e2e-playwright/tests/sync/webdav-sync.spec.ts
Normal 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();
|
||||
});
|
||||
});
|
||||
65
e2e-playwright/tests/task-basic/task-crud.spec.ts
Normal file
65
e2e-playwright/tests/task-basic/task-crud.spec.ts
Normal 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/);
|
||||
});
|
||||
});
|
||||
|
|
@ -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',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
19
e2e-playwright/tests/task-list-basic/simple-subtask.spec.ts
Normal file
19
e2e-playwright/tests/task-list-basic/simple-subtask.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
|
|
@ -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/);
|
||||
});
|
||||
});
|
||||
131
e2e-playwright/tests/work-view/work-view-features.spec.ts
Normal file
131
e2e-playwright/tests/work-view/work-view-features.spec.ts
Normal 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/);
|
||||
});
|
||||
});
|
||||
100
e2e-playwright/tests/work-view/work-view.spec.ts
Normal file
100
e2e-playwright/tests/work-view/work-view.spec.ts
Normal 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/);
|
||||
});
|
||||
});
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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 }) => {
|
||||
|
|
|
|||
|
|
@ -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' });
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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/);
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue