Skip to content

Commit 90b5cbf

Browse files
committed
Bump jest-environment-jsdom to v30
1 parent 09aec77 commit 90b5cbf

13 files changed

Lines changed: 244 additions & 439 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@
278278
"intl": "^1.2.5",
279279
"intl-locales-supported": "^1.0.0",
280280
"jest": "^30.3.0",
281-
"jest-environment-jsdom": "^29.7.0",
281+
"jest-environment-jsdom": "^30.0.0",
282282
"jest-extended": "^5.0.3",
283283
"jest-json-schema": "^6.1.0",
284284
"jest-watch-typeahead": "^3.0.1",

src/amo/components/App/index.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* @flow */
2-
/* global Navigator, navigator */
2+
/* global Navigator, navigator, window */
33
import config from 'config';
44
import * as React from 'react';
55
import { Helmet } from 'react-helmet';
@@ -32,6 +32,7 @@ import { CLIENT_APP_ANDROID } from 'amo/constants';
3232
import ErrorPage from 'amo/components/ErrorPage';
3333
import translate from 'amo/i18n/translate';
3434
import log from 'amo/logger';
35+
import universalWindow from 'amo/window';
3536
import type { AppState } from 'amo/store';
3637
import type { DispatchFunc } from 'amo/types/redux';
3738
import type { InstalledAddon } from 'amo/reducers/installations';
@@ -52,6 +53,7 @@ type PropsFromState = {|
5253
type DefaultProps = {|
5354
_addChangeListeners: (callback: Function, mozAddonManager: Object) => any,
5455
_navigator: typeof navigator | null,
56+
_window: typeof window,
5557
mozAddonManager: $PropertyType<MozNavigator, 'mozAddonManager'>,
5658
userAgent: string | null,
5759
|};
@@ -70,6 +72,7 @@ export class AppBase extends React.Component<Props> {
7072
static defaultProps: DefaultProps = {
7173
_addChangeListeners: addChangeListeners,
7274
_navigator: typeof navigator !== 'undefined' ? navigator : null,
75+
_window: universalWindow,
7376
mozAddonManager: config.get('server')
7477
? {}
7578
: (navigator: MozNavigator).mozAddonManager,
@@ -148,7 +151,7 @@ export class AppBase extends React.Component<Props> {
148151
<ScrollToTop>
149152
<Helmet defaultTitle={defaultTitle} titleTemplate={titleTemplate} />
150153
<ErrorPage>
151-
<Routes />
154+
<Routes _window={this.props._window} />
152155
</ErrorPage>
153156
</ScrollToTop>
154157
</NestedStatus>

src/amo/components/AuthenticateButton/index.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import translate from 'amo/i18n/translate';
1212
import Button from 'amo/components/Button';
1313
import Icon from 'amo/components/Icon';
1414
import log from 'amo/logger';
15+
import universalWindow from 'amo/window';
1516
import type { ButtonType } from 'amo/components/Button';
1617
import type { AppState } from 'amo/store';
1718
import type { ApiState } from 'amo/reducers/api';
@@ -23,12 +24,13 @@ import type { ReactRouterLocationType } from 'amo/types/router';
2324

2425
type HandleLogInFunc = (
2526
location: ReactRouterLocationType,
26-
options?: {| _window: typeof window |},
27+
options?: {| _window?: typeof window |},
2728
) => void;
2829

2930
type HandleLogOutFunction = ({| api: ApiState |}) => Promise<void>;
3031

3132
type DefaultProps = {|
33+
_window?: typeof window,
3234
buttonType?: ButtonType,
3335
noIcon?: boolean,
3436
|};
@@ -58,21 +60,23 @@ type InternalProps = {|
5860

5961
export class AuthenticateButtonBase extends React.Component<InternalProps> {
6062
static defaultProps: DefaultProps = {
63+
_window: universalWindow,
6164
buttonType: 'action',
6265
noIcon: false,
6366
};
6467

6568
onClick: HTMLElementEventHandler = (event: ElementEvent) => {
6669
event.preventDefault();
6770
event.stopPropagation();
68-
const { api, handleLogIn, handleLogOut, location, siteUser } = this.props;
71+
const { _window, api, handleLogIn, handleLogOut, location, siteUser } =
72+
this.props;
6973

7074
invariant(handleLogOut, 'handleLogOut() is undefined');
7175

7276
if (siteUser) {
7377
handleLogOut({ api });
7478
} else {
75-
handleLogIn(location);
79+
handleLogIn(location, { _window });
7680
}
7781
};
7882

@@ -124,7 +128,6 @@ export const mapStateToProps = (
124128
ownProps: Props,
125129
): PropsFromState => {
126130
const defaultHandleLogIn = (location, { _window = window } = {}) => {
127-
// eslint-disable-next-line no-param-reassign
128131
_window.location.assign(startLoginUrl({ location }));
129132
};
130133

src/amo/components/Errors/AuthExpired/index.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import Link from 'amo/components/Link';
99
import { replaceStringsWithJSX } from 'amo/i18n/utils';
1010
import translate from 'amo/i18n/translate';
1111
import { logOutUser } from 'amo/reducers/users';
12+
import universalWindow from 'amo/window';
1213
import type { I18nType } from 'amo/types/i18n';
1314
import type { DispatchFunc } from 'amo/types/redux';
1415

@@ -29,7 +30,7 @@ type InternalProps = {|
2930

3031
export class AuthExpiredBase extends React.Component<InternalProps> {
3132
static defaultProps: DefaultProps = {
32-
_window: typeof window !== 'undefined' ? window : {},
33+
_window: universalWindow,
3334
};
3435

3536
componentDidMount() {
@@ -72,9 +73,7 @@ export class AuthExpiredBase extends React.Component<InternalProps> {
7273
}
7374
}
7475

75-
const AuthExpired: React.ComponentType<Props> = compose(
76-
connect(),
77-
translate(),
78-
)(AuthExpiredBase);
76+
const AuthExpired: React.ComponentType<{| ...Props, ...DefaultProps |}> =
77+
compose(connect(), translate())(AuthExpiredBase);
7978

8079
export default AuthExpired;

src/amo/components/Footer/index.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* @flow */
2+
/* global window */
23
import * as React from 'react';
34
import { compose } from 'redux';
45
import config from 'config';
@@ -8,11 +9,13 @@ import Link from 'amo/components/Link';
89
import { makeQueryStringWithUTM, sanitizeHTML } from 'amo/utils';
910
import translate from 'amo/i18n/translate';
1011
import Icon from 'amo/components/Icon';
12+
import universalWindow from 'amo/window';
1113
import type { I18nType } from 'amo/types/i18n';
1214

1315
import './styles.scss';
1416

1517
type Props = {|
18+
_window?: typeof window,
1619
noLangPicker?: boolean,
1720
includeGoogleDisclaimer?: boolean,
1821
|};
@@ -30,11 +33,13 @@ type InternalProps = {|
3033
export class FooterBase extends React.Component<InternalProps> {
3134
static defaultProps: {| ...Props, ...DefaultProps |} = {
3235
_config: config,
36+
_window: universalWindow,
3337
noLangPicker: false,
3438
};
3539

3640
render(): React.Node {
37-
const { _config, includeGoogleDisclaimer, i18n, noLangPicker } = this.props;
41+
const { _config, _window, includeGoogleDisclaimer, i18n, noLangPicker } =
42+
this.props;
3843
const homepageText = i18n.gettext("Go to Mozilla's homepage");
3944

4045
const footerLinkQueryString = makeQueryStringWithUTM({
@@ -383,7 +388,7 @@ export class FooterBase extends React.Component<InternalProps> {
383388

384389
{!noLangPicker && (
385390
<div className="Footer-language-picker">
386-
<LanguagePicker />
391+
<LanguagePicker _window={_window} />
387392
</div>
388393
)}
389394
</div>

src/amo/components/Page/index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* @flow */
2+
/* global window */
23
import makeClassName from 'classnames';
34
import config from 'config';
45
import * as React from 'react';
@@ -14,6 +15,7 @@ import Footer from 'amo/components/Footer';
1415
import Header from 'amo/components/Header';
1516
import WrongPlatformWarning from 'amo/components/WrongPlatformWarning';
1617
import VPNPromoBanner from 'amo/components/VPNPromoBanner';
18+
import universalWindow from 'amo/window';
1719
import { API_ERRORS_SESSION_EXPIRY, CLIENT_APP_ANDROID } from 'amo/constants';
1820
import { EXPERIMENT_CONFIG } from 'amo/experiments/20210714_amo_vpn_promo';
1921
import log from 'amo/logger';
@@ -26,6 +28,7 @@ import type { WithExperimentInjectedProps } from 'amo/withExperiment';
2628
import './styles.scss';
2729

2830
type Props = {|
31+
_window?: typeof window,
2932
children: React.Node,
3033
errorHandler?: ErrorHandlerType,
3134
includeGoogleDisclaimerInFooter?: boolean,
@@ -51,6 +54,7 @@ type InternalProps = {|
5154
export const PageBase = ({
5255
_config = config,
5356
_log = log,
57+
_window = universalWindow,
5458
children,
5559
clientApp,
5660
errorHandler,
@@ -70,7 +74,7 @@ export const PageBase = ({
7074
errorHandler.capturedError.responseStatusCode === 401 &&
7175
API_ERRORS_SESSION_EXPIRY.includes(errorHandler.capturedError.code)
7276
) {
73-
errorContent = <AuthExpired />;
77+
errorContent = <AuthExpired _window={_window} />;
7478
} else if (
7579
errorHandler.capturedError.responseStatusCode === 401 ||
7680
errorHandler.capturedError.responseStatusCode === 403 ||
@@ -125,7 +129,10 @@ export const PageBase = ({
125129
</div>
126130
</div>
127131

128-
<Footer includeGoogleDisclaimer={includeGoogleDisclaimerInFooter} />
132+
<Footer
133+
_window={_window}
134+
includeGoogleDisclaimer={includeGoogleDisclaimerInFooter}
135+
/>
129136
</div>
130137
);
131138
};

src/amo/components/Routes/index.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* @flow */
2+
/* global window */
23
import config from 'config';
34
import * as React from 'react';
45
import { Route, Switch } from 'react-router-dom';
@@ -39,14 +40,19 @@ import SimulateSyncError from 'amo/pages/error-simulation/SimulateSyncError';
3940
import About from 'amo/pages/StaticPages/About';
4041
import ReviewGuide from 'amo/pages/StaticPages/ReviewGuide';
4142
import TagPage from 'amo/pages/TagPage';
43+
import universalWindow from 'amo/window';
4244
import type { ConfigType } from 'amo/types/config';
4345

4446
type Props = {
4547
_config?: ConfigType,
48+
_window?: typeof window,
4649
};
4750

4851
// If you add a new route here, check the nginx rules maintained by ops.
49-
const Routes = ({ _config = config }: Props = {}): React.Node => (
52+
const Routes = ({
53+
_config = config,
54+
_window = universalWindow,
55+
}: Props = {}): React.Node => (
5056
<Switch>
5157
<Route exact path="/:lang/about" component={About} />
5258
{/* TODO: Post launch update this URL and redirect see #3374/ */}
@@ -105,12 +111,12 @@ const Routes = ({ _config = config }: Props = {}): React.Node => (
105111
<Route
106112
exact
107113
path="/:lang/:application(firefox|android)/users/edit"
108-
component={UserProfileEdit}
114+
render={(props) => <UserProfileEdit _window={_window} {...props} />}
109115
/>
110116
<Route
111117
exact
112118
path="/:lang/:application(firefox|android)/user/:userId/edit/"
113-
component={UserProfileEdit}
119+
render={(props) => <UserProfileEdit _window={_window} {...props} />}
114120
/>
115121
<Route
116122
exact

src/amo/pages/UserProfileEdit/index.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { USERS_EDIT, VIEW_CONTEXT_HOME } from 'amo/constants';
3131
import { withFixedErrorHandler } from 'amo/errorHandler';
3232
import log from 'amo/logger';
3333
import translate from 'amo/i18n/translate';
34+
import universalWindow from 'amo/window';
3435
import { sanitizeHTML } from 'amo/utils';
3536
import Button from 'amo/components/Button';
3637
import Card from 'amo/components/Card';
@@ -70,7 +71,7 @@ type PropsFromState = {|
7071
|};
7172

7273
type DefaultProps = {|
73-
_window: typeof window | Object,
74+
_window: typeof window,
7475
|};
7576

7677
type InternalProps = {|
@@ -108,7 +109,7 @@ type State = {|
108109

109110
export class UserProfileEditBase extends React.Component<InternalProps, State> {
110111
static defaultProps: DefaultProps = {
111-
_window: typeof window !== 'undefined' ? window : {},
112+
_window: universalWindow,
112113
};
113114

114115
constructor(props: InternalProps) {
@@ -157,6 +158,7 @@ export class UserProfileEditBase extends React.Component<InternalProps, State> {
157158
} = prevProps;
158159

159160
const {
161+
_window,
160162
clientApp,
161163
currentUser,
162164
dispatch,
@@ -218,24 +220,22 @@ export class UserProfileEditBase extends React.Component<InternalProps, State> {
218220
toPath = url.parse(toPath).pathname;
219221
if (toPath && !toPath.startsWith('//')) {
220222
try {
221-
this.props._window.location.assign(toPath);
223+
_window.location.assign(toPath);
222224
return;
223225
} catch (error) {
224226
log.warn(`Error redirecting to location: ${toPath}: ${error}`);
225227
}
226228
}
227229
}
228-
this.props._window.location.assign(
229-
`/${lang}/${clientApp}/user/${newUserId}/`,
230-
);
230+
_window.location.assign(`/${lang}/${clientApp}/user/${newUserId}/`);
231231
}
232232

233233
if (
234234
(!prevProps.errorHandler.hasError() &&
235235
this.props.errorHandler.hasError()) ||
236236
(!prevState.successMessage && this.state.successMessage)
237237
) {
238-
this.props._window.scroll(0, 0);
238+
_window.scroll(0, 0);
239239
}
240240
}
241241

tests/unit/amo/components/TestAuthenticateButton.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
/* global window */
21
import * as React from 'react';
32
import { createEvent, fireEvent } from '@testing-library/react';
43
import userEvent from '@testing-library/user-event';
@@ -19,20 +18,15 @@ import {
1918

2019
describe(__filename, () => {
2120
let store;
22-
23-
const savedLocation = window.location;
21+
let fakeWindow;
2422

2523
beforeEach(() => {
2624
store = dispatchClientMetadata().store;
27-
delete window.location;
28-
window.location = Object.assign(new URL('https://example.org'), {
29-
assign: jest.fn(),
30-
});
25+
fakeWindow = { location: { assign: jest.fn() } };
3126
});
3227

3328
afterEach(() => {
3429
jest.clearAllMocks().resetModules();
35-
window.location = savedLocation;
3630
});
3731

3832
const render = ({ location, ...props } = {}) => {
@@ -42,7 +36,10 @@ describe(__filename, () => {
4236
}),
4337
store,
4438
};
45-
return defaultRender(<AuthenticateButton {...props} />, renderOptions);
39+
return defaultRender(
40+
<AuthenticateButton _window={fakeWindow} {...props} />,
41+
renderOptions,
42+
);
4643
};
4744

4845
it('passes along a className', () => {
@@ -97,6 +94,7 @@ describe(__filename, () => {
9794
fireEvent(link, clickEvent);
9895
expect(handleLogIn).toHaveBeenCalledWith(
9996
expect.objectContaining({ pathname: location }),
97+
{ _window: fakeWindow },
10098
);
10199
expect(preventDefaultWatcher).toHaveBeenCalled();
102100
});
@@ -127,7 +125,7 @@ describe(__filename, () => {
127125
expect(startLoginUrl).toHaveBeenCalledWith({
128126
location: expect.objectContaining({ pathname: location }),
129127
});
130-
expect(window.location.assign).toHaveBeenCalledWith(mockLoginURL);
128+
expect(fakeWindow.location.assign).toHaveBeenCalledWith(mockLoginURL);
131129
});
132130

133131
it('calls logOutFromServer on handleLogOut', async () => {

0 commit comments

Comments
 (0)