Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install React Testing Library and export several RTL test helpers #6091

Merged
merged 12 commits into from
Aug 2, 2022
Merged
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
"@svgr/core": "5.5.0",
"@svgr/plugin-svgo": "^4.0.3",
"@testing-library/dom": "^8.12.0",
"@testing-library/jest-dom": "^5.16.3",
"@testing-library/react": "^12.1.5",
"@testing-library/react-hooks": "^7.0.2",
"@testing-library/user-event": "^13.5.0",
"@types/classnames": "^2.2.10",
"@types/enzyme": "^3.10.5",
"@types/jest": "^24.0.6",
Expand All @@ -135,6 +140,7 @@
"@types/react-is": "^17.0.3",
"@types/react-router-dom": "^5.1.5",
"@types/tabbable": "^3.1.2",
"@types/testing-library__jest-dom": "^5.14.3",
"@types/url-parse": "^1.4.8",
"@types/uuid": "^8.3.0",
"@typescript-eslint/eslint-plugin": "^5.10.2",
Expand Down
86 changes: 86 additions & 0 deletions src/components/popover/__snapshots__/popover.rtl.test.tsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`EuiPopover snapshot testing renders 1`] = `
<body>
<div>
<div
class="euiPopover testClass1 testClass2 emotion-euiPopover"
data-test-subj="test subject string"
>
<div
class="euiPopover__anchor css-16vtueo-render"
>
<button />
</div>
</div>
</div>
</body>
`;

exports[`EuiPopover snapshot testing renders with popover contents 1`] = `
<body>
<div>
<div
class="euiPopover euiPopover-isOpen testClass1 testClass2 emotion-euiPopover"
data-test-subj="test subject string"
>
<div
class="euiPopover__anchor css-16vtueo-render"
>
<button />
</div>
</div>
</div>
<div
data-euiportal="true"
>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="-1"
/>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="-1"
/>
<div
data-focus-lock-disabled="disabled"
>
<div
aria-describedby="generated-id"
aria-label="aria-label"
aria-live="off"
aria-modal="true"
class="euiPanel euiPanel--plain euiPanel--paddingMedium euiPopover__panel emotion-euiPanel-grow-m-m-plain-euiPopover__panel-isOpen-bottom"
data-autofocus="true"
data-popover-open="true"
data-popover-panel="true"
role="dialog"
style="top: 16px; left: -22px; will-change: transform, opacity; z-index: 2000;"
tabindex="0"
>
<div
class="emotion-euiPopoverArrow-bottom"
data-popover-arrow="bottom"
style="left: 10px; top: 0px;"
/>
<p
class="emotion-euiScreenReaderOnly"
id="generated-id"
>
You are in a dialog. To close this dialog, hit escape.
</p>
<div>
Hello world
</div>
</div>
</div>
<div
data-focus-guard="true"
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
tabindex="-1"
/>
</div>
</body>
`;
106 changes: 106 additions & 0 deletions src/components/popover/popover.rtl.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RTL tests should just be .test.tsx but since I'm not migrating 100% of EuiPopover's tests currently I just did rtl.test.tsx. End goal everything should be just .test.tsx once we're out of Enzyme.

* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React, { useState } from 'react';
import { fireEvent, waitFor } from '@testing-library/react';
import { render, screen, requiredProps } from '../../test';

import { EuiPopover } from './';

