Skip to content

Commit

Permalink
add unit test
Browse files Browse the repository at this point in the history
Signed-off-by: Hailong Cui <[email protected]>

add more unit test

Signed-off-by: Hailong Cui <[email protected]>
  • Loading branch information
Hailong-am committed Oct 10, 2024
1 parent 3a7a107 commit 024f2fb
Show file tree
Hide file tree
Showing 18 changed files with 813 additions and 52 deletions.
3 changes: 2 additions & 1 deletion src/core/public/chrome/chrome_service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ export class ChromeService {
workspaceList$={workspaces.workspaceList$}
currentWorkspace$={workspaces.currentWorkspace$}
useUpdatedHeader={this.useUpdatedHeader}
GlobalSearchStrategies={globalSearch.getAllSearchStrategies()}
globalSearchStrategies={globalSearch.getAllSearchStrategies()}
/>
),

Expand Down Expand Up @@ -488,6 +488,7 @@ export class ChromeService {
export interface ChromeSetup {
registerCollapsibleNavHeader: (render: CollapsibleNavHeaderRender) => void;
navGroup: ChromeNavGroupServiceSetupContract;
/** {@inheritdoc GlobalSearchService} */
globalSearch: GlobalSearchServiceSetupContract;
}

Expand Down
47 changes: 47 additions & 0 deletions src/core/public/chrome/global_search/global_search_service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

// test global_search_service.ts
import { GlobalSearchService, SearchObjectTypes } from './global_search_service';

describe('GlobalSearchService', () => {
const globalSearchService = new GlobalSearchService();
const setup = globalSearchService.setup();
const start = globalSearchService.start();

it('registerSearchStrategy', async () => {
setup.registerSearchStrategy({
id: 'test',
type: SearchObjectTypes.PAGES,
doSearch: async (query) => {
return [];
},
});

expect(start.getAllSearchStrategies()).toHaveLength(1);
expect(start.getAllSearchStrategies()[0].id).toEqual('test');
expect(start.getAllSearchStrategies()[0].type).toEqual(SearchObjectTypes.PAGES);
});

it('registerSearchStrategy with duplicate id', async () => {
setup.registerSearchStrategy({
id: 'test',
type: SearchObjectTypes.PAGES,
doSearch: async (query) => {
return [];
},
});

setup.registerSearchStrategy({
id: 'test',
type: SearchObjectTypes.PAGES,
doSearch: async (query) => {
return [];
},
});

expect(start.getAllSearchStrategies()).toHaveLength(1);
});
});
22 changes: 20 additions & 2 deletions src/core/public/chrome/global_search/global_search_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface GlobalSearchStrategy {
/**
* do the search and return search result with a React element
* @param value search query
* @param callback callback function when search is done
*/
doSearch(value: string, callback?: () => void): Promise<ReactNode[]>;
}
Expand All @@ -37,7 +38,23 @@ export interface GlobalSearchServiceStartContract {
getAllSearchStrategies(): GlobalSearchStrategy[];
}

/** @experimental */
/**
* {@link GlobalSearchStrategy | APIs} for registering new global search strategy when do search from header search bar .
*
* @example
* Register a GlobalSearchStrategy to search pages
* ```jsx
* chrome.globalSearch.registerSearchStrategy({
* id: 'test',
* type: SearchObjectTypes.PAGES,
* doSearch: async (query) => {
* return [];
* },
* })
* ```
*
* @experimental
*/
export class GlobalSearchService {
private searchStrategies = [] as GlobalSearchStrategy[];

Expand All @@ -47,7 +64,7 @@ export class GlobalSearchService {
});
if (exists) {
// eslint-disable-next-line no-console
console.warn('duplicate SearchStrategy id found');
console.warn('Duplicate SearchStrategy id found');
return;
}
this.searchStrategies.push(searchStrategy);
Expand All @@ -58,6 +75,7 @@ export class GlobalSearchService {
registerSearchStrategy: this.registerSearchStrategy.bind(this),
};
}

