Unverified Commit 4df5f683 authored by Andrey Azov's avatar Andrey Azov Committed by GitHub
Browse files

Migrate remaining tests to react-testing-library, and remove Enzyme (#486)

This finally allows us to remove the dependency on Enzyme.

Also as part of this PR:

- Removed components
  - LaunchbarContainer (serves no purpose)
  - Account (not used)
  - SpeciesTab (superseded by SelectedSpecies component; not used anywhere)
- Removed tests for Launchbar
- Removed the launchbarExpanded and accountExpanded fields from redux and from components
- Changed the keyCode field on KeyboardEvent to event.key
   (typescript marks it as deprecated, whereas Browser support seems to be good these days).
parent fca8ac61
Pipeline #145527 passed with stages
in 10 minutes and 58 seconds
......@@ -12,7 +12,6 @@ module.exports = {
roots: ['<rootDir>/src'],
setupFiles: ['<rootDir>/tests/setup-jest.js'],
setupFilesAfterEnv: [
'<rootDir>/tests/setup-enzyme.ts',
'<rootDir>/tests/setup-rtl.ts'
],
testEnvironment: 'jsdom',
......
This diff is collapsed.
......@@ -97,8 +97,6 @@
"@testing-library/user-event": "12.2.2",
"@types/classnames": "2.2.10",
"@types/d3": "5.16.3",
"@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/faker": "5.1.2",
"@types/jest": "26.0.14",
"@types/lodash": "4.14.162",
......@@ -122,9 +120,6 @@
"copy-webpack-plugin": "8.1.0",
"css-loader": "5.1.4",
"css-minimizer-webpack-plugin": "1.3.0",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.5",
"enzyme-to-json": "3.6.1",
"eslint": "7.11.0",
"eslint-config-prettier": "6.12.0",
"eslint-plugin-prettier": "3.1.4",
......
......@@ -37,7 +37,6 @@ describe('<TrackPanelList />', () => {
const defaultProps: TrackPanelListProps = {
activeGenomeId: faker.lorem.words(),
isDrawerOpened: true,
launchbarExpanded: true,
activeEnsObject: createEnsObject(),
selectedTrackPanelTab: TrackSet.GENOMIC,
genomeTrackCategories: createGenomeCategories(),
......
......@@ -26,7 +26,6 @@ import {
} from 'src/shared/state/ens-object/ensObjectTypes';
import { RootState } from 'src/store';
import { getIsDrawerOpened } from '../../drawer/drawerSelectors';
import { getLaunchbarExpanded } from 'src/header/headerSelectors';
import {
getBrowserActiveEnsObject,
getBrowserTrackStates,
......@@ -45,7 +44,6 @@ import styles from './TrackPanelList.scss';
export type TrackPanelListProps = {
activeGenomeId: string | null;
isDrawerOpened: boolean;
launchbarExpanded: boolean;
activeEnsObject: EnsObject | null;
selectedTrackPanelTab: TrackSet;
genomeTrackCategories: GenomeTrackCategory[];
......@@ -147,7 +145,6 @@ const mapStateToProps = (state: RootState) => {
return {
activeGenomeId,
isDrawerOpened: getIsDrawerOpened(state),
launchbarExpanded: getLaunchbarExpanded(state),
activeEnsObject: getBrowserActiveEnsObject(state),
selectedTrackPanelTab: getSelectedTrackPanelTab(state),
genomeTrackCategories: activeGenomeId
......
......@@ -15,15 +15,16 @@
*/
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import faker from 'faker';
import Zmenu, { ZmenuProps } from './Zmenu';
import ZmenuContent from './ZmenuContent';
import { createZmenuContent } from 'tests/fixtures/browser';
jest.mock('./ZmenuContent', () => () => <div>ZmenuContent</div>);
jest.mock('./ZmenuContent', () => () => (
<div data-test-id="zmenuContent">ZmenuContent</div>
));
jest.mock('./ZmenuInstantDownload', () => () => (
<div>ZmenuInstantDownload</div>
));
......@@ -43,17 +44,14 @@ describe('<Zmenu />', () => {
onLeave: jest.fn()
};
let wrapper: any;
beforeEach(() => {
jest.resetAllMocks();
wrapper = mount(<Zmenu {...defaultProps} />);
});
describe('rendering', () => {
test('renders zmenu content', () => {
expect(wrapper.find(ZmenuContent)).toHaveLength(1);
const { queryByTestId } = render(<Zmenu {...defaultProps} />);
expect(queryByTestId('zmenuContent')).toBeTruthy();
});
});
});
......@@ -16,7 +16,8 @@
import React from 'react';
import { Provider } from 'react-redux';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import faker from 'faker';
import configureMockStore from 'redux-mock-store';
......@@ -24,65 +25,84 @@ import {
ZmenuContent,
ZmenuContentProps,
ZmenuContentItem,
ZmenuContentItemProps,
ZmenuContentLine
ZmenuContentItemProps
} from './ZmenuContent';
import {
Markup,
ZmenuContentItem as ZmenuContentItemType
} from './zmenu-types';
import { Markup } from './zmenu-types';
import { createZmenuContent } from 'tests/fixtures/browser';
jest.mock('./ZmenuAppLinks', () => () => <div>ZmenuAppLinks</div>);
describe('<ZmenuContent />', () => {
afterEach(() => {
jest.resetAllMocks();
});
const mockReduxState = {};
const mockStoreCreator = configureMockStore();
const mockStore = mockStoreCreator(() => mockReduxState);
const mockStoreCreator = configureMockStore();
const mockStore = mockStoreCreator(() => ({}));
const wrappingComponent = (props: any) => (
<Provider store={mockStore}>{props.children}</Provider>
const defaultProps: ZmenuContentProps = {
content: createZmenuContent()
};
const renderZmenuContent = (
props: Partial<ZmenuContentProps> = {},
store = mockStore
) =>
render(
<Provider store={store}>
<ZmenuContent {...defaultProps} {...props} />
</Provider>
);
const defaultProps: ZmenuContentProps = {
content: createZmenuContent()
};
let wrapper: any;
describe('<ZmenuContent />', () => {
beforeEach(() => {
wrapper = mount(<ZmenuContent {...defaultProps} />, { wrappingComponent });
jest.resetAllMocks();
});
describe('rendering', () => {
it('renders the correct zmenu content information', () => {
const firstLineData = wrapper
.find(ZmenuContentLine)
.first()
.props()
.blocks.map((items: ZmenuContentItemType[]) => items[0].text);
firstLineData.forEach((lineText: string) => {
expect(wrapper.find('.zmenuContentLine').first().text()).toContain(
lineText
);
const { container } = renderZmenuContent();
const zmenuContentLine = defaultProps.content[0].lines[0];
const renderedContentBlocks = container.querySelectorAll(
'.zmenuContentBlock'
);
// check that the number of blocks of text is correct
expect(renderedContentBlocks.length).toBe(zmenuContentLine.length);
// check that the text from each block of text has been rendered
zmenuContentLine.forEach((block, index) => {
const blockText = block.reduce((acc, { text }) => acc + text, '');
expect(renderedContentBlocks[index].textContent).toBe(blockText);
});
zmenuContentLine.forEach((block, blockIndex) => {
block.forEach((blockItem, blockItemIndex) => {
const renderedElement = renderedContentBlocks[
blockIndex
].querySelectorAll('span')[blockItemIndex];
if (blockItem.markup.includes(Markup.LIGHT)) {
expect(renderedElement.classList.contains('markupLight'));
}
if (blockItem.markup.includes(Markup.STRONG)) {
expect(renderedElement.classList.contains('markupStrong'));
}
});
});
});
});
describe('behaviour', () => {
it('changes focus feature when feature link is clicked', () => {
describe('<ZmenuContentItem />', () => {
it('calls function to change focus feature when feature link is clicked', () => {
const props: ZmenuContentItemProps = {
id: faker.lorem.words(),
markup: [Markup.FOCUS],
text: faker.lorem.words(),
changeFocusObject: jest.fn()
};
const wrapper = mount(<ZmenuContentItem {...props} />);
const { container } = render(<ZmenuContentItem {...props} />);
userEvent.click(container.firstChild as HTMLDivElement);
wrapper.simulate('click');
expect(wrapper.props().changeFocusObject).toHaveBeenCalledTimes(1);
expect(props.changeFocusObject).toHaveBeenCalledTimes(1);
});
});
});
......@@ -4,13 +4,6 @@
position: relative;
display: grid;
grid-template-columns: 40px 1fr 10px 1fr 40px;
&.default {
height: calc(100vh - 220px);
}
&.taller {
height: calc(100vh - 165px);
}
}
.tabList {
......
......@@ -16,7 +16,6 @@
import React from 'react';
import { connect } from 'react-redux';
import { RootState } from 'src/store';
import AttributesAccordion from './attributes-accordion/AttributesAccordion';
import FiltersAccordion from './filter-accordion/FiltersAccordion';
......@@ -24,19 +23,19 @@ import Overlay from 'src/shared/components/overlay/Overlay';
import CustomDownloadInfoCard from '../../components/info-card/CustomDownloadInfoCard';
import PreviewCard from 'src/content/app/custom-download/containers/content/preview-card/PreviewCard';
import PreviewDownload from './preview-download/PreviewDownload';
import { getLaunchbarExpanded } from 'src/header/headerSelectors';
import {
getShowPreviewResult,
getShowExampleData
} from 'src/content/app/custom-download/state/customDownloadSelectors';
import { setShowExampleData } from 'src/content/app/custom-download/state/customDownloadActions';
import { RootState } from 'src/store';
import styles from './CustomDownloadContent.scss';
type StateProps = {
showSummary: boolean;
showExampleData: boolean;
launchBarExpanded: boolean;
};
type DispatchProps = {
......@@ -46,11 +45,8 @@ type DispatchProps = {
type Props = StateProps & DispatchProps;
const CustomDownloadContent = (props: Props) => {
const wrapperHeightClassName = props.launchBarExpanded
? styles.default
: styles.taller;
return (
<div className={`${styles.wrapper} ${wrapperHeightClassName}`}>
<div className={styles.wrapper}>
{props.showExampleData && (
<>
<Overlay />
......@@ -88,7 +84,6 @@ const mapDispatchToProps: DispatchProps = {
const mapStateToProps = (state: RootState): StateProps => ({
showSummary: getShowPreviewResult(state),
launchBarExpanded: getLaunchbarExpanded(state),
showExampleData: getShowExampleData(state)
});
......
......@@ -15,7 +15,7 @@
*/
import React from 'react';
import { mount, render } from 'enzyme';
import { render } from '@testing-library/react';
import BasePairsRuler from './BasePairsRuler';
......@@ -26,16 +26,21 @@ const defaultProps = {
describe('<BasePairsRuler />', () => {
describe('rendering', () => {
it('renders inside an <svg> element if standalone', () => {
const wrapper = render(
it('renders an <svg> element if standalone', () => {
const { container } = render(
<BasePairsRuler {...defaultProps} standalone={true} />
);
expect(wrapper.is('svg')).toBe(true);
expect((container.firstChild as Element).tagName).toBe('svg');
});
it('renders inside a <g> element (svg group) if not standalone', () => {
const wrapper = render(<BasePairsRuler {...defaultProps} />);
expect(wrapper.is('g')).toBe(true);
it('renders a <g> element (svg group) if not standalone', () => {
const { getByTestId } = render(
<svg data-test-id="test wrapper">
<BasePairsRuler {...defaultProps} />
</svg>
);
const wrapper = getByTestId('test wrapper');
expect((wrapper.firstChild as Element).tagName).toBe('g');
});
});
......@@ -47,7 +52,7 @@ describe('<BasePairsRuler />', () => {
it('passes calculated ticks to the callback', () => {
const callback = jest.fn();
mount(<BasePairsRuler {...props} onTicksCalculated={callback} />);
render(<BasePairsRuler {...props} onTicksCalculated={callback} />);
expect(callback).toHaveBeenCalledTimes(1);
const payload = callback.mock.calls[0][0];
......
......@@ -15,14 +15,13 @@
*/
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import {
DefaultTranscriptListItem,
DefaultTranscriptListItemProps
} from './DefaultTranscriptListItem';
import TranscriptsListItemInfo from '../transcripts-list-item-info/TranscriptsListItemInfo';
import UnsplicedTranscript from 'src/content/app/entity-viewer/gene-view/components/unspliced-transcript/UnsplicedTranscript';
import { createTranscript } from 'tests/fixtures/entity-viewer/transcript';
import {
......@@ -31,20 +30,18 @@ import {
} from 'tests/fixtures/entity-viewer/gene';
jest.mock('../transcripts-list-item-info/TranscriptsListItemInfo', () => () => (
<div>TranscriptsListItemInfo</div>
<div data-test-id="transcriptsListItemInfo">TranscriptsListItemInfo</div>
));
jest.mock(
'src/content/app/entity-viewer/gene-view/components/unspliced-transcript/UnsplicedTranscript',
() => () => <div>UnsplicedTranscript</div>
() => () => <div data-test-id="unsplicedTranscript">UnsplicedTranscript</div>
);
const toggleTranscriptInfo = jest.fn();
describe('<DefaultTranscriptListItem />', () => {
let wrapper: any;
afterEach(() => {
beforeEach(() => {
jest.resetAllMocks();
});
......@@ -58,35 +55,43 @@ describe('<DefaultTranscriptListItem />', () => {
};
const renderComponent = (props?: Partial<DefaultTranscriptListItemProps>) =>
mount(<DefaultTranscriptListItem {...defaultProps} {...props} />);
render(<DefaultTranscriptListItem {...defaultProps} {...props} />);
it('displays unspliced transcript', () => {
wrapper = renderComponent();
expect(wrapper.exists(UnsplicedTranscript)).toBe(true);
const { queryByTestId } = renderComponent();
expect(queryByTestId('unsplicedTranscript')).toBeTruthy();
});
it('toggles transcript item info onClick', () => {
wrapper = renderComponent();
wrapper.find('.clickableTranscriptArea').simulate('click');
const { container } = renderComponent();
const clickableArea = container.querySelector(
'.clickableTranscriptArea'
) as HTMLElement;
const transcriptLabel = container.querySelector('.right') as HTMLElement;
userEvent.click(clickableArea);
expect(toggleTranscriptInfo).toHaveBeenCalledTimes(1);
wrapper.find('.right').simulate('click');
userEvent.click(transcriptLabel);
expect(toggleTranscriptInfo).toHaveBeenCalledTimes(2);
});
it('hides transcript info by default', () => {
wrapper = renderComponent();
const { queryByTestId } = renderComponent();
expect(wrapper.exists(TranscriptsListItemInfo)).toBe(false);
expect(queryByTestId('transcriptsListItemInfo')).toBeFalsy();
});
it('displays transcript info if expandTranscript is true', () => {
wrapper = renderComponent({ expandTranscript: true });
const { queryByTestId } = renderComponent({ expandTranscript: true });
expect(wrapper.exists(TranscriptsListItemInfo)).toBe(true);
expect(queryByTestId('transcriptsListItemInfo')).toBeTruthy();
});
it('displays selected transcript', () => {
wrapper = renderComponent({ isDefault: true });
expect(wrapper.find('.transcriptQualityLabel').text()).toBe('Selected');
const { container } = renderComponent({ isDefault: true });
expect(
container.querySelector('.transcriptQualityLabel')?.textContent
).toBe('Selected');
});
});
......@@ -15,33 +15,28 @@
*/
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router';
import {
TranscriptsListItemInfo,
TranscriptsListItemInfoProps
} from './TranscriptsListItemInfo';
import { InstantDownloadTranscript } from 'src/shared/components/instant-download';
import ViewInApp from 'src/shared/components/view-in-app/ViewInApp';
import { createGene } from 'tests/fixtures/entity-viewer/gene';
import { createTranscript } from 'tests/fixtures/entity-viewer/transcript';
jest.mock('@apollo/client', () => ({
gql: jest.fn(),
useQuery: jest.fn(() => ({
data: null,
loading: true
}))
}));
jest.mock('src/shared/components/view-in-app/ViewInApp', () => () => (
<div>ViewInApp</div>
<div data-test-id="viewInApp">ViewInApp</div>
));
jest.mock('src/shared/components/instant-download', () => ({
InstantDownloadTranscript: () => <div>InstantDownloadTranscript</div>
InstantDownloadTranscript: () => (
<div data-test-id="instantDownloadTranscript">
InstantDownloadTranscript
</div>
)
}));
const transcript = createTranscript();
......@@ -57,66 +52,54 @@ const defaultProps = {
};
const renderComponent = (props?: Partial<TranscriptsListItemInfoProps>) => {
const completeProps = {
...defaultProps,
...props
};
return mount(
return render(
<MemoryRouter>
<TranscriptsListItemInfo {...completeProps} />
<TranscriptsListItemInfo {...defaultProps} {...props} />
</MemoryRouter>
);
};
describe('<TranscriptsListItemInfo /', () => {
let wrapper: any;
beforeEach(() => {
wrapper = renderComponent();
});
/*
* FIXME: the test below will have to change when api payload updates:
* 1) we will use protein length from api response instead of calculating it ourselves
* 2) we will check that protein product is present on a transcript instead of looking at CDS
*/
it('displays amino acid length when transcript has CDS', () => {
it('displays amino acid length when transcript has a protein product', () => {
const { container } = renderComponent();
const expectedProteinLength =
defaultProps.transcript.product_generating_contexts[0].product?.length;
expect(wrapper.find('.topMiddle strong').text()).toMatch(
expect(container.querySelector('.topMiddle strong')?.textContent).toMatch(
`${expectedProteinLength}`
);
});
it('contains the download link', () => {
expect(wrapper.find('.downloadLink')).toHaveLength(1);
const { container } = renderComponent();
expect(container.querySelector('.downloadLink')).toBeTruthy();
});
it('renders ViewInApp component', () => {
expect(wrapper.find(ViewInApp)).toHaveLength(1);
const { queryByTestId } = renderComponent();
expect(queryByTestId('viewInApp')).toBeTruthy();
});
it('hides Download component by default', () => {
expect(wrapper.find(InstantDownloadTranscript)).toHaveLength(0);
const { queryByTestId } = renderComponent();
expect(queryByTestId('instantDownloadTranscript')).toBeFalsy();
});
it('shows Download component by default if expandDownload is true', () => {
wrapper = renderComponent({
it('shows Download component if expandDownload is true', () => {
const { queryByTestId } = renderComponent({
expandDownload: true
});