describe('EuiPopover', () => {
describe('snapshot testing', () => {
it('renders', () => {
const { baseElement } = render(
<EuiPopover
button={<button />}
closePopover={() => {}}
{...requiredProps}
/>
);

// NOTE: Using baseElement instead of container is required for components that use portals
expect(baseElement).toMatchSnapshot();
Comment on lines +32 to +33
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want everyone to share the pain I had of figuring this out in a 3 year old github thread when google wasn't returning any relevant results 💀

});

it('renders with popover contents', () => {
const { baseElement } = render(
<EuiPopover
button={<button />}
closePopover={() => {}}
isOpen={true}
{...requiredProps}
>
Hello world
</EuiPopover>
);

expect(baseElement).toMatchSnapshot();
});
});

const mockPopoverInteraction = jest.fn();
const MockPopoverComponent = () => {
const [isOpen, setIsOpen] = useState(false);
const togglePopover = () => setIsOpen(!isOpen);
const closePopover = () => setIsOpen(false);

return (
<EuiPopover
button={<button onClick={togglePopover}>Open popover</button>}
closePopover={closePopover}
isOpen={isOpen}
data-test-subj="popover"
>
<span data-test-subj="fff">Popover content</span>
<button onClick={mockPopoverInteraction}>Button inside popover</button>
</EuiPopover>
);
};

describe('open/close behavior', () => {
it('opens the popover, contents', () => {
render(<MockPopoverComponent />);

expect(screen.queryByText('Popover content')).toBeFalsy();

fireEvent.click(screen.getByText('Open popover'));

expect(screen.queryByText('Popover content')).toBeTruthy();
});

it('allows interacting with popover children', async () => {
render(<MockPopoverComponent />);

fireEvent.click(screen.getByText('Open popover'));
await waitFor(() => {
expect(screen.queryByText('Popover content')).toBeTruthy();
});

fireEvent.click(screen.getByText('Button inside popover'));
expect(mockPopoverInteraction).toHaveBeenCalledTimes(1);
});

it('closes the popover on escape key press', async () => {
render(<MockPopoverComponent />);

fireEvent.click(screen.getByText('Open popover'));

await waitFor(() => {
expect(screen.queryByText('Popover content')).toBeTruthy();
});

fireEvent.keyDown(screen.queryByTestSubject('popover')!, {
key: 'Escape',
});

await waitFor(() => {
expect(screen.queryByText('Popover content')).toBeFalsy();
});
});
});
});
2 changes: 2 additions & 0 deletions src/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ export {
} from './react_warnings';
export { sleep } from './sleep';
export * from './emotion-prefix';
export * from './rtl_data_test_subj_queries';
export { render, screen } from './rtl_custom_render';
57 changes: 57 additions & 0 deletions src/test/rtl_custom_render.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { ReactElement } from 'react';
import {
queries,
render,
RenderOptions,
screen,
Screen,
within,
} from '@testing-library/react';

import { EuiProvider } from '../components';

import * as dataTestSubjQueries from './rtl_data_test_subj_queries';

/**
* Custom render() fn with EuiProvider and query helpers
*
* @see https://testing-library.com/docs/react-testing-library/setup#custom-render
* @see https://testing-library.com/docs/react-testing-library/setup#add-custom-queries
*/

const customRender = (
ui: ReactElement,
{ queries: renderQueries, ...options }: RenderOptions = {}
) =>
render(ui, {
queries: {
...queries,
...dataTestSubjQueries,
...(renderQueries || {}),
},
wrapper: EuiProvider,
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
...options,
});

export { customRender as render };

/**
* Custom screen util with EUI query helpers
*
* @see https://testing-library.com/docs/queries/about/#screen
* @see https://github.com/testing-library/dom-testing-library/issues/516
*/
const customScreen: Screen<typeof queries & typeof dataTestSubjQueries> = {
...screen,
...within<typeof dataTestSubjQueries>(document.body, dataTestSubjQueries),
};

export { customScreen as screen };
46 changes: 46 additions & 0 deletions src/test/rtl_data_test_subj_queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import {
queryHelpers,
buildQueries,
AllByAttribute,
GetErrorFunction,
Matcher,
} from '@testing-library/react';

type QueryAllByAttribute = Parameters<AllByAttribute>;
const queryAllByTestSubject = (
...args: [
QueryAllByAttribute[1],
QueryAllByAttribute[2],
QueryAllByAttribute[3]?
]
) => queryHelpers.queryAllByAttribute('data-test-subj', ...args);

const getMultipleError: GetErrorFunction<[Matcher]> = (_, TestSubjectValue) =>
`Found multiple elements with the data-test-subj attribute of: ${TestSubjectValue}`;
const getMissingError: GetErrorFunction<[Matcher]> = (_, TestSubjectValue) =>
`Unable to find an element with the data-test-subj attribute of: ${TestSubjectValue}`;

const [
queryByTestSubject,
getAllByTestSubject,
getByTestSubject,
findAllByTestSubject,
findByTestSubject,
] = buildQueries(queryAllByTestSubject, getMultipleError, getMissingError);

export {
queryByTestSubject,
queryAllByTestSubject,
getByTestSubject,
getAllByTestSubject,
findAllByTestSubject,
findByTestSubject,
};
Loading