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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ es
lib
test-env
types
eui.d.ts
**/*.d.ts
package.json
docs
packages
Expand Down
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
11 changes: 11 additions & 0 deletions scripts/compile-eui.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ function compileBundle() {
return null;
}
});

// dtsGenerator is unfortunately having massive issues with RTL type defs, so we're
// temporarily defining manual `.d.ts` files and copying them to each compiled dir
shell.mkdir('-p', `${dir}/rtl`);
glob('./src/test/rtl/**/*.d.ts', undefined, (error, files) => {
files.forEach(file => {
const splitPath = file.split('/');
const fileName = splitPath[splitPath.length - 1];
shell.cp('-f', `${file}`, `${dir}/rtl/${fileName}`);
});
});
})
console.log(chalk.green('✔ Finished test utils files'));

Expand Down
4 changes: 2 additions & 2 deletions scripts/jest/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
},
"setupFiles": [
"<rootDir>/scripts/jest/setup/enzyme.js",
"<rootDir>/scripts/jest/setup/throw_on_console_error.js"
"<rootDir>/scripts/jest/setup/throw_on_console_error.js",
"<rootDir>/scripts/jest/setup/mocks.js"
cee-chen marked this conversation as resolved.
Show resolved Hide resolved
],
"setupFilesAfterEnv": [
"<rootDir>/scripts/jest/setup/mocks.js",
"<rootDir>/scripts/jest/setup/polyfills.js",
"<rootDir>/scripts/jest/setup/unmount_enzyme.js"
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,12 @@ import { Timer } from '@elastic/eui/lib/services/time';`}
</EuiText>
<EuiSpacer />
<EuiCodeBlock language="jsx" isCopyable fontSize="m">
{"import { findTestSubject } from '@elastic/eui/lib/test';"}
{
"import { findTestSubject } from '@elastic/eui/lib/test'; // Enzyme"
}
{
"import { findByTestSubject, render, screen } from '@elastic/eui/lib/test/rtl'; // React Testing Library"
}
</EuiCodeBlock>
</>
),
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>
`;
105 changes: 105 additions & 0 deletions src/components/popover/popover.rtl.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
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 } from '@testing-library/react';
import {
render,
screen,
waitForEuiPopoverOpen,
waitForEuiPopoverClose,
} from '../../test/rtl';
import { 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 waitForEuiPopoverOpen();

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 waitForEuiPopoverOpen();

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

await waitForEuiPopoverClose();
});
});
});
7 changes: 7 additions & 0 deletions src/test/rtl/component_helpers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* Ensure the EuiPopover being tested is open/closed before contiuning
* Note: Because EuiPopover is portalled, we want to query `document`
* instead of the `container` returned by RTL's render()
*/
export declare const waitForEuiPopoverOpen: () => Promise<void>;
export declare const waitForEuiPopoverClose: () => Promise<void>;
26 changes: 26 additions & 0 deletions src/test/rtl/component_helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* 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 { waitFor } from '@testing-library/react';

/**
* Ensure the EuiPopover being tested is open/closed before contiuning
* Note: Because EuiPopover is portalled, we want to query `document`
* instead of the `container` returned by RTL's render()
*/
export const waitForEuiPopoverOpen = async () =>
await waitFor(() => {
const openPopover = document.querySelector('[data-popover-open]');
expect(openPopover).toBeTruthy();
});

export const waitForEuiPopoverClose = async () =>
await waitFor(() => {
const openPopover = document.querySelector('[data-popover-open]');
expect(openPopover).toBeFalsy();
});
74 changes: 74 additions & 0 deletions src/test/rtl/custom_render.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { ReactElement } from 'react';
import { queries, RenderOptions, Screen } from '@testing-library/react';
import * as dataTestSubjQueries from './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
*/
declare const customRender: (ui: ReactElement, { queries: renderQueries, ...options }?: RenderOptions) => import("@testing-library/react").RenderResult<{
getByLabelText: typeof queries.getByLabelText;
getAllByLabelText: typeof queries.getAllByLabelText;
queryByLabelText: typeof queries.queryByLabelText;
queryAllByLabelText: typeof queries.queryAllByLabelText;
findByLabelText: typeof queries.findByLabelText;
findAllByLabelText: typeof queries.findAllByLabelText;
getByPlaceholderText: typeof queries.getByPlaceholderText;
getAllByPlaceholderText: typeof queries.getAllByPlaceholderText;
queryByPlaceholderText: typeof queries.queryByPlaceholderText;
queryAllByPlaceholderText: typeof queries.queryAllByPlaceholderText;
findByPlaceholderText: typeof queries.findByPlaceholderText;
findAllByPlaceholderText: typeof queries.findAllByPlaceholderText;
getByText: typeof queries.getByText;
getAllByText: typeof queries.getAllByText;
queryByText: typeof queries.queryByText;
queryAllByText: typeof queries.queryAllByText;
findByText: typeof queries.findByText;
findAllByText: typeof queries.findAllByText;
getByAltText: typeof queries.getByAltText;
getAllByAltText: typeof queries.getAllByAltText;
queryByAltText: typeof queries.queryByAltText;
queryAllByAltText: typeof queries.queryAllByAltText;
findByAltText: typeof queries.findByAltText;
findAllByAltText: typeof queries.findAllByAltText;
getByTitle: typeof queries.getByTitle;
getAllByTitle: typeof queries.getAllByTitle;
queryByTitle: typeof queries.queryByTitle;
queryAllByTitle: typeof queries.queryAllByTitle;
findByTitle: typeof queries.findByTitle;
findAllByTitle: typeof queries.findAllByTitle;
getByDisplayValue: typeof queries.getByDisplayValue;
getAllByDisplayValue: typeof queries.getAllByDisplayValue;
queryByDisplayValue: typeof queries.queryByDisplayValue;
queryAllByDisplayValue: typeof queries.queryAllByDisplayValue;
findByDisplayValue: typeof queries.findByDisplayValue;
findAllByDisplayValue: typeof queries.findAllByDisplayValue;
getByRole: typeof queries.getByRole;
getAllByRole: typeof queries.getAllByRole;
queryByRole: typeof queries.queryByRole;
queryAllByRole: typeof queries.queryAllByRole;
findByRole: typeof queries.findByRole;
findAllByRole: typeof queries.findAllByRole;
getByTestId: typeof queries.getByTestId;
getAllByTestId: typeof queries.getAllByTestId;
queryByTestId: typeof queries.queryByTestId;
queryAllByTestId: typeof queries.queryAllByTestId;
findByTestId: typeof queries.findByTestId;
findAllByTestId: typeof queries.findAllByTestId;
queryByTestSubject: import("@testing-library/react").QueryBy<[import("@testing-library/react").Matcher]>;
queryAllByTestSubject: (container: HTMLElement, id: import("@testing-library/react").Matcher, options?: import("@testing-library/react").MatcherOptions | undefined) => HTMLElement[];
getByTestSubject: import("@testing-library/react").GetBy<[import("@testing-library/react").Matcher]>;
getAllByTestSubject: import("@testing-library/react").GetAllBy<[import("@testing-library/react").Matcher]>;
findAllByTestSubject: import("@testing-library/react").FindAllBy<[import("@testing-library/react").Matcher]>;
findByTestSubject: import("@testing-library/react").FindBy<[import("@testing-library/react").Matcher]>;
}, HTMLElement, HTMLElement>;
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
*/
declare const customScreen: Screen<typeof queries & typeof dataTestSubjQueries>;
export { customScreen as screen };
Loading