mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Unit Testing] Configure react-testing-library queries to use Kibana's data-test-subj instead of default data-testid (#59445)
This commit is contained in:
parent
03d082c59e
commit
8a3e9c6b32
20 changed files with 72 additions and 100 deletions
|
@ -68,7 +68,10 @@ export default {
|
|||
'<rootDir>/src/dev/jest/setup/polyfills.js',
|
||||
'<rootDir>/src/dev/jest/setup/enzyme.js',
|
||||
],
|
||||
setupFilesAfterEnv: ['<rootDir>/src/dev/jest/setup/mocks.js'],
|
||||
setupFilesAfterEnv: [
|
||||
'<rootDir>/src/dev/jest/setup/mocks.js',
|
||||
'<rootDir>/src/dev/jest/setup/react_testing_library.js',
|
||||
],
|
||||
coverageDirectory: '<rootDir>/target/kibana-coverage/jest',
|
||||
coverageReporters: !!process.env.CODE_COVERAGE ? ['json'] : ['html', 'text'],
|
||||
moduleFileExtensions: ['js', 'json', 'ts', 'tsx'],
|
||||
|
|
32
src/dev/jest/setup/react_testing_library.js
Normal file
32
src/dev/jest/setup/react_testing_library.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* Licensed to Elasticsearch B.V. under one or more contributor
|
||||
* license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright
|
||||
* ownership. Elasticsearch B.V. licenses this file to you under
|
||||
* the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
/**
|
||||
* Have to import "/pure" here to not register afterEach() hook clean up
|
||||
* in the very beginning. There are couple tests which fail with clean up hook.
|
||||
* On CI they run before first test which imports '@testing-library/react'
|
||||
* and registers afterEach hook so the whole suite is passing.
|
||||
* This have to be fixed as we depend on test order execution
|
||||
* https://github.com/elastic/kibana/issues/59469
|
||||
*/
|
||||
import { configure } from '@testing-library/react/pure';
|
||||
|
||||
// instead of default 'data-testid', use kibana's 'data-test-subj'
|
||||
configure({ testIdAttribute: 'data-test-subj' });
|
|
@ -40,6 +40,7 @@ export function createJestConfig({ kibanaDirectory, xPackKibanaDirectory }) {
|
|||
setupFilesAfterEnv: [
|
||||
`<rootDir>/dev-tools/jest/setup/setup_test.js`,
|
||||
`${kibanaDirectory}/src/dev/jest/setup/mocks.js`,
|
||||
`${kibanaDirectory}/src/dev/jest/setup/react_testing_library.js`,
|
||||
],
|
||||
testMatch: ['**/*.test.{js,ts,tsx}'],
|
||||
transform: {
|
||||
|
|
|
@ -10,4 +10,3 @@
|
|||
*/
|
||||
|
||||
import 'jest-styled-components';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
|
|
|
@ -28,9 +28,9 @@ export function KeyValueTable({
|
|||
{keyValuePairs.map(({ key, value }) => (
|
||||
<EuiTableRow key={key}>
|
||||
<EuiTableRowCell>
|
||||
<strong data-testid="dot-key">{key}</strong>
|
||||
<strong data-test-subj="dot-key">{key}</strong>
|
||||
</EuiTableRowCell>
|
||||
<EuiTableRowCell data-testid="value">
|
||||
<EuiTableRowCell data-test-subj="value">
|
||||
<FormattedValue value={value} />
|
||||
</EuiTableRowCell>
|
||||
</EuiTableRow>
|
||||
|
|
|
@ -256,7 +256,7 @@ export function CustomSelectionTable({
|
|||
{!singleSelection && (
|
||||
<EuiCheckbox
|
||||
id={`${item.id}-checkbox`}
|
||||
data-testid={`${item.id}-checkbox`}
|
||||
data-test-subj={`${item.id}-checkbox`}
|
||||
checked={isItemSelected(item.id)}
|
||||
onChange={() => toggleItem(item.id)}
|
||||
type="inList"
|
||||
|
@ -265,7 +265,7 @@ export function CustomSelectionTable({
|
|||
{singleSelection && (
|
||||
<EuiRadio
|
||||
id={item.id}
|
||||
data-testid={`${item.id}-radio-button`}
|
||||
data-test-subj={`${item.id}-radio-button`}
|
||||
checked={isItemSelected(item.id)}
|
||||
onChange={() => toggleItem(item.id)}
|
||||
disabled={timeseriesOnly && item.isSingleMetricViewerJob === false}
|
||||
|
|
|
@ -8,7 +8,6 @@ import React from 'react';
|
|||
import { render } from '@testing-library/react';
|
||||
|
||||
import * as CheckPrivilige from '../../../../../privilege/check_privilege';
|
||||
import { queryByTestSubj } from '../../../../../util/test_utils';
|
||||
|
||||
import { DeleteAction } from './action_delete';
|
||||
|
||||
|
@ -21,24 +20,22 @@ jest.mock('../../../../../privilege/check_privilege', () => ({
|
|||
|
||||
describe('DeleteAction', () => {
|
||||
test('When canDeleteDataFrameAnalytics permission is false, button should be disabled.', () => {
|
||||
const { container } = render(<DeleteAction item={mockAnalyticsListItem} />);
|
||||
expect(queryByTestSubj(container, 'mlAnalyticsJobDeleteButton')).toHaveAttribute('disabled');
|
||||
const { getByTestId } = render(<DeleteAction item={mockAnalyticsListItem} />);
|
||||
expect(getByTestId('mlAnalyticsJobDeleteButton')).toHaveAttribute('disabled');
|
||||
});
|
||||
|
||||
test('When canDeleteDataFrameAnalytics permission is true, button should not be disabled.', () => {
|
||||
const mock = jest.spyOn(CheckPrivilige, 'checkPermission');
|
||||
mock.mockImplementation(p => p === 'canDeleteDataFrameAnalytics');
|
||||
const { container } = render(<DeleteAction item={mockAnalyticsListItem} />);
|
||||
const { getByTestId } = render(<DeleteAction item={mockAnalyticsListItem} />);
|
||||
|
||||
expect(queryByTestSubj(container, 'mlAnalyticsJobDeleteButton')).not.toHaveAttribute(
|
||||
'disabled'
|
||||
);
|
||||
expect(getByTestId('mlAnalyticsJobDeleteButton')).not.toHaveAttribute('disabled');
|
||||
|
||||
mock.mockRestore();
|
||||
});
|
||||
|
||||
test('When job is running, delete button should be disabled.', () => {
|
||||
const { container } = render(
|
||||
const { getByTestId } = render(
|
||||
<DeleteAction
|
||||
item={{
|
||||
...mockAnalyticsListItem,
|
||||
|
@ -47,6 +44,6 @@ describe('DeleteAction', () => {
|
|||
/>
|
||||
);
|
||||
|
||||
expect(queryByTestSubj(container, 'mlAnalyticsJobDeleteButton')).toHaveAttribute('disabled');
|
||||
expect(getByTestId('mlAnalyticsJobDeleteButton')).toHaveAttribute('disabled');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -149,7 +149,7 @@ exports[`CalendarForm Renders calendar form 1`] = `
|
|||
grow={false}
|
||||
>
|
||||
<EuiButton
|
||||
data-testid="ml_save_calendar_button"
|
||||
data-test-subj="ml_save_calendar_button"
|
||||
fill={true}
|
||||
isDisabled={true}
|
||||
onClick={[MockFunction]}
|
||||
|
|
|
@ -220,7 +220,7 @@ export const CalendarForm = ({
|
|||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
data-testid="ml_save_calendar_button"
|
||||
data-test-subj="ml_save_calendar_button"
|
||||
fill
|
||||
onClick={isEdit ? onEdit : onCreate}
|
||||
isDisabled={saveButtonDisabled}
|
||||
|
|
|
@ -133,7 +133,7 @@ exports[`EventsTable Renders events table with search bar 1`] = `
|
|||
"filters": Array [],
|
||||
"toolsRight": Array [
|
||||
<EuiButton
|
||||
data-testid="ml_new_event"
|
||||
data-test-subj="ml_new_event"
|
||||
iconType="plusInCircle"
|
||||
isDisabled={false}
|
||||
onClick={[MockFunction]}
|
||||
|
@ -146,7 +146,7 @@ exports[`EventsTable Renders events table with search bar 1`] = `
|
|||
/>
|
||||
</EuiButton>,
|
||||
<EuiButton
|
||||
data-testid="ml_import_events"
|
||||
data-test-subj="ml_import_events"
|
||||
iconType="importAction"
|
||||
isDisabled={false}
|
||||
onClick={[MockFunction]}
|
||||
|
|
|
@ -91,7 +91,7 @@ export const EventsTable = ({
|
|||
name: '',
|
||||
render: event => (
|
||||
<DeleteButton
|
||||
data-testid="event_delete"
|
||||
data-test-subj="event_delete"
|
||||
canDeleteCalendar={canDeleteCalendar}
|
||||
onClick={() => {
|
||||
onDeleteClick(event.event_id);
|
||||
|
@ -106,7 +106,7 @@ export const EventsTable = ({
|
|||
<EuiButton
|
||||
isDisabled={canCreateCalendar === false}
|
||||
key="ml_new_event"
|
||||
data-testid="ml_new_event"
|
||||
data-test-subj="ml_new_event"
|
||||
size="s"
|
||||
iconType="plusInCircle"
|
||||
onClick={showNewEventModal}
|
||||
|
@ -119,7 +119,7 @@ export const EventsTable = ({
|
|||
<EuiButton
|
||||
isDisabled={canCreateCalendar === false}
|
||||
key="ml_import_event"
|
||||
data-testid="ml_import_events"
|
||||
data-test-subj="ml_import_events"
|
||||
size="s"
|
||||
iconType="importAction"
|
||||
onClick={showImportModal}
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('ImportModal', () => {
|
|||
instance.setState(testState);
|
||||
wrapper.update();
|
||||
expect(wrapper.state('selectedEvents').length).toBe(2);
|
||||
const deleteButton = wrapper.find('[data-testid="event_delete"]');
|
||||
const deleteButton = wrapper.find('[data-test-subj="event_delete"]');
|
||||
const button = deleteButton.find('EuiButtonEmpty').first();
|
||||
button.simulate('click');
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ describe('NewCalendar', () => {
|
|||
test('Import modal shown on Import Events button click', () => {
|
||||
const wrapper = mountWithIntl(<NewCalendar {...props} />);
|
||||
|
||||
const importButton = wrapper.find('[data-testid="ml_import_events"]');
|
||||
const importButton = wrapper.find('[data-test-subj="ml_import_events"]');
|
||||
const button = importButton.find('EuiButton');
|
||||
button.simulate('click');
|
||||
|
||||
|
@ -127,7 +127,7 @@ describe('NewCalendar', () => {
|
|||
test('New event modal shown on New event button click', () => {
|
||||
const wrapper = mountWithIntl(<NewCalendar {...props} />);
|
||||
|
||||
const importButton = wrapper.find('[data-testid="ml_new_event"]');
|
||||
const importButton = wrapper.find('[data-test-subj="ml_new_event"]');
|
||||
const button = importButton.find('EuiButton');
|
||||
button.simulate('click');
|
||||
|
||||
|
@ -154,7 +154,7 @@ describe('NewCalendar', () => {
|
|||
|
||||
const wrapper = mountWithIntl(<NewCalendar {...noCreateProps} />);
|
||||
|
||||
const buttons = wrapper.find('[data-testid="ml_save_calendar_button"]');
|
||||
const buttons = wrapper.find('[data-test-subj="ml_save_calendar_button"]');
|
||||
const saveButton = buttons.find('EuiButton');
|
||||
|
||||
expect(saveButton.prop('isDisabled')).toBe(true);
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { queryHelpers } from '@testing-library/react';
|
||||
|
||||
/**
|
||||
* 'react-testing-library provides 'queryByTestId()' to get
|
||||
* elements with a 'data-testid' attribut. This custom method
|
||||
* supports querying for the Kibana style 'data-test-subj' attribute.
|
||||
* @param {HTMLElement} container The wrapping HTML element.
|
||||
* @param {Matcher} id The 'data-test-subj' id.
|
||||
* @returns {HTMLElement | null}
|
||||
*/
|
||||
|
||||
export const queryByTestSubj = queryHelpers.queryByAttribute.bind(null, 'data-test-subj');
|
|
@ -55,7 +55,6 @@ export const HeaderNavigation: React.FunctionComponent<{ basename: string }> = R
|
|||
return tabs.map((tab, index) => {
|
||||
return (
|
||||
<EuiTab
|
||||
data-testid={`${tab.id}EndpointTab`}
|
||||
data-test-subj={`${tab.id}EndpointTab`}
|
||||
key={index}
|
||||
href={`${basename}${tab.href}`}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { AlertIndex } from './index';
|
|||
import { appStoreFactory } from '../../store';
|
||||
import { coreMock } from 'src/core/public/mocks';
|
||||
import { KibanaContextProvider } from '../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { fireEvent, waitForElement, act } from '@testing-library/react';
|
||||
import { fireEvent, act } from '@testing-library/react';
|
||||
import { RouteCapture } from '../route_capture';
|
||||
import { createMemoryHistory, MemoryHistory } from 'history';
|
||||
import { Router } from 'react-router-dom';
|
||||
|
@ -24,18 +24,6 @@ describe('when on the alerting page', () => {
|
|||
let history: MemoryHistory<never>;
|
||||
let store: ReturnType<typeof appStoreFactory>;
|
||||
|
||||
/**
|
||||
* @testing-library/react provides `queryByTestId`, but that uses the data attribute
|
||||
* 'data-testid' whereas our FTR and EUI's tests all use 'data-test-subj'. While @testing-library/react
|
||||
* could be configured to use 'data-test-subj', it is not currently configured that way.
|
||||
*
|
||||
* This provides an equivalent function to `queryByTestId` but that uses our 'data-test-subj' attribute.
|
||||
*/
|
||||
let queryByTestSubjId: (
|
||||
renderResult: reactTestingLibrary.RenderResult,
|
||||
testSubjId: string
|
||||
) => Promise<Element | null>;
|
||||
|
||||
beforeEach(async () => {
|
||||
/**
|
||||
* Create a 'history' instance that is only in-memory and causes no side effects to the testing environment.
|
||||
|
@ -70,17 +58,6 @@ describe('when on the alerting page', () => {
|
|||
</Provider>
|
||||
);
|
||||
};
|
||||
queryByTestSubjId = async (renderResult, testSubjId) => {
|
||||
return await waitForElement(
|
||||
/**
|
||||
* Use document.body instead of container because EUI renders things like popover out of the DOM heirarchy.
|
||||
*/
|
||||
() => document.body.querySelector(`[data-test-subj="${testSubjId}"]`),
|
||||
{
|
||||
container: renderResult.container,
|
||||
}
|
||||
);
|
||||
};
|
||||
});
|
||||
it('should show a data grid', async () => {
|
||||
await render().findByTestId('alertListGrid');
|
||||
|
@ -147,7 +124,7 @@ describe('when on the alerting page', () => {
|
|||
/**
|
||||
* Use our helper function to find the flyout's close button, as it uses a different test ID attribute.
|
||||
*/
|
||||
const closeButton = await queryByTestSubjId(renderResult, 'euiFlyoutCloseButton');
|
||||
const closeButton = await renderResult.findByTestId('euiFlyoutCloseButton');
|
||||
if (closeButton) {
|
||||
fireEvent.click(closeButton);
|
||||
}
|
||||
|
@ -169,16 +146,13 @@ describe('when on the alerting page', () => {
|
|||
describe('when the user changes page size to 10', () => {
|
||||
beforeEach(async () => {
|
||||
const renderResult = render();
|
||||
const paginationButton = await queryByTestSubjId(
|
||||
renderResult,
|
||||
'tablePaginationPopoverButton'
|
||||
);
|
||||
const paginationButton = await renderResult.findByTestId('tablePaginationPopoverButton');
|
||||
if (paginationButton) {
|
||||
act(() => {
|
||||
fireEvent.click(paginationButton);
|
||||
});
|
||||
}
|
||||
const show10RowsButton = await queryByTestSubjId(renderResult, 'tablePagination-10-rows');
|
||||
const show10RowsButton = await renderResult.findByTestId('tablePagination-10-rows');
|
||||
if (show10RowsButton) {
|
||||
act(() => {
|
||||
fireEvent.click(show10RowsButton);
|
||||
|
|
|
@ -142,7 +142,7 @@ export const AlertIndex = memo(() => {
|
|||
if (columnId === 'alert_type') {
|
||||
return (
|
||||
<EuiLink
|
||||
data-testid="alertTypeCellLink"
|
||||
data-test-subj="alertTypeCellLink"
|
||||
onClick={() =>
|
||||
history.push(urlFromQueryParams({ ...queryParams, selected_alert: row.id }))
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ export const AlertIndex = memo(() => {
|
|||
return (
|
||||
<>
|
||||
{hasSelectedAlert && (
|
||||
<EuiFlyout data-testid="alertDetailFlyout" size="l" onClose={handleFlyoutClose}>
|
||||
<EuiFlyout data-test-subj="alertDetailFlyout" size="l" onClose={handleFlyoutClose}>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>
|
||||
|
@ -226,7 +226,7 @@ export const AlertIndex = memo(() => {
|
|||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
)}
|
||||
<EuiPage data-test-subj="alertListPage" data-testid="alertListPage">
|
||||
<EuiPage data-test-subj="alertListPage">
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader>
|
||||
|
@ -250,7 +250,6 @@ export const AlertIndex = memo(() => {
|
|||
renderCellValue={renderCellValue}
|
||||
pagination={pagination}
|
||||
data-test-subj="alertListGrid"
|
||||
data-testid="alertListGrid"
|
||||
/>
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
|
|
|
@ -20,7 +20,7 @@ export const AlertDetailResolver = styled(
|
|||
const { store } = storeFactory(context);
|
||||
|
||||
return (
|
||||
<div className={className} data-test-subj="alertResolver" data-testid="alertResolver">
|
||||
<div className={className} data-test-subj="alertResolver">
|
||||
<Provider store={store}>
|
||||
<Resolver selectedEvent={selectedEvent} />
|
||||
</Provider>
|
||||
|
|
|
@ -22,11 +22,6 @@ describe('when on the managing page', () => {
|
|||
let history: MemoryHistory<never>;
|
||||
let store: ReturnType<typeof appStoreFactory>;
|
||||
|
||||
let queryByTestSubjId: (
|
||||
renderResult: reactTestingLibrary.RenderResult,
|
||||
testSubjId: string
|
||||
) => Promise<Element | null>;
|
||||
|
||||
beforeEach(async () => {
|
||||
history = createMemoryHistory<never>();
|
||||
store = appStoreFactory(coreMock.createStart(), true);
|
||||
|
@ -43,20 +38,11 @@ describe('when on the managing page', () => {
|
|||
</Provider>
|
||||
);
|
||||
};
|
||||
|
||||
queryByTestSubjId = async (renderResult, testSubjId) => {
|
||||
return await reactTestingLibrary.waitForElement(
|
||||
() => document.body.querySelector(`[data-test-subj="${testSubjId}"]`),
|
||||
{
|
||||
container: renderResult.container,
|
||||
}
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
it('should show a table', async () => {
|
||||
const renderResult = render();
|
||||
const table = await queryByTestSubjId(renderResult, 'managementListTable');
|
||||
const table = await renderResult.findByTestId('managementListTable');
|
||||
expect(table).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@ -64,7 +50,7 @@ describe('when on the managing page', () => {
|
|||
it('should not show the flyout', () => {
|
||||
const renderResult = render();
|
||||
expect.assertions(1);
|
||||
return queryByTestSubjId(renderResult, 'managementDetailsFlyout').catch(e => {
|
||||
return renderResult.findByTestId('managementDetailsFlyout').catch(e => {
|
||||
expect(e).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -89,14 +75,14 @@ describe('when on the managing page', () => {
|
|||
let renderResult: reactTestingLibrary.RenderResult;
|
||||
beforeEach(async () => {
|
||||
renderResult = render();
|
||||
const detailsLink = await queryByTestSubjId(renderResult, 'hostnameCellLink');
|
||||
const detailsLink = await renderResult.findByTestId('hostnameCellLink');
|
||||
if (detailsLink) {
|
||||
reactTestingLibrary.fireEvent.click(detailsLink);
|
||||
}
|
||||
});
|
||||
|
||||
it('should show the flyout', () => {
|
||||
return queryByTestSubjId(renderResult, 'managementDetailsFlyout').then(flyout => {
|
||||
return renderResult.findByTestId('managementDetailsFlyout').then(flyout => {
|
||||
expect(flyout).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
@ -115,7 +101,7 @@ describe('when on the managing page', () => {
|
|||
});
|
||||
it('should show the flyout', () => {
|
||||
const renderResult = render();
|
||||
return queryByTestSubjId(renderResult, 'managementDetailsFlyout').then(flyout => {
|
||||
return renderResult.findByTestId('managementDetailsFlyout').then(flyout => {
|
||||
expect(flyout).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('useCamera on an unpainted element', () => {
|
|||
const camera = useCamera();
|
||||
const { ref, onMouseDown } = camera;
|
||||
projectionMatrix = camera.projectionMatrix;
|
||||
return <div data-testid={testID} onMouseDown={onMouseDown} ref={ref} />;
|
||||
return <div data-test-subj={testID} onMouseDown={onMouseDown} ref={ref} />;
|
||||
};
|
||||
|
||||
simulator = sideEffectSimulator();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue