From a578cbfa86654c8ef44813f2c80d4937db055dc2 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Thu, 19 Mar 2026 11:00:12 +0100 Subject: [PATCH] test(e2e): fix recurring task tests failing in CI production builds Replace window.ng.getComponent() calls with UI-based interactions. Angular debug APIs are unavailable in production builds (CI uses ng build which defaults to production config with enableProdMode). - Use calendar picker instead of signal manipulation for date setting - Remove page.evaluate() model verification (input value checks suffice) - Replace waitForTimeout with proper toBeVisible/toBeEnabled waits - Fix date input focus issues with click + pressSequentially - Use en-GB date format (DD/MM/YYYY) matching app default locale --- ...curring-future-start-date-bug-6856.spec.ts | 52 +++++++++---------- ...ecurring-start-date-epoch-bug-6860.spec.ts | 49 ++++------------- 2 files changed, 34 insertions(+), 67 deletions(-) diff --git a/e2e/tests/recurring/recurring-future-start-date-bug-6856.spec.ts b/e2e/tests/recurring/recurring-future-start-date-bug-6856.spec.ts index 850670eb92..808dc4418f 100644 --- a/e2e/tests/recurring/recurring-future-start-date-bug-6856.spec.ts +++ b/e2e/tests/recurring/recurring-future-start-date-bug-6856.spec.ts @@ -28,43 +28,42 @@ test.describe('Recurring Task - Future Start Date (#6856)', () => { // 2. Open task detail and click on recur to open the repeat dialog await task.hover(); - await page.waitForTimeout(200); const detailBtn = page.getByRole('button', { name: 'Show/Hide additional info', }); + await expect(detailBtn).toBeVisible({ timeout: 5000 }); await detailBtn.click(); - await page.waitForTimeout(300); const recurItem = page .locator('task-detail-item') .filter({ has: page.locator('mat-icon[svgIcon="repeat"]') }); + await expect(recurItem).toBeVisible({ timeout: 5000 }); await recurItem.click(); - // 3. Wait for the repeat dialog and set a future start date + // 3. Wait for the repeat dialog and set a future start date via calendar const repeatDialog = page.locator('mat-dialog-container'); await repeatDialog.waitFor({ state: 'visible', timeout: 10000 }); - // Set startDate via Angular's dialog component model signal - // (mat-datepicker's signal-based inputs don't work from page.evaluate) - const futureDate = new Date(); - futureDate.setMonth(futureDate.getMonth() + 1); - futureDate.setDate(15); - const year = futureDate.getFullYear(); - const month = String(futureDate.getMonth() + 1).padStart(2, '0'); - const day = String(futureDate.getDate()).padStart(2, '0'); - const futureDateStr = `${year}-${month}-${day}`; + // Open the calendar popup + const calendarToggle = repeatDialog.locator('mat-datepicker-toggle button'); + await calendarToggle.click(); - await page.evaluate((dateStr) => { - const ng = (window as any).ng; - const dialogEl = document.querySelector( - 'mat-dialog-container dialog-edit-task-repeat-cfg', - ); - if (!ng || !dialogEl) throw new Error('Dialog component not found'); - const component = ng.getComponent(dialogEl); - if (!component) throw new Error('Dialog component instance not found'); - component.repeatCfg.update((cfg: any) => ({ ...cfg, startDate: dateStr })); - }, futureDateStr); - await page.waitForTimeout(300); + const calendar = page.locator('.mat-calendar'); + await expect(calendar).toBeVisible({ timeout: 5000 }); + + // Navigate to next month to ensure the date is in the future + const nextMonthBtn = page.getByRole('button', { name: /next month/i }); + await nextMonthBtn.click(); + + // Select the first available day in next month + const firstDay = page + .locator('.mat-calendar-body-cell:not(.mat-calendar-body-disabled)') + .first(); + await expect(firstDay).toBeVisible({ timeout: 5000 }); + await firstDay.click(); + + // Wait for calendar to close + await expect(calendar).not.toBeVisible({ timeout: 5000 }); // Save the repeat config — wait for the button to be enabled first const saveBtn = repeatDialog.getByRole('button', { name: /Save/i }); @@ -72,16 +71,13 @@ test.describe('Recurring Task - Future Start Date (#6856)', () => { await saveBtn.click(); await repeatDialog.waitFor({ state: 'hidden', timeout: 10000 }); - // 4. Wait for effects and persistence to process - await page.waitForTimeout(5000); - - // 5. Reload and navigate to today view to verify persisted state + // 4. Reload and navigate to today view to verify persisted state await page.reload(); await workViewPage.waitForTaskList(); await page.goto('/#/tag/TODAY/tasks'); await workViewPage.waitForTaskList(); - // 6. Assert: task should NOT be visible in today's task list + // 5. Assert: task should NOT be visible in today's task list // Bug #6856: The task appears immediately instead of being scheduled // for the configured future start date. await expect(taskPage.getTaskByText(taskTitle)).not.toBeVisible({ timeout: 5000 }); diff --git a/e2e/tests/recurring/recurring-start-date-epoch-bug-6860.spec.ts b/e2e/tests/recurring/recurring-start-date-epoch-bug-6860.spec.ts index 032f77dd47..22b58343b5 100644 --- a/e2e/tests/recurring/recurring-start-date-epoch-bug-6860.spec.ts +++ b/e2e/tests/recurring/recurring-start-date-epoch-bug-6860.spec.ts @@ -30,6 +30,7 @@ test.describe('Recurring Task - Start Date Epoch Bug (#6860)', () => { const detailBtn = page.getByRole('button', { name: 'Show/Hide additional info', }); + await expect(detailBtn).toBeVisible({ timeout: 5000 }); await detailBtn.click(); const recurItem = page @@ -66,28 +67,9 @@ test.describe('Recurring Task - Start Date Epoch Bug (#6860)', () => { expect(inputValue).not.toBe(''); expect(inputValue).not.toContain('1970'); - // 6. Also verify via the model that it's not epoch - const startDate = await page.evaluate(() => { - const ng = (window as any).ng; - const dialogEl = document.querySelector( - 'mat-dialog-container dialog-edit-task-repeat-cfg', - ); - if (!ng || !dialogEl) throw new Error('Dialog component not found'); - const component = ng.getComponent(dialogEl); - if (!component) throw new Error('Dialog component instance not found'); - return component.repeatCfg().startDate; - }); - - expect(startDate).toBeDefined(); - expect(startDate).not.toBe('1970-01-01'); - - // The date should be in the future (next month) - const today = new Date(); - const startDateObj = new Date(startDate + 'T00:00:00'); - expect(startDateObj.getTime()).toBeGreaterThan(today.getTime()); - - // 7. Save and verify the date survives persistence + // 6. Save and verify the date survives persistence const saveBtn = repeatDialog.getByRole('button', { name: /Save/i }); + await expect(saveBtn).toBeEnabled({ timeout: 5000 }); await saveBtn.click(); await repeatDialog.waitFor({ state: 'hidden', timeout: 10000 }); }); @@ -112,6 +94,7 @@ test.describe('Recurring Task - Start Date Epoch Bug (#6860)', () => { const detailBtn = page.getByRole('button', { name: 'Show/Hide additional info', }); + await expect(detailBtn).toBeVisible({ timeout: 5000 }); await detailBtn.click(); const recurItem = page @@ -128,8 +111,10 @@ test.describe('Recurring Task - Start Date Epoch Bug (#6860)', () => { // The app defaults to en-GB locale (DD/MM/YYYY format) const dateInput = repeatDialog.getByRole('textbox', { name: /start date/i }); await expect(dateInput).toBeVisible(); - await dateInput.clear(); - await dateInput.fill('15/06/2026'); + // Click to focus the input, then select all and type the date + await dateInput.click(); + await dateInput.press('Control+a'); + await dateInput.pressSequentially('15/06/2026', { delay: 50 }); // Trigger change by pressing Tab to blur await dateInput.press('Tab'); @@ -138,23 +123,9 @@ test.describe('Recurring Task - Start Date Epoch Bug (#6860)', () => { expect(inputValue).not.toBe(''); expect(inputValue).not.toContain('1970'); - // 6. Verify via model that the date was preserved - const startDate = await page.evaluate(() => { - const ng = (window as any).ng; - const dialogEl = document.querySelector( - 'mat-dialog-container dialog-edit-task-repeat-cfg', - ); - if (!ng || !dialogEl) throw new Error('Dialog component not found'); - const component = ng.getComponent(dialogEl); - if (!component) throw new Error('Dialog component instance not found'); - return component.repeatCfg().startDate; - }); - - expect(startDate).toBeDefined(); - expect(startDate).not.toBe('1970-01-01'); - - // 7. Save + // 6. Save const saveBtn = repeatDialog.getByRole('button', { name: /Save/i }); + await expect(saveBtn).toBeEnabled({ timeout: 5000 }); await saveBtn.click(); await repeatDialog.waitFor({ state: 'hidden', timeout: 10000 }); });