Skip to content

Commit b4b12b0

Browse files
authored
Merge pull request #20058 from mozilla/fix-signout-test-race-condition-v2
fix(functional-tests): harden tests against sign-out race condition
2 parents 5a9813b + f446fee commit b4b12b0

3 files changed

Lines changed: 20 additions & 32 deletions

File tree

packages/functional-tests/pages/layout.ts

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* License, v. 2.0. If a copy of the MPL was not distributed with this
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

5-
import { Page } from '@playwright/test';
5+
import { Page, expect } from '@playwright/test';
66
import { BaseTarget } from '../lib/targets/base';
77

88
import {
@@ -74,36 +74,17 @@ export abstract class BaseLayout {
7474
}
7575

7676
async checkWebChannelMessage(command: FirefoxCommand) {
77-
await this.page.evaluate(async (command) => {
78-
const noNotificationError = new Error(
79-
`NoSuchBrowserNotification - ${command}`
77+
// Retry across navigations — a client-side redirect after page.goto
78+
// can destroy the execution context mid-evaluate.
79+
await expect(async () => {
80+
const messages = await this.page.evaluate(() =>
81+
JSON.parse(sessionStorage.getItem('webChannelEvents') || '[]')
8082
);
81-
82-
await new Promise((resolve, reject) => {
83-
const timeoutHandle = setTimeout(
84-
() => reject(noNotificationError),
85-
5000
86-
);
87-
88-
function findMessage() {
89-
const messages = JSON.parse(
90-
sessionStorage.getItem('webChannelEvents') || '[]'
91-
);
92-
const m = messages.find(
93-
(x: { command: string }) => x.command === command
94-
);
95-
96-
if (m) {
97-
clearTimeout(timeoutHandle);
98-
resolve(m);
99-
} else {
100-
setTimeout(findMessage, 50);
101-
}
102-
}
103-
104-
findMessage();
105-
});
106-
}, command);
83+
const found = messages.find(
84+
(x: { command: string }) => x.command === command
85+
);
86+
expect(found).toBeTruthy();
87+
}).toPass({ timeout: 5000 });
10788
}
10889

10990
async getWebChannelEvents(): Promise<

packages/functional-tests/pages/settings/layout.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,13 @@ export abstract class SettingsLayout extends BaseLayout {
8686

8787
async signOut() {
8888
await this.avatarDropDownMenuToggle.click();
89-
await this.avatarMenuSignOut.click();
9089

91-
await expect(this.page).not.toHaveURL(/settings/);
90+
// Wait for the hard navigation (window.location.assign) to complete.
91+
// SPA redirects during sign-out can change the URL before the real
92+
// page load fires, so we wait for the actual 'load' event.
93+
await Promise.all([
94+
this.page.waitForEvent('load'),
95+
this.avatarMenuSignOut.click(),
96+
]);
9297
}
9398
}

packages/functional-tests/pages/signin.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@ export class SigninPage extends PasskeyPage {
142142
}
143143

144144
async fillOutEmailFirstForm(email: string) {
145+
// Ensure the page is ready after a hard navigation.
146+
await expect(this.emailTextbox).toBeVisible();
145147
await this.emailTextbox.fill(email);
146148
await this.emailFirstSubmitButton.click();
147149
}

0 commit comments

Comments
 (0)