test(plugins/search): complete SearchBarNext coverage

Signed-off-by: Camila Belo <camilaibs@gmail.com>
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Camila Belo
2021-05-26 21:11:35 +02:00
committed by Eric Peterson
parent 51e2b40cbb
commit c568e721f4
3 changed files with 137 additions and 39 deletions
@@ -15,7 +15,8 @@
*/
import React from 'react';
import { renderInTestApp } from '@backstage/test-utils';
import { screen, render, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useApi } from '@backstage/core';
import { SearchContextProvider } from '../SearchContext';
@@ -23,12 +24,10 @@ import { SearchBarNext } from './SearchBarNext';
jest.mock('@backstage/core', () => ({
...jest.requireActual('@backstage/core'),
useApi: jest.fn(),
useApi: jest.fn().mockReturnValue({}),
}));
describe('<SearchBarNext />', () => {
const _alphaPerformSearch = jest.fn();
describe('SearchBarNext', () => {
const initialState = {
term: '',
pageCursor: '',
@@ -36,24 +35,119 @@ describe('<SearchBarNext />', () => {
types: ['*'],
};
beforeEach(() => {
_alphaPerformSearch.mockResolvedValue([]);
(useApi as jest.Mock).mockReturnValue({ _alphaPerformSearch });
});
const name = 'Search term';
const term = 'term';
const _alphaPerformSearch = jest.fn().mockResolvedValue({});
(useApi as jest.Mock).mockReturnValue({ _alphaPerformSearch });
afterAll(() => {
jest.resetAllMocks();
});
it('renders without exploding', async () => {
const { getByRole } = await renderInTestApp(
it('Renders without exploding', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchBarNext />
</SearchContextProvider>,
);
expect(
getByRole('textbox', { name: 'search backstage' }),
).toBeInTheDocument();
await waitFor(() => {
expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
});
});
it('Renders based on initial search', async () => {
render(
<SearchContextProvider initialState={{ ...initialState, term }}>
<SearchBarNext />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByRole('textbox', { name })).toHaveValue(term);
});
});
it('Updates term state when text is entered', async () => {
render(
<SearchContextProvider initialState={initialState}>
<SearchBarNext />
</SearchContextProvider>,
);
const textbox = screen.getByRole('textbox', { name });
const value = 'value';
userEvent.type(textbox, value);
await waitFor(() => {
expect(textbox).toHaveValue(value);
});
expect(_alphaPerformSearch).toHaveBeenLastCalledWith(
expect.objectContaining({ term: value }),
);
});
it('Clear button clears term state', async () => {
render(
<SearchContextProvider initialState={{ ...initialState, term }}>
<SearchBarNext />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByRole('textbox', { name })).toHaveValue(term);
});
userEvent.click(screen.getByRole('button', { name: 'Clear term' }));
await waitFor(() => {
expect(screen.getByRole('textbox', { name })).toHaveValue('');
});
expect(_alphaPerformSearch).toHaveBeenLastCalledWith(
expect.objectContaining({ term: '' }),
);
});
it('Adheres to provided debounceTime', async () => {
jest.useFakeTimers();
const debounceTime = 600;
render(
<SearchContextProvider initialState={initialState}>
<SearchBarNext debounceTime={debounceTime} />
</SearchContextProvider>,
);
await waitFor(() => {
expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
});
const textbox = screen.getByRole('textbox', { name });
const value = 'value';
userEvent.type(textbox, value);
expect(_alphaPerformSearch).not.toHaveBeenLastCalledWith(
expect.objectContaining({ term: value }),
);
act(() => {
jest.advanceTimersByTime(debounceTime);
});
await waitFor(() => {
expect(textbox).toHaveValue(value);
});
expect(_alphaPerformSearch).toHaveBeenLastCalledWith(
expect.objectContaining({ term: value }),
);
});
});
@@ -14,17 +14,26 @@
* limitations under the License.
*/
import React, { useState } from 'react';
import React, { ChangeEvent, useState } from 'react';
import { useDebounce } from 'react-use';
import { Paper, InputBase, IconButton, makeStyles } from '@material-ui/core';
import {
Theme,
Paper,
InputBase,
InputAdornment,
IconButton,
makeStyles,
} from '@material-ui/core';
import SearchIcon from '@material-ui/icons/Search';
import ClearButton from '@material-ui/icons/Clear';
import { useSearch } from '../SearchContext';
const useStyles = makeStyles(() => ({
const useStyles = makeStyles((theme: Theme) => ({
root: {
display: 'flex',
alignItems: 'center',
padding: theme.spacing(0, 0, 0, 1.5),
},
input: {
flex: 1,
@@ -40,36 +49,29 @@ export const SearchBarNext = ({ debounceTime = 0 }: Props) => {
const { term, setTerm } = useSearch();
const [value, setValue] = useState<string>(term);
useDebounce(
() => {
setTerm(value);
},
debounceTime,
[value],
);
useDebounce(() => setTerm(value), debounceTime, [value]);
const handleSearch = (event: React.ChangeEvent | React.FormEvent) => {
event.preventDefault();
setValue((event.target as HTMLInputElement).value as string);
const handleSearch = (e: ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
const handleClearSearchBar = () => {
setTerm('');
};
const handleClear = () => setValue('');
return (
<Paper component="form" onSubmit={handleSearch} className={classes.root}>
<IconButton disabled type="submit" aria-label="search">
<SearchIcon />
</IconButton>
<Paper component="form" className={classes.root}>
<InputBase
className={classes.input}
placeholder="Search in Backstage"
value={value}
onChange={handleSearch}
inputProps={{ 'aria-label': 'search backstage' }}
inputProps={{ 'aria-label': 'Search term' }}
startAdornment={
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
}
/>
<IconButton aria-label="search" onClick={handleClearSearchBar}>
<IconButton aria-label="Clear term" onClick={handleClear}>
<ClearButton />
</IconButton>
</Paper>
@@ -24,9 +24,7 @@ import { SearchContextProvider } from '../SearchContext';
jest.mock('@backstage/core', () => ({
...jest.requireActual('@backstage/core'),
useApi: jest.fn().mockReturnValue({
_alphaPerformSearch: jest.fn().mockResolvedValue({}),
}),
useApi: jest.fn().mockReturnValue({}),
}));
describe('SearchFilterNext', () => {
@@ -44,6 +42,10 @@ describe('SearchFilterNext', () => {
const _alphaPerformSearch = jest.fn().mockResolvedValue({});
(useApi as jest.Mock).mockReturnValue({ _alphaPerformSearch });
afterAll(() => {
jest.resetAllMocks();
});
it('Check that element was rendered and received props', async () => {
const CustomFilter = (props: { name: string }) => <h6>{props.name}</h6>;