public start() {
return {
getAllSearchStrategies: () => this.searchStrategies,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@
margin-right: $euiSize;
background-color: transparent;
flex-grow: 0;

.searchInput {
border-radius: $euiSizeS;
background-color: transparent;
}
}

.searchBarIcon {
Expand Down
4 changes: 2 additions & 2 deletions src/core/public/chrome/ui/header/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export interface HeaderProps {
workspaceList$: Observable<WorkspaceObject[]>;
currentWorkspace$: WorkspacesStart['currentWorkspace$'];
useUpdatedHeader?: boolean;
GlobalSearchStrategies?: GlobalSearchStrategy[];
globalSearchStrategies?: GlobalSearchStrategy[];
}

const hasValue = (value: any) => {
Expand All @@ -149,7 +149,7 @@ export function Header({
navGroupEnabled,
setCurrentNavGroup,
useUpdatedHeader,
GlobalSearchStrategies,
globalSearchStrategies,
...observables
}: HeaderProps) {
const isVisible = useObservable(observables.isVisible$, false);
Expand Down
179 changes: 179 additions & 0 deletions src/core/public/chrome/ui/header/header_search_bar.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { act, fireEvent, render, waitFor } from '@testing-library/react';
import { HeaderSearchBarIcon, HeaderSearchBar } from './header_search_bar';
import { GlobalSearchStrategy, SearchObjectTypes } from '../../global_search';
import { EuiText } from '@elastic/eui';

describe('<HeaderSearchBarIcon />', () => {
const searchFn = jest.fn().mockResolvedValue([]);
const searchFnBar = jest.fn().mockResolvedValue([]);
const globalSearchStrategies: GlobalSearchStrategy[] = [
{
id: 'foo',
type: SearchObjectTypes.PAGES,
doSearch: searchFn,
},
{
id: 'bar',
type: SearchObjectTypes.PAGES,
doSearch: searchFnBar,
},
];

it('render HeaderSearchBarIcon correctly without search results', () => {
const { getByTestId, queryByText } = render(
<HeaderSearchBarIcon globalSearchStrategies={globalSearchStrategies} />
);
const searchIcon = getByTestId('search-icon');
expect(searchIcon).toBeVisible();

fireEvent.click(searchIcon);

expect(getByTestId('search-input')).toBeVisible();

act(() => {
fireEvent.change(getByTestId('search-input'), {
target: { value: 'index' },
});
});

expect(searchFn).toHaveBeenCalled();

waitFor(() => {
expect(queryByText('No results found.')).toBeInTheDocument();
});
});

it('render HeaderSearchBarIcon correctly with search results', () => {
const { getByTestId, queryByText } = render(
<HeaderSearchBarIcon globalSearchStrategies={globalSearchStrategies} />
);
const searchIcon = getByTestId('search-icon');
expect(searchIcon).toBeVisible();

fireEvent.click(searchIcon);

expect(getByTestId('search-input')).toBeVisible();

searchFn.mockResolvedValue([<EuiText>index page</EuiText>]);
act(() => {
fireEvent.change(getByTestId('search-input'), {
target: { value: 'index' },
});
});

expect(searchFn).toHaveBeenCalled();

waitFor(() => {
expect(queryByText('index page')).toBeInTheDocument();
});
});
});

describe('<HeaderSearchBar />', () => {
const searchFn = jest.fn().mockResolvedValue([]);
const searchFnBar = jest.fn().mockResolvedValue([]);
const globalSearchStrategies: GlobalSearchStrategy[] = [
{
id: 'foo',
type: SearchObjectTypes.PAGES,
doSearch: searchFn,
},
{
id: 'bar',
type: SearchObjectTypes.PAGES,
doSearch: searchFnBar,
},
];

it('render HeaderSearchBar with panel', () => {
const { getByTestId } = render(
<HeaderSearchBar globalSearchStrategies={globalSearchStrategies} panel />
);
const searchPanel = getByTestId('search-result-panel');
expect(searchPanel).toBeVisible();
});

it('render HeaderSearchBar with search result', () => {
const { getByTestId, queryByText } = render(
<HeaderSearchBar globalSearchStrategies={globalSearchStrategies} panel />
);
const searchPanel = getByTestId('search-result-panel');
expect(searchPanel).toBeVisible();

expect(getByTestId('search-input')).toBeVisible();

searchFn.mockResolvedValue([<EuiText>index page</EuiText>]);
searchFnBar.mockResolvedValue([<EuiText>index polices</EuiText>]);
act(() => {
fireEvent.change(getByTestId('search-input'), {
target: { value: 'index' },
});
});

expect(searchFn).toHaveBeenCalled();

// merge page results together
waitFor(() => {
expect(queryByText('index page')).toBeInTheDocument();
expect(queryByText('index polices')).toBeInTheDocument();
});
});

it('render HeaderSearchBar with reject search result', () => {
const { getByTestId, queryByText } = render(
<HeaderSearchBar globalSearchStrategies={globalSearchStrategies} panel />
);
const searchPanel = getByTestId('search-result-panel');
expect(searchPanel).toBeVisible();

expect(getByTestId('search-input')).toBeVisible();

searchFn.mockResolvedValue([<EuiText>index page</EuiText>]);
searchFnBar.mockRejectedValue(new Error('Async search error'));

act(() => {
fireEvent.change(getByTestId('search-input'), {
target: { value: 'index' },
});
});

expect(searchFn).toHaveBeenCalled();

// ignore reject and show pages for success search≠
waitFor(() => {
expect(queryByText('index page')).toBeInTheDocument();
});
});

it('render HeaderSearchBar with all reject search result', () => {
const { getByTestId, queryByText } = render(
<HeaderSearchBar globalSearchStrategies={globalSearchStrategies} panel />
);
const searchPanel = getByTestId('search-result-panel');
expect(searchPanel).toBeVisible();

expect(getByTestId('search-input')).toBeVisible();

searchFnBar.mockRejectedValue(new Error('Async search error'));
searchFn.mockRejectedValue(new Error('Async search error'));

act(() => {
fireEvent.change(getByTestId('search-input'), {
target: { value: 'index' },
});
});

expect(searchFn).toHaveBeenCalled();

// show no result for all reject search
waitFor(() => {
expect(queryByText('No results found.')).toBeInTheDocument();
});
});
});
Loading

0 comments on commit 024f2fb

Please sign in to comment.