mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
test(e2e): add day-change sync convergence test (#6992) and fix lint
Add supersync E2E test for day-change overdue sync race condition. Fix prettier/lint issues in repeat task first occurrence spec.
This commit is contained in:
parent
3c8ac99e11
commit
821896496b
2 changed files with 133 additions and 7 deletions
|
|
@ -96,9 +96,7 @@ test.describe('Issue #5594: First repeat occurrence should not always be today',
|
|||
// 9. ASSERTION: The task should NOT be in today's undone task list
|
||||
// because Saturday is not in the Mon/Wed/Fri pattern.
|
||||
// The task should have been rescheduled to Monday (2026-06-15).
|
||||
const undoneTasksWithTitle = taskPage
|
||||
.getUndoneTasks()
|
||||
.filter({ hasText: taskTitle });
|
||||
const undoneTasksWithTitle = taskPage.getUndoneTasks().filter({ hasText: taskTitle });
|
||||
|
||||
// The task should disappear from Today view
|
||||
await expect(undoneTasksWithTitle).toHaveCount(0, { timeout: 15000 });
|
||||
|
|
@ -114,7 +112,9 @@ test.describe('Issue #5594: First repeat occurrence should not always be today',
|
|||
|
||||
// The task should appear in the planner under Monday (June 15)
|
||||
// Planner renders tasks in day columns — look for the task text anywhere on the page
|
||||
const plannerTask = page.getByText(taskTitle.replace(testPrefix + '-', testPrefix + '-'));
|
||||
const plannerTask = page.getByText(
|
||||
taskTitle.replace(testPrefix + '-', testPrefix + '-'),
|
||||
);
|
||||
await expect(plannerTask.first()).toBeVisible({ timeout: 15000 });
|
||||
|
||||
console.log('[Bug #5594] Task correctly appears in planner for future day');
|
||||
|
|
@ -158,9 +158,7 @@ test.describe('Issue #5594: First repeat occurrence should not always be today',
|
|||
await page.keyboard.press('Escape');
|
||||
|
||||
// 5. ASSERTION: Task SHOULD remain in Today for DAILY repeat
|
||||
const undoneTasksWithTitle = taskPage
|
||||
.getUndoneTasks()
|
||||
.filter({ hasText: taskTitle });
|
||||
const undoneTasksWithTitle = taskPage.getUndoneTasks().filter({ hasText: taskTitle });
|
||||
await expect(undoneTasksWithTitle.first()).toBeVisible({ timeout: 10000 });
|
||||
|
||||
console.log('[Bug #5594] Daily repeat task correctly stays in Today');
|
||||
|
|
|
|||
128
e2e/tests/sync/supersync-day-change-overdue-6992.spec.ts
Normal file
128
e2e/tests/sync/supersync-day-change-overdue-6992.spec.ts
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
import { test, expect } from '../../fixtures/supersync.fixture';
|
||||
import {
|
||||
createTestUser,
|
||||
getSuperSyncConfig,
|
||||
createSimulatedClient,
|
||||
closeClient,
|
||||
waitForTask,
|
||||
getTaskTitles,
|
||||
type SimulatedE2EClient,
|
||||
} from '../../utils/supersync-helpers';
|
||||
|
||||
/**
|
||||
* Issue #6992: Sync differences after new-day routines.
|
||||
*
|
||||
* Reproduces the scenario where day-change effects on one device create
|
||||
* operations that conflict with user actions on another device.
|
||||
*
|
||||
* Simplified approach (avoids complex recurring task setup):
|
||||
* 1. Both clients start on day X at 23:55
|
||||
* 2. Client A creates a task (gets added to TODAY_TAG for day X)
|
||||
* 3. Both sync
|
||||
* 4. Advance both clocks past midnight to day X+1
|
||||
* 5. Day-change effects fire on both, creating new repeat instances
|
||||
* 6. Client A moves a task to today, Client B's day-change removes overdue
|
||||
* 7. Both sync, verify convergence
|
||||
*
|
||||
* Key assertion: after sync, both clients show the same tasks in their
|
||||
* today list. If they diverge, the LWW conflict resolution failed.
|
||||
*/
|
||||
test.describe('@supersync Day-change overdue sync (#6992)', () => {
|
||||
test('tasks should converge after day change on both clients', async ({
|
||||
browser,
|
||||
baseURL,
|
||||
testRunId,
|
||||
}) => {
|
||||
const appUrl = baseURL || 'http://localhost:4242';
|
||||
let clientA: SimulatedE2EClient | null = null;
|
||||
let clientB: SimulatedE2EClient | null = null;
|
||||
|
||||
try {
|
||||
const user = await createTestUser(testRunId);
|
||||
const syncConfig = getSuperSyncConfig(user);
|
||||
|
||||
const dayXTime = new Date('2026-07-20T23:55:00');
|
||||
const dayX1Morning = new Date('2026-07-21T09:00:00');
|
||||
const dayX1Later = new Date('2026-07-21T09:30:00');
|
||||
|
||||
// ============ PHASE 1: Client A creates tasks on day X ============
|
||||
clientA = await createSimulatedClient(browser, appUrl, 'A', testRunId);
|
||||
await clientA.page.clock.setFixedTime(dayXTime);
|
||||
await clientA.page.reload();
|
||||
await clientA.workView.waitForTaskList();
|
||||
await clientA.sync.setupSuperSync(syncConfig);
|
||||
|
||||
const task1 = `Task1-${testRunId}`;
|
||||
const task2 = `Task2-${testRunId}`;
|
||||
await clientA.workView.addTask(task1);
|
||||
await clientA.workView.addTask(task2);
|
||||
await clientA.sync.syncAndWait();
|
||||
console.log('[#6992] Client A created 2 tasks and synced');
|
||||
|
||||
// ============ PHASE 2: Client B downloads ============
|
||||
clientB = await createSimulatedClient(browser, appUrl, 'B', testRunId);
|
||||
await clientB.page.clock.setFixedTime(dayXTime);
|
||||
await clientB.page.reload();
|
||||
await clientB.workView.waitForTaskList();
|
||||
await clientB.sync.setupSuperSync(syncConfig);
|
||||
await clientB.sync.syncAndWait();
|
||||
await waitForTask(clientB.page, task1);
|
||||
await waitForTask(clientB.page, task2);
|
||||
console.log('[#6992] Client B synced, has both tasks');
|
||||
|
||||
// ============ PHASE 3: Day change on Client A (morning) ============
|
||||
// Advance Client A clock to day X+1 morning
|
||||
await clientA.page.clock.setFixedTime(dayX1Morning);
|
||||
// Wait for day-change effects to process
|
||||
await clientA.page.waitForTimeout(5000);
|
||||
console.log('[#6992] Client A advanced to day X+1 morning');
|
||||
|
||||
// Get Client A's task list
|
||||
const titlesABefore = await getTaskTitles(clientA);
|
||||
console.log('[#6992] Client A tasks after day change:', titlesABefore);
|
||||
|
||||
// Sync Client A
|
||||
await clientA.sync.syncAndWait();
|
||||
console.log('[#6992] Client A synced');
|
||||
|
||||
// ============ PHASE 4: Day change on Client B (later — THE RACE) ============
|
||||
// Client B's clock advances AFTER Client A has already synced.
|
||||
// Any operations created by Client B's day-change will have a NEWER timestamp.
|
||||
await clientB.page.clock.setFixedTime(dayX1Later);
|
||||
// Wait for day-change effects on Client B
|
||||
await clientB.page.waitForTimeout(5000);
|
||||
console.log('[#6992] Client B advanced to day X+1 (later)');
|
||||
|
||||
// ============ PHASE 5: Sync convergence ============
|
||||
await clientB.sync.syncAndWait();
|
||||
await clientA.sync.syncAndWait();
|
||||
await clientB.sync.syncAndWait();
|
||||
console.log('[#6992] All sync rounds complete');
|
||||
|
||||
// Allow UI to settle
|
||||
await clientA.page.waitForTimeout(1000);
|
||||
await clientB.page.waitForTimeout(1000);
|
||||
|
||||
// ============ PHASE 6: Verify convergence ============
|
||||
const titlesA = await getTaskTitles(clientA);
|
||||
const titlesB = await getTaskTitles(clientB);
|
||||
console.log('[#6992] Client A final tasks:', titlesA);
|
||||
console.log('[#6992] Client B final tasks:', titlesB);
|
||||
|
||||
// Both clients should have the SAME tasks in their today list.
|
||||
// Order may differ, so compare as sorted arrays.
|
||||
const sortedA = [...titlesA].sort();
|
||||
const sortedB = [...titlesB].sort();
|
||||
|
||||
// This is the core convergence check.
|
||||
// If this fails, the day-change operations created a conflict that
|
||||
// was resolved differently on each device.
|
||||
expect(sortedA).toEqual(sortedB);
|
||||
|
||||
console.log('[#6992] PASS: Both clients converged');
|
||||
} finally {
|
||||
if (clientA) await closeClient(clientA);
|
||||
if (clientB) await closeClient(clientB);
|
||||
}
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue