mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 17:05:48 +00:00
SuperSync page object improvements: - Add networkidle wait before interacting with sync dialog - Add explicit mat-dialog-container wait before form interaction - Add toBeAttached() assertions for element stability - Use toPass() with progressive backoff for dropdown interactions - Dismiss existing dropdown overlays before retrying - Add blur() calls in password change dialog for Angular validation - Add try-catch for fresh client dialog race condition Task detail tests: - Add blur() after time input fill to ensure Angular registers the change before clicking Save These changes fix intermittent failures when running E2E tests with multiple workers in parallel.
289 lines
10 KiB
TypeScript
289 lines
10 KiB
TypeScript
import { test, expect } from '../../fixtures/supersync.fixture';
|
|
import {
|
|
createTestUser,
|
|
getSuperSyncConfig,
|
|
createSimulatedClient,
|
|
closeClient,
|
|
waitForTask,
|
|
type SimulatedE2EClient,
|
|
} from '../../utils/supersync-helpers';
|
|
|
|
/**
|
|
* SuperSync Repeat Task Advanced E2E Tests
|
|
*
|
|
* Tests advanced repeat task configuration scenarios:
|
|
* - Modifying repeat config while instances exist
|
|
* - Concurrent instance creation (deterministic IDs)
|
|
* - Deleting repeat config
|
|
* - deletedInstanceDates sync
|
|
*/
|
|
|
|
test.describe('@supersync Repeat Task Advanced Sync', () => {
|
|
/**
|
|
* Test: Scheduled task (with dueDay) syncs between clients
|
|
*
|
|
* This is a simpler test than full repeat config - tests dueDay sync.
|
|
*
|
|
* Actions:
|
|
* 1. Client A creates task with dueDay set to today (via sd:today shorthand)
|
|
* 2. Client A syncs
|
|
* 3. Client B syncs
|
|
* 4. Verify task appears on Client B with scheduled indicator
|
|
*/
|
|
test('Scheduled task with dueDay syncs to other client', async ({
|
|
browser,
|
|
baseURL,
|
|
testRunId,
|
|
}) => {
|
|
const uniqueId = Date.now();
|
|
let clientA: SimulatedE2EClient | null = null;
|
|
let clientB: SimulatedE2EClient | null = null;
|
|
|
|
try {
|
|
const user = await createTestUser(testRunId);
|
|
const syncConfig = getSuperSyncConfig(user);
|
|
|
|
// ============ PHASE 1: Client A Creates Scheduled Task ============
|
|
clientA = await createSimulatedClient(browser, baseURL!, 'A', testRunId);
|
|
await clientA.sync.setupSuperSync(syncConfig);
|
|
|
|
// Use sd:today shorthand to set dueDay to today
|
|
const taskName = `ScheduledTask-${uniqueId}`;
|
|
await clientA.workView.addTask(`${taskName} sd:today`);
|
|
console.log('[Scheduled Test] Client A created scheduled task');
|
|
|
|
// Verify task was created
|
|
await waitForTask(clientA.page, taskName);
|
|
|
|
// ============ PHASE 2: Sync to Server ============
|
|
await clientA.sync.syncAndWait();
|
|
console.log('[Scheduled Test] Client A synced');
|
|
|
|
// ============ PHASE 3: Client B Downloads ============
|
|
clientB = await createSimulatedClient(browser, baseURL!, 'B', testRunId);
|
|
await clientB.sync.setupSuperSync(syncConfig);
|
|
await clientB.sync.syncAndWait();
|
|
console.log('[Scheduled Test] Client B synced');
|
|
|
|
// ============ PHASE 4: Verify on Client B ============
|
|
await waitForTask(clientB.page, taskName);
|
|
|
|
const taskLocatorB = clientB.page.locator(`task:has-text("${taskName}")`);
|
|
await expect(taskLocatorB).toBeVisible();
|
|
|
|
// Task should show scheduled indicator (sun icon for today)
|
|
// The exact indicator depends on UI, but task should exist
|
|
console.log('[Scheduled Test] Task visible on Client B');
|
|
|
|
console.log('[Scheduled Test] Scheduled task synced successfully');
|
|
} finally {
|
|
if (clientA) await closeClient(clientA);
|
|
if (clientB) await closeClient(clientB);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Test: Task with time estimate syncs correctly
|
|
*
|
|
* Actions:
|
|
* 1. Client A creates task with time estimate (via t:1h shorthand)
|
|
* 2. Client A syncs
|
|
* 3. Client B syncs
|
|
* 4. Verify time estimate appears on Client B
|
|
*/
|
|
test('Task with time estimate syncs to other client', async ({
|
|
browser,
|
|
baseURL,
|
|
testRunId,
|
|
}) => {
|
|
const uniqueId = Date.now();
|
|
let clientA: SimulatedE2EClient | null = null;
|
|
let clientB: SimulatedE2EClient | null = null;
|
|
|
|
try {
|
|
const user = await createTestUser(testRunId);
|
|
const syncConfig = getSuperSyncConfig(user);
|
|
|
|
// ============ PHASE 1: Client A Creates Task with Estimate ============
|
|
clientA = await createSimulatedClient(browser, baseURL!, 'A', testRunId);
|
|
await clientA.sync.setupSuperSync(syncConfig);
|
|
|
|
// Use t:1h shorthand to set 1 hour estimate
|
|
const taskName = `EstimateTask-${uniqueId}`;
|
|
await clientA.workView.addTask(`${taskName} t:1h`);
|
|
console.log('[Estimate Test] Client A created task with 1h estimate');
|
|
|
|
await waitForTask(clientA.page, taskName);
|
|
|
|
// Task exists on Client A (estimate is stored but may not be visible in compact view)
|
|
const taskLocatorA = clientA.page.locator(`task:has-text("${taskName}")`);
|
|
await expect(taskLocatorA).toBeVisible({ timeout: 5000 });
|
|
console.log('[Estimate Test] Task visible on Client A');
|
|
|
|
// ============ PHASE 2: Sync to Server ============
|
|
await clientA.sync.syncAndWait();
|
|
console.log('[Estimate Test] Client A synced');
|
|
|
|
// ============ PHASE 3: Client B Downloads ============
|
|
clientB = await createSimulatedClient(browser, baseURL!, 'B', testRunId);
|
|
await clientB.sync.setupSuperSync(syncConfig);
|
|
await clientB.sync.syncAndWait();
|
|
console.log('[Estimate Test] Client B synced');
|
|
|
|
// ============ PHASE 4: Verify Task on Client B ============
|
|
await waitForTask(clientB.page, taskName);
|
|
|
|
const taskLocatorB = clientB.page.locator(`task:has-text("${taskName}")`);
|
|
await expect(taskLocatorB).toBeVisible({ timeout: 5000 });
|
|
console.log('[Estimate Test] Task visible on Client B');
|
|
|
|
// Note: Time estimate is synced as task data, but UI display varies
|
|
// The key test is that the task synced successfully
|
|
console.log('[Estimate Test] Task with time estimate synced successfully');
|
|
} finally {
|
|
if (clientA) await closeClient(clientA);
|
|
if (clientB) await closeClient(clientB);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Test: Updating scheduled task properties syncs
|
|
*
|
|
* Actions:
|
|
* 1. Client A creates scheduled task
|
|
* 2. Client B syncs and receives it
|
|
* 3. Client A updates the task (changes title, adds estimate)
|
|
* 4. Both sync
|
|
* 5. Verify Client B has updated properties
|
|
*/
|
|
test('Updating scheduled task properties syncs', async ({
|
|
browser,
|
|
baseURL,
|
|
testRunId,
|
|
}) => {
|
|
const uniqueId = Date.now();
|
|
let clientA: SimulatedE2EClient | null = null;
|
|
let clientB: SimulatedE2EClient | null = null;
|
|
|
|
try {
|
|
const user = await createTestUser(testRunId);
|
|
const syncConfig = getSuperSyncConfig(user);
|
|
|
|
// ============ PHASE 1: Setup ============
|
|
clientA = await createSimulatedClient(browser, baseURL!, 'A', testRunId);
|
|
await clientA.sync.setupSuperSync(syncConfig);
|
|
|
|
const originalName = `Original-${uniqueId}`;
|
|
const updatedName = `Updated-${uniqueId}`;
|
|
|
|
await clientA.workView.addTask(`${originalName} sd:today`);
|
|
await clientA.sync.syncAndWait();
|
|
|
|
clientB = await createSimulatedClient(browser, baseURL!, 'B', testRunId);
|
|
await clientB.sync.setupSuperSync(syncConfig);
|
|
await clientB.sync.syncAndWait();
|
|
|
|
await waitForTask(clientB.page, originalName);
|
|
console.log('[Update Test] Both clients have original task');
|
|
|
|
// ============ PHASE 2: Client A Updates Task ============
|
|
const taskLocatorA = clientA.page.locator(`task:has-text("${originalName}")`);
|
|
await taskLocatorA.dblclick();
|
|
|
|
const editInput = clientA.page.locator(
|
|
'input.mat-mdc-input-element:focus, textarea:focus',
|
|
);
|
|
await editInput.waitFor({ state: 'visible', timeout: 5000 });
|
|
await editInput.fill(updatedName);
|
|
await clientA.page.keyboard.press('Enter');
|
|
await clientA.page.waitForTimeout(500);
|
|
console.log('[Update Test] Client A renamed task');
|
|
|
|
// ============ PHASE 3: Sync Update ============
|
|
await clientA.sync.syncAndWait();
|
|
await clientB.sync.syncAndWait();
|
|
|
|
// ============ PHASE 4: Verify Update on Client B ============
|
|
await waitForTask(clientB.page, updatedName);
|
|
|
|
// Original name should no longer exist
|
|
const originalExists = await clientB.page
|
|
.locator(`task:has-text("${originalName}")`)
|
|
.count();
|
|
expect(originalExists).toBe(0);
|
|
|
|
console.log('[Update Test] Task update synced successfully');
|
|
} finally {
|
|
if (clientA) await closeClient(clientA);
|
|
if (clientB) await closeClient(clientB);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Test: Both clients create same scheduled task concurrently
|
|
*
|
|
* This tests the deterministic ID behavior - if both clients
|
|
* schedule the same task for the same day, they should converge.
|
|
*
|
|
* Actions:
|
|
* 1. Client A creates task scheduled for today
|
|
* 2. Client B creates different task scheduled for today (concurrent)
|
|
* 3. Both sync
|
|
* 4. Verify both tasks exist on both clients
|
|
*/
|
|
test('Concurrent scheduled task creation merges correctly', async ({
|
|
browser,
|
|
baseURL,
|
|
testRunId,
|
|
}) => {
|
|
const uniqueId = Date.now();
|
|
let clientA: SimulatedE2EClient | null = null;
|
|
let clientB: SimulatedE2EClient | null = null;
|
|
|
|
try {
|
|
const user = await createTestUser(testRunId);
|
|
const syncConfig = getSuperSyncConfig(user);
|
|
|
|
// ============ PHASE 1: Setup Both Clients ============
|
|
clientA = await createSimulatedClient(browser, baseURL!, 'A', testRunId);
|
|
await clientA.sync.setupSuperSync(syncConfig);
|
|
|
|
clientB = await createSimulatedClient(browser, baseURL!, 'B', testRunId);
|
|
await clientB.sync.setupSuperSync(syncConfig);
|
|
|
|
// ============ PHASE 2: Concurrent Task Creation ============
|
|
const taskAName = `TaskA-${uniqueId}`;
|
|
const taskBName = `TaskB-${uniqueId}`;
|
|
|
|
// Both create tasks scheduled for today, without syncing
|
|
await clientA.workView.addTask(`${taskAName} sd:today`);
|
|
console.log('[Concurrent Test] Client A created scheduled task');
|
|
|
|
await clientB.workView.addTask(`${taskBName} sd:today`);
|
|
console.log('[Concurrent Test] Client B created scheduled task');
|
|
|
|
// ============ PHASE 3: Sync Both ============
|
|
await clientA.sync.syncAndWait();
|
|
await clientB.sync.syncAndWait();
|
|
await clientA.sync.syncAndWait(); // Final convergence
|
|
console.log('[Concurrent Test] All clients synced');
|
|
|
|
// ============ PHASE 4: Verify Both Tasks Exist on Both Clients ============
|
|
await waitForTask(clientA.page, taskAName);
|
|
await waitForTask(clientA.page, taskBName);
|
|
await waitForTask(clientB.page, taskAName);
|
|
await waitForTask(clientB.page, taskBName);
|
|
|
|
const countA = await clientA.page.locator('task').count();
|
|
const countB = await clientB.page.locator('task').count();
|
|
|
|
expect(countA).toBe(countB);
|
|
expect(countA).toBe(2);
|
|
|
|
console.log('[Concurrent Test] Both scheduled tasks exist on both clients');
|
|
} finally {
|
|
if (clientA) await closeClient(clientA);
|
|
if (clientB) await closeClient(clientB);
|
|
}
|
|
});
|
|
});
|