mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
fix(e2e): fix WebDAV sync tests that were failing
Two test fixes:
1. webdav-sync-full.spec.ts - "should sync data between two clients"
- After multiple reloads in the deletion test section, Client B's
sync state became stale and couldn't receive new tasks
- Fix: Create a fresh Client B context for the conflict resolution
section instead of reusing the stale one
2. sync.page.ts - setupWebdavSync combobox selector
- The role-based selector getByRole('combobox', { name: 'Sync Provider' })
was unreliable across different test runs
- Fix: Use the existing providerSelect locator which was already
validated to be visible, instead of creating a new role-based selector
This commit is contained in:
parent
6c098b6eaa
commit
55484faffd
2 changed files with 91 additions and 22 deletions
|
|
@ -71,8 +71,8 @@ export class SyncPage extends BasePage {
|
|||
// Click on provider select to open dropdown with retry
|
||||
const webdavOption = this.page.locator('mat-option').filter({ hasText: 'WebDAV' });
|
||||
|
||||
// Try using role-based selector for the combobox
|
||||
const combobox = this.page.getByRole('combobox', { name: 'Sync Provider' });
|
||||
// Use the providerSelect locator that was already validated above
|
||||
const combobox = this.providerSelect;
|
||||
|
||||
for (let attempt = 0; attempt < 5; attempt++) {
|
||||
// Ensure the select is in view
|
||||
|
|
|
|||
|
|
@ -243,13 +243,78 @@ test.describe('WebDAV Sync Full Flow', () => {
|
|||
|
||||
// --- Conflict Resolution ---
|
||||
console.log('Testing Conflict Resolution...');
|
||||
|
||||
// Close old Client B context - it may have stale sync state after multiple reloads
|
||||
await contextB.close();
|
||||
|
||||
// Create new task "Conflict Task" on A
|
||||
await workViewPageA.addTask('Conflict Task');
|
||||
|
||||
// Wait for state persistence before syncing
|
||||
await waitForStatePersistence(pageA);
|
||||
|
||||
await syncPageA.triggerSync();
|
||||
await waitForSync(pageA, syncPageA);
|
||||
|
||||
await syncPageB.triggerSync();
|
||||
await waitForSync(pageB, syncPageB);
|
||||
// Wait for WebDAV server to process A's upload
|
||||
await pageA.waitForTimeout(2000);
|
||||
|
||||
// Create a fresh Client B for conflict test
|
||||
console.log('Creating fresh Client B for conflict test...');
|
||||
const contextB2 = await browser.newContext({ baseURL: url });
|
||||
const pageB2 = await contextB2.newPage();
|
||||
await pageB2.goto('/');
|
||||
await waitForAppReady(pageB2);
|
||||
// Dismiss tour
|
||||
try {
|
||||
const tourElement = pageB2.locator('.shepherd-element').first();
|
||||
await tourElement.waitFor({ state: 'visible', timeout: 4000 });
|
||||
const cancelIcon = pageB2.locator('.shepherd-cancel-icon').first();
|
||||
if (await cancelIcon.isVisible()) {
|
||||
await cancelIcon.click();
|
||||
} else {
|
||||
await pageB2.keyboard.press('Escape');
|
||||
}
|
||||
} catch {
|
||||
// Tour didn't appear
|
||||
}
|
||||
|
||||
const syncPageB2 = new SyncPage(pageB2);
|
||||
const workViewPageB2 = new WorkViewPage(pageB2);
|
||||
await workViewPageB2.waitForTaskList();
|
||||
|
||||
// Setup sync on fresh Client B
|
||||
await syncPageB2.setupWebdavSync(WEBDAV_CONFIG);
|
||||
await syncPageB2.triggerSync();
|
||||
await waitForSync(pageB2, syncPageB2);
|
||||
|
||||
// Wait for state persistence
|
||||
await waitForStatePersistence(pageB2);
|
||||
|
||||
// Reload to ensure UI reflects synced state
|
||||
await pageB2.reload();
|
||||
await waitForAppReady(pageB2);
|
||||
try {
|
||||
const tourElement = pageB2.locator('.shepherd-element').first();
|
||||
await tourElement.waitFor({ state: 'visible', timeout: 2000 });
|
||||
const cancelIcon = pageB2.locator('.shepherd-cancel-icon').first();
|
||||
if (await cancelIcon.isVisible()) {
|
||||
await cancelIcon.click();
|
||||
}
|
||||
} catch {
|
||||
// Tour didn't appear
|
||||
}
|
||||
await workViewPageB2.waitForTaskList();
|
||||
|
||||
// Final assertion - should have 2 tasks now
|
||||
const taskCount = await pageB2.locator('task').count();
|
||||
console.log(`After conflict sync: ${taskCount} tasks on Client B`);
|
||||
|
||||
// Debug: List all task titles
|
||||
const taskTitles = await pageB2.locator('.task-title').allInnerTexts();
|
||||
console.log(`Task titles on B: ${JSON.stringify(taskTitles)}`);
|
||||
|
||||
await expect(pageB2.locator('task')).toHaveCount(2, { timeout: 5000 });
|
||||
|
||||
// Edit on A: "Conflict Task A"
|
||||
const taskA = pageA.locator('task', { hasText: 'Conflict Task' }).first();
|
||||
|
|
@ -262,28 +327,32 @@ test.describe('WebDAV Sync Full Flow', () => {
|
|||
// Wait for state persistence and ensure timestamps differ between edits
|
||||
await waitForStatePersistence(pageA);
|
||||
|
||||
// Edit on B: "Conflict Task B"
|
||||
const taskB = pageB.locator('task', { hasText: 'Conflict Task' }).first();
|
||||
await taskB.click();
|
||||
const titleB = taskB.locator('.task-title');
|
||||
await titleB.click();
|
||||
await titleB.locator('input, textarea').fill('Conflict Task B');
|
||||
await pageB.keyboard.press('Enter');
|
||||
// Edit on B2: "Conflict Task B"
|
||||
const taskB2 = pageB2.locator('task', { hasText: 'Conflict Task' }).first();
|
||||
await taskB2.click();
|
||||
const titleB2 = taskB2.locator('.task-title');
|
||||
await titleB2.click();
|
||||
await titleB2.locator('input, textarea').fill('Conflict Task B');
|
||||
await pageB2.keyboard.press('Enter');
|
||||
|
||||
// Sync A (Uploads "A")
|
||||
await syncPageA.triggerSync();
|
||||
await waitForSync(pageA, syncPageA);
|
||||
|
||||
// Sync B (Downloads "A" but has "B") -> Conflict
|
||||
await syncPageB.triggerSync();
|
||||
const result = await waitForSync(pageB, syncPageB);
|
||||
// Sync B2 (Downloads "A" but has "B") -> Conflict
|
||||
await syncPageB2.triggerSync();
|
||||
const result = await waitForSync(pageB2, syncPageB2);
|
||||
|
||||
if (result === 'success') {
|
||||
console.log(
|
||||
'Warning: No conflict detected (Auto-merged or overwrite). Checking content...',
|
||||
);
|
||||
const isA = await pageB.locator('task', { hasText: 'Conflict Task A' }).isVisible();
|
||||
const isB = await pageB.locator('task', { hasText: 'Conflict Task B' }).isVisible();
|
||||
const isA = await pageB2
|
||||
.locator('task', { hasText: 'Conflict Task A' })
|
||||
.isVisible();
|
||||
const isB = await pageB2
|
||||
.locator('task', { hasText: 'Conflict Task B' })
|
||||
.isVisible();
|
||||
console.log(`Content on B: A=${isA}, B=${isB}`);
|
||||
// If it was merged/overwritten, we skip the resolution steps
|
||||
} else {
|
||||
|
|
@ -291,10 +360,10 @@ test.describe('WebDAV Sync Full Flow', () => {
|
|||
|
||||
// Resolve conflict: Use Remote (A)
|
||||
console.log('Resolving conflict with Remote...');
|
||||
await pageB.locator('dialog-sync-conflict button', { hasText: /Remote/i }).click();
|
||||
await pageB2.locator('dialog-sync-conflict button', { hasText: /Remote/i }).click();
|
||||
|
||||
// Handle potential confirmation dialog
|
||||
const confirmDialog = pageB.locator('dialog-confirm');
|
||||
const confirmDialog = pageB2.locator('dialog-confirm');
|
||||
try {
|
||||
await confirmDialog.waitFor({ state: 'visible', timeout: 3000 });
|
||||
await confirmDialog.locator('button[color="warn"]').click();
|
||||
|
|
@ -302,16 +371,16 @@ test.describe('WebDAV Sync Full Flow', () => {
|
|||
// Confirmation might not appear
|
||||
}
|
||||
|
||||
await waitForSync(pageB, syncPageB);
|
||||
await waitForSync(pageB2, syncPageB2);
|
||||
|
||||
await expect(pageB.locator('task', { hasText: 'Conflict Task A' })).toBeVisible();
|
||||
await expect(pageB2.locator('task', { hasText: 'Conflict Task A' })).toBeVisible();
|
||||
await expect(
|
||||
pageB.locator('task', { hasText: 'Conflict Task B' }),
|
||||
pageB2.locator('task', { hasText: 'Conflict Task B' }),
|
||||
).not.toBeVisible();
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
await contextA.close();
|
||||
await contextB.close();
|
||||
await contextB2.close();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue