From f31aa0f4a4344c34d2437907ad5b4b4f59272646 Mon Sep 17 00:00:00 2001 From: Johannes Millan Date: Wed, 14 Jan 2026 16:16:08 +0100 Subject: [PATCH] fix(test): improve e2e sync test stability - Add settle delays after bulk task creation for UI/NgRx processing - Wait for done button visibility before clicking - Use toPass() retry pattern for CSS class assertions - Add explicit triggerSync() before expecting conflict dialog --- .../supersync-advanced-edge-cases.spec.ts | 22 +++++++++++++++---- .../supersync-import-conflict-dialog.spec.ts | 7 ++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/e2e/tests/sync/supersync-advanced-edge-cases.spec.ts b/e2e/tests/sync/supersync-advanced-edge-cases.spec.ts index 073497a5f..97e697701 100644 --- a/e2e/tests/sync/supersync-advanced-edge-cases.spec.ts +++ b/e2e/tests/sync/supersync-advanced-edge-cases.spec.ts @@ -48,8 +48,13 @@ test.describe('@supersync SuperSync Advanced Edge Cases', () => { const taskName = `Bulk${i}-${testRunId}`; taskNames.push(taskName); await clientA.workView.addTask(taskName); + // Small settle delay to let UI and NgRx store process each task + await clientA.page.waitForTimeout(100); } + // Allow operations to fully persist to IndexedDB before sync + await clientA.page.waitForTimeout(500); + // Sync A -> B await clientA.sync.syncAndWait(); await clientB.sync.syncAndWait(); @@ -129,11 +134,16 @@ test.describe('@supersync SuperSync Advanced Edge Cases', () => { // Mark initial task as done const initialTaskLocator = clientA.page.locator(`task:has-text("${initialTask}")`); await initialTaskLocator.hover(); - await initialTaskLocator.locator('.task-done-btn').click(); + // Wait for done button to be visible after hover reveals it + const doneBtn = initialTaskLocator.locator('.task-done-btn'); + await doneBtn.waitFor({ state: 'visible', timeout: 5000 }); + await doneBtn.click(); await clientA.sync.syncAndWait(); // Client B "reconnects" (syncs after missing many updates) await clientB.sync.syncAndWait(); + // Extra settle time for state propagation after receiving many operations + await clientB.page.waitForTimeout(500); // Verify B has all the changes await waitForTask(clientB.page, offlineTask1); @@ -141,9 +151,13 @@ test.describe('@supersync SuperSync Advanced Edge Cases', () => { await waitForTask(clientB.page, offlineTask3); // Initial task should be marked as done - // Use more specific locator to target the done task (avoids matching both backlog and done section) - const initialTaskB = clientB.page.locator(`task.isDone:has-text("${initialTask}")`); - await expect(initialTaskB).toBeVisible(); + // Use toPass() to handle Angular change detection lag for CSS class update + await expect(async () => { + const initialTaskB = clientB.page.locator( + `task.isDone:has-text("${initialTask}")`, + ); + await expect(initialTaskB).toBeVisible({ timeout: 2000 }); + }).toPass({ timeout: 10000, intervals: [500, 1000, 2000] }); console.log('[Stale] Stale client reconnected and received all changes'); } finally { diff --git a/e2e/tests/sync/supersync-import-conflict-dialog.spec.ts b/e2e/tests/sync/supersync-import-conflict-dialog.spec.ts index b37044099..3f0406f79 100644 --- a/e2e/tests/sync/supersync-import-conflict-dialog.spec.ts +++ b/e2e/tests/sync/supersync-import-conflict-dialog.spec.ts @@ -397,8 +397,11 @@ test.describe('@supersync @import-conflict Sync Import Conflict Dialog', () => { // ============ PHASE 3: Client B Syncs and Chooses CANCEL ============ console.log('[CANCEL] Phase 3: Client B syncs and chooses CANCEL'); - // The auto-sync after enabling will trigger the conflict dialog - // Use specific locator for the sync import conflict dialog + // Trigger sync - this will cause the sync import conflict dialog to appear + // (matches the pattern from other tests in this file) + await clientB.sync.triggerSync(); + + // Wait for the sync import conflict dialog to appear const dialog = clientB.page.locator('dialog-sync-import-conflict'); await expect(dialog).toBeVisible({ timeout: 15000 });