Skip to content

Commit 4c9d8d7

Browse files
Merge pull request #20087 from mozilla/FXA-12949
fix(settings): fix metrics not always emitted for third party auth
2 parents e3da348 + 399648e commit 4c9d8d7

4 files changed

Lines changed: 106 additions & 138 deletions

File tree

packages/fxa-react/lib/utils.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ export function hardNavigate(
1616
href: string,
1717
additionalQueryParams: Record<string, string> = {},
1818
includeCurrentQueryParams = false,
19-
replace: boolean = false
19+
replace: boolean = false,
20+
timeoutMs: number = DEFAULT_NAVIGATE_TIMEOUT_MS
2021
) {
2122
// If there are any query params in the href, we automatically include them in the new url.
2223
const url = new URL(href, window.location.origin);
@@ -40,11 +41,11 @@ export function hardNavigate(
4041
if (replace) {
4142
setTimeout(() => {
4243
window.location.replace(url.href);
43-
}, DEFAULT_NAVIGATE_TIMEOUT_MS);
44+
}, timeoutMs);
4445
} else {
4546
setTimeout(() => {
4647
window.location.href = url.href;
47-
}, DEFAULT_NAVIGATE_TIMEOUT_MS);
48+
}, timeoutMs);
4849
}
4950
}
5051

