1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-25 03:20:23 +01:00

Increase Jest unit test coverage for the Swaps feature to ~25% (#10900)

* Swaps: Show a network name dynamically in a tooltip

* Replace “Ethereum” with “$1”, change “Test” to “Testnet”

* Replace 이더리움 with $1

* Translate network names, use ‘Ethereum’ by default if a translation is not available yet

* Reorder messages to resolve ESLint issues

* Add a snapshot test for the FeeCard component, increase Jest threshold

* Enable snapshot testing into external .snap files in ESLint

* Add the “networkNameEthereum” key in ko/messages.json, remove default “Ethereum” value

* Throw an error if chain ID is not supported by the Swaps feature

* Use string literals when calling the `t` fn,

* Watch Jest tests silently (no React warnings in terminal, only errors)

* Add @testing-library/jest-dom, import it before running Jest tests

* Add snapshot testing of Swaps’ React components for happy paths, increase minimum threshold for Jest

* Add the test/jest folder for Jest setup and shared functions, use it in Swaps Jest tests

* Fix ESLint issues, update linting config

* Enable ESLint for .snap files (Jest snapshots), throw an error if a snapshot is bigger than 50 lines

* Don’t run lint:fix for .snap files

* Move `createProps` outside of `describe` blocks, move store creation inside tests

* Use translations instead of keys, update a rendering function to load translations

* Make sure all Jest snapshots are shorter than 50 lines (default limit)

* Add / update props for Swaps tests

* Fix React warnings when running tests for Swaps
This commit is contained in:
Daniel 2021-04-21 12:34:35 -07:00 committed by GitHub
parent d01bc9bb51
commit fbbdaf04ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 1149 additions and 188 deletions

View File

@ -70,7 +70,7 @@ module.exports = {
},
overrides: [
{
files: ['ui/**/*.js', 'test/lib/render-helpers.js'],
files: ['ui/**/*.js', 'test/lib/render-helpers.js', 'test/jest/*.js'],
plugins: ['react'],
extends: ['plugin:react/recommended', 'plugin:react-hooks/recommended'],
rules: {
@ -108,17 +108,28 @@ module.exports = {
},
{
files: ['**/*.test.js'],
excludedFiles: ['ui/**/*.test.js'],
excludedFiles: ['ui/**/*.test.js', 'ui/app/__mocks__/*.js'],
extends: ['@metamask/eslint-config-mocha'],
rules: {
'mocha/no-setup-in-describe': 'off',
},
},
{
files: ['ui/**/*.test.js'],
files: ['**/__snapshots__/*.snap'],
plugins: ['jest'],
rules: {
'jest/no-large-snapshots': [
'error',
{ maxSize: 50, inlineMaxSize: 50 },
],
},
},
{
files: ['ui/**/*.test.js', 'ui/app/__mocks__/*.js'],
extends: ['@metamask/eslint-config-jest'],
rules: {
'jest/no-restricted-matchers': 'off',
'import/unambiguous': 'off',
},
},
{

View File

@ -3,12 +3,13 @@ module.exports = {
coverageDirectory: 'jest-coverage/',
coverageThreshold: {
global: {
branches: 6.94,
functions: 8.85,
lines: 11.76,
statements: 11.78,
branches: 21.24,
functions: 23.01,
lines: 27.19,
statements: 27.07,
},
},
setupFiles: ['./test/setup.js', './test/env.js'],
setupFilesAfterEnv: ['./test/jest/setup.js'],
testMatch: ['**/ui/**/?(*.)+(test).js'],
};

View File

@ -24,6 +24,7 @@
"test:unit:global": "mocha --exit --require test/env.js --require test/setup.js --recursive test/unit-global/*.test.js",
"test:unit:jest": "jest",
"test:unit:jest:watch": "jest --watch",
"test:unit:jest:watch:silent": "jest --watch --silent",
"test:unit:jest:ci": "jest --maxWorkers=2",
"test:unit:lax": "mocha --exit --require test/env.js --require test/setup.js --ignore './app/scripts/controllers/permissions/*.test.js' --recursive './{app,shared}/**/*.test.js'",
"test:unit:strict": "mocha --exit --require test/env.js --require test/setup.js --recursive './app/scripts/controllers/permissions/*.test.js'",
@ -38,7 +39,7 @@
"test:coverage:path": "nyc --check-coverage yarn test:unit:path",
"ganache:start": "./development/run-ganache.sh",
"sentry:publish": "node ./development/sentry-publish.js",
"lint": "prettier --check '**/*.json' && eslint . --ext js --cache && yarn lint:styles",
"lint": "prettier --check '**/*.json' && eslint . --ext js,snap --cache && yarn lint:styles",
"lint:fix": "prettier --write '**/*.json' && eslint . --ext js --cache --fix",
"lint:changed": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint",
"lint:changed:fix": "{ git ls-files --others --exclude-standard ; git diff-index --name-only --diff-filter=d HEAD ; } | grep --regexp='[.]js$' | tr '\\n' '\\0' | xargs -0 eslint --fix",
@ -218,6 +219,7 @@
"@storybook/core": "^6.1.17",
"@storybook/react": "^6.1.17",
"@storybook/storybook-deployer": "^2.8.7",
"@testing-library/jest-dom": "^5.11.10",
"@testing-library/react": "^10.4.8",
"@testing-library/react-hooks": "^3.2.1",
"@types/react": "^16.9.53",

4
test/jest/index.js Normal file
View File

@ -0,0 +1,4 @@
import { createSwapsMockStore } from './mock-store';
import { renderWithProvider } from './rendering';
export { createSwapsMockStore, renderWithProvider };

69
test/jest/mock-store.js Normal file
View File

@ -0,0 +1,69 @@
import { MAINNET_CHAIN_ID } from '../../shared/constants/network';
export const createSwapsMockStore = () => {
return {
swaps: {
customGas: {
fallBackPrice: 5,
},
},
metamask: {
provider: {
chainId: MAINNET_CHAIN_ID,
},
cachedBalances: {
[MAINNET_CHAIN_ID]: 5,
},
accounts: {
'0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc': {
address: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
balance: '0x0',
},
'0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b': {
address: '0xec1adf982415d2ef5ec55899b9bfb8bc0f29251b',
balance: '0x0',
},
},
selectedAddress: '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc',
frequentRpcListDetail: [],
swapsState: {
quotes: {},
fetchParams: {
metaData: {
sourceTokenInfo: {
symbol: 'BAT',
},
destinationTokenInfo: {
symbol: 'ETH',
},
},
},
tokens: [
{
erc20: true,
symbol: 'BAT',
decimals: 18,
address: '0x0D8775F648430679A709E98d2b0Cb6250d2887EF',
},
{
erc20: true,
symbol: 'USDT',
decimals: 6,
address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
},
],
tradeTxId: null,
approveTxId: null,
quotesLastFetched: null,
customMaxGas: '',
customGasPrice: null,
selectedAggId: null,
customApproveTxData: '',
errorKey: '',
topAggId: null,
routeState: '',
swapsFeatureIsLive: false,
},
},
};
};

59
test/jest/rendering.js Normal file
View File

@ -0,0 +1,59 @@
import React, { useMemo } from 'react';
import { Provider } from 'react-redux';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { I18nContext, LegacyI18nProvider } from '../../ui/app/contexts/i18n';
import { getMessage } from '../../ui/app/helpers/utils/i18n-helper';
import * as en from '../../app/_locales/en/messages.json';
export const I18nProvider = (props) => {
const { currentLocale, current, en: eng } = props;
const t = useMemo(() => {
return (key, ...args) =>
getMessage(currentLocale, current, key, ...args) ||
getMessage(currentLocale, eng, key, ...args);
}, [currentLocale, current, eng]);
return (
<I18nContext.Provider value={t}>{props.children}</I18nContext.Provider>
);
};
I18nProvider.propTypes = {
currentLocale: PropTypes.string,
current: PropTypes.object,
en: PropTypes.object,
children: PropTypes.node,
};
I18nProvider.defaultProps = {
children: undefined,
};
export function renderWithProvider(component, store) {
const Wrapper = ({ children }) => {
const WithoutStore = () => (
<MemoryRouter initialEntries={['/']} initialIndex={0}>
<I18nProvider currentLocale="en" current={en} en={en}>
<LegacyI18nProvider>{children}</LegacyI18nProvider>
</I18nProvider>
</MemoryRouter>
);
return store ? (
<Provider store={store}>
<WithoutStore></WithoutStore>
</Provider>
) : (
<WithoutStore></WithoutStore>
);
};
Wrapper.propTypes = {
children: PropTypes.node,
};
return render(component, { wrapper: Wrapper });
}

2
test/jest/setup.js Normal file
View File

@ -0,0 +1,2 @@
// jest-setup.js is for Jest-specific setup only and runs before our Jest tests.
import '@testing-library/jest-dom';

6
ui/app/__mocks__/react-router-dom.js vendored Normal file
View File

@ -0,0 +1,6 @@
const originalModule = jest.requireActual('react-router-dom');
module.exports = {
...originalModule,
useHistory: jest.fn(),
};

View File

@ -0,0 +1,16 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ActionableMessage renders the component with initial props 1`] = `
<div>
<div
class="actionable-message"
>
<div
class="actionable-message__message"
>
I am an actionable message!
</div>
</div>
</div>
`;

View File

@ -80,6 +80,6 @@ ActionableMessage.propTypes = {
}),
className: PropTypes.string,
type: PropTypes.string,
withRightButton: PropTypes.boolean,
withRightButton: PropTypes.bool,
infoTooltipText: PropTypes.string,
};

View File

@ -0,0 +1,22 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import ActionableMessage from '.';
const createProps = (customProps = {}) => {
return {
message: 'I am an actionable message!',
...customProps,
};
};
describe('ActionableMessage', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { container, getByText } = renderWithProvider(
<ActionableMessage {...props} />,
);
expect(getByText(props.message)).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});

View File

@ -0,0 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`AwaitingSwap renders the component with initial props 1`] = `
<div
class="awaiting-swap__main-descrption"
>
<span>
Your
<span
class="awaiting-swap__amount-and-symbol"
>
ETH
</span>
will be added to your account once this transaction has processed.
</span>
</div>
`;
exports[`AwaitingSwap renders the component with initial props 2`] = `
<div
class="swaps-footer"
>
<div
class="swaps-footer__buttons"
>
<div
class="page-container__footer swaps-footer__custom-page-container-footer-class"
>
<footer>
<button
class="button btn-primary page-container__footer-button swaps-footer__custom-page-container-footer-button-class swaps-footer__custom-page-container-footer-button-class--single"
data-testid="page-container-footer-next"
disabled=""
role="button"
tabindex="0"
>
View in activity
</button>
</footer>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,37 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import {
renderWithProvider,
createSwapsMockStore,
} from '../../../../../test/jest';
import AwaitingSwap from '.';
const createProps = (customProps = {}) => {
return {
swapComplete: false,
txHash: 'txHash',
tokensReceived: 'tokensReceived',
submittingSwap: true,
inputValue: 5,
maxSlippage: 3,
...customProps,
};
};
describe('AwaitingSwap', () => {
it('renders the component with initial props', () => {
const store = configureMockStore()(createSwapsMockStore());
const { getByText } = renderWithProvider(
<AwaitingSwap {...createProps()} />,
store,
);
expect(getByText('Processing')).toBeInTheDocument();
expect(getByText('View on Etherscan')).toBeInTheDocument();
expect(getByText('View in activity')).toBeInTheDocument();
expect(
document.querySelector('.awaiting-swap__main-descrption'),
).toMatchSnapshot();
expect(document.querySelector('.swaps-footer')).toMatchSnapshot();
});
});

View File

@ -94,6 +94,7 @@ export default function CountdownTimer({
return (
<div className="countdown-timer">
<div
data-testid="countdown-timer__timer-container"
className={classnames('countdown-timer__timer-container', {
'countdown-timer__timer-container--warning':
warningTime && timeBelowWarningTime(timer, warningTime),

View File

@ -0,0 +1,31 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import {
renderWithProvider,
createSwapsMockStore,
} from '../../../../../test/jest';
import CountdownTimer from '.';
const createProps = (customProps = {}) => {
return {
timeStarted: 1,
timeOnly: true,
timerBase: 5,
warningTime: '0:30',
labelKey: 'swapNewQuoteIn',
infoTooltipLabelKey: 'swapQuotesAreRefreshed',
...customProps,
};
};
describe('CountdownTimer', () => {
it('renders the component with initial props', () => {
const store = configureMockStore()(createSwapsMockStore());
const { getByTestId } = renderWithProvider(
<CountdownTimer {...createProps()} />,
store,
);
expect(getByTestId('countdown-timer__timer-container')).toBeInTheDocument();
});
});

View File

@ -0,0 +1,20 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DropdownInputPair renders the component with initial props 1`] = `
<div
class="MuiFormControl-root MuiTextField-root dropdown-input-pair__input MuiFormControl-marginDense MuiFormControl-fullWidth"
>
<div
class="MuiInputBase-root MuiInput-root TextField-inputRoot-12 MuiInputBase-fullWidth MuiInput-fullWidth MuiInputBase-formControl MuiInput-formControl MuiInputBase-marginDense MuiInput-marginDense"
>
<input
aria-invalid="false"
class="MuiInputBase-input MuiInput-input MuiInputBase-inputMarginDense MuiInput-inputMarginDense"
dir="auto"
placeholder="0"
type="text"
value=""
/>
</div>
</div>
`;

View File

@ -0,0 +1,23 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import DropdownInputPair from '.';
const createProps = (customProps = {}) => {
return {
...customProps,
};
};
describe('DropdownInputPair', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { getByPlaceholderText } = renderWithProvider(
<DropdownInputPair {...props} />,
);
expect(getByPlaceholderText('0')).toBeInTheDocument();
expect(
document.querySelector('.dropdown-input-pair__input'),
).toMatchSnapshot();
});
});

View File

@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`DropdownSearchList renders the component with initial props 1`] = `
<div>
<div
class="dropdown-search-list"
tabindex="0"
>
<div
class="dropdown-search-list__selector-closed-container"
>
<div
class="dropdown-search-list__selector-closed"
>
<img
alt=""
class="url-icon dropdown-search-list__selector-closed-icon"
src="iconUrl"
/>
<div
class="dropdown-search-list__labels"
>
<div
class="dropdown-search-list__item-labels"
>
<span
class="dropdown-search-list__closed-primary-label"
>
symbol
</span>
</div>
</div>
</div>
<i
class="fa fa-caret-down fa-lg dropdown-search-list__caret"
/>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,25 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import DropdownSearchList from '.';
const createProps = (customProps = {}) => {
return {
startingItem: {
iconUrl: 'iconUrl',
symbol: 'symbol',
},
...customProps,
};
};
describe('DropdownSearchList', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { container, getByText } = renderWithProvider(
<DropdownSearchList {...props} />,
);
expect(container).toMatchSnapshot();
expect(getByText('symbol')).toBeInTheDocument();
});
});

View File

@ -0,0 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ExchangeRateDisplay renders the component with initial props 1`] = `
<div>
<div
class="exchange-rate-display"
>
<span>
1
</span>
<span
class="exchange-rate-display__bold"
>
ETH
</span>
<span>
=
</span>
<span>
0.1
</span>
<span
class="exchange-rate-display__bold"
>
BAT
</span>
<div
class="exchange-rate-display__switch-arrows"
>
<svg
fill="none"
height="13"
viewBox="0 0 13 13"
width="13"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.15294 4.38514H9.99223L8.50853 5.86884C8.30421 6.07297 8.30421 6.40418 8.50853 6.60869C8.61069 6.71085 8.74443 6.76203 8.87836 6.76203C9.01229 6.76203 9.14603 6.71085 9.24819 6.60869L11.6249 4.23219C11.649 4.20803 11.6707 4.1814 11.6899 4.15305C11.6947 4.14563 11.6981 4.13726 11.7025 4.12965C11.7154 4.10815 11.7282 4.08646 11.7381 4.06325C11.7426 4.05222 11.7447 4.04043 11.7487 4.0292C11.7558 4.00827 11.7636 3.98754 11.7681 3.96547C11.775 3.93161 11.7786 3.89717 11.7786 3.86198C11.7786 3.82678 11.775 3.79235 11.7681 3.75849C11.7638 3.73642 11.756 3.71568 11.7487 3.69476C11.7447 3.68353 11.7428 3.67174 11.7381 3.6607C11.7282 3.63749 11.7156 3.616 11.7025 3.59431C11.6981 3.5867 11.6947 3.57833 11.6899 3.57091C11.6707 3.54256 11.649 3.51593 11.6249 3.49177L9.24876 1.11564C9.04444 0.911322 8.71342 0.911322 8.50891 1.11564C8.30459 1.31977 8.30459 1.65098 8.50891 1.85549L9.99223 3.339H4.15294C2.22978 3.339 0.665039 4.90374 0.665039 6.8269C0.665039 7.11588 0.899227 7.35007 1.1882 7.35007C1.47718 7.35007 1.71137 7.11588 1.71137 6.8269C1.71137 5.48037 2.80659 4.38514 4.15294 4.38514ZM12.2066 6.57445C11.9177 6.57445 11.6835 6.80864 11.6835 7.09762C11.6835 8.44396 10.5883 9.53919 9.24191 9.53919H3.40262L4.88632 8.05549C5.09064 7.85136 5.09064 7.52014 4.88632 7.31563C4.682 7.11112 4.35098 7.11131 4.14647 7.31563L1.76977 9.69233C1.74561 9.71649 1.72393 9.74312 1.70471 9.77147C1.70015 9.7787 1.69691 9.78669 1.69273 9.79392C1.6796 9.81561 1.66647 9.83748 1.65677 9.86126C1.6524 9.87211 1.6503 9.88371 1.64631 9.89475C1.63927 9.91586 1.63128 9.93679 1.62671 9.95905C1.61986 9.99291 1.61625 10.0273 1.61625 10.0625C1.61625 10.0977 1.61986 10.1322 1.62671 10.166C1.63109 10.1883 1.63908 10.2092 1.64631 10.2303C1.6503 10.2414 1.65221 10.253 1.65677 10.2638C1.66666 10.2874 1.6796 10.3093 1.69273 10.3312C1.69691 10.3384 1.70015 10.3464 1.70471 10.3536C1.72393 10.382 1.74561 10.4086 1.76977 10.4328L4.14609 12.8091C4.24825 12.9112 4.38199 12.9624 4.51592 12.9624C4.64985 12.9624 4.78359 12.9112 4.88575 12.8091C5.09007 12.6049 5.09007 12.2737 4.88575 12.0692L3.40243 10.5857H9.24172C11.1649 10.5857 12.7296 9.02097 12.7296 7.09781C12.7298 6.80864 12.4956 6.57445 12.2066 6.57445Z"
fill="black"
/>
</svg>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,28 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import ExchangeRateDisplay from '.';
const createProps = (customProps = {}) => {
return {
primaryTokenValue: '2000000000000000000',
primaryTokenDecimals: 18,
primaryTokenSymbol: 'ETH',
secondaryTokenValue: '200000000000000000',
secondaryTokenDecimals: 18,
secondaryTokenSymbol: 'BAT',
...customProps,
};
};
describe('ExchangeRateDisplay', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { container, getByText } = renderWithProvider(
<ExchangeRateDisplay {...props} />,
);
expect(getByText(props.primaryTokenSymbol)).toBeInTheDocument();
expect(getByText(props.secondaryTokenSymbol)).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});

View File

@ -1,172 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`FeeCard renders the component with initial props 1`] = `
<div>
<div
class="fee-card__savings-and-quotes-header"
>
<div
class="fee-card"
class="fee-card__savings-and-quotes-row"
>
<div
class="fee-card__savings-and-quotes-header"
<p
class="fee-card__savings-text"
>
<div
class="fee-card__savings-and-quotes-row"
Using the best quote
</p>
<div
class="fee-card__quote-link-container"
>
<p
class="fee-card__quote-link-text"
>
<p
class="fee-card__savings-text"
>
[swapUsingBestQuote]
</p>
<div
class="fee-card__quote-link-container"
>
<p
class="fee-card__quote-link-text"
>
[swapNQuotes]
</p>
<div
class="fee-card__caret-right"
>
<i
class="fa fa-angle-up"
/>
</div>
</div>
6 quotes
</p>
<div
class="fee-card__caret-right"
>
<i
class="fa fa-angle-up"
/>
</div>
</div>
</div>
</div>
`;
exports[`FeeCard renders the component with initial props 2`] = `
<div
class="fee-card__top-bordered-row"
>
<div
class="fee-card__row-label"
>
<div
class="fee-card__main"
class="fee-card__row-header-text"
>
Quote includes a 0.875% MetaMask fee
</div>
<div
class="info-tooltip"
>
<div
class="fee-card__row-header"
>
<div>
<div
class="fee-card__row-header-text--bold"
>
[swapEstimatedNetworkFee]
</div>
<div
class="info-tooltip"
>
<div
class="fee-card__row-label fee-card__info-tooltip-container"
>
<div
aria-describedby="tippy-tooltip-1"
class="info-tooltip__tooltip-container fee-card__info-tooltip-content-container"
data-original-title="null"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<img
alt=""
src="images/mm-info-icon.svg"
/>
</div>
</div>
</div>
</div>
<div>
<div
class="fee-card__row-header-secondary--bold"
/>
<div
class="fee-card__row-header-primary--bold"
/>
</div>
</div>
<div
class="fee-card__row-header"
>
<div>
<div
class="fee-card__row-header-text"
>
[swapMaxNetworkFees]
</div>
<div
class="fee-card__link"
>
[edit]
</div>
</div>
<div>
<div
class="fee-card__row-header-secondary"
/>
</div>
</div>
<div
class="fee-card__row-header"
class="fee-card__info-tooltip-container"
>
<div
class="fee-card__row-label"
aria-describedby="tippy-tooltip-3"
class="info-tooltip__tooltip-container"
data-original-title="null"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<div
class="fee-card__row-header-text"
>
[swapThisWillAllowApprove]
</div>
<div
class="info-tooltip"
>
<div>
<div
aria-describedby="tippy-tooltip-2"
class="info-tooltip__tooltip-container fee-card__info-tooltip-container"
data-original-title="null"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<img
alt=""
src="images/mm-info-icon.svg"
/>
</div>
</div>
</div>
</div>
<div
class="fee-card__link"
>
[swapEditLimit]
</div>
</div>
<div
class="fee-card__top-bordered-row"
>
<div
class="fee-card__row-label"
>
<div
class="fee-card__row-header-text"
>
[swapQuoteIncludesRate]
</div>
<div
class="info-tooltip"
>
<div
class="fee-card__info-tooltip-container"
>
<div
aria-describedby="tippy-tooltip-3"
class="info-tooltip__tooltip-container"
data-original-title="null"
data-tooltipped=""
style="display: inline;"
tabindex="0"
>
<img
alt=""
src="images/mm-info-icon.svg"
/>
</div>
</div>
</div>
<img
alt=""
src="images/mm-info-icon.svg"
/>
</div>
</div>
</div>

View File

@ -1,31 +1,61 @@
import React from 'react';
import { render } from '@testing-library/react';
import { renderWithProvider } from '../../../../../test/jest';
import { MAINNET_CHAIN_ID } from '../../../../../shared/constants/network';
import FeeCard from './fee-card';
import FeeCard from '.';
const createProps = (customProps = {}) => {
return {
primaryFee: {
fee: '0.0441 ETH',
maxFee: '0.04851 ETH',
},
secondaryFee: {
fee: '$101.98',
maxFee: '$112.17',
},
hideTokenApprovalRow: false,
onFeeCardMaxRowClick: jest.fn(),
tokenApprovalTextComponent: (
<span
key="swaps-view-quote-approve-symbol-1"
className="view-quote__bold"
>
ABC
</span>
),
tokenApprovalSourceTokenSymbol: 'ABC',
onTokenApprovalClick: jest.fn(),
metaMaskFee: '0.875',
isBestQuote: true,
numberOfQuotes: 6,
onQuotesClick: jest.fn(),
tokenConversionRate: 0.015,
chainId: MAINNET_CHAIN_ID,
...customProps,
};
};
describe('FeeCard', () => {
const createProps = (customProps = {}) => {
return {
primaryFee: '1 ETH',
secondaryFee: '2500 USD',
hideTokenApprovalRow: false,
onFeeCardMaxRowClick: jest.fn(),
tokenApprovalTextComponent: <></>,
tokenApprovalSourceTokenSymbol: 'ABC',
onTokenApprovalClick: jest.fn(),
metaMaskFee: '0.875',
isBestQuote: true,
numberOfQuotes: 6,
onQuotesClick: jest.fn(),
tokenConversionRate: 0.015,
chainId: MAINNET_CHAIN_ID,
...customProps,
};
};
it('renders the component with initial props', () => {
const { container } = render(<FeeCard {...createProps()} />);
expect(container).toMatchSnapshot();
const props = createProps();
const { getByText } = renderWithProvider(<FeeCard {...props} />);
expect(getByText('Using the best quote')).toBeInTheDocument();
expect(getByText('6 quotes')).toBeInTheDocument();
expect(getByText('Max network fee')).toBeInTheDocument();
expect(getByText('Estimated network fee')).toBeInTheDocument();
expect(getByText(props.primaryFee.fee)).toBeInTheDocument();
expect(getByText(props.primaryFee.maxFee)).toBeInTheDocument();
expect(getByText(props.secondaryFee.fee)).toBeInTheDocument();
expect(getByText(props.secondaryFee.maxFee)).toBeInTheDocument();
expect(
getByText('Quote includes a 0.875% MetaMask fee'),
).toBeInTheDocument();
expect(
document.querySelector('.fee-card__savings-and-quotes-header'),
).toMatchSnapshot();
expect(
document.querySelector('.fee-card__top-bordered-row'),
).toMatchSnapshot();
});
});

View File

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`IntroPopup renders the component with initial props 1`] = `
<div>
<div
class="intro-popup"
/>
</div>
`;

View File

@ -0,0 +1,24 @@
import React from 'react';
import configureMockStore from 'redux-mock-store';
import {
renderWithProvider,
createSwapsMockStore,
} from '../../../../../test/jest';
import IntroPopup from '.';
const createProps = (customProps = {}) => {
return {
onClose: jest.fn(),
...customProps,
};
};
describe('IntroPopup', () => {
it('renders the component with initial props', () => {
const store = configureMockStore()(createSwapsMockStore());
const props = createProps();
const { container } = renderWithProvider(<IntroPopup {...props} />, store);
expect(container).toMatchSnapshot();
});
});

View File

@ -0,0 +1,109 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`MainQuoteSummary renders the component with initial props 1`] = `
<div
class="main-quote-summary__source-row"
>
<span
class="main-quote-summary__source-row-value"
title="2"
>
2
</span>
<i
class="icon-with-fallback__fallback url-icon__fallback main-quote-summary__icon-fallback"
>
E
</i>
<span
class="main-quote-summary__source-row-symbol"
title="ETH"
>
ETH
</span>
</div>
`;
exports[`MainQuoteSummary renders the component with initial props 2`] = `
<div
class="main-quote-summary__destination-row"
>
<i
class="icon-with-fallback__fallback url-icon__fallback main-quote-summary__icon-fallback"
>
B
</i>
<span
class="main-quote-summary__destination-row-symbol"
>
BAT
</span>
</div>
`;
exports[`MainQuoteSummary renders the component with initial props 3`] = `
<div
class="main-quote-summary__quote-large"
>
<div>
<div
class=""
style="display: inline;"
tabindex="0"
>
<span
class="main-quote-summary__quote-large-number"
style="font-size: 60px; line-height: 48px;"
>
0.2
</span>
</div>
</div>
</div>
`;
exports[`MainQuoteSummary renders the component with initial props 4`] = `
<div
class="main-quote-summary__exchange-rate-container"
>
<div
class="exchange-rate-display main-quote-summary__exchange-rate-display"
>
<span>
1
</span>
<span
class=""
>
ETH
</span>
<span>
=
</span>
<span>
0.1
</span>
<span
class=""
>
BAT
</span>
<div
class="exchange-rate-display__switch-arrows"
>
<svg
fill="none"
height="13"
viewBox="0 0 13 13"
width="13"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M4.15294 4.38514H9.99223L8.50853 5.86884C8.30421 6.07297 8.30421 6.40418 8.50853 6.60869C8.61069 6.71085 8.74443 6.76203 8.87836 6.76203C9.01229 6.76203 9.14603 6.71085 9.24819 6.60869L11.6249 4.23219C11.649 4.20803 11.6707 4.1814 11.6899 4.15305C11.6947 4.14563 11.6981 4.13726 11.7025 4.12965C11.7154 4.10815 11.7282 4.08646 11.7381 4.06325C11.7426 4.05222 11.7447 4.04043 11.7487 4.0292C11.7558 4.00827 11.7636 3.98754 11.7681 3.96547C11.775 3.93161 11.7786 3.89717 11.7786 3.86198C11.7786 3.82678 11.775 3.79235 11.7681 3.75849C11.7638 3.73642 11.756 3.71568 11.7487 3.69476C11.7447 3.68353 11.7428 3.67174 11.7381 3.6607C11.7282 3.63749 11.7156 3.616 11.7025 3.59431C11.6981 3.5867 11.6947 3.57833 11.6899 3.57091C11.6707 3.54256 11.649 3.51593 11.6249 3.49177L9.24876 1.11564C9.04444 0.911322 8.71342 0.911322 8.50891 1.11564C8.30459 1.31977 8.30459 1.65098 8.50891 1.85549L9.99223 3.339H4.15294C2.22978 3.339 0.665039 4.90374 0.665039 6.8269C0.665039 7.11588 0.899227 7.35007 1.1882 7.35007C1.47718 7.35007 1.71137 7.11588 1.71137 6.8269C1.71137 5.48037 2.80659 4.38514 4.15294 4.38514ZM12.2066 6.57445C11.9177 6.57445 11.6835 6.80864 11.6835 7.09762C11.6835 8.44396 10.5883 9.53919 9.24191 9.53919H3.40262L4.88632 8.05549C5.09064 7.85136 5.09064 7.52014 4.88632 7.31563C4.682 7.11112 4.35098 7.11131 4.14647 7.31563L1.76977 9.69233C1.74561 9.71649 1.72393 9.74312 1.70471 9.77147C1.70015 9.7787 1.69691 9.78669 1.69273 9.79392C1.6796 9.81561 1.66647 9.83748 1.65677 9.86126C1.6524 9.87211 1.6503 9.88371 1.64631 9.89475C1.63927 9.91586 1.63128 9.93679 1.62671 9.95905C1.61986 9.99291 1.61625 10.0273 1.61625 10.0625C1.61625 10.0977 1.61986 10.1322 1.62671 10.166C1.63109 10.1883 1.63908 10.2092 1.64631 10.2303C1.6503 10.2414 1.65221 10.253 1.65677 10.2638C1.66666 10.2874 1.6796 10.3093 1.69273 10.3312C1.69691 10.3384 1.70015 10.3464 1.70471 10.3536C1.72393 10.382 1.74561 10.4086 1.76977 10.4328L4.14609 12.8091C4.24825 12.9112 4.38199 12.9624 4.51592 12.9624C4.64985 12.9624 4.78359 12.9112 4.88575 12.8091C5.09007 12.6049 5.09007 12.2737 4.88575 12.0692L3.40243 10.5857H9.24172C11.1649 10.5857 12.7296 9.02097 12.7296 7.09781C12.7298 6.80864 12.4956 6.57445 12.2066 6.57445Z"
fill="#037DD6"
/>
</svg>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,39 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import MainQuoteSummary from '.';
const createProps = (customProps = {}) => {
return {
sourceValue: '2000000000000000000',
sourceDecimals: 18,
sourceSymbol: 'ETH',
destinationValue: '200000000000000000',
destinationDecimals: 18,
destinationSymbol: 'BAT',
...customProps,
};
};
describe('MainQuoteSummary', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { getAllByText } = renderWithProvider(
<MainQuoteSummary {...props} />,
);
expect(getAllByText(props.sourceSymbol)).toHaveLength(2);
expect(getAllByText(props.destinationSymbol)).toHaveLength(2);
expect(
document.querySelector('.main-quote-summary__source-row'),
).toMatchSnapshot();
expect(
document.querySelector('.main-quote-summary__destination-row'),
).toMatchSnapshot();
expect(
document.querySelector('.main-quote-summary__quote-large'),
).toMatchSnapshot();
expect(
document.querySelector('.main-quote-summary__exchange-rate-container'),
).toMatchSnapshot();
});
});

View File

@ -0,0 +1,77 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SearchableItemList renders the component with initial props 1`] = `
<div
class="MuiFormControl-root MuiTextField-root searchable-item-list__search MuiFormControl-fullWidth"
data-testid="search-list-items"
>
<div
class="MuiInputBase-root MuiInput-root TextField-inputRoot-12 MuiInputBase-fullWidth MuiInput-fullWidth Mui-focused Mui-focused TextField-inputFocused-11 MuiInputBase-formControl MuiInput-formControl MuiInputBase-adornedStart"
>
<div
class="MuiInputAdornment-root MuiInputAdornment-positionStart"
style="margin-right: 12px;"
>
<img
alt=""
height="17"
src="images/search.svg"
width="17"
/>
</div>
<input
aria-invalid="false"
autocomplete="off"
class="MuiInputBase-input MuiInput-input MuiInputBase-inputAdornedStart"
dir="auto"
type="text"
value=""
/>
</div>
</div>
`;
exports[`SearchableItemList renders the component with initial props 2`] = `
<div
class="searchable-item-list__item searchable-item-list__item--selected"
tabindex="0"
>
<img
alt=""
class="url-icon"
src="iconUrl"
/>
<div
class="searchable-item-list__labels"
>
<div
class="searchable-item-list__item-labels"
>
<span
class="searchable-item-list__primary-label"
>
primaryLabel
</span>
<span
class="searchable-item-list__secondary-label"
>
secondaryLabel
</span>
</div>
<div
class="searchable-item-list__right-labels"
>
<span
class="searchable-item-list__right-primary-label"
>
rightPrimaryLabel
</span>
<span
class="searchable-item-list__right-secondary-label"
>
rightSecondaryLabel
</span>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,60 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import SearchableItemList from '.';
const createProps = (customProps = {}) => {
return {
defaultToAll: true,
listTitle: 'listTitle',
itemsToSearch: [
{
iconUrl: 'iconUrl',
selected: true,
primaryLabel: 'primaryLabel',
secondaryLabel: 'secondaryLabel',
rightPrimaryLabel: 'rightPrimaryLabel',
rightSecondaryLabel: 'rightSecondaryLabel',
},
],
fuseSearchKeys: [
{
name: 'name',
weight: 0.499,
},
{
name: 'symbol',
weight: 0.499,
},
{
name: 'address',
weight: 0.002,
},
],
...customProps,
};
};
describe('SearchableItemList', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { getByText } = renderWithProvider(<SearchableItemList {...props} />);
expect(getByText(props.listTitle)).toBeInTheDocument();
expect(getByText(props.itemsToSearch[0].primaryLabel)).toBeInTheDocument();
expect(
getByText(props.itemsToSearch[0].secondaryLabel),
).toBeInTheDocument();
expect(
getByText(props.itemsToSearch[0].rightPrimaryLabel),
).toBeInTheDocument();
expect(
getByText(props.itemsToSearch[0].rightSecondaryLabel),
).toBeInTheDocument();
expect(
document.querySelector('.searchable-item-list__search'),
).toMatchSnapshot();
expect(
document.querySelector('.searchable-item-list__item'),
).toMatchSnapshot();
});
});

