Merge pull request #6174 from mozilla/jh/settings-react-improved-testing

refactor(settings + react): update tests to be more consistent and DRY
This commit is contained in:
Jody Heavener 2020-08-12 09:51:10 -04:00 коммит произвёл GitHub
Родитель bc31d7d329 02272c50a4
Коммит 1b7e0c0b84
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
28 изменённых файлов: 113 добавлений и 126 удалений

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

@ -3,32 +3,20 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import AppErrorBoundary from '.';
describe('AppErrorBoundary', () => {
beforeEach(() => {
// HACK: Swallow the exception thrown by BadComponent
// it bubbles up unnecesarily to jest and makes noise
jest.spyOn(console, 'error');
(global.console.error as jest.Mock).mockImplementation(() => {});
});
afterEach(() => {
(global.console.error as jest.Mock).mockRestore();
});
it('renders children that do not cause exceptions', () => {
const GoodComponent = () => <p data-testid="good-component">Hi</p>;
const { queryByTestId } = render(
render(
<AppErrorBoundary>
<GoodComponent />
</AppErrorBoundary>
);
expect(queryByTestId('error-loading-app')).not.toBeInTheDocument();
expect(screen.queryByTestId('error-loading-app')).not.toBeInTheDocument();
});
it('renders a general error dialog on exception in child component', () => {
@ -36,12 +24,12 @@ describe('AppErrorBoundary', () => {
throw new Error('bad');
};
const { queryByTestId } = render(
render(
<AppErrorBoundary>
<BadComponent />
</AppErrorBoundary>
);
expect(queryByTestId('error-loading-app')).toBeInTheDocument();
expect(screen.queryByTestId('error-loading-app')).toBeInTheDocument();
});
});

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

@ -4,7 +4,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import AppErrorDialog from '.';
describe('AppErrorDialog', () => {

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

@ -4,7 +4,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Footer from '.';
describe('Footer', () => {

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

@ -4,7 +4,6 @@
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import LinkExternal from './index';
it('renders without imploding', () => {

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

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

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

@ -1,11 +1,8 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { render } from '@testing-library/react';
import LogoLockup from '.';
afterEach(cleanup);
// TO DO: functional test for `data-testid="logo-text"` to be
// TODO: functional test for `data-testid="logo-text"` to be
// hidden at mobile
describe('LogoLockup', () => {

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

@ -1,21 +1,17 @@
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import Portal from './index';
afterEach(cleanup);
it('renders children in a new element outside original DOM parent', () => {
const { container, getByTestId } = render(
const { container } = render(
<div data-testid="portal-parent">
<Portal id="foo">
<div data-testid="children">Hi mom</div>
</Portal>
</div>
);
const childrenEl = getByTestId('children');
expect(getByTestId('portal-parent')).not.toContainElement(childrenEl);
const childrenEl = screen.getByTestId('children');
expect(screen.getByTestId('portal-parent')).not.toContainElement(childrenEl);
const containerParent = container.parentNode as ParentNode;
expect(containerParent.querySelector('#foo')).toContainElement(childrenEl);
expect(containerParent.querySelector('#foo')).toHaveAttribute(
@ -25,7 +21,7 @@ it('renders children in a new element outside original DOM parent', () => {
});
it('renders multiple instances with the same ID to the same DOM parent', () => {
const { container, getByTestId } = render(
const { container } = render(
<div data-testid="portal-parent">
<Portal id="foo">
<div data-testid="p1">Hi mom</div>
@ -44,9 +40,9 @@ it('renders multiple instances with the same ID to the same DOM parent', () => {
expect(portals.length).toEqual(2);
const fooPortal = containerParent.querySelector('#foo');
expect(fooPortal).toContainElement(getByTestId('p1'));
expect(fooPortal).toContainElement(getByTestId('p2'));
expect(fooPortal).not.toContainElement(getByTestId('p3'));
expect(fooPortal).toContainElement(screen.getByTestId('p1'));
expect(fooPortal).toContainElement(screen.getByTestId('p2'));
expect(fooPortal).not.toContainElement(screen.getByTestId('p3'));
});
it('applies a11y improvements when id is set to "modal"', () => {
@ -58,15 +54,15 @@ it('applies a11y improvements when id is set to "modal"', () => {
const headerId = 'some-header-id';
const descId = 'some-desc-id';
const { getByTestId } = render(
render(
<Portal id="modal" {...{ headerId, descId }}>
<div>Hi mom</div>
</Portal>
);
const body = document.body;
const root = getByTestId('root');
const adjacentToRoot = getByTestId('adjacent-to-root');
const root = screen.getByTestId('root');
const adjacentToRoot = screen.getByTestId('adjacent-to-root');
const modal = body.querySelector('#modal');
expect(body.classList).toContain('overflow-hidden');

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

@ -1,13 +1,10 @@
import React from 'react';
import { render, fireEvent, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { render, fireEvent } from '@testing-library/react';
import Survey, { CreateHandleIframeTask } from './index';
const surveyURL = 'https://my-survey-url.mozilla.org/';
afterEach(cleanup);
describe('Survey', () => {
const onSurveyClose = jest.fn();
@ -19,7 +16,10 @@ describe('Survey', () => {
const { queryByTestId } = subject();
const surveyContainer = queryByTestId('survey-component');
expect(surveyContainer).toHaveAttribute('aria-label', 'Firefox accounts optional user survey');
expect(surveyContainer).toHaveAttribute(
'aria-label',
'Firefox accounts optional user survey'
);
expect(surveyContainer).toBeVisible();
});

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

@ -5,11 +5,13 @@
module.exports = {
roots: ['<rootDir>'],
transform: {
'^.+\\.tsx?$': 'ts-jest',
'^.+\\.(ts|tsx)?$': 'ts-jest',
'^.+\\.svg$': '<rootDir>/svg-transform.js',
},
moduleNameMapper: {
'\\.(css|scss)$': 'identity-obj-proxy',
},
testPathIgnorePatterns: ['/dist/', '/node_modules/'],
// Matches create-react-app
setupFilesAfterEnv: ['./setupTests.ts'],
};

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

@ -0,0 +1,11 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
/* eslint-disable no-undef */
import '@testing-library/jest-dom/extend-expect';
window.console.error = jest.fn();
window.console.log = jest.fn();
window.console.warn = jest.fn();

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

@ -2,9 +2,8 @@
* 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/. */
import React, { useRef, ReactNode } from 'react';
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import AlertBar from './index';
import { AlertBarRootAndContextProvider } from '../../lib/AlertBarContext';
@ -20,7 +19,9 @@ describe('AlertBar', () => {
</AlertBar>
</AlertBarRootAndContextProvider>
);
expect(screen.getByTestId('alert-bar-root')).toContainElement(screen.getByTestId('alert-bar'));
expect(screen.getByTestId('alert-bar-root')).toContainElement(
screen.getByTestId('alert-bar')
);
expect(screen.queryByTestId('children')).toBeInTheDocument();
expect(screen.queryByTestId('alert-bar')).toHaveAttribute('role', 'alert');
expect(screen.getByTestId('alert-bar-dismiss')).toHaveAttribute(
@ -37,7 +38,7 @@ describe('AlertBar', () => {
);
expect(screen.getByTestId('alert-bar-portal')).toBeInTheDocument;
})
});
it('calls onDismiss on button click', () => {
const { rerender } = render(<AlertBarRootAndContextProvider />);

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

@ -5,7 +5,6 @@
import React from 'react';
import { render, act } from '@testing-library/react';
import { MockedProvider, MockLink } from '@apollo/client/testing';
import '@testing-library/jest-dom/extend-expect';
import App from '.';
import FlowEvent from '../../lib/flow-event';

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

@ -2,25 +2,26 @@
* 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/. */
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { render } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import { MockedCache } from '../../models/_mocks';
import AppLayout from '.';
it('renders the app with children', () => {
const { getByTestId } = render(
render(
<MockedCache>
<AppLayout>
<p data-testid="test-child">Hello, world!</p>
</AppLayout>
</MockedCache>
);
expect(getByTestId('app')).toBeInTheDocument();
expect(getByTestId('content-skip')).toBeInTheDocument();
expect(getByTestId('header')).toBeInTheDocument();
expect(getByTestId('footer')).toBeInTheDocument();
expect(getByTestId('nav')).toBeInTheDocument();
expect(getByTestId('main')).toBeInTheDocument();
expect(getByTestId('main')).toContainElement(getByTestId('test-child'));
expect(screen.getByTestId('app')).toBeInTheDocument();
expect(screen.getByTestId('content-skip')).toBeInTheDocument();
expect(screen.getByTestId('header')).toBeInTheDocument();
expect(screen.getByTestId('footer')).toBeInTheDocument();
expect(screen.getByTestId('nav')).toBeInTheDocument();
expect(screen.getByTestId('main')).toBeInTheDocument();
expect(screen.getByTestId('main')).toContainElement(
screen.getByTestId('test-child')
);
});

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

@ -4,7 +4,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Avatar from '.';
describe('Avatar', () => {

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

@ -4,7 +4,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import ContentSkip from '.';
describe('ContentSkip', () => {

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

@ -4,11 +4,10 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MockedCache } from '../../models/_mocks';
import HeaderLockup from '.';
// TO DO: functional test for `data-testid="header-menu"` to be visible in
// TODO: functional test for `data-testid="header-menu"` to be visible in
// mobile & tablet but hidden at desktop
describe('HeaderLockup', () => {

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

@ -4,8 +4,6 @@
import React from 'react';
import { render, fireEvent, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Modal from './index';
it('renders as expected', () => {
@ -24,7 +22,7 @@ it('renders as expected', () => {
it('accepts an alternate className', () => {
const onDismiss = jest.fn();
const { queryByTestId } = render(
render(
<Modal
headerId="some-header"
descId="some-description"
@ -34,17 +32,19 @@ it('accepts an alternate className', () => {
<div data-testid="children">Hi mom</div>
</Modal>
);
expect(queryByTestId('modal-content-container')).toHaveClass('barquux');
expect(screen.queryByTestId('modal-content-container')).toHaveClass(
'barquux'
);
});
it('calls onDismiss on click outside', () => {
const onDismiss = jest.fn();
const { container, getByTestId } = render(
const { container } = render(
<Modal headerId="some-header" descId="some-description" {...{ onDismiss }}>
<div data-testid="children">Hi mom</div>
</Modal>
);
fireEvent.click(getByTestId('modal-content-container'));
fireEvent.click(screen.getByTestId('modal-content-container'));
expect(onDismiss).not.toHaveBeenCalled();
fireEvent.click(container);
expect(onDismiss).toHaveBeenCalled();
@ -63,10 +63,10 @@ it('calls onDismiss on esc key press', () => {
it('shifts focus to the tab fence when opened', () => {
const onDismiss = jest.fn();
const { getByTestId } = render(
render(
<Modal headerId="some-header" descId="some-description" {...{ onDismiss }}>
<div data-testid="children">Hi mom</div>
</Modal>
);
expect(document.activeElement).toBe(getByTestId('modal-tab-fence'));
expect(document.activeElement).toBe(screen.getByTestId('modal-tab-fence'));
});

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

@ -4,7 +4,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { MockedCache } from '../../models/_mocks';
import Nav from '.';

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

@ -3,32 +3,31 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import Security from '.';
describe('Security', () => {
it('renders "fresh load" <Security/> with correct content', async () => {
const { findByText, findAllByText } = render(
render(
<Security
accountRecoveryKeyEnabled={false}
twoFactorAuthEnabled={false}
/>
);
expect(await findByText('Recovery key')).toBeTruthy;
expect(await findByText('Two-step authentication')).toBeTruthy;
expect(await screen.findByText('Recovery key')).toBeTruthy;
expect(await screen.findByText('Two-step authentication')).toBeTruthy;
const result = await findAllByText('Not Set');
const result = await screen.findAllByText('Not Set');
expect(result).toHaveLength(2);
});
it('renders "enabled two factor" and "recovery key present" <Security/> with correct content', async () => {
const { findAllByText } = render(
render(
<Security accountRecoveryKeyEnabled={true} twoFactorAuthEnabled={true} />
);
const result = await findAllByText('Enabled');
const result = await screen.findAllByText('Enabled');
expect(result).toHaveLength(2);
});
});

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

@ -15,7 +15,6 @@ type SecurityProps = {
export const Security = ({
twoFactorAuthEnabled,
accountRecoveryKeyEnabled,
className,
}: SecurityProps) => {
const getValue = (settingOption: boolean) =>
settingOption ? 'Enabled' : 'Not Set';

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

@ -3,7 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import Settings from './index';
import { MockedCache } from '../../models/_mocks';

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

@ -3,36 +3,33 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from 'react';
import { render, cleanup } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import { render, screen } from '@testing-library/react';
import UnitRow from '.';
afterEach(cleanup);
describe('UnitRow', () => {
it('renders as expected with minimal required attributes', () => {
const { getByTestId, queryByTestId } = render(
<UnitRow header="Foxy" headerValue={null} />
);
render(<UnitRow header="Foxy" headerValue={null} />);
expect(getByTestId('unit-row-header').textContent).toContain('Foxy');
expect(getByTestId('unit-row-header-value').textContent).toContain('None');
expect(queryByTestId('unit-row-route')).toBeNull();
expect(queryByTestId('unit-row-modal')).toBeNull();
expect(screen.getByTestId('unit-row-header').textContent).toContain('Foxy');
expect(screen.getByTestId('unit-row-header-value').textContent).toContain(
'None'
);
expect(screen.queryByTestId('unit-row-route')).toBeNull();
expect(screen.queryByTestId('unit-row-modal')).toBeNull();
});
it('renders the children', () => {
const { getByTestId } = render(
render(
<UnitRow header="Display name" headerValue={null}>
<p data-testid="children">The children!</p>
</UnitRow>
);
expect(getByTestId('children')).toBeInTheDocument();
expect(screen.getByTestId('children')).toBeInTheDocument();
});
it('renders as expected with `route` prop and non-null `headerValue`', () => {
const { getByTestId } = render(
render(
<UnitRow
header="Display name"
headerValue="Fred Flinstone"
@ -40,21 +37,23 @@ describe('UnitRow', () => {
/>
);
expect(getByTestId('unit-row-header').textContent).toContain(
expect(screen.getByTestId('unit-row-header').textContent).toContain(
'Display name'
);
expect(getByTestId('unit-row-header-value').textContent).toContain(
expect(screen.getByTestId('unit-row-header-value').textContent).toContain(
'Fred Flinstone'
);
expect(getByTestId('unit-row-route')).toHaveAttribute(
expect(screen.getByTestId('unit-row-route')).toHaveAttribute(
'href',
'/display_name'
);
expect(getByTestId('unit-row-route').textContent).toContain('Change');
expect(screen.getByTestId('unit-row-route').textContent).toContain(
'Change'
);
});
it('renders as expected with `revealModal` prop', () => {
const { getByTestId } = render(
render(
<UnitRow
header="Display name"
headerValue={null}
@ -62,11 +61,11 @@ describe('UnitRow', () => {
/>
);
expect(getByTestId('unit-row-modal').textContent).toContain('Add');
expect(screen.getByTestId('unit-row-modal').textContent).toContain('Add');
});
it('renders non-default `noHeaderValueText` and `noHeaderValueCtaText`', () => {
const { getByTestId } = render(
render(
<UnitRow
header="Display name"
headerValue={null}
@ -76,9 +75,11 @@ describe('UnitRow', () => {
/>
);
expect(getByTestId('unit-row-header-value').textContent).toContain(
expect(screen.getByTestId('unit-row-header-value').textContent).toContain(
'Not set'
);
expect(getByTestId('unit-row-route').textContent).toContain('Create');
expect(screen.getByTestId('unit-row-route').textContent).toContain(
'Create'
);
});
});

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

@ -2,14 +2,13 @@
* 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/. */
import React, { useRef, ReactNode } from 'react';
import React from 'react';
import { storiesOf } from '@storybook/react';
import { UnitRowSecondaryEmail } from '.';
import { AlertBarRootAndContextProvider } from '../../lib/AlertBarContext';
storiesOf('Components|UnitRowSecondaryEmail', module)
.add('basic', () =>
<AlertBarRootAndContextProvider>
<UnitRowSecondaryEmail primaryEmail="user@example.com" />
</AlertBarRootAndContextProvider>
);
storiesOf('Components|UnitRowSecondaryEmail', module).add('basic', () => (
<AlertBarRootAndContextProvider>
<UnitRowSecondaryEmail primaryEmail="user@example.com" />
</AlertBarRootAndContextProvider>
));

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

@ -4,7 +4,6 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import UnitRowWithAvatar from '.';
describe('UnitRowWithAvatar', () => {

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

@ -2,7 +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/. */
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { render, wait, screen } from '@testing-library/react';
import { MockedCache } from '../../models/_mocks';

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

@ -66,19 +66,18 @@ describe('decode', () => {
describe('development', () => {
beforeAll(() => {
Object.defineProperty(process.env, 'NODE_ENV', { value: 'development' });
jest.spyOn(global.console, 'warn').mockReturnValue();
});
it('warns when server config is missing', () => {
decode(null);
expect(global.console.warn).toHaveBeenCalledWith(
expect(window.console.warn).toHaveBeenCalledWith(
'fxa-settings is missing server config'
);
});
it('warns when an invalid server config is supplied', () => {
decode('thou shalt not decode');
expect(global.console.warn).toHaveBeenCalledWith(
expect(window.console.warn).toHaveBeenCalledWith(
'fxa-settings server config is invalid'
);
});

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

@ -13,7 +13,6 @@ const mockNow = 1002003004005;
beforeEach(() => {
// `sendBeacon` is undefined in this context
window.navigator.sendBeacon = jest.fn();
global.console.error = jest.fn();
});
it('does not send metrics when uninitialized', () => {

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

@ -0,0 +1,9 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
import '@testing-library/jest-dom/extend-expect';
window.console.error = jest.fn();
window.console.log = jest.fn();
window.console.warn = jest.fn();