packages/fxa-settings/src/components/ThirdPartyAuth/__snapshots__/index.test.tsx.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ exports[`ThirdPartyAuthComponent buttons match snapshot: apple 1`] = `
44
<button
55
aria-label="Continue with Apple"
66
class="w-[60px] h-[60px] p-4 flex items-center justify-center rounded-full border focus-visible-default outline-offset-2
7-
bg-black border-transparent
8-
"
9-
type="submit"
7+
bg-black border-transparent
8+
"
9+
type="button"
1010
>
1111
<svg
1212
class="w-full h-auto"
@@ -20,9 +20,9 @@ exports[`ThirdPartyAuthComponent buttons match snapshot: google 1`] = `
2020
<button
2121
aria-label="Continue with Google"
2222
class="w-[60px] h-[60px] p-4 flex items-center justify-center rounded-full border focus-visible-default outline-offset-2
23-
bg-[#F9F4F4] border-[#747775] border-[1px]
24-
"
25-
type="submit"
23+
bg-[#F9F4F4] border-[#747775] border-[1px]
24+
"
25+
type="button"
2626
>
2727
<svg
2828
class="w-full h-auto"

packages/fxa-settings/src/components/ThirdPartyAuth/index.test.tsx

Lines changed: 44 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
44

55
import React from 'react';
6-
import { screen } from '@testing-library/react';
6+
import { screen, waitFor } from '@testing-library/react';
7+
import userEvent from '@testing-library/user-event';
78
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
9+
import * as ReactUtils from 'fxa-react/lib/utils';
810
import { Subject } from './mocks';
911

1012
const mockViewWithNoPasswordSet = jest.fn();
@@ -14,14 +16,14 @@ const mockStartGoogleAuthFromLogin = jest.fn();
1416
const mockStartAppleAuthFromLogin = jest.fn();
1517
const mockStartGoogleAuthFromReg = jest.fn();
1618
const mockStartAppleAuthFromReg = jest.fn();
17-
const mockGleanIsDone = jest.fn();
19+
const mockGleanIsDone = jest.fn().mockResolvedValue(undefined);
1820

1921
jest.mock('../../lib/glean', () => {
2022
return {
2123
__esModule: true,
2224
default: {
2325
isDone: () => {
24-
mockGleanIsDone();
26+
return mockGleanIsDone();
2527
},
2628
emailFirst: {
2729
googleOauthStart: () => {
@@ -56,22 +58,20 @@ function renderWith(props?: any) {
5658
return renderWithLocalizationProvider(<Subject {...props} />);
5759
}
5860

59-
const mockFormSubmit = jest.fn();
60-
// jsdom does not implement HTMLFormElement.prototype.submit.
61-
HTMLFormElement.prototype.submit = mockFormSubmit;
62-
6361
describe('ThirdPartyAuthComponent', () => {
64-
// Form submission not supported in jest test. Instead, prevent the submission
65-
// and illustrate a 'short circuit' operation.
66-
const onSubmit = (e: any) => {
67-
e.preventDefault();
68-
return false;
69-
};
70-
const onContinueWithApple = jest.fn().mockImplementation(onSubmit);
71-
const onContinueWithGoogle = jest.fn().mockImplementation(onSubmit);
62+
const onContinueWithApple = jest.fn();
63+
const onContinueWithGoogle = jest.fn();
64+
let hardNavigateSpy: jest.SpyInstance;
65+
66+
beforeEach(() => {
67+
hardNavigateSpy = jest
68+
.spyOn(ReactUtils, 'hardNavigate')
69+
.mockImplementation(() => {});
70+
});
7271

7372
afterEach(() => {
74-
jest.resetAllMocks();
73+
hardNavigateSpy.mockRestore();
74+
jest.clearAllMocks();
7575
});
7676

7777
it('renders', async () => {
@@ -84,69 +84,68 @@ describe('ThirdPartyAuthComponent', () => {
8484

8585
await screen.findByLabelText('Continue with Google');
8686
await screen.findByLabelText('Continue with Apple');
87-
expect(mockFormSubmit).not.toHaveBeenCalled();
88-
89-
expect(
90-
(await screen.findByTestId('google-signin-form-state')).getAttribute(
91-
'value'
92-
)
93-
).toEqual('http%3A%2F%2Flocalhost%2F%3FflowId%3D123');
94-
95-
expect(
96-
(await screen.findByTestId('apple-signin-form-state')).getAttribute(
97-
'value'
98-
)
99-
).toEqual('http%3A%2F%2Flocalhost%2F%3FflowId%3D123');
10087
});
10188

10289
it('submits apple form', async () => {
90+
const user = userEvent.setup();
10391
renderWith({
10492
enabled: true,
10593
showSeparator: true,
10694
onContinueWithApple,
10795
onContinueWithGoogle,
96+
flowQueryParams: { flowId: '123' },
10897
});
10998

11099
const button = await screen.findByLabelText('Continue with Apple');
111-
button.click();
100+
await user.click(button);
112101

113-
// Ensure the hidden input for state was updated.
114-
expect(
115-
(await screen.findByTestId('apple-signin-form-state')).getAttribute(
116-
'value'
117-
)
118-
).not.toEqual('');
119102
expect(onContinueWithApple).toHaveBeenCalled();
120103
expect(onContinueWithGoogle).not.toHaveBeenCalled();
104+
await waitFor(() => {
105+
expect(hardNavigateSpy).toHaveBeenCalledWith(
106+
expect.any(String),
107+
expect.objectContaining({
108+
state: 'http%3A%2F%2Flocalhost%2F%3FflowId%3D123',
109+
}),
110+
false,
111+
false,
112+
0
113+
);
114+
});
121115
});
122116

123117
it('submits google form', async () => {
118+
const user = userEvent.setup();
124119
renderWith({
125120
enabled: true,
126121
showSeparator: true,
127122
onContinueWithApple,
128123
onContinueWithGoogle,
124+
flowQueryParams: { flowId: '123' },
129125
});
130126

131127
const button = await screen.findByLabelText('Continue with Google');
132-
button.click();
128+
await user.click(button);
133129

134-
// Ensure the hidden input for state was updated.
135-
expect(
136-
(await screen.findByTestId('google-signin-form-state')).getAttribute(
137-
'value'
138-
)
139-
).not.toEqual('');
140130
expect(onContinueWithGoogle).toHaveBeenCalled();
141131
expect(onContinueWithApple).not.toHaveBeenCalled();
132+
await waitFor(() => {
133+
expect(hardNavigateSpy).toHaveBeenCalledWith(
134+
expect.any(String),
135+
expect.objectContaining({
136+
state: 'http%3A%2F%2Flocalhost%2F%3FflowId%3D123',
137+
}),
138+
false,
139+
false,
140+
0
141+
);
142+
});
142143
});
143144

144145
it('hides separator', async () => {
145146
renderWith({
146147
enabled: true,
147148
showSeparator: false,
148-
onContinueWithApple,
149-
onContinueWithGoogle,
150149
});
151150

152151
expect(screen.queryByText('Or')).toBeNull();
@@ -157,8 +156,6 @@ describe('ThirdPartyAuthComponent', () => {
157156
renderWith({
158157
enabled: true,
159158
showSeparator: true,
160-
onContinueWithApple,
161-
onContinueWithGoogle,
162159
});
163160

164161
expect(screen.queryByText('Or')).toBeDefined();
@@ -168,8 +165,6 @@ describe('ThirdPartyAuthComponent', () => {
168165
it('buttons match snapshot', async () => {
169166
renderWith({
170167
enabled: true,
171-
onContinueWithApple,
172-
onContinueWithGoogle,
173168
});
174169

175170
const googleButton = await screen.findByLabelText('Continue with Google');
@@ -183,8 +178,6 @@ describe('ThirdPartyAuthComponent', () => {
183178
renderWith({
184179
enabled: true,
185180
showSeparator: false,
186-
onContinueWithApple,
187-
onContinueWithGoogle,
188181
});
189182
expect(mockViewWithNoPasswordSet).toHaveBeenCalled();
190183
});
@@ -193,8 +186,6 @@ describe('ThirdPartyAuthComponent', () => {
193186
renderWith({
194187
enabled: true,
195188
showSeparator: false,
196-
onContinueWithApple,
197-
onContinueWithGoogle,
198189
viewName: 'index',
199190
});
200191
const button = await screen.findByLabelText('Continue with Google');
@@ -207,8 +198,6 @@ describe('ThirdPartyAuthComponent', () => {
207198
renderWith({
208199
enabled: true,
209200
showSeparator: false,
210-
onContinueWithApple,
211-
onContinueWithGoogle,
212201
viewName: 'index',
213202
});
214203
const button = await screen.findByLabelText('Continue with Apple');
@@ -220,8 +209,6 @@ describe('ThirdPartyAuthComponent', () => {
220209
renderWith({
221210
enabled: true,
222211
showSeparator: false,
223-
onContinueWithApple,
224-
onContinueWithGoogle,
225212
viewName: 'signin',
226213
});
227214
const button = await screen.findByLabelText('Continue with Google');
@@ -234,8 +221,6 @@ describe('ThirdPartyAuthComponent', () => {
234221
renderWith({
235222
enabled: true,
236223
showSeparator: false,
237-
onContinueWithApple,
238-
onContinueWithGoogle,
239224
viewName: 'signin',
240225
});
241226
const button = await screen.findByLabelText('Continue with Apple');
@@ -247,8 +232,6 @@ describe('ThirdPartyAuthComponent', () => {
247232
renderWith({
248233
enabled: true,
249234
showSeparator: false,
250-
onContinueWithApple,
251-
onContinueWithGoogle,
252235
viewName: 'signup',
253236
});
254237
const button = await screen.findByLabelText('Continue with Google');
@@ -261,8 +244,6 @@ describe('ThirdPartyAuthComponent', () => {
261244
renderWith({
262245
enabled: true,
263246
showSeparator: false,
264-
onContinueWithApple,
265-
onContinueWithGoogle,
266247
viewName: 'signup',
267248
});
268249
const button = await screen.findByLabelText('Continue with Apple');

0 commit comments

Comments
 (0)