View File

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SelectQuotePopover renders the component with initial props 1`] = `
<div>
<div
class="select-quote-popover"
/>
</div>
`;

View File

@ -0,0 +1,24 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import SelectQuotePopover from '.';
const createProps = (customProps = {}) => {
return {
onClose: jest.fn(),
onSubmit: jest.fn(),
swapToSymbol: 'ETH',
initialAggId: 'initialAggId',
onQuoteDetailsIsOpened: jest.fn(),
...customProps,
};
};
describe('SelectQuotePopover', () => {
it('renders the component with initial props', () => {
const { container } = renderWithProvider(
<SelectQuotePopover {...createProps()} />,
);
expect(container).toMatchSnapshot();
});
});

View File

@ -0,0 +1,42 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SlippageButtons renders the component with initial props 1`] = `
<div
class="slippage-buttons__header"
>
<div
class="slippage-buttons__header-text"
>
Advanced Options
</div>
</div>
`;
exports[`SlippageButtons renders the component with initial props 2`] = `
<div
class="button-group slippage-buttons__button-group radio-button-group"
role="radiogroup"
>
<button
aria-checked="false"
class="button-group__button radio-button"
role="radio"
>
2%
</button>
<button
aria-checked="true"
class="button-group__button radio-button button-group__button--active radio-button--active"
role="radio"
>
3%
</button>
<button
aria-checked="false"
class="button-group__button slippage-buttons__button-group-custom-button radio-button"
role="radio"
>
custom
</button>
</div>
`;

