Skip to content

Commit 855f929

Browse files
authored
Merge pull request #19944 from mozilla/FXA-12379
chore(tests): Add smartwindow Playwright tests
2 parents c5bcdb0 + b75fad2 commit 855f929

4 files changed

Lines changed: 204 additions & 6 deletions

File tree

packages/functional-tests/lib/query-params.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@ export const relayDesktopOAuthQueryParams = new URLSearchParams({
5555
service: 'relay',
5656
});
5757

58+
export const smartWindowDesktopOAuthQueryParams = new URLSearchParams({
59+
...Object.fromEntries(oauthWebchannelV1.entries()),
60+
client_id: FF_OAUTH_CLIENT_ID, // Firefox Desktop
61+
code_challenge_method: 'S256',
62+
code_challenge: '2oc_C4v1qHeefWAGu5LI5oDG1oX4FV_Itc148D8_oQI',
63+
// eslint-disable-next-line camelcase
64+
keys_jwk:
65+
'eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6ImdUejVIWFJfa2pxSFRtMG43ZjhxcDMybVZFaHZ1cGo1dXNUV1h5TWZsb1kiLCJ5IjoiVER5TlhkalhibHZld1pWLVc5MXNDZU9fRWd0NU9WYXhpblBzOEFTQ3owZyJ9',
66+
scope: 'https://identity.mozilla.com/apps/oldsync',
67+
state: 'fakestate',
68+
automatedBrowser: 'true',
69+
service: 'smartwindow',
70+
});
71+
5872
export const syncDesktopV3QueryParams = new URLSearchParams({
5973
context: 'fx_desktop_v3',
6074
service: 'sync',

packages/functional-tests/pages/layout.ts

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ export abstract class BaseLayout {
1919
*/
2020
abstract get path(): string;
2121

22-
constructor(public page: Page, protected readonly target: BaseTarget) {}
22+
constructor(
23+
public page: Page,
24+
protected readonly target: BaseTarget
25+
) {}
2326

2427
protected get baseUrl() {
2528
return this.target.baseUrl;
@@ -103,6 +106,36 @@ export abstract class BaseLayout {
103106
}, command);
104107
}
105108

109+
async getWebChannelEvents(): Promise<
110+
Array<{ command: string; data: Record<string, unknown> }>
111+
> {
112+
return await this.page.evaluate(() => {
113+
return JSON.parse(sessionStorage.getItem('webChannelEvents') || '[]');
114+
});
115+
}
116+
117+
/**
118+
* Asserts that a web channel message with the given command was sent
119+
* and contains the expected services object in its data.
120+
*/
121+
async checkWebChannelMessageServices(
122+
command: FirefoxCommand,
123+
expectedServices: Record<string, unknown>
124+
) {
125+
await this.checkWebChannelMessage(command);
126+
const events = await this.getWebChannelEvents();
127+
const event = events.find((e) => e.command === command);
128+
if (!event) {
129+
throw new Error(`No web channel event found for command: ${command}`);
130+
}
131+
const services = (event.data as { services?: unknown })?.services;
132+
if (JSON.stringify(services) !== JSON.stringify(expectedServices)) {
133+
throw new Error(
134+
`Expected services ${JSON.stringify(expectedServices)} but got ${JSON.stringify(services)}`
135+
);
136+
}
137+
}
138+
106139
async listenToWebChannelMessages() {
107140
await this.page.evaluate(() => {
108141
function listener(msg: { detail: string }) {
@@ -112,7 +145,7 @@ export abstract class BaseLayout {
112145
);
113146
events.push({
114147
command: detail.message.command,
115-
detail: detail.message.data,
148+
data: detail.message.data,
116149
});
117150
sessionStorage.setItem('webChannelEvents', JSON.stringify(events));
118151
}

packages/functional-tests/tests/misc/relayIntegration.spec.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ test.describe('relay integration', () => {
4242
await page.waitForURL(/settings/);
4343

4444
await signup.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
45-
await signup.checkWebChannelMessage(FirefoxCommand.Login);
45+
await signup.checkWebChannelMessageServices(FirefoxCommand.Login, {
46+
relay: {},
47+
});
4648
});
4749

4850
test('signin with Relay desktop', async ({
@@ -62,7 +64,9 @@ test.describe('relay integration', () => {
6264
await page.waitForURL(/settings/);
6365

6466
await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
65-
await signin.checkWebChannelMessage(FirefoxCommand.Login);
67+
await signin.checkWebChannelMessageServices(FirefoxCommand.Login, {
68+
relay: {},
69+
});
6670
});
6771

6872
test('signin with Relay desktop - with confirm email', async ({
@@ -87,7 +91,9 @@ test.describe('relay integration', () => {
8791
await page.waitForURL(/settings/);
8892

8993
await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
90-
await signin.checkWebChannelMessage(FirefoxCommand.Login);
94+
await signin.checkWebChannelMessageServices(FirefoxCommand.Login, {
95+
relay: {},
96+
});
9197
});
9298

9399
test('signin with Relay desktop - with 2FA', async ({
@@ -128,6 +134,8 @@ test.describe('relay integration', () => {
128134
await page.waitForURL(/settings/);
129135

130136
await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
131-
await signin.checkWebChannelMessage(FirefoxCommand.Login);
137+
await signin.checkWebChannelMessageServices(FirefoxCommand.Login, {
138+
relay: {},
139+
});
132140
});
133141
});
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
import { FirefoxCommand } from '../../lib/channels';
6+
import { expect, test } from '../../lib/fixtures/standard';
7+
import { smartWindowDesktopOAuthQueryParams } from '../../lib/query-params';
8+
import { getTotpCode } from '../../lib/totp';
9+
10+
test.describe('smart window integration', () => {
11+
test('signup with SmartWindow desktop', async ({
12+
target,
13+
syncOAuthBrowserPages: { confirmSignupCode, page, signup },
14+
testAccountTracker,
15+
}) => {
16+
const { email, password } =
17+
testAccountTracker.generateSignupAccountDetails();
18+
19+
await signup.goto('/authorization', smartWindowDesktopOAuthQueryParams);
20+
21+
await expect(
22+
signup.page.getByText('Firefox Smart Window', { exact: false })
23+
).toBeVisible();
24+
25+
await signup.emailTextbox.fill(email);
26+
await signup.submitButton.click();
27+
28+
await page.waitForURL(/signup/);
29+
30+
await signup.passwordTextbox.fill(password);
31+
await signup.createAccountButton.click();
32+
33+
await page.waitForURL(/confirm_signup_code/);
34+
35+
const code = await target.emailClient.getVerifyShortCode(email);
36+
await confirmSignupCode.fillOutCodeForm(code);
37+
38+
await page.waitForURL(/settings/);
39+
40+
await signup.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
41+
await signup.checkWebChannelMessageServices(FirefoxCommand.Login, {
42+
smartwindow: {},
43+
});
44+
});
45+
46+
test('signin with SmartWindow desktop', async ({
47+
syncOAuthBrowserPages: { page, signin },
48+
testAccountTracker,
49+
}) => {
50+
const { email, password } = await testAccountTracker.signUp();
51+
52+
await signin.goto('/authorization', smartWindowDesktopOAuthQueryParams);
53+
54+
await expect(
55+
signin.page.getByText('Firefox Smart Window', { exact: false })
56+
).toBeVisible();
57+
58+
await signin.fillOutEmailFirstForm(email);
59+
60+
await signin.fillOutPasswordForm(password);
61+
62+
await page.waitForURL(/settings/);
63+
64+
await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
65+
await signin.checkWebChannelMessageServices(FirefoxCommand.Login, {
66+
smartwindow: {},
67+
});
68+
});
69+
70+
test('signin with SmartWindow desktop - with confirm email', async ({
71+
target,
72+
syncOAuthBrowserPages: { signinTokenCode, page, signin },
73+
testAccountTracker,
74+
}) => {
75+
const { email, password } = await testAccountTracker.signUpSync();
76+
77+
await signin.goto('/authorization', smartWindowDesktopOAuthQueryParams);
78+
79+
await expect(
80+
signin.page.getByText('Firefox Smart Window', { exact: false })
81+
).toBeVisible();
82+
83+
await signin.fillOutEmailFirstForm(email);
84+
85+
await signin.fillOutPasswordForm(password);
86+
87+
await page.waitForURL(/signin_token_code/);
88+
const code = await target.emailClient.getVerifyLoginCode(email);
89+
await signinTokenCode.fillOutCodeForm(code);
90+
91+
await page.waitForURL(/settings/);
92+
93+
await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
94+
await signin.checkWebChannelMessageServices(FirefoxCommand.Login, {
95+
smartwindow: {},
96+
});
97+
});
98+
99+
test('signin with SmartWindow desktop - with 2FA', async ({
100+
target,
101+
syncOAuthBrowserPages: { signinTotpCode, totp, page, signin, settings },
102+
testAccountTracker,
103+
}) => {
104+
const credentials = await testAccountTracker.signUp();
105+
const { email, password } = credentials;
106+
107+
// Sign-in without Sync, otw you will get prompted to create a recovery key
108+
await page.goto(target.contentServerUrl, { waitUntil: 'load' });
109+
110+
await signin.fillOutEmailFirstForm(email);
111+
await signin.fillOutPasswordForm(password);
112+
113+
await expect(settings.settingsHeading).toBeVisible();
114+
await settings.totp.addButton.click();
115+
await settings.confirmMfaGuard(email);
116+
const { secret } =
117+
await totp.setUpTwoStepAuthWithQrAndBackupCodesChoice(credentials);
118+
await expect(settings.totp.status).toHaveText('Enabled');
119+
await settings.signOut();
120+
121+
await signin.goto('/authorization', smartWindowDesktopOAuthQueryParams);
122+
123+
await expect(
124+
signin.page.getByText('Firefox Smart Window', { exact: false })
125+
).toBeVisible();
126+
127+
await signin.fillOutEmailFirstForm(email);
128+
129+
await signin.fillOutPasswordForm(password);
130+
131+
await page.waitForURL(/signin_totp_code/);
132+
133+
const totpCode = await getTotpCode(secret);
134+
await signinTotpCode.fillOutCodeForm(totpCode);
135+
136+
await page.waitForURL(/settings/);
137+
138+
await signin.checkWebChannelMessage(FirefoxCommand.OAuthLogin);
139+
await signin.checkWebChannelMessageServices(FirefoxCommand.Login, {
140+
smartwindow: {},
141+
});
142+
});
143+
});

0 commit comments

Comments
 (0)