Commit 6b2d4441 by Alex Khomenko Committed by GitHub

Testing: Use React Testing Library (#27072)

* Add RTL

* Add setupTests.ts

* Refactor DashboardSearch tests
parent 35040099
......@@ -12,6 +12,7 @@ module.exports = {
testRegex: '(\\.|/)(test)\\.(jsx?|tsx?)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
setupFiles: ['jest-canvas-mock', './public/test/jest-shim.ts', './public/test/jest-setup.ts'],
setupFilesAfterEnv: ['./public/test/setupTests.ts'],
snapshotSerializers: ['enzyme-to-json/serializer'],
globals: { 'ts-jest': { isolatedModules: true } },
moduleNameMapper: {
......
......@@ -75,6 +75,8 @@
"@grafana/eslint-config": "2.0.0",
"@microsoft/api-extractor": "7.8.2-pr1796.0",
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@testing-library/jest-dom": "^5.11.3",
"@testing-library/react": "^10.4.8",
"@testing-library/react-hooks": "^3.2.1",
"@types/angular": "1.6.56",
"@types/angular-route": "1.7.0",
......@@ -112,6 +114,7 @@
"@types/slate": "0.47.1",
"@types/slate-plain-serializer": "0.6.1",
"@types/slate-react": "0.22.5",
"@types/testing-library__jest-dom": "^5.9.2",
"@types/testing-library__react-hooks": "^3.2.0",
"@types/tinycolor2": "1.4.2",
"@typescript-eslint/eslint-plugin": "3.6.0",
......
......@@ -93,7 +93,7 @@ export const TagFilter: FC<Props> = ({
};
return (
<div className={styles.tagFilter}>
<div className={styles.tagFilter} aria-label="Tag filter">
{isClearable && tags.length > 0 && (
<span className={styles.clear} onClick={() => onTagChange([])}>
Clear tags
......
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { render, fireEvent, screen, waitFor, act } from '@testing-library/react';
import { mockSearch } from './mocks';
import { DashboardSearch, Props } from './DashboardSearch';
import { searchResults } from '../testData';
......@@ -15,18 +14,13 @@ afterEach(() => {
jest.useRealTimers();
});
const setup = async (testProps?: Partial<Props>): Promise<any> => {
const setup = (testProps?: Partial<Props>) => {
const props: any = {
onCloseSearch: () => {},
...testProps,
};
let wrapper;
//@ts-ignore
await act(async () => {
wrapper = await mount(<DashboardSearch {...props} />);
jest.runAllTimers();
});
return wrapper;
render(<DashboardSearch {...props} />);
jest.runAllTimers();
};
/**
......@@ -35,7 +29,9 @@ const setup = async (testProps?: Partial<Props>): Promise<any> => {
*/
describe('DashboardSearch', () => {
it('should call search api with default query when initialised', async () => {
await setup();
setup();
await waitFor(() => screen.getByPlaceholderText('Search dashboards by name'));
expect(mockSearch).toHaveBeenCalledTimes(1);
expect(mockSearch).toHaveBeenCalledWith({
......@@ -51,19 +47,13 @@ describe('DashboardSearch', () => {
});
it('should call api with updated query on query change', async () => {
let wrapper = await setup();
//@ts-ignore
await act(async () => {
// @ts-ignore
await wrapper
.find({ placeholder: 'Search dashboards by name' })
.hostNodes()
//@ts-ignore
.prop('onChange')({ currentTarget: { value: 'Test' } });
setup();
const input = await screen.findByPlaceholderText('Search dashboards by name');
await act((async () => {
await fireEvent.input(input, { target: { value: 'Test' } });
jest.runAllTimers();
});
}) as any);
expect(mockSearch).toHaveBeenCalledWith({
query: 'Test',
......@@ -78,58 +68,61 @@ describe('DashboardSearch', () => {
});
it("should render 'No results' message when there are no dashboards", async () => {
let wrapper = await setup();
wrapper.update();
expect(
wrapper
.findWhere((c: any) => c.type() === 'div' && c.text() === 'No dashboards matching your query were found.')
.exists()
).toBe(true);
setup();
const message = await screen.findByText('No dashboards matching your query were found.');
expect(message).toBeInTheDocument();
});
it('should render search results', async () => {
//@ts-ignore
mockSearch.mockImplementation(() => Promise.resolve(searchResults));
let wrapper = await setup();
wrapper.update();
expect(wrapper.find({ 'aria-label': 'Search section' })).toHaveLength(2);
expect(wrapper.find({ 'aria-label': 'Search items' }).children()).toHaveLength(2);
mockSearch.mockResolvedValueOnce(searchResults);
setup();
const section = await screen.findAllByLabelText('Search section');
expect(section).toHaveLength(2);
expect(screen.getAllByLabelText('Search items')).toHaveLength(2);
});
it('should call search with selected tags', async () => {
let wrapper = await setup();
setup();
//@ts-ignore
await act(async () => {
//@ts-ignore
await wrapper.find('TagFilter').prop('onChange')(['TestTag']);
jest.runAllTimers();
});
await waitFor(() => screen.getByLabelText('Tag filter'));
// Get the actual element for the underlying Select component, since Select doesn't accept aria- props
const tagComponent = screen.getByLabelText('Tag filter').querySelector('div') as Node;
fireEvent.keyDown(tagComponent, { keyCode: 40 });
expect(mockSearch).toHaveBeenCalledWith({
query: '',
skipRecent: false,
skipStarred: false,
tag: ['TestTag'],
starred: false,
folderIds: [],
layout: SearchLayout.Folders,
sort: undefined,
});
});
await waitFor(() => screen.getByText('tag1'));
fireEvent.click(screen.getByText('tag1'));
it('should call search api with provided search params', async () => {
const params = { query: 'test query', tag: ['tag1'], sort: { value: 'asc' } };
await setup({ params });
expect(tagComponent).toBeInTheDocument();
expect(mockSearch).toHaveBeenCalledTimes(1);
expect(mockSearch).toHaveBeenCalledWith(
expect.objectContaining({
query: 'test query',
await waitFor(() =>
expect(mockSearch).toHaveBeenCalledWith({
query: '',
skipRecent: false,
skipStarred: false,
tag: ['tag1'],
sort: 'asc',
starred: false,
folderIds: [],
layout: SearchLayout.Folders,
sort: undefined,
})
);
});
it('should call search api with provided search params', async () => {
const params = { query: 'test query', tag: ['tag1'], sort: { value: 'asc' } };
setup({ params });
await waitFor(() => {
expect(mockSearch).toHaveBeenCalledTimes(1);
expect(mockSearch).toHaveBeenCalledWith(
expect.objectContaining({
query: 'test query',
tag: ['tag1'],
sort: 'asc',
})
);
});
});
});
export const mockSearch = jest.fn(() => {
export const mockSearch = jest.fn<any, any>(() => {
return Promise.resolve([]);
});
jest.mock('app/core/services/search_srv', () => {
return {
SearchSrv: jest.fn().mockImplementation(() => {
return {
search: mockSearch,
getDashboardTags: jest.fn(() => Promise.resolve(['Tag1', 'Tag2'])),
getDashboardTags: jest.fn(() =>
Promise.resolve([
{ term: 'tag1', count: 2 },
{ term: 'tag2', count: 10 },
])
),
getSortOptions: jest.fn(() => Promise.resolve({ sortOptions: [{ name: 'test', displayName: 'Test' }] })),
};
}),
......
import '@testing-library/jest-dom';
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment