chore(deps): upgrade react-scripts and webpack to v5

Because:
 - we want to upgrade our React/CRA packages to react-scripts and
   webpack v5

This commit:
 - upgrades react-scripts and webpack to v5 for Settings, Payments, and
   Admin Panel
   - upgrades other dependencies in order to make the react-scripts and
     webpack upgrades work
 - hybridizes fxa-auth-client and fxa-shared into dual module format
   packages
This commit is contained in:
Barry Chen 2023-06-23 13:31:01 -05:00
Родитель 049ec6cb4c
Коммит a0b2cda909
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 228DB2785954A0D0
286 изменённых файлов: 8459 добавлений и 9371 удалений

Просмотреть файл

@ -88,7 +88,9 @@
"@nx/jest": "16.3.1",
"@nx/js": "^16.3.1",
"@nx/node": "^16.3.1",
"@nx/workspace": "^16.3.1",
"@nx/workspace": "16.3.1",
"@storybook/html-webpack5": "^7.0.23",
"@storybook/react-webpack5": "^7.0.23",
"@types/jest": "^29.5.1",
"@types/mysql": "^2",
"@types/node": "^18.16.1",
@ -117,10 +119,15 @@
"packages/*"
],
"resolutions": {
"@babel/core": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/types": "^7.22.5",
"@grpc/grpc-js": "~1.6.0",
"@nestjs/cli/typescript": "^4.5.2",
"@svgr/webpack": "^8.0.1",
"@types/node": "^18.14.2",
"browserid-crypto": "https://github.com/mozilla-fxa/browserid-crypto.git#5979544d13eeb15a02d0b9a6a7a08a698d54d37d",
"css-minimizer-webpack-plugin": ">=4 <5",
"elliptic": ">=6.5.4",
"eslint-plugin-import": "^2.25.2",
"fbjs/isomorphic-fetch": "^3.0.0",

Просмотреть файл

@ -10,9 +10,10 @@ const CI = !!process.env.CI;
// see .vscode/launch.json
const DEBUG = !!process.env.DEBUG;
const SLOWMO = parseInt(process.env.PLAYWRIGHT_SLOWMO || '0');
const NUM_WORKERS = parseInt(process.env.PLAYWRIGHT_WORKERS || '2');
let retries = 0,
workers = 2,
workers = NUM_WORKERS || 2,
maxFailures = 0;
if (CI) {
// Overall maxFailures is now dependent on the number of retries, workers

Просмотреть файл

@ -51,7 +51,7 @@
"react": "^16.13.0",
"react-dom": "^16.13.0",
"react-router-dom": "^6.9.0",
"react-scripts": "^4.0.3",
"react-scripts": "^5.0.1",
"serve-static": "^1.14.2"
},
"devDependencies": {
@ -90,6 +90,7 @@
"tailwindcss": "^3.3.1",
"tailwindcss-textshadow": "^2.1.3",
"ts-jest": "^29.1.0",
"typescript": "^4.9.3"
"typescript": "^4.9.3",
"webpack": "^5.84.1"
}
}

Просмотреть файл

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import { mockConfigBuilder } from './lib/config';
import {
@ -11,6 +10,7 @@ import {
AdminPanelGroup,
AdminPanelGuard,
} from 'fxa-shared/guards';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
it('renders without imploding', () => {
const guard = new AdminPanelGuard(AdminPanelEnv.Prod);
@ -21,6 +21,8 @@ it('renders without imploding', () => {
group: guard.getGroup(AdminPanelGroup.SupportAgentProd),
},
});
const { queryByTestId } = render(<App {...{ config }} />);
const { queryByTestId } = renderWithLocalizationProvider(
<App {...{ config }} />
);
expect(queryByTestId('app')).toBeInTheDocument();
});

Просмотреть файл