View File

@ -0,0 +1,31 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import SlippageButtons from '.';
const createProps = (customProps = {}) => {
return {
onSelect: jest.fn(),
maxAllowedSlippage: 15,
currentSlippage: 3,
...customProps,
};
};
describe('SlippageButtons', () => {
it('renders the component with initial props', () => {
const { getByText } = renderWithProvider(
<SlippageButtons {...createProps()} />,
);
expect(getByText('2%')).toBeInTheDocument();
expect(getByText('3%')).toBeInTheDocument();
expect(getByText('custom')).toBeInTheDocument();
expect(getByText('Advanced Options')).toBeInTheDocument();
expect(
document.querySelector('.slippage-buttons__header'),
).toMatchSnapshot();
expect(
document.querySelector('.slippage-buttons__button-group'),
).toMatchSnapshot();
});
});

View File

@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SwapsFooter renders the component with initial props 1`] = `
<div>
<div
class="swaps-footer"
>
<div
class="swaps-footer__buttons"
>
<div
class="page-container__footer swaps-footer__custom-page-container-footer-class"
>
<footer>
<button
class="button btn-default page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
data-testid="page-container-footer-cancel"
role="button"
tabindex="0"
>
Back
</button>
<button
class="button btn-primary page-container__footer-button swaps-footer__custom-page-container-footer-button-class"
data-testid="page-container-footer-next"
role="button"
tabindex="0"
>
submitText
</button>
</footer>
</div>
</div>
<div
class="swaps-footer__bottom-text"
>
Terms of Service
</div>
</div>
</div>
`;

View File

@ -0,0 +1,28 @@
import React from 'react';
import { renderWithProvider } from '../../../../../test/jest';
import SwapsFooter from '.';
const createProps = (customProps = {}) => {
return {
onCancel: jest.fn(),
onSubmit: jest.fn(),
submitText: 'submitText',
disabled: false,
showTermsOfService: true,
...customProps,
};
};
describe('SwapsFooter', () => {
it('renders the component with initial props', () => {
const props = createProps();
const { container, getByText } = renderWithProvider(
<SwapsFooter {...props} />,
);
expect(getByText(props.submitText)).toBeInTheDocument();
expect(getByText('Back')).toBeInTheDocument();
expect(getByText('Terms of Service')).toBeInTheDocument();
expect(container).toMatchSnapshot();
});
});

View File

@ -3522,6 +3522,20 @@
dom-accessibility-api "^0.5.0"
pretty-format "^25.5.0"
"@testing-library/jest-dom@^5.11.10":
version "5.11.10"
resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.11.10.tgz#1cd90715023e1627f5ed26ab3b38e6f22d77046c"
integrity sha512-FuKiq5xuk44Fqm0000Z9w0hjOdwZRNzgx7xGGxQYepWFZy+OYUMOT/wPI4nLYXCaVltNVpU1W/qmD88wLWDsqQ==
dependencies:
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
aria-query "^4.2.2"
chalk "^3.0.0"
css "^3.0.0"
css.escape "^1.5.1"
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react-hooks@^3.2.1":
version "3.2.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-3.2.1.tgz#19b6caa048ef15faa69d439c469033873ea01294"
@ -3720,6 +3734,14 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@*":
version "26.0.22"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.22.tgz#8308a1debdf1b807aa47be2838acdcd91e88fbe6"
integrity sha512-eeWwWjlqxvBxc4oQdkueW5OF/gtfSceKk4OnOAGlUSwS/liBRtZppbJuz1YkgbrbfGOoeBHun9fOvXnjNwrSOw==
dependencies:
jest-diff "^26.0.0"
pretty-format "^26.0.0"
"@types/json-schema@^7.0.3":
version "7.0.7"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
@ -3906,6 +3928,13 @@
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.6.tgz#a9ca4b70a18b270ccb2bc0aaafefd1d486b7ea74"
integrity sha512-W+bw9ds02rAQaMvaLYxAbJ6cvguW/iJXNT6lTssS1ps6QdrMKttqEAMEG/b5CR8TZl3/L7/lH0ZV5nNR1LXikA==
"@types/testing-library__jest-dom@^5.9.1":
version "5.9.5"
resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz#5bf25c91ad2d7b38f264b12275e5c92a66d849b0"
integrity sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==
dependencies:
"@types/jest" "*"
"@types/testing-library__react-hooks@^3.0.0":
version "3.2.0"
resolved "https://registry.yarnpkg.com/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.2.0.tgz#52f3a109bef06080e3b1e3ae7ea1c014ce859897"
@ -5299,6 +5328,11 @@ atob@^2.0.0:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a"
integrity sha1-ri1acpR38onWDdf5amMUoi3Wwio=
atob@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
autoprefixer@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.1.0.tgz#374cf35be1c0e8fce97408d876f95f66f5cb4641"
@ -8492,6 +8526,11 @@ css-what@2.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
css@2.X, css@^2.0.0, css@^2.2.1:
version "2.2.3"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.3.tgz#f861f4ba61e79bedc962aa548e5780fd95cbc6be"
@ -8502,6 +8541,15 @@ css@2.X, css@^2.0.0, css@^2.2.1:
source-map-resolve "^0.5.1"
urix "^0.1.0"
css@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
dependencies:
inherits "^2.0.4"
source-map "^0.6.1"
source-map-resolve "^0.6.0"
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@ -15490,7 +15538,7 @@ jest-config@^26.6.3:
micromatch "^4.0.2"
pretty-format "^26.6.2"
jest-diff@^26.6.2:
jest-diff@^26.0.0, jest-diff@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
@ -20905,7 +20953,7 @@ pretty-format@^25.5.0:
ansi-styles "^4.0.0"
react-is "^16.12.0"
pretty-format@^26.6.2:
pretty-format@^26.0.0, pretty-format@^26.6.2:
version "26.6.2"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
@ -23984,6 +24032,14 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.1:
source-map-url "^0.4.0"
urix "^0.1.0"
source-map-resolve@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
dependencies:
atob "^2.1.2"
decode-uri-component "^0.2.0"
source-map-support@0.5.12:
version "0.5.12"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599"