diff --git a/apps/browser-extension/tests/e2e/06-field-level-merge.spec.ts b/apps/browser-extension/tests/e2e/06-field-level-merge.spec.ts index 0c70238b3..631e69323 100644 --- a/apps/browser-extension/tests/e2e/06-field-level-merge.spec.ts +++ b/apps/browser-extension/tests/e2e/06-field-level-merge.spec.ts @@ -81,19 +81,33 @@ test.describe.serial('6. Field-Level Merge', () => { .then((c) => c.fillNotes(clientBNotes)) .then((c) => c.screenshot('6.4-client-b-before-save.png')) .then((c) => c.saveCredential()) - .then((c) => c.screenshot('6.4-client-b-after-save.png')) - // Wait for background sync (including merge) to complete before next test - .then((c) => c.triggerSync()); + .then((c) => c.screenshot('6.4-client-b-after-save.png')); + + // Wait for the save operation to be persisted before moving to next test + await clientB.popup.waitForTimeout(1000); }); test('6.5 Client B verifies field-level merge result', async () => { - // Sync to get the merged result from server after Client B's save triggered the merge + // Trigger sync to get the merged result from server + // The save in 6.4 triggered background sync+merge on server + await clientB.triggerSync(); + + // Navigate to vault to ensure we're in a stable state + await clientB.goToVault(); + + // Click credential and open edit form await clientB - .triggerSync() - .then((c) => c.clickCredential(credentialName)) + .clickCredential(credentialName) .then((c) => c.openEditForm()) .then((c) => c.screenshot('6.5-client-b-merged-form.png')); + // Wait for merged values to appear (polling until sync completes) + await clientB + .waitForFieldValue(FieldSelectors.LOGIN_USERNAME, clientAUsername) + .then((c) => c.waitForFieldValue(FieldSelectors.LOGIN_PASSWORD, clientBPassword)) + .then((c) => c.waitForFieldValue(FieldSelectors.LOGIN_NOTES, clientBNotes)); + + // Verify the values are correct const usernameValue = await clientB.getFieldValue(FieldSelectors.LOGIN_USERNAME); expect(usernameValue).toBe(clientAUsername); @@ -108,12 +122,25 @@ test.describe.serial('6. Field-Level Merge', () => { }); test('6.6 Client A syncs and verifies merged credential', async () => { + // Trigger sync to get the merged result from server + await clientA.triggerSync(); + + // Navigate to vault to ensure we're in a stable state + await clientA.goToVault(); + + // Click credential and open edit form await clientA - .triggerSync() - .then((c) => c.clickCredential(credentialName)) + .clickCredential(credentialName) .then((c) => c.openEditForm()) .then((c) => c.screenshot('6.6-client-a-synced-form.png')); + // Wait for merged values to appear (polling until sync completes) + await clientA + .waitForFieldValue(FieldSelectors.LOGIN_USERNAME, clientAUsername) + .then((c) => c.waitForFieldValue(FieldSelectors.LOGIN_PASSWORD, clientBPassword)) + .then((c) => c.waitForFieldValue(FieldSelectors.LOGIN_NOTES, clientBNotes)); + + // Verify the values are correct const usernameValue = await clientA.getFieldValue(FieldSelectors.LOGIN_USERNAME); expect(usernameValue).toBe(clientAUsername); diff --git a/apps/browser-extension/tests/fixtures/TestClient.ts b/apps/browser-extension/tests/fixtures/TestClient.ts index 764610289..9277a2faa 100644 --- a/apps/browser-extension/tests/fixtures/TestClient.ts +++ b/apps/browser-extension/tests/fixtures/TestClient.ts @@ -343,6 +343,22 @@ export class TestClient { return this.popup.locator(selector).inputValue(); } + /** + * Wait for a field to have a specific value. + * Useful for waiting for sync/merge operations to complete. + */ + async waitForFieldValue(selector: string, expectedValue: string, timeout: number = Timeouts.LONG): Promise { + await this.popup.waitForFunction( + ({ sel, expected }) => { + const input = document.querySelector(sel) as HTMLInputElement | HTMLTextAreaElement; + return input?.value === expected; + }, + { timeout }, + { sel: selector, expected: expectedValue } + ); + return this; + } + /** * Verify a credential exists in the vault list. */