@ -3,9 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import Subscription from '.';
import { MozSubscription } from 'fxa-admin-server/src/graphql';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
const subscription: MozSubscription = {
created: 1583259953 * 1e3,
@ -24,7 +25,7 @@ const subscription: MozSubscription = {
};
it('renders each field as expected', () => {
render(<Subscription {...subscription} />);
renderWithLocalizationProvider(<Subscription {...subscription} />);
screen.getByText(subscription.productName);
screen.getByText(subscription.status);

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import PageRelyingParties from '.';
import {
@ -20,6 +20,7 @@ import {
AdminPanelGuard,
} from '../../../../fxa-shared/guards';
import { mockConfigBuilder } from '../../lib/config';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
// Setup the current user hook. Required for Guards.
const mockGuard = new AdminPanelGuard(AdminPanelEnv.Prod);
@ -43,7 +44,7 @@ jest.mock('../../hooks/UserContext.ts', () => ({
}));
it('renders without imploding and shows loading text', () => {
render(
renderWithLocalizationProvider(
<MockedProvider mocks={[mockGetRelyingParties()]} addTypename={false}>
<PageRelyingParties />
</MockedProvider>
@ -54,7 +55,7 @@ it('renders without imploding and shows loading text', () => {
});
it('renders as expected with zero relying parties', async () => {
render(
renderWithLocalizationProvider(
<MockedProvider mocks={[mockGetRelyingParties()]} addTypename={false}>
<PageRelyingParties />
</MockedProvider>
@ -63,7 +64,7 @@ it('renders as expected with zero relying parties', async () => {
});
it('renders as expected with a relying party containing all fields', async () => {
render(
renderWithLocalizationProvider(
<MockedProvider
mocks={[mockGetRelyingParties([MOCK_RP_ALL_FIELDS])]}
addTypename={false}
@ -81,7 +82,7 @@ it('renders as expected with a relying party containing all fields', async () =>
});
it('updates notes', async () => {
render(
renderWithLocalizationProvider(
<MockedProvider
mocks={[
mockGetRelyingParties([MOCK_RP_ALL_FIELDS]),
@ -109,7 +110,7 @@ it('updates notes', async () => {
});
it('shows error if notes fail to update', async () => {
render(
renderWithLocalizationProvider(
<MockedProvider
mocks={[
mockGetRelyingParties([MOCK_RP_ALL_FIELDS]),
@ -137,7 +138,7 @@ it('shows error if notes fail to update', async () => {
});
it('renders as expected with a relying party containing falsy fields', async () => {
render(
renderWithLocalizationProvider(
<MockedProvider
mocks={[mockGetRelyingParties([MOCK_RP_FALSY_FIELDS])]}
addTypename={false}

Просмотреть файл

@ -0,0 +1,5 @@
# A JavaScript Client for Firefox Accounts
### Dual Package
Note that this is a dual package. The impetus for it was Webpack 5 compat. (And we'd want to move to all-ESM eventually.) But CommonJS was kept for backwards compatibility until we can be certain of its removal.

Просмотреть файл

@ -2,20 +2,36 @@
"name": "fxa-auth-client",
"version": "0.0.0",
"description": "",
"main": "dist/server/packages/fxa-auth-client/server.js",
"main": "dist/server/cjs/packages/fxa-auth-client/server.js",
"module": "dist/server/esm/packages/fxa-auth-client/server.js",
"exports": {
".": "./dist/server/packages/fxa-auth-client/server.js",
".": {
"import": "./dist/server/esm/packages/fxa-auth-client/server.js",
"require": "./dist/server/cjs/packages/fxa-auth-client/server.js"
},
"./browser": "./dist/browser/packages/fxa-auth-client/browser.js",
"./lib/crypto": "./dist/server/packages/fxa-auth-client/lib/crypto.js",
"./lib/hawk": "./dist/server/packages/fxa-auth-client/lib/hawk.js",
"./lib/recoveryKey": "./dist/server/packages/fxa-auth-client/lib/recoveryKey.js",
"./lib/utils": "./dist/server/packages/fxa-auth-client/lib/utils.js",
"./lib/crypto": {
"import": "./dist/server/esm/packages/fxa-auth-client/lib/crypto.js",
"require": "./dist/server/cjs/packages/fxa-auth-client/lib/crypto.js"
},
"./lib/hawk": {
"import": "./dist/server/esm/packages/fxa-auth-client/lib/hawk.js",
"require": "./dist/server/cjs/packages/fxa-auth-client/lib/hawk.js"
},
"./lib/recoveryKey": {
"import": "./dist/server/esm/packages/fxa-auth-client/lib/recoveryKey.js",
"require": "./dist/server/cjs/packages/fxa-auth-client/lib/recoveryKey.js"
},
"./lib/utils": {
"import": "./dist/server/esm/packages/fxa-auth-client/lib/utils.js",
"require": "./dist/server/cjs/packages/fxa-auth-client/lib/utils.js"
},
"./lib/": "./lib/"
},
"scripts": {
"lint": "eslint . --ext .ts",
"postinstall": "(tsc --build tsconfig.browser.json && tsc --build) || true",
"build": "tsc --build tsconfig.browser.json && tsc --build",
"build": "tsc --build tsconfig.browser.json && tsc --build && tsc --build tsconfig.cjs.json",
"compile": "tsc --noEmit",
"ts-check": "tsc --noEmit",
"test": "mocha -r esbuild-register test/*",

Просмотреть файл

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"types": ["mocha", "./lib/types"]
},
"include": ["./lib/**/*", "./server.ts"],
"exclude": ["dist", "node_modules"]
}

Просмотреть файл

@ -0,0 +1,7 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"outDir": "./dist/server/cjs",
},
}

Просмотреть файл

@ -1,11 +1,6 @@
{
"extends": "../../tsconfig.base.json",
"extends": "./tsconfig.base.json",
"compilerOptions": {
"module": "commonjs",
"composite": true,
"outDir": "./dist/server",
"types": ["mocha", "./lib/types"]
"outDir": "./dist/server/esm",
},
"include": ["./lib/**/*", "./server.ts"],
"exclude": ["dist", "node_modules"]
}

Просмотреть файл

@ -0,0 +1,16 @@
{
"sourceType": "unambiguous",
"presets": [
[
"@babel/preset-env",
{
"targets": {
"chrome": 100
}
}
],
"@babel/preset-typescript",
"@babel/preset-react"
],
"plugins": []
}

Просмотреть файл

@ -7,9 +7,18 @@ module.exports = {
'../lib/senders/emails/**/*.stories.tsx',
'../lib/senders/emails/**/*.stories.ts',
],
staticDirs: process.env.STORYBOOK_BUILD !== 'true' ? ['..'] : undefined,
addons: [
'@storybook/addon-docs',
'@storybook/addon-controls',
'@storybook/addon-toolbars',
],
core: {
builder: 'webpack5',
},
framework: {
name: '@storybook/html-webpack5',
options: {},
},
features: { storyStoreV7: false },
};

Просмотреть файл

@ -39,8 +39,8 @@
"audit-orphaned-stripe-accounts": "CONFIG_FILES='config/secrets.json' node -r esbuild-register ./scripts/audit-orphaned-customers.ts",
"remove-unverified-accounts": "CONFIG_FILES='config/secrets.json' node -r esbuild-register ./scripts/remove-unverified-accounts.ts",
"emails-scss": "node -r esbuild-register ./lib/senders/emails/sass-compile-files.ts",
"storybook": "yarn l10n-prime && yarn install-ejs && yarn emails-scss && NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 6010 --no-version-updates -s ./",
"build-storybook": "yarn l10n-prime && yarn install-ejs && yarn emails-scss && NODE_OPTIONS=--openssl-legacy-provider build-storybook && yarn build-storybook-copy-locales && yarn build-storybook-copy-templates",
"storybook": "yarn l10n-prime && yarn install-ejs && yarn emails-scss && NODE_OPTIONS=--openssl-legacy-provider storybook dev -p 6010 --no-version-updates ",
"build-storybook": "yarn l10n-prime && yarn install-ejs && yarn emails-scss && NODE_OPTIONS=--openssl-legacy-provider STORYBOOK_BUILD=true storybook build && yarn build-storybook-copy-locales && yarn build-storybook-copy-templates",
"build-storybook-copy-locales": "mkdir -p ./storybook-static/public/locales && cp -R ./public/locales ./storybook-static/public",
"build-storybook-copy-templates": "mkdir -p ./storybook-static/lib/senders/emails/templates && cp -R ./lib/senders/emails ./storybook-static/lib/senders",
"install-ejs": "./scripts/install-ejs.sh",
@ -141,12 +141,17 @@
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@storybook/addon-controls": "^6.5.9",
"@storybook/addon-docs": "^6.5.10",
"@storybook/addon-toolbars": "^6.3.12",
"@storybook/html": "^6.5.10",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@storybook/addon-controls": "^7.0.23",
"@storybook/addon-docs": "^7.0.23",
"@storybook/addon-toolbars": "^7.0.23",
"@storybook/html": "^7.0.23",
"@storybook/html-webpack5": "^7.0.23",
"@types/async-retry": "^1",
"@types/babel__core": "7.1.14",
"@types/babel__preset-env": "^7",
"@types/chai": "^4.2.18",
"@types/chai-as-promised": "^7",
"@types/dedent": "^0",
@ -163,6 +168,8 @@
"@types/node": "^18.14.2",
"@types/node-zendesk": "^2.0.2",
"@types/nodemailer": "^6.4.2",
"@types/react": "^18",
"@types/react-dom": "^18",
"@types/request": "2.48.5",
"@types/safe-regex": "1.1.2",
"@types/sass": "^1",
@ -172,7 +179,7 @@
"acorn": "^8.8.0",
"async-retry": "^1.3.3",
"audit-filter": "^0.5.0",
"babel-loader": "^8.3.0",
"babel-loader": "^9.1.2",
"binary-split": "1.0.5",
"chai": "^4.3.6",
"chai-as-promised": "^7.1.1",
@ -205,16 +212,19 @@
"pm2": "^5.3.0",
"prettier": "^2.3.1",
"proxyquire": "^2.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"read": "2.1.0",
"rimraf": "^5.0.0",
"sass": "^1.59.2",
"simplesmtp": "0.3.35",
"sinon": "^9.0.3",
"storybook": "^7.0.23",
"through": "2.3.8",
"type-fest": "^3.12.0",
"typesafe-node-firestore": "^1.4.1",
"typescript": "^4.9.3",
"webpack": "^4.43.0",
"webpack": "^5.84.1",
"webpack-watch-files-plugin": "^1.2.1",
"ws": "^8.11.0"
},

Просмотреть файл

@ -53,7 +53,7 @@
"@sentry/node": "^6.19.1",
"asmcrypto.js": "^0.22.0",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.3.0",
"babel-loader": "^9.1.2",
"backbone": "^1.4.1",
"backbone.cocktail": "0.5.15",
"base32-decode": "1.0.0",

Просмотреть файл

@ -34,6 +34,8 @@ module.exports = function (config) {
config.get('pairing.server_base_uri')
);
const PAIRING_SERVER_HTTP = PAIRING_SERVER_WEBSOCKET.replace(/^ws/, 'http');
// We need this for Webpack 5
const WEBPACK_DEV_SERVER = 'ws://localhost:3000';
const SENTRY_SERVER = 'https://*.sentry.io';
const GOOGLE_AUTH = 'https://accounts.google.com';
const APPLE_AUTH = 'https://appleid.apple.com';
@ -69,6 +71,7 @@ module.exports = function (config) {
const styleSrc = addCdnRuleIfRequired([SELF]);
if (config.get('env') === 'development') {
connectSrc.push(config.get('public_url').replace(/^http/, 'ws'));
connectSrc.push(WEBPACK_DEV_SERVER);
scriptSrc.push("'unsafe-inline'");
styleSrc.push("'unsafe-inline'");
}

Просмотреть файл

@ -42,7 +42,7 @@ suite.tests['blockingRules'] = function () {
assert.isFalse(reportOnly);
const connectSrc = directives.connectSrc;
assert.lengthOf(connectSrc, config.get('env') === 'development' ? 10 : 9);
assert.lengthOf(connectSrc, config.get('env') === 'development' ? 11 : 10);
assert.include(connectSrc, Sources.AUTH_SERVER);
assert.include(connectSrc, Sources.GLEAN_SERVER);
assert.include(connectSrc, Sources.GQL_SERVER);

Просмотреть файл

@ -2,10 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const {
permitAdditionalJSImports,
} = require('fxa-react/configs/rescripts');
const { permitAdditionalJSImports } = require('fxa-react/configs/rescripts');
module.exports = [
permitAdditionalJSImports,
];
module.exports = [permitAdditionalJSImports];

Просмотреть файл

@ -5,17 +5,19 @@
module.exports = {
stories: ['../src/**/*.stories.tsx'],
staticDirs: ['../public'],
core: {
builder: 'webpack5',
},
addons: [
'@storybook/preset-create-react-app',
'@storybook/addon-styling',
{
name: '@storybook/addon-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
{
name: 'storybook-addon-mock/register',
name: 'storybook-addon-mock',
},
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
features: { storyStoreV7: false },
};

Просмотреть файл

@ -2,4 +2,48 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = require('fxa-react/configs/storybooks').customizeWebpackConfig;
const path = require('path');
const customizeWebpackConfig =
require('fxa-react/configs/storybooks').customizeWebpackConfig;
const webpack5Fallbacks = {
stream: false,
timers: false,
http: false,
https: false,
zlib: false,
};
const includeSrcForSvgs = ({ config }) => {
const customizedConfig = customizeWebpackConfig({ config });
return {
...customizedConfig,
resolve: {
...customizedConfig.resolve,
fallback: {
...(customizedConfig.resolve.fallback || {}),
...webpack5Fallbacks,
},
},
module: {
...customizedConfig.module,
rules: [
{
oneOf: customizedConfig.module.rules[0]['oneOf'].map((x) => {
if (x.test && x.test.test && x.test.test('.scss')) {
return {
...x,
include: [path.resolve('../src')],
};
}
return x;
}),
},
],
},
};
};
module.exports = includeSrcForSvgs;

Просмотреть файл

@ -23,8 +23,8 @@
"test-unit": "yarn build && yarn merge-ftl-test && JEST_JUNIT_OUTPUT_FILE=../../artifacts/tests/$npm_package_name/jest-unit.xml jest --coverage --runInBand --logHeapUsage --verbose --config server/jest.config.js --forceExit -t '^(?!.*?#integration).*' --ci --reporters=default --reporters=jest-junit",
"test-integration": "yarn build && yarn merge-ftl-test && JEST_JUNIT_OUTPUT_FILE=../../artifacts/tests/$npm_package_name/jest-integration.xml SKIP_PREFLIGHT_CHECK=true PUBLIC_URL=/ INLINE_RUNTIME_CHUNK=false rescripts test --watchAll=false --ci --reporters=default --reporters=jest-junit",
"format": "prettier --write --config ../../_dev/.prettierrc '**'",
"storybook": "NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 6006",
"build-storybook": "yarn merge-ftl && NODE_ENV=production yarn build-css && NODE_OPTIONS=--openssl-legacy-provider build-storybook && cp -r public/images storybook-static/ && cp -r public/locales ./storybook-static/locales",
"storybook": "NODE_OPTIONS=--openssl-legacy-provider storybook dev -p 6006",
"build-storybook": "yarn merge-ftl && NODE_ENV=production yarn build-css && NODE_OPTIONS=--openssl-legacy-provider storybook build && cp -r public/images storybook-static/ && cp -r public/locales ./storybook-static/locales",
"merge-ftl": "yarn l10n-prime && grunt merge-ftl && yarn l10n-bundle",
"merge-ftl-test": "yarn l10n-prime && grunt merge-ftl:test",
"watch-ftl": "grunt watch-ftl"
@ -51,19 +51,25 @@
},
"homepage": "https://github.com/mozilla/fxa/tree/main/packages/fxa-payments-server#README.md",
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/core": "^7.22.5",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@fluent/bundle": "^0.18.0",
"@fluent/langneg": "^0.6.2",
"@fluent/react": "^0.13.1",
"@fluent/react": "^0.14.1",
"@rescripts/cli": "~0.0.16",
"@storybook/addon-actions": "^6.5.9",
"@storybook/addon-links": "^6.5.9",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/addons": "^6.5.9",
"@storybook/react": "^6.5.9",
"@storybook/addon-actions": "^7.0.23",
"@storybook/addon-links": "^7.0.23",
"@storybook/addon-styling": "^1.3.0",
"@storybook/addons": "^7.0.23",
"@storybook/preset-create-react-app": "^7.0.23",
"@storybook/react": "^7.0.23",
"@storybook/react-webpack5": "^7.0.23",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5",
"@types/accept-language-parser": "^1.5.1",
"@types/babel__preset-env": "^7",
"@types/classnames": "^2.3.1",
"@types/jest": "^26.0.23",
"@types/jsdom": "^16.2.11",
@ -78,7 +84,6 @@
"@types/sinon": "10.0.1",
"@types/storybook__addon-actions": "^5.2.1",
"@types/storybook__addon-links": "^5.2.1",
"@types/storybook__react": "^5.2.1",
"@types/superagent": "^4.1.11",
"@types/uuid": "^7.0.2",
"@types/webpack": "5.28.0",
@ -87,6 +92,7 @@
"audit-filter": "^0.5.0",
"autoprefixer": "^10.4.7",
"babel-eslint": "^10.1.0",
"babel-loader": "^9.1.2",
"browserslist": "^4.21.4",
"eslint": "^7.32.0",
"eslint-config-react-app": "^6.0.0",
@ -108,13 +114,14 @@
"prettier": "^2.3.1",
"redux-devtools-extension": "^2.13.9",
"sinon": "^9.0.3",
"storybook-addon-mock": "2.4.1",
"storybook": "^7.0.23",
"storybook-addon-mock": "4.0.0",
"supertest": "^6.3.0",
"tailwindcss": "^3.3.1",
"ts-jest": "^29.1.0",
"typescript": "^4.9.3",
"wait-for-expect": "^3.0.2",
"webpack": "^4.43.0"
"webpack": "^5.84.1"
},
"dependencies": {
"@sentry/browser": "^6.19.7",
@ -150,7 +157,7 @@
"react-dom": "^16.12.0",
"react-redux": "^8.0.2",
"react-router-dom": "^6.9.0",
"react-scripts": "^4.0.3",
"react-scripts": "^5.0.1",
"react-stripe-elements": "^6.1.2",
"react-test-renderer": "^17.0.2",
"react-transition-group": "^4.4.2",

Просмотреть файл

@ -43,8 +43,13 @@ module.exports = function (config) {
// CSP directives required for GA
// https://developers.google.com/tag-platform/tag-manager/csp#google_analytics_4_google_analytics
const GA_SCRIPT_SRC = 'https://*.googletagmanager.com';
const GA_IMG_SRC = 'https://*.google-analytics.com https://*.googletagmanager.com';
const GA_CONNECT_SRC = 'https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com';
const GA_IMG_SRC =
'https://*.google-analytics.com https://*.googletagmanager.com';
const GA_CONNECT_SRC =
'https://*.google-analytics.com https://*.analytics.google.com https://*.googletagmanager.com';
// We need this for Webpack 5
const WEBPACK_DEV_SERVER = 'ws://localhost:3032';
//
// Double quoted values
@ -100,7 +105,7 @@ module.exports = function (config) {
scriptSrc: addCdnRuleIfRequired([
SELF,
STRIPE_SCRIPT_URL,
PAYPAL_SCRIPT_URL
PAYPAL_SCRIPT_URL,
]),
styleSrc: addCdnRuleIfRequired([SELF, UNSAFE_INLINE]),
},
@ -131,6 +136,7 @@ module.exports = function (config) {
if (config.get('env') === 'development') {
rules.directives.connectSrc.push(HOT_RELOAD_WEBSOCKET);
rules.directives.connectSrc.push(WEBPACK_DEV_SERVER);
}
// If GA is enabled, add directives
@ -141,8 +147,8 @@ module.exports = function (config) {
Object.assign(rules.Sources, {
GA_CONNECT_SRC,
GA_IMG_SRC,
GA_SCRIPT_SRC
})
GA_SCRIPT_SRC,
});
}
return rules;

Просмотреть файл

@ -100,11 +100,11 @@ export const App = ({
return (
<AppContext.Provider value={appContextValue}>
<Head />
<AppLocalizationProvider
userLocales={navigatorLanguages}
bundles={['payments', 'react']}
>
<Head />
<Localized id="document" attrs={{ title: true }}>
<AppErrorBoundary>
<StripeProvider apiKey={config.stripe.apiKey}>

Просмотреть файл

@ -1,13 +1,14 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { AlertBar } from './index';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
it('renders as expected', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AlertBar
dataTestId="children"
headerId="alert-bar-header"
@ -25,7 +26,7 @@ it('renders as expected', () => {
});
it('renders success alert', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AlertBar
className="alert-success"
dataTestId="children"
@ -42,7 +43,7 @@ it('renders success alert', () => {
});
it('renders error alert', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AlertBar
className="alert-error"
dataTestId="children"
@ -57,7 +58,7 @@ it('renders error alert', () => {
});
it('renders newsletter error alert', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AlertBar
className="alert-newsletter-error"
dataTestId="children"
@ -72,7 +73,7 @@ it('renders newsletter error alert', () => {
});
it('renders pending alert', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AlertBar
className="alert-pending"
dataTestId="children"

Просмотреть файл

@ -8,6 +8,7 @@ import AppLayout, { SignInLayout, SettingsLayout } from './index';
import TermsAndPrivacy from '../TermsAndPrivacy';
import { SELECTED_PLAN } from '../../lib/mock-data';
import { RawMetadata } from 'fxa-shared/subscriptions/types';
import AppLocalizationProvider from 'fxa-react/lib/AppLocalizationProvider';
afterEach(cleanup);
@ -22,11 +23,13 @@ describe('AppLayout', () => {
const subject = () => {
return render(
<AppContext.Provider value={defaultAppContext}>
<AppLayout>
<div data-testid="children">
<TermsAndPrivacy plan={SELECTED_PLAN} />
</div>
</AppLayout>
<AppLocalizationProvider messages={{ en: ['testo: lol'] }}>
<AppLayout>
<div data-testid="children">
<TermsAndPrivacy plan={SELECTED_PLAN} />
</div>
</AppLayout>
</AppLocalizationProvider>
</AppContext.Provider>
);
};
@ -80,9 +83,11 @@ describe('SettingsLayout', () => {
return render(
<AppContext.Provider value={appContextValue}>
<SettingsLayout>
<div data-testid="children">Testing</div>
</SettingsLayout>
<AppLocalizationProvider messages={{ en: ['testo: lol'] }}>
<SettingsLayout>
<div data-testid="children">Testing</div>
</SettingsLayout>
</AppLocalizationProvider>
</AppContext.Provider>
);
};

Просмотреть файл

@ -1,10 +1,4 @@
import {
render,
screen,
cleanup,
fireEvent,
waitFor,
} from '@testing-library/react';
import { screen, cleanup, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { CouponForm, checkPromotionCode } from './index';
import { CouponDetails } from 'fxa-shared/dto/auth/payments/coupon';
@ -39,6 +33,7 @@ jest.mock('../../lib/apiClient', () => {
// eslint-disable-next-line import/first
import { APIError, apiRetrieveCouponDetails } from '../../lib/apiClient';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
beforeEach(() => {
(apiRetrieveCouponDetails as jest.Mock)
@ -57,7 +52,7 @@ describe('CouponForm', () => {
describe('CouponForm component', () => {
it('renders as expected', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<CouponForm
planId={SELECTED_PLAN.plan_id}
coupon={undefined}
@ -89,7 +84,7 @@ describe('CouponForm', () => {
maximallyRedeemed: false,
};
const subject = () => {
return render(
return renderWithLocalizationProvider(
<CouponForm
planId={SELECTED_PLAN.plan_id}
coupon={coupon}
@ -113,7 +108,7 @@ describe('CouponForm', () => {
.mockResolvedValue(COUPON_DETAILS_INVALID);
const mockSetCoupon = jest.fn();
const subject = () => {
return render(
return renderWithLocalizationProvider(
<CouponForm
planId={SELECTED_PLAN.plan_id}
coupon={undefined}
@ -171,7 +166,7 @@ describe('CouponForm', () => {
maximallyRedeemed: false,
};
const subject = () => {
return render(
return renderWithLocalizationProvider(
<CouponForm
planId={SELECTED_PLAN.plan_id}
coupon={coupon}
@ -189,7 +184,7 @@ describe('CouponForm', () => {
it('has the input and buttons disabled during processing of a subscription', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<CouponForm
planId={SELECTED_PLAN.plan_id}
coupon={undefined}
@ -218,7 +213,7 @@ describe('CouponForm', () => {
maximallyRedeemed: false,
};
const subject = () => {
return render(
return renderWithLocalizationProvider(
<CouponForm
planId={SELECTED_PLAN.plan_id}
coupon={coupon}
@ -250,7 +245,7 @@ describe('CouponForm', () => {
.mockResolvedValue(coupon);
const subject = () => {
return render(
return renderWithLocalizationProvider(
<AppContext.Provider
value={{
...defaultAppContext,
@ -284,7 +279,7 @@ describe('CouponForm', () => {
.mockResolvedValue(COUPON_DETAILS_INVALID);
const subject = () => {
return render(
return renderWithLocalizationProvider(
<AppContext.Provider
value={{
...defaultAppContext,

Просмотреть файл

@ -3,31 +3,40 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import { cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { DialogMessage } from './index';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
it('renders as expected', () => {
const onDismiss = jest.fn();
const ariaLabelledBy = "message-header";
const ariaDescribedBy = "message-description";
const { queryByTestId } = render(
const ariaLabelledBy = 'message-header';
const ariaDescribedBy = 'message-description';
const { queryByTestId } = renderWithLocalizationProvider(
<DialogMessage
onDismiss={onDismiss}
headerId={ariaLabelledBy}
descId={ariaDescribedBy}
>
<div id={ariaLabelledBy} data-testid="children">Message for Mom</div>
<div id={ariaLabelledBy} data-testid="children">
Message for Mom
</div>
<p id={ariaDescribedBy}>Hi mom</p>
</DialogMessage>
);
expect(queryByTestId('dialog-message-container')).toHaveClass('blocker');
expect(queryByTestId('children')).toBeInTheDocument();
expect(queryByTestId('dialog-message-information')).toHaveAttribute('aria-labelledby', ariaLabelledBy);
expect(queryByTestId('dialog-message-information')).toHaveAttribute('aria-describedby', ariaDescribedBy);
expect(queryByTestId('dialog-message-information')).toHaveAttribute(
'aria-labelledby',
ariaLabelledBy
);
expect(queryByTestId('dialog-message-information')).toHaveAttribute(
'aria-describedby',
ariaDescribedBy
);
expect(queryByTestId('dialog-message-information')).toHaveAttribute(
'role',
'dialog'
@ -36,16 +45,18 @@ it('renders as expected', () => {
it('accepts an alternate className', () => {
const onDismiss = jest.fn();
const ariaLabelledBy = "barquux-message-header";
const ariaDescribedBy = "barquux-message-description";
const { queryByTestId } = render(
const ariaLabelledBy = 'barquux-message-header';
const ariaDescribedBy = 'barquux-message-description';
const { queryByTestId } = renderWithLocalizationProvider(
<DialogMessage
onDismiss={onDismiss}
className="barquux"
headerId={ariaLabelledBy}
descId={ariaDescribedBy}
>
<div id={ariaLabelledBy} data-testid="children">Message for Mom</div>
<div id={ariaLabelledBy} data-testid="children">
Message for Mom
</div>
<p id={ariaDescribedBy}>Hi mom</p>
</DialogMessage>
);
@ -54,15 +65,17 @@ it('accepts an alternate className', () => {
it('calls onDismiss on click outside', () => {
const onDismiss = jest.fn();
const ariaLabelledBy = "dismiss-message-header";
const ariaDescribedBy = "dismiss-message-description";
const { container, getByTestId } = render(
const ariaLabelledBy = 'dismiss-message-header';
const ariaDescribedBy = 'dismiss-message-description';
const { container, getByTestId } = renderWithLocalizationProvider(
<DialogMessage
onDismiss={onDismiss}
headerId={ariaLabelledBy}
descId={ariaDescribedBy}
>
<div id={ariaLabelledBy} data-testid="children">Message for Mom</div>
<div id={ariaLabelledBy} data-testid="children">
Message for Mom
</div>
<p id={ariaDescribedBy}>Hi mom</p>
</DialogMessage>
);
@ -73,11 +86,13 @@ it('calls onDismiss on click outside', () => {
});
it('hides the close button when onDismiss is not supplied', () => {
const ariaLabelledBy = "dismiss-header";
const ariaDescribedBy = "dismiss-description";
const { queryByTestId } = render(
const ariaLabelledBy = 'dismiss-header';
const ariaDescribedBy = 'dismiss-description';
const { queryByTestId } = renderWithLocalizationProvider(
<DialogMessage headerId={ariaLabelledBy} descId={ariaDescribedBy}>
<div id={ariaLabelledBy} data-testid="children">Message for Mom</div>
<div id={ariaLabelledBy} data-testid="children">
Message for Mom
</div>
<p id={ariaDescribedBy}>Hi mom</p>
</DialogMessage>
);

Просмотреть файл

@ -3,11 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Header from './index';
import { Profile } from '../../store/types';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
let userProfile: Profile = {
avatar: './avatar.svg',
@ -26,7 +27,9 @@ afterEach(cleanup);
describe('Header', () => {
it('renders as expected', () => {
const subject = () => {
return render(<Header {...{ profile: userProfile }} />);
return renderWithLocalizationProvider(
<Header {...{ profile: userProfile }} />
);
};
const { queryByTestId } = subject();
@ -35,10 +38,10 @@ describe('Header', () => {
expect(avatar).toHaveAttribute('alt', userProfile.displayName);
});
it('renders without profile', () => {
it('renderWithLocalizationProviders without profile', () => {
userProfile.displayName = null;
const subject = () => {
return render(<Header />);
return renderWithLocalizationProvider(<Header />);
};
const { queryByTestId } = subject();
@ -50,7 +53,9 @@ describe('Header', () => {
it('alt falls back to email is displayName is null', () => {
userProfile.displayName = null;
const subject = () => {
return render(<Header {...{ profile: userProfile }} />);
return renderWithLocalizationProvider(
<Header {...{ profile: userProfile }} />
);
};
const { queryByTestId } = subject();

Просмотреть файл

@ -1,19 +1,24 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { LoadingOverlay } from './index';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
it('renders as null isLoading=false', () => {
const { queryByTestId } = render(<LoadingOverlay isLoading={false} />);
const { queryByTestId } = renderWithLocalizationProvider(
<LoadingOverlay isLoading={false} />
);
const result = queryByTestId('loading-overlay');
expect(result).not.toBeInTheDocument();
});
it('renders as expected when isLoading=true', () => {
const { queryByTestId } = render(<LoadingOverlay isLoading={true} />);
const { queryByTestId } = renderWithLocalizationProvider(
<LoadingOverlay isLoading={true} />
);
const result = queryByTestId('loading-overlay');
expect(result).toBeInTheDocument();
});

Просмотреть файл

@ -1,13 +1,14 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { LoadingSpinner } from './index';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
it('renders as expected', () => {
const { queryByTestId } = render(<LoadingSpinner />);
const { queryByTestId } = renderWithLocalizationProvider(<LoadingSpinner />);
const result = queryByTestId('loading-spinner');
expect(result).toBeInTheDocument();
});

Просмотреть файл

@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { fireEvent, render, waitFor } from '@testing-library/react';
import { fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { apiFetchAccountStatus } from '../../lib/apiClient';
import {
@ -9,6 +9,7 @@ import {
} from './index';
import { Localized } from '@fluent/react';
import { CheckoutType } from 'fxa-shared/subscriptions/types';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
jest.mock('../../lib/apiClient', () => ({
apiFetchAccountStatus: jest.fn(),
@ -75,7 +76,7 @@ const WrapNewUserEmailForm = ({
describe('NewUserEmailForm test', () => {
it('renders as expected', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}
@ -110,7 +111,7 @@ describe('NewUserEmailForm test', () => {
it('renders as expected, with metadata configuration', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}
@ -135,7 +136,7 @@ describe('NewUserEmailForm test', () => {
it('shows error when invalid email is input to first field', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}
@ -156,7 +157,7 @@ describe('NewUserEmailForm test', () => {
it('shows no error when valid email is input to first field', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}
@ -176,7 +177,7 @@ describe('NewUserEmailForm test', () => {
it('shows no error when empty string is provided to second field', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}
@ -200,7 +201,7 @@ describe('NewUserEmailForm test', () => {
it('shows error when emails do not match', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}
@ -225,7 +226,7 @@ describe('NewUserEmailForm test', () => {
it('shows no error when emails match', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<WrapNewUserEmailForm
accountExistsReturnValue={false}
invalidDomain={false}

Просмотреть файл

@ -1,11 +1,12 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { PaypalButton, PaypalButtonProps } from './index';
import { PickPartial } from '../../lib/types';
import { CUSTOMER, PLAN } from '../../lib/mock-data';
import { CheckoutType } from 'fxa-shared/subscriptions/types';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
const Subject = ({
disabled = false,
@ -52,7 +53,9 @@ describe('PaypalButton', () => {
it("Doesn't render the PayPal button if the PayPal script fails to load", async () => {
// The script is loaded in this button's consumer (e.g. SubscriptionCreate), so we
// can guarantee that it won't be loaded for the button in isolation
render(<Subject checkoutType={CheckoutType.WITH_ACCOUNT} />);
renderWithLocalizationProvider(
<Subject checkoutType={CheckoutType.WITH_ACCOUNT} />
);
expect(screen.queryByTestId('paypal-button')).not.toBeInTheDocument();
});
});

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import TestRenderer from 'react-test-renderer';
@ -9,7 +9,12 @@ import {
getLocalizedDateString,
} from '../../lib/formats';
import { Customer, Plan } from '../../store/types';
import { MOCK_PLANS, getLocalizedMessage } from '../../lib/test-utils';
import {
MOCK_PLANS,
getLocalizedMessage,
renderWithLocalizationProvider,
withLocalizationProvider,
} from '../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
import AppContext, { defaultAppContext } from '../../lib/AppContext';
@ -109,7 +114,7 @@ afterEach(() => {
describe('PaymentConfirmation', () => {
it('renders as expected', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfile,
@ -137,7 +142,7 @@ describe('PaymentConfirmation', () => {
it('renders as expected with no display name', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfileNoDisplayName,
@ -160,7 +165,7 @@ describe('PaymentConfirmation', () => {
it('renders as expected with custom success button label text', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfile,
@ -189,7 +194,7 @@ describe('PaymentConfirmation', () => {
it('renders as expected with custom success button label text localized to xx-pirate', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<AppContext.Provider
value={{ ...defaultAppContext, navigatorLanguages: ['xx-pirate'] }}
>
@ -227,7 +232,7 @@ describe('PaymentConfirmation', () => {
},
});
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfile,
@ -260,7 +265,7 @@ describe('PaymentConfirmation', () => {
},
});
const subject = () => {
return render(
return renderWithLocalizationProvider(
<AppContext.Provider
value={{ ...defaultAppContext, navigatorLanguages: ['fy-NL'] }}
>
@ -292,7 +297,7 @@ describe('PaymentConfirmation', () => {
it('renders with the invoice total amount when an invoice is present', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<AppContext.Provider value={{ ...defaultAppContext }}>
<PaymentConfirmation
{...{
@ -314,7 +319,7 @@ describe('PaymentConfirmation', () => {
it('renders without Order details if not available', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfile,
@ -335,7 +340,7 @@ describe('PaymentConfirmation', () => {
describe('When payment_provider is "paypal"', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfile,
@ -362,7 +367,7 @@ describe('PaymentConfirmation', () => {
describe('When payment_provider is "stripe"', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentConfirmation
{...{
profile: userProfile,
@ -413,7 +418,7 @@ describe('PaymentConfirmation', () => {
};
const testRenderer = TestRenderer.create(
<PaymentConfirmation {...props} />
withLocalizationProvider(<PaymentConfirmation {...props} />)
);
const testInstance = testRenderer.root;

Просмотреть файл

@ -1,11 +1,15 @@
import '@testing-library/jest-dom/extend-expect';
import { act, cleanup, fireEvent, render } from '@testing-library/react';
import { act, cleanup, fireEvent } from '@testing-library/react';
import React from 'react';
import TestRenderer from 'react-test-renderer';
import { PaymentConsentCheckbox } from '.';
import { MOCK_PLANS } from '../../lib/test-utils';
import {
MOCK_PLANS,
renderWithLocalizationProvider,
withLocalizationProvider,
} from '../../lib/test-utils';
import useValidatorState from '../../lib/validator';
import { Plan } from '../../store/types';
import { Form } from '../fields';
@ -39,7 +43,9 @@ describe('components/PaymentConsentCheckbox', () => {
const plan_id = 'plan_daily';
const plan = findMockPlan(plan_id);
const props = { plan };
const { findByTestId } = render(<WrapCheckbox {...props} />);
const { findByTestId } = renderWithLocalizationProvider(
<WrapCheckbox {...props} />
);
const checkbox = await findByTestId('confirm');
expect(checkbox).toBeVisible();
@ -53,7 +59,9 @@ describe('components/PaymentConsentCheckbox', () => {
plan,
onClick: onClickSpy,
};
const { findByTestId } = render(<WrapCheckbox {...props} />);
const { findByTestId } = renderWithLocalizationProvider(
<WrapCheckbox {...props} />
);
const checkbox = await findByTestId('confirm');
await act(async () => {
@ -68,7 +76,9 @@ describe('components/PaymentConsentCheckbox', () => {
function runTests(plan: Plan, expectedMsgId: string) {
const props = { plan };
const testRenderer = TestRenderer.create(<WrapCheckbox {...props} />);
const testRenderer = TestRenderer.create(
withLocalizationProvider(<WrapCheckbox {...props} />)
);
const testInstance = testRenderer.root;
const legalCheckbox = testInstance.findByProps({ id: expectedMsgId });

Просмотреть файл

@ -3,6 +3,7 @@ import { PaymentErrorView } from './index';
import { SELECTED_PLAN } from '../../lib/mock-data';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Meta } from '@storybook/react';
import AppLocalizationProvider from 'fxa-react/lib/AppLocalizationProvider';
export default {
title: 'components/PaymentError',
@ -11,20 +12,25 @@ export default {
const storyWithProps = () => {
const story = () => (
<BrowserRouter>
<Routes>
<Route
path="*"
element={
<PaymentErrorView
error={{ code: 'general_paypal_error' }}
actionFn={() => {}}
plan={SELECTED_PLAN}
/>
}
/>
</Routes>
</BrowserRouter>
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<BrowserRouter>
<Routes>
<Route
path="*"
element={
<PaymentErrorView
error={{ code: 'general_paypal_error' }}
actionFn={() => {}}
plan={SELECTED_PLAN}
/>
}
/>
</Routes>
</BrowserRouter>
</AppLocalizationProvider>
);
return story;

Просмотреть файл

@ -1,7 +1,10 @@
import React from 'react';
import { render, cleanup, fireEvent, act } from '@testing-library/react';
import { cleanup, fireEvent, act } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { getLocalizedMessage } from '../../lib/test-utils';
import {
getLocalizedMessage,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
@ -27,7 +30,7 @@ describe('PaymentErrorView test with l10n', () => {
});
it('renders as expected', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<PaymentErrorView
actionFn={() => {}}
error={{ code: 'general_paypal_error' }}
@ -54,7 +57,7 @@ describe('PaymentErrorView test with l10n', () => {
it('calls passed onRetry function when retry button clicked', async () => {
const onRetry = jest.fn();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<PaymentErrorView
actionFn={onRetry}
error={{ code: 'general_paypal_error' }}
@ -70,7 +73,7 @@ describe('PaymentErrorView test with l10n', () => {
});
it('navigates to the correct relative URL when the "Manage my subscription" button is clicked', async () => {
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<PaymentErrorView
actionFn={() => {}}
error={{ code: 'no_subscription_change' }}
@ -86,7 +89,7 @@ describe('PaymentErrorView test with l10n', () => {
});
it('uses the given SubscriptionTitle', async () => {
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<PaymentErrorView
subscriptionTitle={<SubscriptionTitle screenType={'noplanchange'} />}
actionFn={() => {}}
@ -107,7 +110,7 @@ describe('PaymentErrorView test with l10n', () => {
});
it('does not render the ActionButton for post-subscription creation errors', async () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<PaymentErrorView
actionFn={() => {}}
error={{ code: 'fxa_fetch_profile_customer_error' }}
@ -129,7 +132,7 @@ describe('PaymentErrorView test with l10n', () => {
});
it('shows FxA legal links in footer when isPasswordlessCheckout is true', async () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<PaymentErrorView
actionFn={() => {}}
error={{ code: 'general_paypal_error' }}

Просмотреть файл

@ -1,11 +1,5 @@
import React from 'react';
import {
render,
cleanup,
act,
fireEvent,
queryByTestId,
} from '@testing-library/react';
import { cleanup, act, fireEvent, queryByTestId } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import waitForExpect from 'wait-for-expect';
@ -16,6 +10,7 @@ import {
mockStripeElementOnBlurFns,
elementChangeResponse,
MOCK_CUSTOMER,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import PaymentForm, { PaymentFormProps } from './index';
@ -68,7 +63,8 @@ const Subject = ({
};
it('renders all expected default fields and elements', () => {
const { container, queryAllByTestId, getByTestId } = render(<Subject />);
const { container, queryAllByTestId, getByTestId } =
renderWithLocalizationProvider(<Subject />);
expect(container.querySelector('button.cancel')).not.toBeInTheDocument();
expect(container.querySelector('span.spinner')).not.toBeInTheDocument();
@ -81,7 +77,7 @@ it('renders all expected default fields and elements', () => {
});
it('renders error tooltips for invalid stripe elements', () => {
const { getByTestId } = render(<Subject />);
const { getByTestId } = renderWithLocalizationProvider(<Subject />);
const mockErrors = {
cardElement: 'CARD BAD',
@ -109,7 +105,7 @@ it('renders error tooltips for invalid stripe elements', () => {
});
const renderWithValidFields = (props?: SubjectProps) => {
const renderResult = render(<Subject {...props} />);
const renderResult = renderWithLocalizationProvider(<Subject {...props} />);
const { getByTestId } = renderResult;
expect(getByTestId('submit')).toHaveClass('payment-button-disabled');
@ -155,12 +151,14 @@ it('when confirm = true, enables submit button when all fields are valid and che
});
it('omits the confirmation checkbox when confirm = false', () => {
const { queryByTestId } = render(<Subject {...{ confirm: false }} />);
const { queryByTestId } = renderWithLocalizationProvider(
<Subject {...{ confirm: false }} />
);
expect(queryByTestId('confirm')).not.toBeInTheDocument();
});
it('includes the confirmation checkbox when confirm = true and plan supplied', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject {...{ confirm: true, plan: SELECTED_PLAN }} />
);
expect(queryByTestId('confirm')).toBeInTheDocument();
@ -203,25 +201,31 @@ it('renders a progress spinner when submitted, disables further submission (issu
});
it('renders a progress spinner when inProgress = true', () => {
const { queryByTestId } = render(<Subject {...{ inProgress: true }} />);
const { queryByTestId } = renderWithLocalizationProvider(
<Subject {...{ inProgress: true }} />
);
expect(queryByTestId('loading-spinner')).toBeInTheDocument();
});
it('renders a progress spinner when inProgress = true and onCancel supplied', () => {
const onCancel = jest.fn();
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject {...{ inProgress: true, onCancel }} />
);
expect(queryByTestId('loading-spinner')).toBeInTheDocument();
});
it('includes the cancel button when onCancel supplied', () => {
const { queryByTestId } = render(<Subject {...{ onCancel: jest.fn() }} />);
const { queryByTestId } = renderWithLocalizationProvider(
<Subject {...{ onCancel: jest.fn() }} />
);
expect(queryByTestId('cancel')).toBeInTheDocument();
});
it('displays an error for empty name', () => {
const { getByText, getByTestId } = render(<Subject />);
const { getByText, getByTestId } = renderWithLocalizationProvider(
<Subject />
);
fireEvent.change(getByTestId('name'), { target: { value: '123' } });
fireEvent.change(getByTestId('name'), { target: { value: '' } });
fireEvent.blur(getByTestId('name'));
@ -269,7 +273,7 @@ it('does not call onSubmit if somehow submitted while in progress', async () =>
describe('with existing card', () => {
it('renders correctly', () => {
const { queryByTestId, queryByText } = render(
const { queryByTestId, queryByText } = renderWithLocalizationProvider(
<Subject customer={MOCK_CUSTOMER} plan={SELECTED_PLAN} />
);
expect(queryByTestId('card-logo-and-last-four')).toBeInTheDocument();
@ -281,14 +285,16 @@ describe('with existing card', () => {
it('renders the payment form for customer without subscriptions', () => {
const customer = { ...MOCK_CUSTOMER, subscriptions: [] };
const { queryByTestId } = render(<Subject customer={customer} />);
const { queryByTestId } = renderWithLocalizationProvider(
<Subject customer={customer} />
);
expect(queryByTestId('name')).toBeInTheDocument();
expect(queryByTestId('card-details')).not.toBeInTheDocument();
});
it('calls the submit handler', async () => {
const onSubmit = jest.fn();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<Subject
customer={MOCK_CUSTOMER}
plan={SELECTED_PLAN}
@ -304,7 +310,7 @@ describe('with existing card', () => {
describe('with existing PayPal billing agreement', () => {
it('renders correctly', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject
customer={{ ...MOCK_CUSTOMER, payment_provider: 'paypal' }}
plan={SELECTED_PLAN}
@ -316,7 +322,7 @@ describe('with existing PayPal billing agreement', () => {
it('calls the submit handler', async () => {
const onSubmit = jest.fn();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<Subject
customer={{ ...MOCK_CUSTOMER, payment_provider: 'paypal' }}
plan={SELECTED_PLAN}

Просмотреть файл

@ -1,10 +1,14 @@
import '@testing-library/jest-dom/extend-expect';
import { act, cleanup, fireEvent, render } from '@testing-library/react';
import { act, cleanup, fireEvent } from '@testing-library/react';
import React from 'react';
import { PaymentMethodHeader, PaymentMethodHeaderType } from '.';
import { getLocalizedMessage, MOCK_PLANS } from '../../lib/test-utils';
import {
getLocalizedMessage,
MOCK_PLANS,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
@ -19,7 +23,9 @@ describe('components/PaymentMethodHeader', () => {
it('render header without prefix', async () => {
const plan = MOCK_PLANS[0];
const props = { plan, onClick: () => {} };
const { queryByTestId } = render(<PaymentMethodHeader {...props} />);
const { queryByTestId } = renderWithLocalizationProvider(
<PaymentMethodHeader {...props} />
);
expect(queryByTestId('header')).toBeInTheDocument();
expect(queryByTestId('header-prefix')).not.toBeInTheDocument();
@ -32,7 +38,9 @@ describe('components/PaymentMethodHeader', () => {
onClick: () => {},
type: PaymentMethodHeaderType.SecondStep,
};
const { queryByTestId } = render(<PaymentMethodHeader {...props} />);
const { queryByTestId } = renderWithLocalizationProvider(
<PaymentMethodHeader {...props} />
);
expect(queryByTestId('header')).not.toBeInTheDocument();
expect(queryByTestId('header-prefix')).toBeInTheDocument();
@ -72,7 +80,9 @@ describe('components/PaymentMethodHeader', () => {
it('Checkbox renders as expected', async () => {
const plan = MOCK_PLANS[0];
const props = { plan, onClick: () => {} };
const { findByTestId } = render(<PaymentMethodHeader {...props} />);
const { findByTestId } = renderWithLocalizationProvider(
<PaymentMethodHeader {...props} />
);
const checkbox = await findByTestId('confirm');
expect(checkbox).toBeVisible();
@ -82,7 +92,9 @@ describe('components/PaymentMethodHeader', () => {
const plan = MOCK_PLANS[0];
const onClickSpy = jest.fn();
const props = { plan, onClick: onClickSpy };
const { findByTestId } = render(<PaymentMethodHeader {...props} />);
const { findByTestId } = renderWithLocalizationProvider(
<PaymentMethodHeader {...props} />
);
const checkbox = await findByTestId('confirm');
await act(async () => {
@ -99,7 +111,9 @@ describe('components/PaymentMethodHeader', () => {
const expectedMsg =
'I authorize Mozilla, maker of Firefox products, to charge my payment method for the amount shown, according to Terms of ServiceOpens in new window and Privacy NoticeOpens in new window, until I cancel my subscription.';
const { findByTestId } = render(<PaymentMethodHeader {...props} />);
const { findByTestId } = renderWithLocalizationProvider(
<PaymentMethodHeader {...props} />
);
const checkbox = await findByTestId('confirm');

Просмотреть файл

@ -1,7 +1,10 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { getLocalizedMessage } from '../../lib/test-utils';
import {
getLocalizedMessage,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
@ -15,7 +18,9 @@ describe('PaymentProcessing tests', () => {
});
it('renders as expected', () => {
const { queryByTestId } = render(<PaymentProcessing provider="paypal" />);
const { queryByTestId } = renderWithLocalizationProvider(
<PaymentProcessing provider="paypal" />
);
const subscriptionTitle = queryByTestId('subscription-processing-title');
expect(subscriptionTitle).toBeInTheDocument();

Просмотреть файл

@ -1,16 +1,17 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import PaymentProviderDetails from './index';
import * as Customers from '../../lib/mock-data';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
describe('PaymentProviderDetails', () => {
describe('payment_method === "stripe" and an expirationDate is provided', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentProviderDetails {...{ customer: Customers.CUSTOMER }} />
);
};
@ -30,7 +31,7 @@ describe('PaymentProviderDetails', () => {
describe('When payment_method === "paypal"', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PaymentProviderDetails {...{ customer: Customers.PAYPAL_CUSTOMER }} />
);
};

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, cleanup, fireEvent, waitFor } from '@testing-library/react';
import { cleanup, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import TestRenderer from 'react-test-renderer';
@ -13,7 +13,12 @@ import {
getLocalizedCurrency,
getLocalizedCurrencyString,
} from '../../lib/formats';
import { MOCK_PLANS, getLocalizedMessage } from '../../lib/test-utils';
import {
MOCK_PLANS,
getLocalizedMessage,
renderWithLocalizationProvider,
withLocalizationProvider,
} from '../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
import { updateConfig } from '../../lib/config';
@ -111,7 +116,7 @@ afterEach(() => {
describe('PlanDetails', () => {
it('renders as expected without tax', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -154,7 +159,7 @@ describe('PlanDetails', () => {
},
};
const subject = () => {
return render(<PlanDetails {...props} />);
return renderWithLocalizationProvider(<PlanDetails {...props} />);
};
const { queryByTestId } = subject();
@ -192,7 +197,7 @@ describe('PlanDetails', () => {
},
};
const subject = () => {
return render(<PlanDetails {...props} />);
return renderWithLocalizationProvider(<PlanDetails {...props} />);
};
const { queryByTestId } = subject();
@ -247,7 +252,7 @@ describe('PlanDetails', () => {
},
};
const subject = () => {
return render(<PlanDetails {...props} />);
return renderWithLocalizationProvider(<PlanDetails {...props} />);
};
const { queryByTestId } = subject();
@ -265,7 +270,7 @@ describe('PlanDetails', () => {
},
});
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -295,7 +300,7 @@ describe('PlanDetails', () => {
},
});
const subject = () => {
return render(
return renderWithLocalizationProvider(
<AppContext.Provider
value={{ ...defaultAppContext, navigatorLanguages: ['fy-NL'] }}
>
@ -326,7 +331,7 @@ describe('PlanDetails', () => {
});
it('renders product_name when product:name is not present', () => {
render(
renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -352,7 +357,7 @@ describe('PlanDetails', () => {
},
};
render(
renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -371,7 +376,7 @@ describe('PlanDetails', () => {
it('hides expand button when showExpandButton is false', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -393,7 +398,7 @@ describe('PlanDetails', () => {
it('shows and hides detail section when expand button is clicked', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -423,7 +428,7 @@ describe('PlanDetails', () => {
it('sets role to "complementary" when isMobile is false', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -445,7 +450,7 @@ describe('PlanDetails', () => {
it('does not set role to "complementary" when isMobile is true', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -464,7 +469,7 @@ describe('PlanDetails', () => {
it('does not show the coupon success message when there is no coupon used', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -507,7 +512,7 @@ describe('PlanDetails', () => {
};
const subject = () => {
return render(<PlanDetails {...props} />);
return renderWithLocalizationProvider(<PlanDetails {...props} />);
};
const { queryByTestId } = subject();
@ -535,7 +540,7 @@ describe('PlanDetails', () => {
it('for coupon info box without couponDurationDate, display coupon-success message', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -560,7 +565,7 @@ describe('PlanDetails', () => {
it('for coupon info box with couponDurationDate, display coupon-success-with-date message', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -586,7 +591,7 @@ describe('PlanDetails', () => {
it('do not show either coupon-success message, if info box is empty', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -609,7 +614,7 @@ describe('PlanDetails', () => {
it('show total for 100% coupon', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanDetails
{...{
profile: userProfile,
@ -661,7 +666,9 @@ describe('PlanDetails', () => {
},
};
const testRenderer = TestRenderer.create(<PlanDetails {...props} />);
const testRenderer = TestRenderer.create(
withLocalizationProvider(<PlanDetails {...props} />)
);
const testInstance = testRenderer.root;
const planPriceComponent = testInstance.findByProps({

Просмотреть файл

@ -7,6 +7,7 @@ import { PlanErrorDialog } from './index';
import { PLANS } from '../../lib/mock-data';
import { FetchState, Plan } from '../../store/types';
import { Meta } from '@storybook/react';
import AppLocalizationProvider from 'fxa-react/lib/AppLocalizationProvider';
export default {
title: 'components/PlanErrorDialog',
@ -25,7 +26,12 @@ const storyWithContext = (
storyName?: string
) => {
const story = () => (
<PlanErrorDialog locationReload={locationReload} plans={plans} />
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<PlanErrorDialog locationReload={locationReload} plans={plans} />
</AppLocalizationProvider>
);
if (storyName) story.storyName = storyName;

Просмотреть файл

@ -1,10 +1,11 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import PlanErrorDialog from './index';
import { PLANS } from '../../lib/mock-data';
import { FetchState, Plan } from '../../store/types';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
const locationReload = () => {};
const plans: FetchState<Plan[], any> = {
@ -18,7 +19,7 @@ afterEach(cleanup);
describe('PlanErrorDialog', () => {
it('renders as expected for no plan for product', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanErrorDialog
{...{
locationReload,
@ -34,7 +35,7 @@ describe('PlanErrorDialog', () => {
it('renders as expected for no selectedPlan', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PlanErrorDialog
{...{
locationReload,

Просмотреть файл

@ -1,7 +1,7 @@
import { PriceDetails } from '.';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
const defaultPriceDetailsProps = {
total: 2000,
@ -12,7 +12,9 @@ describe('PriceDetails', () => {
describe('PriceDetails component', () => {
it('renders NoInterval component', () => {
const subject = () => {
return render(<PriceDetails {...defaultPriceDetailsProps} />);
return renderWithLocalizationProvider(
<PriceDetails {...defaultPriceDetailsProps} />
);
};
const { queryByTestId } = subject();
@ -21,7 +23,7 @@ describe('PriceDetails', () => {
it('renders Interval component', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'month'}
@ -40,7 +42,9 @@ describe('PriceDetails', () => {
describe('NoInterval', () => {
it('renders without tax', () => {
const subject = () => {
return render(<PriceDetails {...defaultPriceDetailsProps} />);
return renderWithLocalizationProvider(
<PriceDetails {...defaultPriceDetailsProps} />
);
};
const { queryByTestId } = subject();
@ -50,7 +54,7 @@ describe('PriceDetails', () => {
it('renders with tax with showTax set', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
tax={300}
@ -68,7 +72,9 @@ describe('PriceDetails', () => {
it('renders with tax without showTax set', () => {
const subject = () => {
return render(<PriceDetails {...defaultPriceDetailsProps} tax={300} />);
return renderWithLocalizationProvider(
<PriceDetails {...defaultPriceDetailsProps} tax={300} />
);
};
const { queryByTestId } = subject();
@ -80,7 +86,7 @@ describe('PriceDetails', () => {
it('renders with tax, even though tax isnt provided', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails {...defaultPriceDetailsProps} showTax={true} />
);
};
@ -97,7 +103,7 @@ describe('PriceDetails', () => {
describe('Without tax', () => {
it('renders for one day', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'day'}
@ -114,7 +120,7 @@ describe('PriceDetails', () => {
it('renders for multiple days', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'day'}
@ -131,7 +137,7 @@ describe('PriceDetails', () => {
it('renders for one week', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'week'}
@ -148,7 +154,7 @@ describe('PriceDetails', () => {
it('renders for multiple weeks', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'week'}
@ -165,7 +171,7 @@ describe('PriceDetails', () => {
it('renders for one month', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'month'}
@ -182,7 +188,7 @@ describe('PriceDetails', () => {
it('renders for multiple months', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'month'}
@ -199,7 +205,7 @@ describe('PriceDetails', () => {
it('renders for one year', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'year'}
@ -216,7 +222,7 @@ describe('PriceDetails', () => {
it('renders for multiple years', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'year'}
@ -235,7 +241,7 @@ describe('PriceDetails', () => {
describe('With tax', () => {
it('renders for one day', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'day'}
@ -253,7 +259,7 @@ describe('PriceDetails', () => {
it('renders for multiple days', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'day'}
@ -273,7 +279,7 @@ describe('PriceDetails', () => {
it('renders for one week', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'week'}
@ -291,7 +297,7 @@ describe('PriceDetails', () => {
it('renders for multiple weeks', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'week'}
@ -311,7 +317,7 @@ describe('PriceDetails', () => {
it('renders for one month', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'month'}
@ -329,7 +335,7 @@ describe('PriceDetails', () => {
it('renders for multiple months', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'month'}
@ -349,7 +355,7 @@ describe('PriceDetails', () => {
it('renders for one year', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'year'}
@ -367,7 +373,7 @@ describe('PriceDetails', () => {
it('renders for multiple years', () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<PriceDetails
{...defaultPriceDetailsProps}
interval={'year'}

Просмотреть файл

@ -1,10 +1,13 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import SubscriptionTitle, { SubscriptionTitleProps, titles } from './index';
import { getLocalizedMessage } from '../../lib/test-utils';
import {
getLocalizedMessage,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle } from '@fluent/bundle';
@ -21,7 +24,9 @@ describe('SubscriptionTitle', () => {
});
it('renders as expected', async () => {
const subject = () => {
return render(<SubscriptionTitle {...defaultProps} />);
return renderWithLocalizationProvider(
<SubscriptionTitle {...defaultProps} />
);
};
const { findByTestId } = subject();
const component = await findByTestId('subscription-create-title');
@ -43,7 +48,9 @@ describe('SubscriptionTitle', () => {
it('renders as expected for SubscriptionSuccess', async () => {
const subject = () => {
return render(<SubscriptionTitle screenType="success" />);
return renderWithLocalizationProvider(
<SubscriptionTitle screenType="success" />
);
};
const { findByTestId } = subject();
const component = await findByTestId('subscription-success-title');
@ -65,7 +72,9 @@ describe('SubscriptionTitle', () => {
it('renders as expected for PaymentProcessing', async () => {
const subject = () => {
return render(<SubscriptionTitle screenType="processing" />);
return renderWithLocalizationProvider(
<SubscriptionTitle screenType="processing" />
);
};
const { findByTestId } = subject();
const component = await findByTestId('subscription-processing-title');
@ -87,7 +96,7 @@ describe('SubscriptionTitle', () => {
it('renders the subtitle as expected', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<SubscriptionTitle
{...{
screenType: 'noplanchange',

Просмотреть файл

@ -1,8 +1,11 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MOCK_PLANS } from '../../lib/test-utils';
import {
MOCK_PLANS,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { TermsAndPrivacy } from './index';
import { defaultAppContext, AppContext } from '../../lib/AppContext';
import { DEFAULT_PRODUCT_DETAILS } from 'fxa-shared/subscriptions/metadata';
@ -72,7 +75,7 @@ afterEach(() => {
});
it('renders as expected with a plan with no legal doc links metadata', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<TermsAndPrivacy plan={planWithNoLegalLinks} />
);
@ -99,7 +102,7 @@ it('renders as expected with a plan with no legal doc links metadata', () => {
});
it('renders as expected when passed "showFXALinks" option', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<TermsAndPrivacy plan={planWithNoLegalLinks} showFXALinks={true} />
);
@ -108,7 +111,9 @@ it('renders as expected when passed "showFXALinks" option', () => {
});
it('renders as expected with default locale', () => {
const { queryByTestId } = render(<TermsAndPrivacy plan={plan} />);
const { queryByTestId } = renderWithLocalizationProvider(
<TermsAndPrivacy plan={plan} />
);
const termsLink = queryByTestId('terms');
expect(termsLink).toBeInTheDocument();
expect(termsLink).toHaveAttribute('href', enTermsOfServiceURL);
@ -118,7 +123,7 @@ it('renders as expected with default locale', () => {
});
it('renders as expected with fr locale', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AppContext.Provider
value={{ ...defaultAppContext, navigatorLanguages: ['fr'] }}
>
@ -139,7 +144,7 @@ it('renders as expected with firestore config and default locale', () => {
useFirestoreProductConfigs: true,
},
});
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<TermsAndPrivacy plan={planWithConfiguration} />
);
const termsLink = queryByTestId('terms');
@ -156,7 +161,7 @@ it('renders as expected with firestore config and fr locale', () => {
useFirestoreProductConfigs: true,
},
});
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AppContext.Provider
value={{ ...defaultAppContext, navigatorLanguages: ['fr'] }}
>

Просмотреть файл

@ -1,5 +1,5 @@
import React, { useRef } from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import { cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { Omit } from '../../lib/types';
import ScreenInfo from '../../lib/screen-info';
@ -10,6 +10,7 @@ import {
MIN_HEIGHT_TO_SHOW_TOOLTIP_BELOW,
MIN_WIDTH_TO_SHOW_TOOLTIP_BELOW,
} from './index';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
const LABEL_TEXT = 'Valid frobnitz required.';
@ -56,7 +57,9 @@ const Subject = (props: SubjectProps) => {
};
it('renders children as label', () => {
const { queryByText } = render(<Subject>{LABEL_TEXT}</Subject>);
const { queryByText } = renderWithLocalizationProvider(
<Subject>{LABEL_TEXT}</Subject>
);
const result = queryByText(LABEL_TEXT);
expect(result).toBeInTheDocument();
expect(result).toHaveClass('tooltip');
@ -66,7 +69,7 @@ it('renders children as label', () => {
});
it('renders with expected id and class names', () => {
const { getByText } = render(
const { getByText } = renderWithLocalizationProvider(
<Subject id="xyzzy" extraClassNames="frobnitz">
{LABEL_TEXT}
</Subject>
@ -81,7 +84,7 @@ it('renders with expected id and class names', () => {
it('handles being dismissible', () => {
const onDismiss = jest.fn();
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject dismissible onDismiss={onDismiss}>
{LABEL_TEXT}
</Subject>
@ -93,14 +96,16 @@ it('handles being dismissible', () => {
});
it('throws no error if onDismiss was not supplied to dismissable', () => {
const { queryByTestId } = render(<Subject dismissible>{LABEL_TEXT}</Subject>);
const { queryByTestId } = renderWithLocalizationProvider(
<Subject dismissible>{LABEL_TEXT}</Subject>
);
const control = queryByTestId('dismiss-button');
expect(control).toBeInTheDocument();
fireEvent.click(control as Element);
});
it('displays label above with showBelow=false', () => {
const { getByText } = render(
const { getByText } = renderWithLocalizationProvider(
<Subject showBelow={false}>{LABEL_TEXT}</Subject>
);
const result = getByText(LABEL_TEXT);
@ -110,7 +115,7 @@ it('displays label above with showBelow=false', () => {
});
it('displays label above on short window', () => {
const { getByText } = render(
const { getByText } = renderWithLocalizationProvider(
<Subject clientHeight={MIN_HEIGHT_TO_SHOW_TOOLTIP_BELOW - 10}>
{LABEL_TEXT}
</Subject>
@ -122,7 +127,7 @@ it('displays label above on short window', () => {
});
it('overrides showBelow={true} on short window', () => {
const { getByText } = render(
const { getByText } = renderWithLocalizationProvider(
<Subject
showBelow={true}
clientHeight={MIN_HEIGHT_TO_SHOW_TOOLTIP_BELOW - 10}
@ -137,7 +142,7 @@ it('overrides showBelow={true} on short window', () => {
});
it('displays label above on narrow window', () => {
const { getByText } = render(
const { getByText } = renderWithLocalizationProvider(
<Subject
showBelow={true}
clientWidth={MIN_WIDTH_TO_SHOW_TOOLTIP_BELOW - 10}
@ -152,7 +157,7 @@ it('displays label above on narrow window', () => {
});
it('overrides showBelow={true} on narrow window', () => {
const { getByText } = render(
const { getByText } = renderWithLocalizationProvider(
<Subject
showBelow={true}
clientWidth={MIN_WIDTH_TO_SHOW_TOOLTIP_BELOW - 10}

Просмотреть файл

@ -1,5 +1,5 @@
import React, { useState, useCallback, useContext, useRef } from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import { cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import {
FieldGroup,
@ -19,13 +19,14 @@ import {
State as ValidatorState,
MiddlewareReducer as ValidatorMiddlewareReducer,
} from '../../lib/validator';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
describe('Form', () => {
it('renders a form that provides children with a validator', () => {
const validatorStateRef = mkValidatorStateRef();
const { container } = render(
const { container } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<Field fieldType="input" name="foo" label="This is a label">
<p>Hi mom</p>
@ -50,7 +51,7 @@ describe('Form', () => {
describe('FieldGroup', () => {
it('wraps children in className="input-row-group"', () => {
const { container } = render(
const { container } = renderWithLocalizationProvider(
<FieldGroup>
<div>Hi mom</div>
</FieldGroup>
@ -62,7 +63,7 @@ describe('FieldGroup', () => {
describe('Field', () => {
it('renders a label when available', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<TestForm>
<Field fieldType="input" name="foo" label="This is a label">
<p>Hi mom</p>
@ -101,7 +102,9 @@ describe('Field', () => {
</TestForm>
);
};
const { container, queryAllByTestId } = render(<Subject />);
const { container, queryAllByTestId } = renderWithLocalizationProvider(
<Subject />
);
queryAllByTestId('execute').forEach(fireEvent.click);
const tooltip = container.querySelector('aside.tooltip');
expect(tooltip).not.toBeNull();
@ -132,7 +135,9 @@ describe('Field', () => {
</TestForm>
);
};
const { container, queryAllByTestId } = render(<Subject />);
const { container, queryAllByTestId } = renderWithLocalizationProvider(
<Subject />
);
queryAllByTestId('execute').forEach(fireEvent.click);
const tooltip = container.querySelector('aside.tooltip');
expect(tooltip).toBeNull();
@ -140,7 +145,7 @@ describe('Field', () => {
it('registers a field with validator state', () => {
const validatorStateRef = mkValidatorStateRef();
render(
renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<Field fieldType="input" name="foo">
<p>Hi mom</p>
@ -175,7 +180,7 @@ describe('defaultInputValidator', () => {
describe('Input', () => {
it('considers an optional field without onValidate as always valid', () => {
const validatorStateRef = mkValidatorStateRef();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<Input
data-testid="input-1"
@ -193,7 +198,7 @@ describe('Input', () => {
it('enforces non-empty content in required fields', () => {
const validatorStateRef = mkValidatorStateRef();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<Input
data-testid="input-1"
@ -241,7 +246,7 @@ describe('Input', () => {
});
const validatorStateRef = mkValidatorStateRef();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<Input
data-testid="testInput"
@ -301,7 +306,7 @@ describe('Input', () => {
valid: false,
error: 'bad thing',
});
const { container, getByTestId } = render(
const { container, getByTestId } = renderWithLocalizationProvider(
<TestForm>
<Input
data-testid="testInput"
@ -352,7 +357,7 @@ describe('StripeElement', () => {
it('does nothing if field value is null', () => {
const MockStripeElement = buildMockStripeElement(null);
const validatorStateRef = mkValidatorStateRef();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement name="input-1" component={MockStripeElement} />
</TestForm>
@ -369,7 +374,7 @@ describe('StripeElement', () => {
error: null,
});
const validatorStateRef = mkValidatorStateRef();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement name="input-1" component={MockStripeElement} />
</TestForm>
@ -385,7 +390,7 @@ describe('StripeElement', () => {
error: { message: 'game over man' },
});
const validatorStateRef = mkValidatorStateRef();
const { container, getByTestId } = render(
const { container, getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement name="input-1" component={MockStripeElement} />
</TestForm>
@ -422,7 +427,7 @@ describe('StripeElement', () => {
const MockStripeElement = buildMockStripeElement(undefined);
const validatorStateRef = mkValidatorStateRef();
const translatedIsRequired = 'IS REQUIRED TRANSLATED';
const { container, getByTestId } = render(
const { container, getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement
data-testid="input-1"
@ -463,7 +468,7 @@ describe('StripeElement', () => {
valid: false,
error: expectedError,
}));
const { container, getByTestId } = render(
const { container, getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement
data-testid="input-1"
@ -486,7 +491,7 @@ describe('StripeElement', () => {
error: { message: 'period.' },
});
const validatorStateRef = mkValidatorStateRef();
const { container, getByTestId } = render(
const { container, getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement name="input-1" component={MockStripeElement} />
</TestForm>
@ -503,7 +508,7 @@ describe('StripeElement', () => {
it('handles complete result from contained stripe element', () => {
const MockStripeElement = buildMockStripeElement({ complete: true });
const validatorStateRef = mkValidatorStateRef();
const { container, getByTestId } = render(
const { container, getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<StripeElement name="input-1" component={MockStripeElement} />
</TestForm>
@ -531,7 +536,7 @@ describe('StripeElement', () => {
describe('Checkbox', () => {
it('renders its own label with a label prop when avaiable', () => {
const { container } = render(
const { container } = renderWithLocalizationProvider(
<TestForm>
<Checkbox name="foo" label="nice label" />
</TestForm>
@ -542,7 +547,7 @@ describe('Checkbox', () => {
});
it('renders children as a label with markup when available', () => {
const { container } = render(
const { container } = renderWithLocalizationProvider(
<TestForm>
<Checkbox name="foo">
nice <span className="label-inner-span">label</span>
@ -558,7 +563,7 @@ describe('Checkbox', () => {
});
it('accepts an alternate className', () => {
const { container } = render(
const { container } = renderWithLocalizationProvider(
<TestForm>
<Checkbox className="fooquux" name="foo" label="nice label" />
</TestForm>
@ -568,7 +573,7 @@ describe('Checkbox', () => {
it('must be checked to be valid when required', () => {
const validatorStateRef = mkValidatorStateRef();
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<TestForm validatorStateRef={validatorStateRef}>
<Checkbox data-testid="checkbox" name="foo" required />
</TestForm>
@ -633,7 +638,9 @@ describe('SubmitButton', () => {
</TestForm>
);
};
const { queryAllByTestId, getByTestId } = render(<Subject />);
const { queryAllByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject />
);
const validatorFns = queryAllByTestId('execute');
fireEvent.click(validatorFns[0]);

Просмотреть файл

@ -1,8 +1,9 @@
import React, { useContext } from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { defaultAppContext, AppContext, AppContextType } from './AppContext';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
@ -29,7 +30,7 @@ it('passes along given app-global props', () => {
accessToken: 'lettherightonein',
};
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<AppContext.Provider value={appContextValue}>
<Subject />
</AppContext.Provider>

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import '@testing-library/jest-dom/extend-expect';
import { cleanup, fireEvent, render, waitFor } from '@testing-library/react';
import { cleanup, fireEvent, waitFor } from '@testing-library/react';
import { CouponDetails } from 'fxa-shared/dto/auth/payments/coupon';
import {
MozillaSubscription,
@ -32,6 +32,7 @@ import {
// eslint-disable-next-line import/first
import { apiInvoicePreview } from '../lib/apiClient';
import { Config } from './config';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
jest.mock('../lib/apiClient', () => {
return {
@ -56,7 +57,7 @@ describe('useCheckboxStateResult', () => {
};
it('updates state with checkbox state as expected', () => {
const { getByTestId } = render(<Subject />);
const { getByTestId } = renderWithLocalizationProvider(<Subject />);
expect(getByTestId('result')).toHaveTextContent('false');
fireEvent.click(getByTestId('checkbox'));
expect(getByTestId('result')).toHaveTextContent('true');
@ -65,7 +66,9 @@ describe('useCheckboxStateResult', () => {
});
it('accepts an initial value', () => {
const { getByTestId } = render(<Subject initialState={true} />);
const { getByTestId } = renderWithLocalizationProvider(
<Subject initialState={true} />
);
expect(getByTestId('result')).toHaveTextContent('true');
});
});
@ -82,14 +85,14 @@ describe('useNonce', () => {
};
it('should render with an initial nonce', () => {
const { getByTestId } = render(<Subject />);
const { getByTestId } = renderWithLocalizationProvider(<Subject />);
const initialNonce = getByTestId('nonce').textContent;
expect(initialNonce).toBeDefined();
expect(initialNonce).not.toBe('');
});
it('should change nonce on refresh', () => {
const { getByTestId } = render(<Subject />);
const { getByTestId } = renderWithLocalizationProvider(<Subject />);
const refreshButton = getByTestId('refresh');
// Click the button a few times for good measure;
@ -135,7 +138,7 @@ describe('useReactGA4Setup', () => {
enabled: false,
},
} as Config;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_GqM9ToKK62qjkK" />
);
@ -150,7 +153,7 @@ describe('useReactGA4Setup', () => {
supportedProductIds: '',
},
} as Config;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_GqM9ToKK62qjkK" />
);
@ -160,7 +163,7 @@ describe('useReactGA4Setup', () => {
it('does not initialize ReactGA4 - productId is not in supportedProductIds', () => {
const config = mockConfig;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_fake" />
);
@ -181,7 +184,7 @@ describe('useReactGA4Setup', () => {
measurementId: '',
},
} as Config;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_GqM9ToKK62qjkK" />
);
@ -197,7 +200,7 @@ describe('useReactGA4Setup', () => {
it('successfully initialize ReactGA4', () => {
const config = mockConfig;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_GqM9ToKK62qjkK" />
);
@ -219,7 +222,7 @@ describe('useReactGA4Setup', () => {
supportedProductIds: 'prod_test_1,prod_GqM9ToKK62qjkK,prod_test_2',
},
} as Config;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_GqM9ToKK62qjkK" />
);
@ -240,7 +243,7 @@ describe('useReactGA4Setup', () => {
testMode: true,
},
} as Config;
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject config={config} productId="prod_GqM9ToKK62qjkK" />
);
@ -284,7 +287,7 @@ describe('useInfoBoxMessage', () => {
};
it('coupon has no value', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject coupon={undefined} selectedPlan={selectedPlan} />
);
expect(queryByTestId('info-box-message')).not.toBeInTheDocument();
@ -292,7 +295,7 @@ describe('useInfoBoxMessage', () => {
});
it('coupon type is "forever"', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject
coupon={{ ...coupon, type: 'forever' }}
selectedPlan={selectedPlan}
@ -303,7 +306,7 @@ describe('useInfoBoxMessage', () => {
});
it('coupon type is an unexpected value', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject
coupon={{ ...coupon, type: 'unexpected-value' }}
selectedPlan={selectedPlan}
@ -314,7 +317,7 @@ describe('useInfoBoxMessage', () => {
});
it('coupon type is "once"', () => {
const { queryByTestId, getByTestId } = render(
const { queryByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject
coupon={{ ...coupon, type: 'once' }}
selectedPlan={selectedPlan}
@ -328,7 +331,7 @@ describe('useInfoBoxMessage', () => {
});
it('coupon type is "repeating" plan interval greater than coupon duration', () => {
const { queryByTestId, getByTestId } = render(
const { queryByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject
coupon={{ ...coupon, type: 'repeating' }}
selectedPlan={{ ...selectedPlan, interval_count: 6 }}
@ -342,7 +345,7 @@ describe('useInfoBoxMessage', () => {
});
it('coupon type is "repeating" plan interval equal to coupon duration', () => {
const { queryByTestId, getByTestId } = render(
const { queryByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject
coupon={{ ...coupon, type: 'repeating' }}
selectedPlan={selectedPlan}
@ -361,7 +364,7 @@ describe('useInfoBoxMessage', () => {
durationInMonths: 2,
type: 'repeating',
};
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<Subject coupon={couponLongerDuration} selectedPlan={selectedPlan} />
);
const date = new Date();
@ -485,7 +488,9 @@ describe('useFetchInvoicePreview', () => {
});
it('returns invoicePreview no subscriptions', async () => {
const { queryByTestId } = render(<Subject planId={PLAN_ID} />);
const { queryByTestId } = renderWithLocalizationProvider(
<Subject planId={PLAN_ID} />
);
await waitFor(() => {
expect(queryByTestId('loading')).toBeInTheDocument();
@ -499,7 +504,7 @@ describe('useFetchInvoicePreview', () => {
});
it('returns invoicePreview no promotionCode', async () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject planId={PLAN_ID} customerSubscriptions={customerSubscriptions} />
);
@ -515,7 +520,7 @@ describe('useFetchInvoicePreview', () => {
});
it('returns invoicePreview with promotionCode', async () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject
planId={PLAN_ID}
customerSubscriptions={customerSubscriptionsWithPromotionCode}
@ -537,7 +542,7 @@ describe('useFetchInvoicePreview', () => {
});
it('empty planId', async () => {
const { queryByTestId } = render(<Subject />);
const { queryByTestId } = renderWithLocalizationProvider(<Subject />);
await waitFor(() => {
expect(queryByTestId('no-invoice')).toBeInTheDocument();
@ -548,7 +553,7 @@ describe('useFetchInvoicePreview', () => {
it('invoicePreview throws error', async () => {
(apiInvoicePreview as jest.Mock).mockClear().mockRejectedValue({});
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<Subject planId={PLAN_ID} customerSubscriptions={customerSubscriptions} />
);

Просмотреть файл

@ -1,4 +1,5 @@
import React, { useContext, ReactNode } from 'react';
import { render } from '@testing-library/react';
import { Provider as ReduxProvider } from 'react-redux';
import { AppContext, AppContextType } from '../../src/lib/AppContext';
import { config, updateConfig } from '../../src/lib/config';
@ -26,6 +27,8 @@ import {
SubsequentInvoicePreview,
} from 'fxa-shared/dto/auth/payments/invoice';
import AppLocalizationProvider from 'fxa-react/lib/AppLocalizationProvider';
declare global {
namespace NodeJS {
interface Global {
@ -1086,4 +1089,25 @@ export function getLocalizedMessage(
return bundle.formatPattern(localizedMessage.value, { ...args });
}
export function renderWithLocalizationProvider(
children,
messages = { en: ['testo: lol'] }
) {
// by default fluent warns about missing messages, but there's no way to
// disable it right now. see
// https://github.com/projectfluent/fluent.js/issues/411
return render(withLocalizationProvider(children, messages));
}
export function withLocalizationProvider(
children,
messages = { en: ['testo: lol'] }
) {
return (
<AppLocalizationProvider messages={messages}>
{children}
</AppLocalizationProvider>
);
}
export default MockApp;

Просмотреть файл

@ -1,5 +1,5 @@
import React, { useCallback, useContext } from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import { cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import {
mainReducer,
@ -10,6 +10,7 @@ import {
defaultState as validatorDefaultState,
Action as ValidatorAction,
} from './validator';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
afterEach(cleanup);
@ -28,7 +29,7 @@ describe('useValidatorState', () => {
const validator = useValidatorState();
return <div>{JSON.stringify(validator.state)}</div>;
};
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
});
@ -192,7 +193,7 @@ const runAgainstValidator = (...fns: Array<(validator: Validator) => any>) => {
return nextState;
};
const { queryAllByTestId } = render(
const { queryAllByTestId } = renderWithLocalizationProvider(
<TestContainer {...{ middleware, results, fns }} />
);
@ -229,11 +230,10 @@ const TestContainer = ({
const TestFn = ({ execute }: { execute: (validator: Validator) => any }) => {
const { validator, results } = useContext(TestContext) as TestContextValue;
const onClick = useCallback(() => results.push(execute(validator)), [
results,
execute,
validator,
]);
const onClick = useCallback(
() => results.push(execute(validator)),
[results, execute, validator]
);
return (
<button data-testid="execute" onClick={onClick}>
Execute

Просмотреть файл

@ -1,13 +1,7 @@
/**
* @jest-environment jsdom
*/
import {
render,
cleanup,
fireEvent,
act,
screen,
} from '@testing-library/react';
import { cleanup, fireEvent, act, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import waitForExpect from 'wait-for-expect';
@ -24,6 +18,7 @@ import {
elementChangeResponse,
MOCK_CHECKOUT_TOKEN,
MOCK_PAYPAL_SUBSCRIPTION_RESULT,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import Checkout, { CheckoutProps } from './index';
@ -187,7 +182,7 @@ describe('routes/Checkout', () => {
it('renders as expected', async () => {
await act(async () => {
render(<Subject planId="testo" />);
renderWithLocalizationProvider(<Subject planId="testo" />);
});
const { findByTestId, getByTestId } = screen;
@ -221,7 +216,7 @@ describe('routes/Checkout', () => {
it('displays checkbox tooltip error when unchecking checkbox', async () => {
await act(async () => {
render(<Subject planId="testo" />);
renderWithLocalizationProvider(<Subject planId="testo" />);
});
const { queryByTestId, findByTestId, queryByText } = screen;
const paymentFormContainer = queryByTestId('payment-form-container');
@ -245,7 +240,7 @@ describe('routes/Checkout', () => {
it('displays checkbox tooltip error when unchecked and clicking on disabled form', async () => {
await act(async () => {
render(<Subject planId="testo" />);
renderWithLocalizationProvider(<Subject planId="testo" />);
});
const { queryByTestId, queryByText } = screen;
const paymentFormContainer = queryByTestId('payment-form-container');
@ -265,7 +260,7 @@ describe('routes/Checkout', () => {
});
it('displays an error with invalid product ID', async () => {
const { findByTestId, queryByTestId } = render(
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject productId="bad_product" />
);
await findByTestId('no-such-plan-error');
@ -274,13 +269,15 @@ describe('routes/Checkout', () => {
it('displays an error on failure to load plans', async () => {
(apiFetchPlans as jest.Mock).mockRejectedValue({});
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const errorEl = await findByTestId('error-loading-plans');
expect(errorEl).toBeInTheDocument();
});
it('displays an error when selecting an inactive / archived plan', async () => {
const { findByTestId } = render(<Subject planId={INACTIVE_PLAN_ID} />);
const { findByTestId } = renderWithLocalizationProvider(
<Subject planId={INACTIVE_PLAN_ID} />
);
const errorEl = await findByTestId('no-such-plan-error');
expect(errorEl).toBeInTheDocument();
});
@ -322,7 +319,7 @@ describe('routes/Checkout', () => {
it('creates the account and subscription successfully', async () => {
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -349,7 +346,7 @@ describe('routes/Checkout', () => {
.mockResolvedValueOnce({ ...CUSTOMER, subscriptions: [] })
.mockResolvedValueOnce(CUSTOMER);
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -364,7 +361,7 @@ describe('routes/Checkout', () => {
FXA_SIGNUP_ERROR
);
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -393,7 +390,7 @@ describe('routes/Checkout', () => {
error: MOCK_STRIPE_CARD_ERROR,
});
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -423,7 +420,7 @@ describe('routes/Checkout', () => {
MOCK_FXA_POST_PASSWORDLESS_SUB_ERROR
);
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -455,7 +452,7 @@ describe('routes/Checkout', () => {
MOCK_FXA_POST_PASSWORDLESS_SUB_ERROR
);
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -488,7 +485,7 @@ describe('routes/Checkout', () => {
.mockRejectedValue(MOCK_CURRENCY_ERROR);
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -508,7 +505,7 @@ describe('routes/Checkout', () => {
describe('newsletter', () => {
it('POSTs to /newsletters if the newsletter checkbox is checked when subscription succeeds', async () => {
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
const shouldSubscribeToNewsletter = true;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -520,7 +517,7 @@ describe('routes/Checkout', () => {
it('Does not POST to /newsletters if the newsletter checkbox is unchecked when subscription succeeds', async () => {
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
const shouldSubscribeToNewsletter = false;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -536,7 +533,7 @@ describe('routes/Checkout', () => {
error: MOCK_STRIPE_CARD_ERROR,
});
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
const shouldSubscribeToNewsletter = true;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -551,7 +548,7 @@ describe('routes/Checkout', () => {
FXA_NEWSLETTER_SIGNUP_ERROR
);
await act(async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
});
const shouldSubscribeToNewsletter = true;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -610,7 +607,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -646,7 +645,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -680,7 +681,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -714,7 +717,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -744,7 +749,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
await fillOutZeForm();
await waitForExpect(() => {
@ -777,7 +784,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
const shouldSubscribeToNewsletter = true;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -803,7 +812,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
const shouldSubscribeToNewsletter = false;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -826,7 +837,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
const shouldSubscribeToNewsletter = true;
await fillOutZeForm(shouldSubscribeToNewsletter);
@ -851,7 +864,9 @@ describe('routes/Checkout', () => {
);
};
await act(async () => {
render(<Subject paypalButtonBase={paypalButtonBase} />);
renderWithLocalizationProvider(
<Subject paypalButtonBase={paypalButtonBase} />
);
});
const shouldSubscribeToNewsletter = true;
await fillOutZeForm(shouldSubscribeToNewsletter);

Просмотреть файл

@ -1,7 +1,10 @@
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MockApp, MOCK_CUSTOMER } from '../../../lib/test-utils';
import {
MockApp,
MOCK_CUSTOMER,
renderWithLocalizationProvider,
} from '../../../lib/test-utils';
import { PLAN, SELECTED_PLAN, PROFILE } from '../../../lib/mock-data';
import { SignInLayout } from '../../../components/AppLayout';
import IapRoadblock, { IapRoadblockProps } from './index';
@ -34,7 +37,7 @@ const MOCK_PROPS: IapRoadblockProps = {
describe('routes/Product/IapRoadblock', () => {
it('renders as expected', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<MockApp>
<SignInLayout>
<IapRoadblock {...MOCK_PROPS} />
@ -56,7 +59,7 @@ describe('routes/Product/IapRoadblock', () => {
it('displays messaging to contact support for help in upgrading', async () => {
const subject = () => {
return render(
return renderWithLocalizationProvider(
<MockApp>
<SignInLayout>
<IapRoadblock

Просмотреть файл

@ -1,8 +1,10 @@
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MockApp } from '../../../lib/test-utils';
import {
MockApp,
renderWithLocalizationProvider,
} from '../../../lib/test-utils';
import { SELECTED_PLAN, PROFILE } from '../../../lib/mock-data';
import { SignInLayout } from '../../../components/AppLayout';
import SubscriptionChangeRoadblock, {
@ -27,7 +29,7 @@ const Subject = () => {
describe('routes/Product/SubscriptionDowngradeRoadblock', () => {
it('renders as expected', async () => {
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const titleEl = await findByTestId('subscription-noplanchange-title');
expect(titleEl).toBeInTheDocument();
const errorEl = await findByTestId('payment-error');

Просмотреть файл

@ -4,13 +4,7 @@
import '@testing-library/jest-dom/extend-expect';
import { PaymentMethod } from '@stripe/stripe-js';
import {
act,
cleanup,
fireEvent,
render,
screen,
} from '@testing-library/react';
import { act, cleanup, fireEvent, screen } from '@testing-library/react';
import waitForExpect from 'wait-for-expect';
import SubscriptionCreate, { SubscriptionCreateProps } from '.';
@ -39,6 +33,7 @@ import {
MOCK_PAYPAL_SUBSCRIPTION_RESULT,
MockApp,
mockStripeElementOnChangeFns,
renderWithLocalizationProvider,
} from '../../../lib/test-utils';
import { PickPartial } from '../../../lib/types';
@ -127,7 +122,7 @@ describe('routes/Product/SubscriptionCreate', () => {
});
it('renders as expected', async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
const { queryByTestId, queryByText } = screen;
const subscriptionCreateTitle = queryByTestId('subscription-create-title');
expect(subscriptionCreateTitle).toBeInTheDocument();
@ -153,7 +148,7 @@ describe('routes/Product/SubscriptionCreate', () => {
});
it('displays checkbox tooltip error when unchecking checkbox', async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
const { queryByTestId, findByTestId, queryByText } = screen;
const paymentFormContainer = queryByTestId('payment-form-container');
expect(paymentFormContainer).toBeInTheDocument();
@ -175,7 +170,7 @@ describe('routes/Product/SubscriptionCreate', () => {
});
it('displays checkbox tooltip error when unchecked and clicking on disabled form', async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
const { queryByTestId, queryByText } = screen;
const paymentFormContainer = queryByTestId('payment-form-container');
expect(paymentFormContainer).toBeInTheDocument();
@ -199,7 +194,7 @@ describe('routes/Product/SubscriptionCreate', () => {
return <button data-testid="paypal-button" />;
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
paypalButtonBase: MockedButtonBase,
@ -226,7 +221,7 @@ describe('routes/Product/SubscriptionCreate', () => {
return <button data-testid="paypal-button" />;
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject customer={CUSTOMER} paypalButtonBase={MockedButtonBase} />
);
});
@ -244,7 +239,7 @@ describe('routes/Product/SubscriptionCreate', () => {
return <button data-testid="paypal-button" />;
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
customer={PAYPAL_CUSTOMER}
paypalButtonBase={MockedButtonBase}
@ -260,7 +255,7 @@ describe('routes/Product/SubscriptionCreate', () => {
});
it('renders as expected for mobile', async () => {
render(<Subject isMobile={true} />);
renderWithLocalizationProvider(<Subject isMobile={true} />);
const { queryByTestId } = screen;
const subscriptionCreateTitle = queryByTestId('subscription-create-title');
expect(subscriptionCreateTitle).toBeInTheDocument();
@ -280,7 +275,7 @@ describe('routes/Product/SubscriptionCreate', () => {
refreshSubscriptions = jest.fn(),
...props
}) {
render(
renderWithLocalizationProvider(
<Subject
{...{
apiClientOverrides,
@ -606,7 +601,7 @@ describe('routes/Product/SubscriptionCreate', () => {
};
const refreshSubscriptions = jest.fn();
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
apiClientOverrides,
@ -642,7 +637,7 @@ describe('routes/Product/SubscriptionCreate', () => {
apiCreateCustomer: jest.fn(),
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
customer: null,
@ -869,7 +864,7 @@ describe('routes/Product/SubscriptionCreate', () => {
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
apiClientOverrides,
@ -893,7 +888,7 @@ describe('routes/Product/SubscriptionCreate', () => {
return <button data-testid="paypal-button" onClick={onError} />;
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
paypalButtonBase: MockedButtonBase,
@ -927,7 +922,7 @@ describe('routes/Product/SubscriptionCreate', () => {
.mockRejectedValue({ code: 'barf apiCapturePaypalPayment' }),
};
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
apiClientOverrides,

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import { cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { config as defaultConfig } from '../../../lib/config';
import { AppContext, defaultAppContext } from '../../../lib/AppContext';
@ -8,6 +8,7 @@ import {
MOCK_PLANS,
MOCK_PROFILE,
MOCK_CUSTOMER,
renderWithLocalizationProvider,
} from '../../../lib/test-utils';
import { SubscriptionSuccess } from './index';
@ -44,7 +45,7 @@ function assertRedirectForProduct(
product_name,
configuration: planConfiguration,
};
const { getByTestId } = render(
const { getByTestId } = renderWithLocalizationProvider(
<AppContext.Provider value={appContextValue}>
<SubscriptionSuccess
{...{
@ -126,7 +127,7 @@ describe('SubscriptionSuccess', () => {
});
it('renders the PlanDetails component on mobile', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AppContext.Provider value={defaultAppContext}>
<SubscriptionSuccess
{...{
@ -144,7 +145,7 @@ describe('SubscriptionSuccess', () => {
});
it('renders the coupon form component when a coupon is present', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AppContext.Provider value={defaultAppContext}>
<SubscriptionSuccess
{...{
@ -171,7 +172,7 @@ describe('SubscriptionSuccess', () => {
});
it('does not renders the coupon form component when a coupon is not present', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AppContext.Provider value={defaultAppContext}>
<SubscriptionSuccess
{...{

Просмотреть файл

@ -1,6 +1,6 @@
import React from 'react';
import TestRenderer from 'react-test-renderer';
import { render, cleanup, fireEvent } from '@testing-library/react';
import { cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { APIError } from '../../../lib/apiClient';
@ -20,6 +20,8 @@ import {
MOCK_PREVIEW_INVOICE_WITH_TAX_INCLUSIVE,
MOCK_PREVIEW_INVOICE_NO_TAX,
MOCK_PREVIEW_INVOICE_WITH_ZERO_TAX_EXCLUSIVE,
renderWithLocalizationProvider,
withLocalizationProvider,
} from '../../../lib/test-utils';
import { getFtlBundle } from 'fxa-react/lib/test-utils';
import { FluentBundle, FluentNumber } from '@fluent/bundle';
@ -57,9 +59,10 @@ async function rendersAsExpected(
invoicePreview = MOCK_PREVIEW_INVOICE_NO_TAX,
selectedPlan = SELECTED_PLAN
) {
const { findByTestId, queryByTestId, container } = render(
<Subject props={{ upgradeFromPlan, invoicePreview, selectedPlan }} />
);
const { findByTestId, queryByTestId, container } =
renderWithLocalizationProvider(
<Subject props={{ upgradeFromPlan, invoicePreview, selectedPlan }} />
);
await findByTestId('subscription-upgrade');
// Could do some more content-based tests here, but basically just a
@ -141,7 +144,7 @@ describe('routes/Product/SubscriptionUpgrade', () => {
useStripeAutomaticTax: true,
},
});
const { findByTestId, queryByTestId } = render(
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject
props={{
invoicePreview: MOCK_PREVIEW_INVOICE_WITH_TAX_INCLUSIVE,
@ -161,7 +164,7 @@ describe('routes/Product/SubscriptionUpgrade', () => {
useStripeAutomaticTax: true,
},
});
const { findByTestId, queryByTestId } = render(
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject
props={{
invoicePreview: MOCK_PREVIEW_INVOICE_WITH_TAX_EXCLUSIVE,
@ -181,7 +184,7 @@ describe('routes/Product/SubscriptionUpgrade', () => {
useStripeAutomaticTax: true,
},
});
const { findByTestId, queryByTestId } = render(
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject
props={{
invoicePreview: MOCK_PREVIEW_INVOICE_WITH_ZERO_TAX_EXCLUSIVE,
@ -198,7 +201,7 @@ describe('routes/Product/SubscriptionUpgrade', () => {
it('can be submitted after confirmation is checked', async () => {
const updateSubscriptionPlanAndRefresh = jest.fn();
const { findByTestId, getByTestId } = render(
const { findByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject
props={{
updateSubscriptionPlanAndRefresh,
@ -223,7 +226,7 @@ describe('routes/Product/SubscriptionUpgrade', () => {
});
it('displays a loading spinner while submitting', async () => {
const { findByTestId, getByTestId } = render(
const { findByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject
props={{
updateSubscriptionPlanStatus: {
@ -242,19 +245,20 @@ describe('routes/Product/SubscriptionUpgrade', () => {
it('displays a dialog when updating subscription results in error', async () => {
const expectedMessage = 'game over man';
const { findByTestId, getByTestId, getByText } = render(
<Subject
props={{
updateSubscriptionPlanStatus: {
error: new APIError({
message: expectedMessage,
}),
loading: false,
result: null,
},
}}
/>
);
const { findByTestId, getByTestId, getByText } =
renderWithLocalizationProvider(
<Subject
props={{
updateSubscriptionPlanStatus: {
error: new APIError({
message: expectedMessage,
}),
loading: false,
result: null,
},
}}
/>
);
await findByTestId('subscription-upgrade');
expect(getByTestId('error-plan-update-failed')).toBeInTheDocument();
@ -262,7 +266,9 @@ describe('routes/Product/SubscriptionUpgrade', () => {
});
it('calls updateSubscriptionPlanMounted and updateSubscriptionPlanEngaged', async () => {
const { findByTestId, getByTestId } = render(<Subject />);
const { findByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject />
);
await findByTestId('subscription-upgrade');
fireEvent.click(getByTestId('confirm'));
expect(updateSubscriptionPlanMounted).toBeCalledTimes(1);
@ -288,7 +294,9 @@ describe('PlanDetailsCard', () => {
function runTests(plan: Plan, expectedMsgId: string, expectedMsg: string) {
const props = { plan: plan };
const testRenderer = TestRenderer.create(<PlanDetailsCard {...props} />);
const testRenderer = TestRenderer.create(
withLocalizationProvider(<PlanDetailsCard {...props} />)
);
const testInstance = testRenderer.root;
const planPriceComponent = testInstance.findByProps({
id: expectedMsgId,
@ -309,7 +317,9 @@ describe('PlanDetailsCard', () => {
const props = { plan: plan };
const testRenderer = TestRenderer.create(<PlanDetailsCard {...props} />);
const testRenderer = TestRenderer.create(
withLocalizationProvider(<PlanDetailsCard {...props} />)
);
const testInstance = testRenderer.root;
const planPriceComponent = testInstance.findByProps({
id: 'plan-details-product',
@ -326,7 +336,9 @@ describe('PlanDetailsCard', () => {
const props = { plan: plan };
const testRenderer = TestRenderer.create(<PlanDetailsCard {...props} />);
const testRenderer = TestRenderer.create(
withLocalizationProvider(<PlanDetailsCard {...props} />)
);
const testInstance = testRenderer.root;
const planPriceComponent = testInstance.findByProps({
id: 'plan-details-product',

Просмотреть файл

@ -2,11 +2,7 @@
* @jest-environment jsdom
*/
import React from 'react';
import {
render,
cleanup,
waitForElementToBeRemoved,
} from '@testing-library/react';
import { cleanup, waitForElementToBeRemoved } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import noc from 'nock';
@ -29,6 +25,7 @@ import {
mockOptionsResponses,
INACTIVE_PLAN_ID,
MOCK_PREVIEW_INVOICE_NO_TAX,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { SignInLayout } from '../../components/AppLayout';
@ -196,7 +193,8 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findAllByText, queryByText, queryAllByText } = render(<Subject />);
const { findAllByText, queryByText, queryAllByText } =
renderWithLocalizationProvider(<Subject />);
await findAllByText('Set up your subscription');
expect(
@ -229,7 +227,7 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findByTestId, queryByTestId } = render(
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject productId="bad_product" />
);
await waitForElementToBeRemoved(queryByTestId('loading-overlay'));
@ -268,7 +266,7 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const errorEl = await findByTestId('error-loading-profile');
expect(errorEl).toBeInTheDocument();
expectNockScopesDone(apiMocks);
@ -286,7 +284,7 @@ describe('routes/Product', () => {
)
.reply(200, MOCK_CUSTOMER),
];
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const errorEl = await findByTestId('error-loading-plans');
expect(errorEl).toBeInTheDocument();
expectNockScopesDone(apiMocks);
@ -310,7 +308,9 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findByTestId } = render(<Subject planId={INACTIVE_PLAN_ID} />);
const { findByTestId } = renderWithLocalizationProvider(
<Subject planId={INACTIVE_PLAN_ID} />
);
const errorEl = await findByTestId('no-such-plan-error');
expect(errorEl).toBeInTheDocument();
expectNockScopesDone(apiMocks);
@ -346,7 +346,7 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const errorEl = await findByTestId('error-loading-customer');
expect(errorEl).toBeInTheDocument();
expectNockScopesDone(apiMocks);
@ -382,7 +382,7 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const errorEl = await findByTestId('product-invoice-preview-error');
expect(errorEl).toBeInTheDocument();
expectNockScopesDone(apiMocks);
@ -422,7 +422,7 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findAllByText } = render(<Subject />);
const { findAllByText } = renderWithLocalizationProvider(<Subject />);
const headingEls = await findAllByText('Set up your subscription');
expect(headingEls.length).toBeGreaterThan(0);
expectNockScopesDone(apiMocks);
@ -462,7 +462,7 @@ describe('routes/Product', () => {
'Access-Control-Allow-Origin': '*',
}),
];
const { findAllByText } = render(<Subject />);
const { findAllByText } = renderWithLocalizationProvider(<Subject />);
const headingEls = await findAllByText('Set up your subscription');
expect(headingEls.length).toBeGreaterThan(0);
expectNockScopesDone(apiMocks);
@ -473,7 +473,7 @@ describe('routes/Product', () => {
planId: 'plan_upgrade',
planEligibility: 'upgrade',
});
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject
{...{
planId: 'plan_upgrade',
@ -496,7 +496,7 @@ describe('routes/Product', () => {
planId: 'plan_no_upgrade',
planEligibility: 'create',
});
const { findAllByText, queryByTestId } = render(
const { findAllByText, queryByTestId } = renderWithLocalizationProvider(
<Subject
{...{
planId: 'plan_no_upgrade',
@ -516,7 +516,7 @@ describe('routes/Product', () => {
it('does not allow a downgrade', async () => {
const apiMocks = initSubscribedApiMocks({ planId: 'plan_no_downgrade' });
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject
{...{
planId: 'plan_no_downgrade',
@ -536,7 +536,7 @@ describe('routes/Product', () => {
it('displays roadblock for a different plan of the same product with no upgrade path', async () => {
const apiMocks = initSubscribedApiMocks({ planId: 'nextlevel' });
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject
{...{
planId: 'nextlevel',
@ -574,7 +574,7 @@ describe('routes/Product', () => {
planId: 'nextlevel',
planEligibility: 'blocked_iap',
});
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject
{...{
planId: 'nextlevel',
@ -594,7 +594,9 @@ describe('routes/Product', () => {
it('displays payment confirmation if user is already subscribed to the product', async () => {
const apiMocks = initSubscribedApiMocks();
const { findByTestId, queryByTestId } = render(<Subject />);
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject />
);
await waitForElementToBeRemoved(queryByTestId('loading-overlay'));
const confirmEl = await findByTestId('payment-confirmation');
expect(confirmEl).toBeInTheDocument();
@ -604,7 +606,7 @@ describe('routes/Product', () => {
it('redirects to content server when there is no access token', async () => {
const navigateToUrl = jest.fn();
const appContext = { ...defaultAppContextValue(), accessToken: undefined };
render(
renderWithLocalizationProvider(
<Subject
productId="fizz"
planId="quux"

Просмотреть файл

@ -1,5 +1,5 @@
import React from 'react';
import { screen, render, cleanup, fireEvent } from '@testing-library/react';
import { screen, cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import ActionButton, { ActionButtonProps } from './ActionButton';
@ -10,6 +10,7 @@ import {
PAYPAL_PAYMENT_ERROR_FUNDING_SOURCE,
PAYPAL_PAYMENT_ERROR_MISSING_AGREEMENT,
} from 'fxa-shared/subscriptions/types';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
const { apiUrl } = defaultConfig().paypal;
@ -40,7 +41,9 @@ describe('routes/Subscriptions/ActionButton', () => {
it('renders revealonclick button when payment_provider is "stripe"', async () => {
const onRevealUpdateClick = jest.fn();
render(<Subject onRevealUpdateClick={onRevealUpdateClick} />);
renderWithLocalizationProvider(
<Subject onRevealUpdateClick={onRevealUpdateClick} />
);
const revealButton = screen.getByTestId('reveal-payment-update-button');
expect(revealButton).toBeInTheDocument();
fireEvent.click(revealButton);
@ -48,7 +51,7 @@ describe('routes/Subscriptions/ActionButton', () => {
});
it('renders paypalActionButton when paypal is payment provider and paypal_payment_error is not set', async () => {
render(
renderWithLocalizationProvider(
<Subject
customer={{
...CUSTOMER,
@ -64,7 +67,7 @@ describe('routes/Subscriptions/ActionButton', () => {
});
it('renders paypalFundingSourceActionButton when paypal is payment provider and paypal_payment_error is set to "funding_source"', async () => {
render(
renderWithLocalizationProvider(
<Subject
customer={{
...CUSTOMER,
@ -82,7 +85,7 @@ describe('routes/Subscriptions/ActionButton', () => {
it('renders paypalMissingAgreementActionButton when paypal is payment provider and paypal_payment_error is set to "missing_agreement"', async () => {
const revealFixPaymentModal = jest.fn();
render(
renderWithLocalizationProvider(
<Subject
revealFixPaymentModal={revealFixPaymentModal}
customer={{

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { screen, render, act, fireEvent } from '@testing-library/react';
import { screen, act, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import waitForExpect from 'wait-for-expect';
import * as Amplitude from '../../../lib/amplitude';
@ -21,6 +21,7 @@ import {
INVOICE_WITH_TAX_INCLUSIVE_DISCOUNT,
INVOICE_WITH_TAX_EXCLUSIVE_DISCOUNT,
INVOICE_WITH_ZERO_TAX_EXCLUSIVE,
renderWithLocalizationProvider,
} from '../../../lib/test-utils';
import { Plan } from 'fxa-payments-server/src/store/types';
import {
@ -71,7 +72,9 @@ describe('CancelSubscriptionPanel', () => {
})) {
describe(`when plan has ${k} interval`, () => {
const runTests = (props: CancelSubscriptionPanelProps) => {
render(<CancelSubscriptionPanel {...props} />);
renderWithLocalizationProvider(
<CancelSubscriptionPanel {...props} />
);
const planPrice = formatPlanPricing(
props.invoice.total,
@ -131,7 +134,9 @@ describe('CancelSubscriptionPanel', () => {
it('should not be displayed when upgradeCTA is not in the plan', () => {
const plan = findMockPlan('plan_daily');
render(<CancelSubscriptionPanel {...baseProps} plan={plan} />);
renderWithLocalizationProvider(
<CancelSubscriptionPanel {...baseProps} plan={plan} />
);
expect(queryByTestId('upgrade-cta')).not.toBeInTheDocument();
});
@ -143,7 +148,7 @@ describe('CancelSubscriptionPanel', () => {
upgradeCTA: 'Upgrade to the ultra super premium plus plan!',
},
};
render(
renderWithLocalizationProvider(
<CancelSubscriptionPanel {...baseProps} plan={upgradeablePlan} />
);
expect(queryByTestId('upgrade-cta')).toBeInTheDocument();
@ -178,7 +183,7 @@ describe('CancelSubscriptionPanel', () => {
},
},
};
render(
renderWithLocalizationProvider(
<CancelSubscriptionPanel {...baseProps} plan={upgradeablePlan} />
);
expect(queryByTestId('upgrade-cta')).toBeInTheDocument();
@ -219,7 +224,7 @@ describe('CancelSubscriptionPanel', () => {
},
},
};
render(
renderWithLocalizationProvider(
<AppContext.Provider
value={{ ...defaultAppContext, navigatorLanguages: ['fr'] }}
>
@ -247,7 +252,7 @@ describe('CancelSubscriptionPanel', () => {
'payment-cancel-btn = blee',
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = findMockPlan('plan_daily');
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -274,7 +279,7 @@ describe('CancelSubscriptionPanel', () => {
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = { ...findMockPlan('plan_daily'), interval_count: 8 };
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel {...baseProps} plan={plan} />
</LocalizationProvider>
@ -294,7 +299,7 @@ describe('CancelSubscriptionPanel', () => {
`sub-next-bill-tax = Your next bill of { $priceAmount } + { $taxAmount } taxes is due due <strong>{ $date }</strong>`,
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = findMockPlan('plan_daily');
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -328,7 +333,7 @@ describe('CancelSubscriptionPanel', () => {
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = { ...findMockPlan('plan_daily'), interval_count: 8 };
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -357,7 +362,7 @@ describe('CancelSubscriptionPanel', () => {
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = { ...findMockPlan('plan_daily'), interval_count: 8 };
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -388,7 +393,7 @@ describe('CancelSubscriptionPanel', () => {
`sub-next-bill-no-tax = Your next bill of { $priceAmount } prices is due due <strong>{ $date }</strong>`,
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = findMockPlan('plan_daily');
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -421,7 +426,7 @@ describe('CancelSubscriptionPanel', () => {
`sub-next-bill-no-tax = Your next bill of { $priceAmount } prices is due due <strong>{ $date }</strong>`,
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = findMockPlan('plan_daily');
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -454,7 +459,7 @@ describe('CancelSubscriptionPanel', () => {
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = { ...findMockPlan('plan_daily'), interval_count: 8 };
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -483,7 +488,7 @@ describe('CancelSubscriptionPanel', () => {
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = { ...findMockPlan('plan_daily'), interval_count: 8 };
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel
{...baseProps}
@ -512,7 +517,7 @@ describe('CancelSubscriptionPanel', () => {
'sub-item-stay-sub = haha never mind',
].forEach((x) => bundle.addResource(new FluentResource(x)));
const plan = findMockPlan('plan_daily');
render(
renderWithLocalizationProvider(
<LocalizationProvider l10n={new ReactLocalization([bundle])}>
<CancelSubscriptionPanel {...baseProps} plan={plan} />
</LocalizationProvider>
@ -537,7 +542,9 @@ describe('CancelSubscriptionPanel', () => {
describe('event handling', () => {
beforeEach(() => {
const plan = findMockPlan('plan_daily');
render(<CancelSubscriptionPanel {...baseProps} plan={plan} />);
renderWithLocalizationProvider(
<CancelSubscriptionPanel {...baseProps} plan={plan} />
);
});
it('closes the cancellation confirmation on Stay Subscribed', () => {
@ -566,7 +573,7 @@ describe('CancelSubscriptionPanel', () => {
(Amplitude.cancelSubscriptionMounted as jest.Mock).mockClear();
(Amplitude.cancelSubscriptionEngaged as jest.Mock).mockClear();
const plan = findMockPlan('plan_daily');
render(
renderWithLocalizationProvider(
<CancelSubscriptionPanel
{...baseProps}
plan={plan}

Просмотреть файл

@ -1,11 +1,5 @@
import React from 'react';
import {
screen,
render,
cleanup,
act,
fireEvent,
} from '@testing-library/react';
import { screen, cleanup, act, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import waitForExpect from 'wait-for-expect';
@ -16,6 +10,7 @@ import {
mockStripeElementOnChangeFns,
elementChangeResponse,
MOCK_PAYPAL_CUSTOMER_RESULT,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import {
CUSTOMER,
@ -118,41 +113,45 @@ describe('routes/Subscriptions/PaymentUpdateFormV2', () => {
});
it('renders with payment update form hidden initially', async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
expect(screen.queryByTestId('payment-update')).toBeInTheDocument();
expect(screen.queryByTestId('paymentForm')).not.toBeInTheDocument();
});
it('renders valid expiration date', async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
expect(screen.queryByTestId('card-expiration-date')).toHaveTextContent(
'Expires February 2099'
);
});
it('does not render expiration date if date is invalid', async () => {
render(<Subject customer={{ ...CUSTOMER, exp_month: undefined }} />);
renderWithLocalizationProvider(
<Subject customer={{ ...CUSTOMER, exp_month: undefined }} />
);
expect(
screen.queryByTestId('card-expiration-date')
).not.toBeInTheDocument();
});
it('reveals the payment update form on clicking Change button', async () => {
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
expect(screen.queryByTestId('payment-update')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('change-button'));
expect(screen.queryByTestId('paymentForm')).toBeInTheDocument();
});
it('renders correctly for paypal', async () => {
render(<Subject customer={{ ...CUSTOMER, payment_provider: 'paypal' }} />);
renderWithLocalizationProvider(
<Subject customer={{ ...CUSTOMER, payment_provider: 'paypal' }} />
);
expect(
screen.getByTestId('change-payment-update-button')?.getAttribute('href')
).toEqual(`${apiUrl}/myaccount/autopay/connect/ba-131243`);
});
it('renders correctly for paypal with archived plan', async () => {
render(
renderWithLocalizationProvider(
<Subject
customer={{ ...CUSTOMER, payment_provider: 'paypal' }}
plan={INACTIVE_PLAN}
@ -173,7 +172,7 @@ describe('routes/Subscriptions/PaymentUpdateFormV2', () => {
const refreshSubscriptions = jest.fn();
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
customer: {
@ -228,7 +227,7 @@ describe('routes/Subscriptions/PaymentUpdateFormV2', () => {
const refreshSubscriptions = jest.fn();
await act(async () => {
render(
renderWithLocalizationProvider(
<Subject
{...{
customer: {
@ -273,7 +272,7 @@ describe('routes/Subscriptions/PaymentUpdateFormV2', () => {
});
it('renders correctly for incorrect funding source for paypal', async () => {
render(
renderWithLocalizationProvider(
<Subject
customer={{
...CUSTOMER,
@ -295,7 +294,7 @@ describe('routes/Subscriptions/PaymentUpdateFormV2', () => {
refreshSubscriptions = jest.fn(),
...props
} = {}) {
render(
renderWithLocalizationProvider(
<Subject
{...{
apiClientOverrides,

Просмотреть файл

@ -1,6 +1,5 @@
import '@testing-library/jest-dom/extend-expect';
import { render } from '@testing-library/react';
import { IapSubscription } from 'fxa-shared/subscriptions/types';
import { SignInLayout } from '../../../components/AppLayout';
@ -9,7 +8,11 @@ import {
IAP_GOOGLE_SUBSCRIPTION,
SELECTED_PLAN,
} from '../../../lib/mock-data';
import { deepCopy, MockApp } from '../../../lib/test-utils';
import {
deepCopy,
MockApp,
renderWithLocalizationProvider,
} from '../../../lib/test-utils';
import { PickPartial } from '../../../lib/types';
import SubscriptionIapItem, {
SubscriptionIapItemProps,
@ -40,7 +43,7 @@ const Subject = ({
describe('routes/Subscriptions/SubscriptionIapItem', () => {
it('renders as expected for a Google Play subscription with autorenew=true', async () => {
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
const subscriptionItemEle = await findByTestId('subscription-item');
expect(subscriptionItemEle).toBeInTheDocument();
const iapDetailsEle = await findByTestId('iap-details');
@ -51,7 +54,7 @@ describe('routes/Subscriptions/SubscriptionIapItem', () => {
expect(manageButton).toBeInTheDocument();
});
it('renders as expected for a Google Play subscription with autorenew=false', async () => {
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject
customerSubscription={
{
@ -67,7 +70,7 @@ describe('routes/Subscriptions/SubscriptionIapItem', () => {
expect(iapDetailsEle).toHaveTextContent('Expires on');
});
it('renders as expected for an App Store subscription', async () => {
const { findByTestId, queryByTestId } = render(
const { findByTestId, queryByTestId } = renderWithLocalizationProvider(
<Subject
customerSubscription={IAP_APPLE_SUBSCRIPTION as IapSubscription}
/>
@ -84,7 +87,7 @@ describe('routes/Subscriptions/SubscriptionIapItem', () => {
it('renders an App store subscription with no expiration data', async () => {
const subscription = deepCopy(IAP_APPLE_SUBSCRIPTION);
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject customerSubscription={subscription as IapSubscription} />
);
const subscriptionItemEle = await findByTestId('subscription-item');
@ -100,7 +103,7 @@ describe('routes/Subscriptions/SubscriptionIapItem', () => {
subscription.expiry_time_millis = 1656759852811;
subscription.auto_renewing = true;
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject customerSubscription={subscription as IapSubscription} />
);
const subscriptionItemEle = await findByTestId('subscription-item');
@ -117,7 +120,7 @@ describe('routes/Subscriptions/SubscriptionIapItem', () => {
subscription.expiry_time_millis = 1656759852811;
subscription.auto_renewing = false;
const { findByTestId } = render(
const { findByTestId } = renderWithLocalizationProvider(
<Subject customerSubscription={subscription as IapSubscription} />
);
const subscriptionItemEle = await findByTestId('subscription-item');

Просмотреть файл

@ -1,6 +1,5 @@
import React, { ReactNode } from 'react';
import {
render,
cleanup,
fireEvent,
RenderResult,
@ -44,6 +43,7 @@ import {
MOCK_LATEST_INVOICE_ITEMS,
MOCK_CUSTOMER_ARCHIVED_PLAN,
MOCK_ACTIVE_SUBSCRIPTIONS_TO_ARCHIVED,
renderWithLocalizationProvider,
} from '../../lib/test-utils';
import { SettingsLayout } from '../../components/AppLayout';
@ -171,7 +171,7 @@ describe('routes/Subscriptions', () => {
mockCustomer: MOCK_CUSTOMER_AFTER_SUBSCRIPTION,
mockActiveSubscriptions: MOCK_ACTIVE_SUBSCRIPTIONS_AFTER_SUBSCRIPTION,
});
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
await screen.findByTestId('subscription-management-loaded');
await waitForExpect(() => {
expect(screen.queryByTestId('loading-overlay')).not.toBeInTheDocument();
@ -188,9 +188,8 @@ describe('routes/Subscriptions', () => {
mockCustomer: MOCK_CUSTOMER_AFTER_SUBSCRIPTION,
});
const { findByTestId, queryAllByTestId, queryByTestId } = render(
<Subject />
);
const { findByTestId, queryAllByTestId, queryByTestId } =
renderWithLocalizationProvider(<Subject />);
if (window.onload) {
dispatchEvent(new Event('load'));
}
@ -206,7 +205,7 @@ describe('routes/Subscriptions', () => {
initApiMocks({
mockCustomer: MOCK_CUSTOMER_AFTER_SUBSCRIPTION,
});
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('manage-pocket-title');
await findByTestId('manage-pocket-link');
});
@ -227,7 +226,9 @@ describe('routes/Subscriptions', () => {
},
})),
});
const { findByTestId, queryAllByTestId } = render(<Subject />);
const { findByTestId, queryAllByTestId } = renderWithLocalizationProvider(
<Subject />
);
await findByTestId('subscription-management-loaded');
expect(queryAllByTestId('upgrade-cta').length).toBe(2);
// Ensure that our HTML in upgradeCTA got rendered as markup
@ -237,7 +238,7 @@ describe('routes/Subscriptions', () => {
it('offers a button for support', async () => {
initApiMocks();
const navigateToUrl = jest.fn();
const { getByTestId, findByTestId } = render(
const { getByTestId, findByTestId } = renderWithLocalizationProvider(
<Subject navigateToUrl={navigateToUrl} />
);
await findByTestId('subscription-management-loaded');
@ -251,7 +252,9 @@ describe('routes/Subscriptions', () => {
mockCustomer: MOCK_CUSTOMER_AFTER_SUBSCRIPTION,
mockActiveSubscriptions: MOCK_ACTIVE_SUBSCRIPTIONS_AFTER_SUBSCRIPTION,
});
const { getAllByTestId, findByTestId } = render(<Subject />);
const { getAllByTestId, findByTestId } = renderWithLocalizationProvider(
<Subject />
);
await findByTestId('subscription-management-loaded');
fireEvent.click(getAllByTestId('reveal-cancel-subscription-button')[0]);
expect(manageSubscriptionsMounted).toBeCalledTimes(1);
@ -260,7 +263,7 @@ describe('routes/Subscriptions', () => {
it('displays profile displayName if available', async () => {
initApiMocks({ displayName: 'Foo Barson' });
const { findByText } = render(<Subject />);
const { findByText } = renderWithLocalizationProvider(<Subject />);
await findByText('Foo Barson');
});
@ -271,7 +274,7 @@ describe('routes/Subscriptions', () => {
});
const navigateToUrl = jest.fn();
render(<Subject navigateToUrl={navigateToUrl} />);
renderWithLocalizationProvider(<Subject navigateToUrl={navigateToUrl} />);
await waitForExpect(() => expect(navigateToUrl).toBeCalled());
expect(navigateToUrl).toBeCalledWith(`${contentServer}/settings`);
@ -291,7 +294,7 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('error-loading-profile');
});
@ -309,7 +312,7 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('error-loading-plans');
});
@ -327,7 +330,7 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('error-loading-customer');
});
@ -345,7 +348,7 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(500, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('error-loading-invoice');
});
@ -363,7 +366,7 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, []);
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('error-subhub-missing-subsequent-invoice');
});
@ -385,7 +388,7 @@ describe('routes/Subscriptions', () => {
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const navigateToUrl = jest.fn();
render(<Subject navigateToUrl={navigateToUrl} />);
renderWithLocalizationProvider(<Subject navigateToUrl={navigateToUrl} />);
await waitForExpect(() => expect(navigateToUrl).toBeCalled());
expect(navigateToUrl).toBeCalledWith(`${contentServer}/settings`);
@ -398,7 +401,8 @@ describe('routes/Subscriptions', () => {
.delete('/v1/oauth/subscriptions/active/sub0.28964929339372136')
.reply(400, {});
const { findByTestId, queryAllByTestId, getByTestId } = render(<Subject />);
const { findByTestId, queryAllByTestId, getByTestId } =
renderWithLocalizationProvider(<Subject />);
// Wait for the page to load with one subscription
await findByTestId('subscription-management-loaded');
@ -428,7 +432,7 @@ describe('routes/Subscriptions', () => {
queryByTestId,
getByTestId,
getAllByTestId,
} = render(<Subject />);
} = renderWithLocalizationProvider(<Subject />);
// Wait for the page to load with one subscription
await findByTestId('subscription-management-loaded');
@ -585,7 +589,7 @@ describe('routes/Subscriptions', () => {
});
const { findByTestId, queryAllByTestId, queryByTestId, getAllByTestId } =
render(<Subject />);
renderWithLocalizationProvider(<Subject />);
// Wait for the page to load with one subscription
await findByTestId('subscription-management-loaded');
@ -810,9 +814,8 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId, getByTestId, getByAltText, queryByTestId } = render(
<Subject />
);
const { findByTestId, getByTestId, getByAltText, queryByTestId } =
renderWithLocalizationProvider(<Subject />);
// Wait for the page to load with one subscription
await findByTestId('subscription-management-loaded');
@ -857,7 +860,9 @@ describe('routes/Subscriptions', () => {
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId, getByTestId } = render(<Subject />);
const { findByTestId, getByTestId } = renderWithLocalizationProvider(
<Subject />
);
// Wait for the page to load with one subscription
await findByTestId('subscription-management-loaded');
@ -902,7 +907,7 @@ describe('routes/Subscriptions', () => {
nock(authServer)
.get('/v1/oauth/subscriptions/invoice/preview-subsequent')
.reply(200, MOCK_SUBSEQUENT_INVOICES);
const { findByTestId } = render(<Subject />);
const { findByTestId } = renderWithLocalizationProvider(<Subject />);
await findByTestId('error-subhub-missing-plan');
});
@ -916,7 +921,7 @@ describe('routes/Subscriptions', () => {
Object.defineProperty(window.location, 'href', { set: setSpy });
const appContext = { accessToken: undefined };
render(<Subject appContext={appContext} />);
renderWithLocalizationProvider(<Subject appContext={appContext} />);
expect(setSpy).toHaveBeenCalledWith(
'https://content.example/subscriptions'
@ -968,9 +973,8 @@ describe('routes/Subscriptions', () => {
},
});
const { findByTestId, queryAllByTestId, queryByTestId } = render(
<Subject />
);
const { findByTestId, queryAllByTestId, queryByTestId } =
renderWithLocalizationProvider(<Subject />);
await findByTestId('subscription-management-loaded');
@ -995,7 +999,9 @@ describe('routes/Subscriptions', () => {
mockSubsequentInvoices: MOCK_SUBSEQUENT_INVOICES,
});
const { findByTestId, queryAllByTestId } = render(<Subject />);
const { findByTestId, queryAllByTestId } = renderWithLocalizationProvider(
<Subject />
);
await findByTestId('subscription-management-loaded');

Просмотреть файл

@ -4,16 +4,20 @@
module.exports = {
stories: ['../**/*.stories.tsx'],
core: {
builder: 'webpack5',
},
addons: [
'@storybook/addon-actions',
'@storybook/addon-links',
{
name: '@storybook/addon-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
'@storybook/addon-styling',
],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
features: { storyStoreV7: false },
docs: {
autodocs: true,
},
};

Просмотреть файл

@ -3,8 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import AppErrorBoundary from '.';
import { renderWithLocalizationProvider } from '../../lib/test-utils/localizationProvider';
window.console.error = jest.fn();
@ -12,7 +13,7 @@ describe('AppErrorBoundary', () => {
it('renders children that do not cause exceptions', () => {
const GoodComponent = () => <p data-testid="good-component">Hi</p>;
render(
renderWithLocalizationProvider(
<AppErrorBoundary>
<GoodComponent />
</AppErrorBoundary>
@ -26,7 +27,7 @@ describe('AppErrorBoundary', () => {
throw new Error('bad');
};
render(
renderWithLocalizationProvider(
<AppErrorBoundary>
<BadComponent />
</AppErrorBoundary>

Просмотреть файл

@ -1,7 +1,13 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import AppErrorDialog from './index';
import AppLocalizationProvider from '../../lib/AppLocalizationProvider';
storiesOf('Components/AppErrorDialog', module).add('basic', () => (
<AppErrorDialog error={new Error('Uh oh!')} />
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<AppErrorDialog error={new Error('Uh oh!')} />
</AppLocalizationProvider>
));

Просмотреть файл

@ -3,12 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render } from '@testing-library/react';
import AppErrorDialog from '.';
import { renderWithLocalizationProvider } from '../../lib/test-utils/localizationProvider';
describe('AppErrorDialog', () => {
it('renders a general error dialog', () => {
const { queryByTestId } = render(
const { queryByTestId } = renderWithLocalizationProvider(
<AppErrorDialog error={new Error('bad')} />
);

Просмотреть файл

@ -5,5 +5,13 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { Footer } from './index';
import AppLocalizationProvider from '../../lib/AppLocalizationProvider';
storiesOf('Components/Footer', module).add('default', () => <Footer />);
storiesOf('Components/Footer', module).add('default', () => (
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<Footer />
</AppLocalizationProvider>
));

Просмотреть файл

@ -3,12 +3,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import Footer from '.';
import { renderWithLocalizationProvider } from '../../lib/test-utils/localizationProvider';
describe('Footer', () => {
it('renders as expected', () => {
render(<Footer />);
renderWithLocalizationProvider(<Footer />);
const linkMozilla = screen.getByTestId('link-mozilla');

Просмотреть файл

@ -5,7 +5,22 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import Head from './index';
import AppLocalizationProvider from '../../lib/AppLocalizationProvider';
storiesOf('Components/Head', module)
.add('basic', () => <Head />)
.add('with title', () => <Head title="neat feature" />);
.add('basic', () => (
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<Head />
</AppLocalizationProvider>
))
.add('with title', () => (
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<Head title="neat feature" />
</AppLocalizationProvider>
));

Просмотреть файл

@ -2,14 +2,25 @@ import React from 'react';
import { storiesOf } from '@storybook/react';
import { Header } from './index';
import { LogoLockup } from '../LogoLockup';
import AppLocalizationProvider from '../../lib/AppLocalizationProvider';
storiesOf('Components/Header', module)
.add('basic', () => (
<Header left={<div>left content</div>} right={<div>right content</div>} />
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<Header left={<div>left content</div>} right={<div>right content</div>} />
</AppLocalizationProvider>
))
.add('with LogoLockup', () => (
<Header
left={<LogoLockup>Some title</LogoLockup>}
right={<div>right content</div>}
/>
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<Header
left={<LogoLockup>Some title</LogoLockup>}
right={<div>right content</div>}
/>
</AppLocalizationProvider>
));

Просмотреть файл

@ -1,9 +1,15 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import LinkExternal from './index';
import AppLocalizationProvider from '../../lib/AppLocalizationProvider';
storiesOf('Components/LinkExternal', module).add('basic', () => (
<LinkExternal href="https://mozilla.org">
Keep the internet open and accessible to all.
</LinkExternal>
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<LinkExternal href="https://mozilla.org">
Keep the internet open and accessible to all.
</LinkExternal>
</AppLocalizationProvider>
));

Просмотреть файл

@ -3,15 +3,15 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render } from '@testing-library/react';
import LinkExternal from './index';
import { renderWithLocalizationProvider } from '../../lib/test-utils/localizationProvider';
it('renders without imploding', () => {
const className = 'mozilla-link';
const href = 'https://mozilla.org/';
const textContent = 'Keep the internet open and accessible to all.';
const renderResult = render(
const renderResult = renderWithLocalizationProvider(
<LinkExternal {...{ className, href }}>{textContent}</LinkExternal>
);

Просмотреть файл

@ -1,12 +1,32 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import LoadingSpinner, { SpinnerType } from './index';
import AppLocalizationProvider from '../../lib/AppLocalizationProvider';
storiesOf('Components/LoadingSpinner', module)
.add('default', () => <LoadingSpinner />)
.add('blue', () => <LoadingSpinner spinnerType={SpinnerType.Blue} />)
.add('default', () => (
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<LoadingSpinner />
</AppLocalizationProvider>
))
.add('blue', () => (
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<LoadingSpinner spinnerType={SpinnerType.Blue} />
</AppLocalizationProvider>
))
.add('white', () => (
<div className="bg-grey-700">
<LoadingSpinner spinnerType={SpinnerType.White} />
</div>
<AppLocalizationProvider
baseDir="./locales"
userLocales={navigator.languages}
>
<div className="bg-grey-700">
<LoadingSpinner spinnerType={SpinnerType.White} />
</div>
</AppLocalizationProvider>
));

Просмотреть файл

@ -1,10 +1,11 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import { LoadingSpinner, SpinnerType } from './index';
import { renderWithLocalizationProvider } from '../../lib/test-utils/localizationProvider';
it('renders defaults as expected', () => {
render(<LoadingSpinner />);
renderWithLocalizationProvider(<LoadingSpinner />);
const result = screen.queryByTestId('loading-spinner');
const spinnerType = screen.queryByTestId('loading-spinner-blue');
expect(result).toBeInTheDocument();
@ -12,7 +13,7 @@ it('renders defaults as expected', () => {
});
it('renders with custom spinner classNames', () => {
render(<LoadingSpinner imageClassName="testclass" />);
renderWithLocalizationProvider(<LoadingSpinner imageClassName="testclass" />);
const result = screen.queryByTestId('loading-spinner');
const spinnerImage = screen.queryByTestId('loading-spinner-blue');
expect(result).toBeInTheDocument();
@ -20,7 +21,9 @@ it('renders with custom spinner classNames', () => {
});
it('renders blue spinner as expected', () => {
render(<LoadingSpinner spinnerType={SpinnerType.Blue} />);
renderWithLocalizationProvider(
<LoadingSpinner spinnerType={SpinnerType.Blue} />
);
const result = screen.queryByTestId('loading-spinner');
const spinnerType = screen.queryByTestId('loading-spinner-blue');
expect(result).toBeInTheDocument();
@ -28,7 +31,9 @@ it('renders blue spinner as expected', () => {
});
it('renders blue spinner as expected', () => {
render(<LoadingSpinner spinnerType={SpinnerType.White} />);
renderWithLocalizationProvider(
<LoadingSpinner spinnerType={SpinnerType.White} />
);
const result = screen.queryByTestId('loading-spinner');
const spinnerType = screen.queryByTestId('loading-spinner-white');
expect(result).toBeInTheDocument();

Просмотреть файл

@ -1,13 +1,15 @@
import React from 'react';
import { render } from '@testing-library/react';
import LogoLockup from '.';
import { renderWithLocalizationProvider } from '../../lib/test-utils/localizationProvider';
// TODO: functional test for `data-testid="logo-text"` to be
// hidden at mobile
describe('LogoLockup', () => {
it('renders as expected', () => {
const { getByTestId } = render(<LogoLockup>Firefox account</LogoLockup>);
const { getByTestId } = renderWithLocalizationProvider(
<LogoLockup>Firefox account</LogoLockup>
);
expect(getByTestId('logo')).toBeInTheDocument();
expect(getByTestId('logo-text').textContent).toContain('Firefox account');
});

Просмотреть файл

@ -44,11 +44,11 @@ const permitAdditionalJSImports = (config) => {
if (
config.module.rules[1] &&
config.module.rules[1].oneOf &&
config.module.rules[1].oneOf[2].test.toString() ===
config.module.rules[1].oneOf[3].test.toString() ===
'/\\.(js|mjs|jsx|ts|tsx)$/'
) {
config.module.rules[1].oneOf[2].include = [
config.module.rules[1].oneOf[2].include,
config.module.rules[1].oneOf[3].include = [
config.module.rules[1].oneOf[3].include,
allFxa,
];
} else {

Просмотреть файл

@ -15,7 +15,7 @@ const customizeWebpackConfig = ({ config }) => ({
...config,
resolve: {
...config.resolve,
plugins: config.resolve.plugins.map((plugin) => {
plugins: (config.resolve.plugins || []).map((plugin) => {
// Rebuild ModuleScopePlugin with some additional allowed paths
if (
plugin.constructor &&
@ -28,7 +28,7 @@ const customizeWebpackConfig = ({ config }) => ({
// Register a few more extensions to resolve
extensions: [...config.resolve.extensions, '.svg', '.scss', '.css', '.png'],
// Add aliases to some packages shared across the project
alias: { ...config.alias, ...additionalJSImports },
alias: { ...config.resolve.alias, ...additionalJSImports },
},
module: {
...config.module,
@ -43,6 +43,8 @@ const customizeWebpackConfig = ({ config }) => ({
use: ['style-loader', 'css-loader', 'sass-loader'],
},
// Support using SVGs as React components
// fxa-react stories need this since that package does not have the
// webpack config from CRA that includes svgr
{
test: /\.svg$/,
use: [
@ -50,9 +52,17 @@ const customizeWebpackConfig = ({ config }) => ({
loader: require.resolve('@svgr/webpack'),
options: {
svgoConfig: {
plugins: {
removeViewBox: false,
},
plugins: [
{
name: 'preset-default',
params: {
overrides: {
// disable plugins
removeViewBox: false,
},
},
},
],
},
},
},

Просмотреть файл

@ -46,11 +46,18 @@ async function createFluentBundleGenerator(
const mergedBundle = fetched.reduce((obj, cur) => Object.assign(obj, cur));
return getBundleGenerator(currentLocales, mergedBundle);
}
function getBundleGenerator(
locales: string[],
messages: { [key: string]: string[] }
) {
return function* generateFluentBundles() {
for (const locale of currentLocales) {
for (const locale of locales) {
const sourceLocale = EN_GB_LOCALES.includes(locale) ? 'en-GB' : locale;
const cx = new FluentBundle(locale);
for (const i of mergedBundle[sourceLocale]) {
for (const i of messages[sourceLocale]) {
const resource = new FluentResource(i);
cx.addResource(resource);
}
@ -71,6 +78,8 @@ type Props = {
userLocales: ReadonlyArray<string>;
bundles: Array<string>;
children: any;
// pass messages directly in, used in testing
messages?: { [key: string]: string[] };
};
export default class AppLocalizationProvider extends Component<Props, State> {
@ -93,10 +102,20 @@ export default class AppLocalizationProvider extends Component<Props, State> {
}
async componentDidMount() {
if (this.props.messages) {
this.setState({
l10n: new ReactLocalization(
getBundleGenerator(
Object.keys(this.props.messages),
this.props.messages
)()
),
});
return;
}
const { baseDir, userLocales, bundles } = this.state;
const currentLocales = parseAcceptLanguage(userLocales.join(', '));
const bundleGenerator = await createFluentBundleGenerator(
baseDir,
currentLocales,

Просмотреть файл

@ -4,7 +4,7 @@
import React from 'react';
import { DecoratorFn } from '@storybook/react';
import AppLocalizationProvider from 'fxa-react/lib/AppLocalizationProvider';
import AppLocalizationProvider from './AppLocalizationProvider';
// This decorator makes the localization bundles available in the stories.
// If a localized string is available, that will be rendered in the storybook,

Просмотреть файл

@ -0,0 +1,25 @@
import { render } from '@testing-library/react';
import AppLocalizationProvider from 'fxa-react/lib/AppLocalizationProvider';
export function renderWithLocalizationProvider(
children: JSX.Element,
messages = { en: ['testo: lol'] }
): ReturnType<typeof render> {
// by default fluent warns about missing messages, but there's no way to
// disable it right now. see
// https://github.com/projectfluent/fluent.js/issues/411
return render(withLocalizationProvider(children, messages));
}
export function withLocalizationProvider(
children: JSX.Element,
messages = { en: ['testo: lol'] }
) {
return (
<AppLocalizationProvider messages={messages}>
{children}
</AppLocalizationProvider>
);
}
export default { renderWithLocalizationProvider, withLocalizationProvider };

Просмотреть файл

@ -4,7 +4,12 @@
import { FluentBundle, FluentDateTime, FluentVariable } from '@fluent/bundle';
import { Message, Pattern } from '@fluent/bundle/esm/ast';
import { Localized, LocalizedProps, ReactLocalization } from '@fluent/react';
import {
Localized,
LocalizedProps,
ReactLocalization,
LocalizationProvider,
} from '@fluent/react';
import React from 'react';
// Going from react page to non-react page requires a hard navigate. This temporary

Просмотреть файл

@ -3,17 +3,17 @@
"version": "0.0.0",
"description": "Shared components for FxA React Apps",
"exports": {
"./components/": "./dist/packages/fxa-react/components/",
"./components/": "./components/",
"./configs/tailwind": "./configs/tailwind.js",
"./configs/rescripts": "./configs/rescripts.js",
"./configs/storybooks": "./configs/storybooks.js",
"./images/": "./images/",
"./extract-imported-components": "./extract-imported-components.js",
"./lib/": "./dist/packages/fxa-react/lib/"
"./lib/": "./lib/"
},
"scripts": {
"build-css": "tailwindcss -i ./styles/tailwind.css -o ./styles/tailwind.out.css",
"build-storybook": "yarn merge-ftl && NODE_ENV=production yarn build-css && NODE_OPTIONS=--openssl-legacy-provider build-storybook && cp -r public/locales ./storybook-static/locales",
"build-storybook": "yarn merge-ftl && NODE_ENV=production yarn build-css && NODE_OPTIONS=--openssl-legacy-provider storybook build && cp -r public/locales ./storybook-static/locales",
"build": "tsc --build && yarn merge-ftl",
"compile": "tsc --noEmit",
"clean": "git clean -fXd",
@ -24,7 +24,7 @@
"start": "yarn merge-ftl && pm2 start pm2.config.js",
"stop": "pm2 stop pm2.config.js",
"delete": "pm2 delete pm2.config.js",
"storybook": "yarn merge-ftl && yarn build-css && NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 6007 --no-version-updates",
"storybook": "yarn merge-ftl && yarn build-css && NODE_OPTIONS=--openssl-legacy-provider storybook dev -p 6007 --no-version-updates",
"test": "yarn merge-ftl-test && JEST_JUNIT_OUTPUT_FILE=../../artifacts/tests/$npm_package_name/jest-unit.xml jest --coverage --runInBand --logHeapUsage --env=jest-environment-jsdom -t '^(?!.*?#integration).*' --ci --reporters=default --reporters=jest-junit ",
"test-unit": "yarn test",
"test-integration": "echo No integration tests present for $npm_package_name",
@ -35,7 +35,7 @@
"dependencies": {
"@fluent/bundle": "^0.18.0",
"@fluent/langneg": "^0.6.2",
"@fluent/react": "^0.13.1",
"@fluent/react": "^0.14.1",
"async-wait-until": "^2.0.12",
"classnames": "^2.3.1",
"fetch-mock": "^9.11.0",
@ -50,16 +50,20 @@
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@storybook/addon-actions": "^6.5.9",
"@storybook/addon-links": "^6.5.9",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/addons": "^6.5.9",
"@storybook/react": "^6.5.9",
"@svgr/webpack": "^5.5.0",
"@babel/preset-env": "^7.22.5",
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@storybook/addon-actions": "^7.0.23",
"@storybook/addon-links": "^7.0.23",
"@storybook/addon-styling": "^1.3.0",
"@storybook/addons": "^7.0.23",
"@storybook/react": "^7.0.23",
"@storybook/react-webpack5": "^7.0.23",
"@testing-library/dom": "^9.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5",
"@testing-library/user-event": "^14.4.3",
"@types/babel__preset-env": "^7",
"@types/camelcase": "5.2.0",
"@types/classnames": "^2.3.1",
"@types/file-loader": "^5.0.0",
@ -74,7 +78,7 @@
"@types/rimraf": "3.0.0",
"@types/sinon": "^10",
"babel-eslint": "^10.1.0",
"babel-loader": "^8.3.0",
"babel-loader": "^9.1.2",
"babel-preset-react-app": "^10.0.1",
"camelcase": "^6.3.0",
"eslint": "^7.32.0",
@ -91,10 +95,11 @@
"prettier": "^2.3.1",
"rimraf": "^5.0.0",
"sass-loader": "^10.0.3",
"storybook": "^7.0.23",
"tailwindcss": "^3.3.1",
"ts-jest": "^29.1.0",
"typescript": "^4.9.3",
"webpack": "^4.43.0"
"webpack": "^5.84.1"
},
"repository": {
"type": "git",

Просмотреть файл

@ -2,39 +2,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const path = require('path');
const fs = require('fs');
const { permitAdditionalJSImports } = require('fxa-react/configs/rescripts');
module.exports = [
{
devServer: (config) => {
const oldWriteToDisk = config.writeToDisk
? config.writeToDisk
: () => false;
const newConfig = {
...config,
writeToDisk: (path) =>
/public\/locales\/\S+.ftl/.test(path) || oldWriteToDisk(path),
};
return newConfig;
},
webpack: (config) => {
let newConfig = { ...config };
if (!newConfig.output.path) {
newConfig = {
...newConfig,
output: {
...newConfig.output,
path: path.resolve(fs.realpathSync(__dirname), 'build'),
},
};
}
return newConfig;
},
},
permitAdditionalJSImports,
];
module.exports = [permitAdditionalJSImports];

Просмотреть файл

@ -3,6 +3,7 @@ import Page from '../Page';
import Copiable from '../Copiable';
import LinkExternal from 'fxa-react/components/LinkExternal';
import Snippet from '../Snippet';
import { withLocalization } from 'fxa-react/lib/storybooks';
/**
* Note: we have a handful of concatenated classes here, which PurgeCSS will not observe
@ -130,4 +131,4 @@ const Breakpoints = ({ config }) => {
);
};
export default Breakpoints;
export default (config) => withLocalization(() => Breakpoints(config));

Просмотреть файл

@ -2,6 +2,7 @@ import React from 'react';
import Page from '../Page';
import Copiable from '../Copiable';
import Snippet from '../Snippet';
import { withLocalization } from 'fxa-react/lib/storybooks';
const Swatch = ({
color,
@ -124,4 +125,4 @@ const Colors = ({ config }) => {
);
};
export default Colors;
export default (config) => withLocalization(() => Colors(config));

Просмотреть файл

@ -3,8 +3,9 @@ import Page from '../Page';
import LinkExternal from 'fxa-react/components/LinkExternal';
import Copiable from '../Copiable';
import Snippet from '../Snippet';
import { withLocalization } from 'fxa-react/lib/AppLocalizationProvider';
const Introduction = () => (
export const Introduction = () => (
<Page title="Introduction🎨">
<div className="flex">
<div className="max-w-3xl pr-6">
@ -82,4 +83,4 @@ const Introduction = () => (
</Page>
);
export default Introduction;
export default () => withLocalization(Introduction);

Просмотреть файл

@ -2,6 +2,7 @@ import React from 'react';
import Page from '../Page';
import Copiable from '../Copiable';
import Snippet from '../Snippet';
import { withLocalization } from 'fxa-react/lib/storybooks';
const nonIntMap = {
px: {
@ -209,4 +210,4 @@ const Spacing = ({ config }) => {
);
};
export default Spacing;
export default (config) => withLocalization(() => Spacing(config));

Просмотреть файл

@ -1,6 +1,7 @@
import React, { useState } from 'react';
import Page from '../Page';
import Copiable from '../Copiable';
import { withLocalization } from 'fxa-react/lib/AppLocalizationProvider';
/**
* Note: we have a handful of concatenated classes here, which PurgeCSS will not observe
@ -117,4 +118,4 @@ export const Typography = ({ config }) => {
);
};
export default Typography;
export default (config) => withLocalization(() => Typography(config));

Просмотреть файл

@ -5,17 +5,19 @@
module.exports = {
stories: ['./design-guide/main.stories.tsx', '../src/**/*.stories.tsx'],
staticDirs: ['../public'],
core: {
builder: 'webpack5',
},
framework: {
name: '@storybook/react-webpack5',
options: {},
},
features: { storyStoreV7: false },
addons: [
'@storybook/addon-actions',
'@storybook/addon-links',
'storybook-addon-rtl',
{
name: '@storybook/addon-postcss',
options: {
postcssLoaderOptions: {
implementation: require('postcss'),
},
},
},
'@storybook/addon-styling',
'@storybook/preset-create-react-app',
],
};

Просмотреть файл

@ -5,7 +5,7 @@
"private": true,
"scripts": {
"build-css": "tailwindcss -i ./src/styles/tailwind.css -o ./src/styles/tailwind.out.css --postcss",
"build-storybook": "yarn merge-ftl && NODE_ENV=production STORYBOOK_BUILD=1 yarn build-css && yarn legal-clone && LOG_LEVEL=TRACE NODE_OPTIONS=--openssl-legacy-provider build-storybook && cp -r public/locales ./storybook-static/locales",
"build-storybook": "yarn merge-ftl && NODE_ENV=production STORYBOOK_BUILD=1 yarn build-css && yarn legal-clone && LOG_LEVEL=TRACE NODE_OPTIONS=--openssl-legacy-provider storybook build && cp -r public/locales ./storybook-static/locales",
"build": "tsc --build ../fxa-react && NODE_ENV=production yarn build-css && yarn legal-clone && yarn merge-ftl && SKIP_PREFLIGHT_CHECK=true INLINE_RUNTIME_CHUNK=false NODE_OPTIONS=--openssl-legacy-provider rescripts build",
"compile": "tsc --noEmit",
"clean": "git clean -fXd",
@ -18,7 +18,7 @@
"start": "yarn merge-ftl && yarn build-css && pm2 start pm2.config.js && ../../_scripts/check-url.sh localhost:3000/settings/static/js/bundle.js",
"stop": "pm2 stop pm2.config.js",
"delete": "pm2 delete pm2.config.js",
"storybook": "yarn legal-clone && STORYBOOK_BUILD=1 yarn build-css && NODE_OPTIONS=--openssl-legacy-provider start-storybook -p 6008 --no-version-updates",
"storybook": "yarn legal-clone && STORYBOOK_BUILD=1 yarn build-css && NODE_OPTIONS=--openssl-legacy-provider storybook dev -p 6008 --no-version-updates",
"test": "yarn legal-clone && yarn merge-ftl-test && SKIP_PREFLIGHT_CHECK=true rescripts test --watchAll=false",
"test-watch": "yarn legal-clone && yarn merge-ftl-test && SKIP_PREFLIGHT_CHECK=true rescripts test",
"test-coverage": "yarn legal-clone && yarn test --coverage --watchAll=false",
@ -71,7 +71,7 @@
"@apollo/client": "^3.4.5",
"@emotion/react": "^11.10.0",
"@emotion/styled": "^11.10.4",
"@fluent/react": "^0.13.1",
"@fluent/react": "^0.14.1",
"@material-ui/core": "v5.0.0-alpha.24",
"@reach/router": "^1.3.4",
"@types/material-ui": "^0.21.8",
@ -91,7 +91,7 @@
"react-easy-crop": "^4.7.4",
"react-hook-form": "^6.15.8",
"react-markdown": "^8.0.5",
"react-scripts": "^4.0.3",
"react-scripts": "^5.0.1",
"react-webcam": "^7.0.0",
"rehype-raw": "^6.1.1",
"subscriptions-transport-ws": "^0.11.0",
@ -99,15 +99,22 @@
"uuid": "^9.0.0"
},
"devDependencies": {
"@babel/core": "^7.15.0",
"@babel/core": "^7.22.5",
"@babel/types": "^7.22.5",
"@rescripts/cli": "0.0.16",
"@sentry/browser": "^6.19.7",
"@sentry/integrations": "^6.19.1",
"@storybook/addon-actions": "^6.5.9",
"@storybook/addon-links": "^6.5.9",
"@storybook/addon-postcss": "^2.0.0",
"@storybook/addons": "^6.5.9",
"@storybook/react": "^6.5.9",
"@storybook/addon-actions": "^7.0.23",
"@storybook/addon-essentials": "^7.0.24",
"@storybook/addon-interactions": "^7.0.24",
"@storybook/addon-links": "^7.0.23",
"@storybook/addon-styling": "^1.3.0",
"@storybook/addons": "^7.0.23",
"@storybook/blocks": "^7.0.24",
"@storybook/preset-create-react-app": "^7.0.23",
"@storybook/react": "^7.0.23",
"@storybook/react-webpack5": "^7.0.23",
"@storybook/testing-library": "^0.0.14-next.2",
"@testing-library/dom": "^9.2.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5",
@ -118,6 +125,7 @@
"@types/jest": "^26.0.23",
"@types/lodash.groupby": "^4",
"@types/node": "^18.14.2",
"@types/prop-types": "^15",
"@types/reach__router": "^1.3.11",
"@types/react": "^17.0.14",
"@types/react-dom": "^17.0.9",
@ -127,7 +135,8 @@
"@types/uuid": "^8",
"@types/webpack": "5.28.0",
"autoprefixer": "^10.4.7",
"babel-loader": "^8.3.0",
"babel-loader": "^9.1.2",
"babel-plugin-named-exports-order": "^0.0.2",
"css-loader": "^3.6.0",
"eslint": "^7.32.0",
"eslint-config-react-app": "^6.0.0",
@ -144,11 +153,13 @@
"postcss": "^8.4.14",
"postcss-assets": "^6.0.0",
"postcss-import": "^15.1.0",
"prop-types": "^15.8.1",
"react-test-renderer": "^17.0.2",
"sinon": "^15.0.1",
"storybook": "^7.0.23",
"storybook-addon-rtl": "^0.5.0",
"style-loader": "^1.3.0",
"tailwindcss": "^3.3.1",
"webpack": "^4.43.0"
"webpack": "^5.84.1"
}
}

Просмотреть файл

@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React, { ReactNode } from 'react';
import { render, act } from '@testing-library/react';
import { act } from '@testing-library/react';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
import App from '.';
import * as Metrics from '../../lib/metrics';
import { useAccount, useInitialState } from '../../models';
@ -70,7 +71,9 @@ describe('metrics', () => {
};
await act(async () => {
render(<App flowQueryParams={updatedFlowQueryParams} />);
renderWithLocalizationProvider(
<App flowQueryParams={updatedFlowQueryParams} />
);
});
expect(flowInit).toHaveBeenCalledWith(true, {

Просмотреть файл

@ -5,7 +5,7 @@
import React from 'react';
import AppLayout from './index';
import { Meta } from '@storybook/react';
import { withLocalization } from '../../../.storybook/decorators';
import { withLocalization } from 'fxa-react/lib/storybooks';
export default {
title: 'Components/AppLayout',

Просмотреть файл

@ -3,11 +3,12 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
import AppLayout from '.';
it('renders as expected with children', async () => {
render(
renderWithLocalizationProvider(
<AppLayout>
<p>Hello, world!</p>
</AppLayout>

Просмотреть файл

@ -7,7 +7,7 @@ import Banner, { BannerType } from '.';
import { Meta } from '@storybook/react';
import AppLayout from '../AppLayout';
import { Subject } from './mocks';
import { withLocalization } from '../../../.storybook/decorators';
import { withLocalization } from 'fxa-react/lib/storybooks';
export default {
title: 'Components/Banner',

Просмотреть файл

@ -6,7 +6,7 @@ import React from 'react';
import { Meta } from '@storybook/react';
import AppLayout from '../AppLayout';
import ButtonDownloadRecoveryKey, { fileContentVariation } from '.';
import { withLocalization } from '../../../.storybook/decorators';
import { withLocalization } from 'fxa-react/lib/storybooks';
import { Account, AppContext } from '../../models';
import { MOCK_ACCOUNT, mockAppContext } from '../../models/mocks';

Просмотреть файл

@ -3,7 +3,8 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { fireEvent, screen } from '@testing-library/react';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
import { Account, AppContext } from '../../models';
import { ButtonDownloadRecoveryKey } from '.';
import { MOCK_ACCOUNT } from '../../models/mocks';
@ -37,7 +38,7 @@ beforeAll(() => {
describe('ButtonDownloadRecoveryKey', () => {
it('renders button as expected', () => {
render(
renderWithLocalizationProvider(
<AppContext.Provider value={{ account }}>
<ButtonDownloadRecoveryKey {...{ recoveryKeyValue, viewName }} />
</AppContext.Provider>
@ -46,7 +47,7 @@ describe('ButtonDownloadRecoveryKey', () => {
});
it('sets the filename as expected with a reasonably-sized email', () => {
render(
renderWithLocalizationProvider(
<AppContext.Provider value={{ account }}>
<ButtonDownloadRecoveryKey {...{ recoveryKeyValue, viewName }} />
</AppContext.Provider>
@ -74,7 +75,7 @@ describe('ButtonDownloadRecoveryKey', () => {
});
it('sets the filename with a truncated email as expected when the email is very long', () => {
render(
renderWithLocalizationProvider(
<AppContext.Provider value={{ account: accountWithLongEmail }}>
<ButtonDownloadRecoveryKey {...{ recoveryKeyValue, viewName }} />
</AppContext.Provider>
@ -98,7 +99,7 @@ describe('ButtonDownloadRecoveryKey', () => {
// including validating that the expected key is included and matches the key in the DataBlock
it('emits a metrics event when the link is clicked', () => {
render(
renderWithLocalizationProvider(
<AppContext.Provider value={{ account }}>
<ButtonDownloadRecoveryKey {...{ recoveryKeyValue, viewName }} />
</AppContext.Provider>

Просмотреть файл

@ -14,7 +14,7 @@ import {
MOCK_SUBHEADING,
} from './mocks';
import { MozServices } from '../../lib/types';
import { withLocalization } from '../../../.storybook/decorators';
import { withLocalization } from 'fxa-react/lib/storybooks';
export default {
title: 'Components/CardHeader',

Просмотреть файл

@ -4,7 +4,8 @@
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import { screen } from '@testing-library/react';
import { renderWithLocalizationProvider } from 'fxa-react/lib/test-utils/localizationProvider';
import CardHeader from '.';
import {
MOCK_DEFAULT_HEADING_FTL_ID,
@ -24,7 +25,7 @@ describe('CardHeader', () => {
// });
it('renders as expected when no serviceName is provided', () => {
render(
renderWithLocalizationProvider(
<CardHeader
headingWithDefaultServiceFtlId={MOCK_DEFAULT_HEADING_FTL_ID}
headingWithCustomServiceFtlId={MOCK_CUSTOM_HEADING_FTL_ID}
@ -41,7 +42,7 @@ describe('CardHeader', () => {
});
it('renders as expected when a serviceName is provided', () => {
render(
renderWithLocalizationProvider(
<CardHeader
headingWithDefaultServiceFtlId={MOCK_DEFAULT_HEADING_FTL_ID}
headingWithCustomServiceFtlId={MOCK_CUSTOM_HEADING_FTL_ID}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше