diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx
index d65278f7ec..fba50f74d8 100644
--- a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx
+++ b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx
@@ -17,59 +17,59 @@
import React from 'react';
import { screen, render, waitFor, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
-
import { configApiRef, analyticsApiRef } from '@backstage/core-plugin-api';
-import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
-import { MockAnalyticsApi, TestApiRegistry } from '@backstage/test-utils';
-
+import { ConfigReader } from '@backstage/core-app-api';
+import {
+ MockAnalyticsApi,
+ TestApiProvider,
+ renderWithEffects,
+} from '@backstage/test-utils';
import { searchApiRef } from '../../api';
import { SearchContextProvider } from '../../context';
import { SearchBar } from './SearchBar';
-jest.mock('@backstage/core-plugin-api', () => ({
- ...jest.requireActual('@backstage/core-plugin-api'),
-}));
+const createInitialState = ({
+ term = '',
+ filters = {},
+ types = ['*'],
+ pageCursor = '',
+} = {}) => ({
+ term,
+ filters,
+ types,
+ pageCursor,
+});
describe('SearchBar', () => {
- const initialState = {
- term: '',
- filters: {},
- types: ['*'],
- pageCursor: '',
- };
+ const query = jest.fn().mockResolvedValue({ results: [] });
- const query = jest.fn().mockResolvedValue({});
- const analyticsApiSpy = new MockAnalyticsApi();
- let apiRegistry: TestApiRegistry;
+ const analyticsApiMock = new MockAnalyticsApi();
- apiRegistry = TestApiRegistry.from(
- [
- configApiRef,
- new ConfigReader({
- app: { title: 'Mock title' },
- }),
- ],
- [searchApiRef, { query }],
- );
+ const configApiMock = new ConfigReader({
+ app: { title: 'Mock title' },
+ });
- const name = 'Search';
- const term = 'term';
+ const searchApiMock = { query };
- afterAll(() => {
- jest.resetAllMocks();
+ beforeEach(() => {
+ jest.clearAllMocks();
});
it('Renders without exploding', async () => {
- render(
-
-
+ await renderWithEffects(
+
+
- ,
+ ,
);
await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
expect(
screen.getByPlaceholderText('Search in Mock title'),
).toBeInTheDocument();
@@ -77,59 +77,72 @@ describe('SearchBar', () => {
});
it('Renders with custom placeholder', async () => {
- render(
-
-
-
+ const placeholder = 'placeholder';
+
+ await renderWithEffects(
+
+
+
- ,
- ,
+ ,
);
await waitFor(() => {
- expect(
- screen.getByPlaceholderText('This is a custom placeholder'),
- ).toBeInTheDocument();
+ expect(screen.getByPlaceholderText(placeholder)).toBeInTheDocument();
});
});
it('Renders based on initial search', async () => {
+ const term = 'term';
+
render(
-
-
+
+
- ,
- ,
+ ,
);
await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toHaveValue(term);
+ expect(screen.getByRole('textbox', { name: 'Search' })).toHaveValue(term);
});
});
it('Updates term state when text is entered', async () => {
- const user = userEvent.setup({ delay: null });
jest.useFakeTimers();
- const defaultDebounceTime = 200;
+ const user = userEvent.setup({ delay: null });
- render(
-
-
+ await renderWithEffects(
+
+
- ,
- ,
+ ,
);
- const textbox = screen.getByRole('textbox', { name });
+ const textbox = screen.getByRole('textbox', { name: 'Search' });
const value = 'value';
await user.type(textbox, value);
act(() => {
- jest.advanceTimersByTime(defaultDebounceTime);
+ jest.advanceTimersByTime(200);
});
await waitFor(() => {
@@ -139,26 +152,36 @@ describe('SearchBar', () => {
expect(query).toHaveBeenLastCalledWith(
expect.objectContaining({ term: value }),
);
+
jest.useRealTimers();
});
it('Clear button clears term state', async () => {
- render(
-
-
+ const term = 'term';
+
+ await renderWithEffects(
+
+
- ,
+ ,
);
+ const textbox = screen.getByRole('textbox', { name: 'Search' });
+
await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toHaveValue(term);
+ expect(textbox).toHaveValue(term);
});
await userEvent.click(screen.getByRole('button', { name: 'Clear' }));
await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toHaveValue('');
+ expect(textbox).toHaveValue('');
});
expect(query).toHaveBeenLastCalledWith(
@@ -167,12 +190,19 @@ describe('SearchBar', () => {
});
it('Should not show clear button', async () => {
- render(
-
-
+ const term = 'term';
+
+ await renderWithEffects(
+
+
- ,
+ ,
);
await waitFor(() => {
@@ -183,170 +213,168 @@ describe('SearchBar', () => {
});
it('Adheres to provided debounceTime', async () => {
- const user = userEvent.setup({ delay: null });
jest.useFakeTimers();
-
+ const user = userEvent.setup({ delay: null });
const debounceTime = 600;
- render(
-
-
+ await renderWithEffects(
+
+
- ,
- ,
+ ,
);
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- });
-
- const textbox = screen.getByRole('textbox', { name });
+ const textbox = await screen.findByRole('textbox', { name: 'Search' });
const value = 'value';
await user.type(textbox, value);
- expect(query).not.toHaveBeenLastCalledWith(
- expect.objectContaining({ term: value }),
- );
+ await waitFor(() => {
+ expect(query).not.toHaveBeenLastCalledWith(
+ expect.objectContaining({ term: value }),
+ );
+ });
act(() => {
jest.advanceTimersByTime(debounceTime);
});
- expect(textbox).toHaveValue(value);
+
+ await waitFor(() => {
+ expect(textbox).toHaveValue(value);
+ });
expect(query).toHaveBeenLastCalledWith(
expect.objectContaining({ term: value }),
);
+
jest.useRealTimers();
});
it('does not capture analytics event if not enabled in app', async () => {
- const user = userEvent.setup({ delay: null });
jest.useFakeTimers();
+ const user = userEvent.setup({ delay: null });
- const debounceTime = 600;
-
- render(
-
-
-
+ await renderWithEffects(
+
+
+
- ,
- ,
+ ,
);
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- });
-
- const textbox = screen.getByRole('textbox', { name });
+ const textbox = await screen.findByRole('textbox', { name: 'Search' });
const value = 'value';
await user.type(textbox, value);
act(() => {
- jest.advanceTimersByTime(debounceTime);
+ jest.advanceTimersByTime(200);
});
- await waitFor(() => expect(textbox).toHaveValue(value));
+ await waitFor(() => {
+ expect(textbox).toHaveValue(value);
+ });
+
+ expect(analyticsApiMock.getEvents()).toHaveLength(0);
- expect(analyticsApiSpy.getEvents()).toHaveLength(0);
jest.useRealTimers();
});
it('captures analytics events if enabled in app', async () => {
- const user = userEvent.setup({ delay: null });
jest.useFakeTimers();
+ const user = userEvent.setup({ delay: null });
+ const types = ['techdocs', 'software-catalog'];
- const debounceTime = 600;
-
- apiRegistry = TestApiRegistry.from(
- [analyticsApiRef, analyticsApiSpy],
- [
- configApiRef,
- new ConfigReader({
- app: {
- title: 'Mock title',
- analytics: {
- ga: {
- trackingId: 'xyz123',
+ await renderWithEffects(
+
-
-
+ }),
+ ],
+ ]}
+ >
+
+
- ,
+ ,
);
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- });
+ const textbox = await screen.findByRole('textbox', { name: 'Search' });
- const textbox = screen.getByRole('textbox', { name });
-
- const value = 'value';
+ let value = 'value';
await user.type(textbox, value);
- expect(analyticsApiSpy.getEvents()).toHaveLength(0);
+ expect(analyticsApiMock.getEvents()).toHaveLength(0);
act(() => {
- jest.advanceTimersByTime(debounceTime);
+ jest.advanceTimersByTime(200);
});
await waitFor(() => expect(textbox).toHaveValue(value));
- expect(analyticsApiSpy.getEvents()).toHaveLength(1);
- expect(analyticsApiSpy.getEvents()[0]).toEqual({
+ expect(analyticsApiMock.getEvents()).toHaveLength(1);
+ expect(analyticsApiMock.getEvents()[0]).toEqual({
action: 'search',
context: {
extension: 'SearchBar',
pluginId: 'search',
routeRef: 'unknown',
- searchTypes: 'software-catalog,techdocs',
+ searchTypes: types.toString(),
},
- subject: 'value',
+ subject: value,
});
await user.clear(textbox);
+ value = 'new value';
+
// make sure new term is captured
- await user.type(textbox, 'new value');
+ await user.type(textbox, value);
act(() => {
- jest.advanceTimersByTime(debounceTime);
+ jest.advanceTimersByTime(200);
});
- await waitFor(() => expect(textbox).toHaveValue('new value'));
+ await waitFor(() => expect(textbox).toHaveValue(value));
- expect(analyticsApiSpy.getEvents()).toHaveLength(2);
- expect(analyticsApiSpy.getEvents()[1]).toEqual({
+ expect(analyticsApiMock.getEvents()).toHaveLength(2);
+ expect(analyticsApiMock.getEvents()[1]).toEqual({
action: 'search',
context: {
extension: 'SearchBar',
pluginId: 'search',
routeRef: 'unknown',
- searchTypes: 'software-catalog,techdocs',
+ searchTypes: types.toString(),
},
- subject: 'new value',
+ subject: value,
});
+
jest.useRealTimers();
});
});
diff --git a/plugins/search/src/components/SearchBar/SearchBar.test.tsx b/plugins/search/src/components/SearchBar/SearchBar.test.tsx
deleted file mode 100644
index c48c3d3ed2..0000000000
--- a/plugins/search/src/components/SearchBar/SearchBar.test.tsx
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright 2021 The Backstage Authors
- *
- * Licensed 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 React from 'react';
-import { screen, render, waitFor, act } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import { configApiRef, analyticsApiRef } from '@backstage/core-plugin-api';
-import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
-import { MockAnalyticsApi, TestApiRegistry } from '@backstage/test-utils';
-import {
- SearchContextProvider,
- searchApiRef,
-} from '@backstage/plugin-search-react';
-
-import { SearchBar } from './SearchBar';
-
-jest.mock('@backstage/core-plugin-api', () => ({
- ...jest.requireActual('@backstage/core-plugin-api'),
-}));
-
-describe('SearchBar', () => {
- const initialState = {
- term: '',
- filters: {},
- types: ['*'],
- pageCursor: '',
- };
-
- const query = jest.fn().mockResolvedValue({});
- const analyticsApiSpy = new MockAnalyticsApi();
- let apiRegistry: TestApiRegistry;
-
- apiRegistry = TestApiRegistry.from(
- [
- configApiRef,
- new ConfigReader({
- app: { title: 'Mock title' },
- }),
- ],
- [searchApiRef, { query }],
- );
-
- const name = 'Search';
- const term = 'term';
-
- afterAll(() => {
- jest.resetAllMocks();
- });
-
- it('Renders without exploding', async () => {
- render(
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- expect(
- screen.getByPlaceholderText('Search in Mock title'),
- ).toBeInTheDocument();
- });
- });
-
- it('Renders with custom placeholder', async () => {
- render(
-
-
-
-
- ,
- ,
- );
-
- await waitFor(() => {
- expect(
- screen.getByPlaceholderText('This is a custom placeholder'),
- ).toBeInTheDocument();
- });
- });
-
- it('Renders based on initial search', async () => {
- render(
-
-
-
-
- ,
- ,
- );
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toHaveValue(term);
- });
- });
-
- it('Updates term state when text is entered', async () => {
- const user = userEvent.setup({ delay: null });
- jest.useFakeTimers();
- const defaultDebounceTime = 200;
-
- render(
-
-
-
-
- ,
- ,
- );
-
- const textbox = screen.getByRole('textbox', { name });
-
- const value = 'value';
-
- await user.type(textbox, value);
-
- act(() => {
- jest.advanceTimersByTime(defaultDebounceTime);
- });
-
- await waitFor(() => {
- expect(textbox).toHaveValue(value);
- });
-
- expect(query).toHaveBeenLastCalledWith(
- expect.objectContaining({ term: value }),
- );
- jest.useRealTimers();
- });
-
- it('Clear button clears term state', async () => {
- render(
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toHaveValue(term);
- });
-
- await userEvent.click(screen.getByRole('button', { name: 'Clear' }));
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toHaveValue('');
- });
-
- expect(query).toHaveBeenLastCalledWith(
- expect.objectContaining({ term: '' }),
- );
- });
-
- it('Should not show clear button', async () => {
- render(
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(
- screen.queryByRole('button', { name: 'Clear' }),
- ).not.toBeInTheDocument();
- });
- });
-
- it('Adheres to provided debounceTime', async () => {
- const user = userEvent.setup({ delay: null });
- jest.useFakeTimers();
-
- const debounceTime = 600;
-
- render(
-
-
-
-
- ,
- ,
- );
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- });
-
- const textbox = screen.getByRole('textbox', { name });
-
- const value = 'value';
-
- await user.type(textbox, value);
-
- expect(query).not.toHaveBeenLastCalledWith(
- expect.objectContaining({ term: value }),
- );
-
- act(() => {
- jest.advanceTimersByTime(debounceTime);
- });
- expect(textbox).toHaveValue(value);
-
- expect(query).toHaveBeenLastCalledWith(
- expect.objectContaining({ term: value }),
- );
- jest.useRealTimers();
- });
-
- it('does not capture analytics event if not enabled in app', async () => {
- const user = userEvent.setup({ delay: null });
- jest.useFakeTimers();
-
- const debounceTime = 600;
-
- render(
-
-
-
-
- ,
- ,
- );
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- });
-
- const textbox = screen.getByRole('textbox', { name });
-
- const value = 'value';
-
- await user.type(textbox, value);
-
- act(() => {
- jest.advanceTimersByTime(debounceTime);
- });
-
- await waitFor(() => expect(textbox).toHaveValue(value));
-
- expect(analyticsApiSpy.getEvents()).toHaveLength(0);
- jest.useRealTimers();
- });
-
- it('captures analytics events if enabled in app', async () => {
- const user = userEvent.setup({ delay: null });
- jest.useFakeTimers();
-
- const debounceTime = 600;
-
- apiRegistry = TestApiRegistry.from(
- [analyticsApiRef, analyticsApiSpy],
- [
- configApiRef,
- new ConfigReader({
- app: {
- title: 'Mock title',
- analytics: {
- ga: {
- trackingId: 'xyz123',
- },
- },
- },
- }),
- ],
- [searchApiRef, { query }],
- );
-
- render(
-
-
-
-
- ,
- );
-
- await waitFor(() => {
- expect(screen.getByRole('textbox', { name })).toBeInTheDocument();
- });
-
- const textbox = screen.getByRole('textbox', { name });
-
- const value = 'value';
-
- await user.type(textbox, value);
-
- expect(analyticsApiSpy.getEvents()).toHaveLength(0);
-
- act(() => {
- jest.advanceTimersByTime(debounceTime);
- });
-
- await waitFor(() => expect(textbox).toHaveValue(value));
-
- expect(analyticsApiSpy.getEvents()).toHaveLength(1);
- expect(analyticsApiSpy.getEvents()[0]).toEqual({
- action: 'search',
- context: {
- extension: 'App',
- pluginId: 'root',
- routeRef: 'unknown',
- searchTypes: 'software-catalog,techdocs',
- },
- subject: 'value',
- });
-
- await user.clear(textbox);
-
- // make sure new term is captured
- await user.type(textbox, 'new value');
-
- act(() => {
- jest.advanceTimersByTime(debounceTime);
- });
-
- await waitFor(() => expect(textbox).toHaveValue('new value'));
-
- expect(analyticsApiSpy.getEvents()).toHaveLength(2);
- expect(analyticsApiSpy.getEvents()[1]).toEqual({
- action: 'search',
- context: {
- extension: 'App',
- pluginId: 'root',
- routeRef: 'unknown',
- searchTypes: 'software-catalog,techdocs',
- },
- subject: 'new value',
- });
- jest.useRealTimers();
- });
-});