Skip to content

Commit

Permalink
test: add unit test for chst history page
Browse files Browse the repository at this point in the history
Signed-off-by: Lin Wang <[email protected]>
  • Loading branch information
wanglam committed Dec 8, 2023
1 parent 9663fb1 commit b9f07db
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 26 deletions.
1 change: 1 addition & 0 deletions public/services/sessions_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class SessionsService {
this.abortController = new AbortController();
this._options = query;
try {
this.status$.next('loading');
this.sessions$.next(
await this._http.get<ISessionFindResponse>(ASSISTANT_API.SESSIONS, {
query: this._options as HttpFetchQuery,
Expand Down
212 changes: 188 additions & 24 deletions public/tabs/history/__tests__/chat_history_page.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,45 @@
*/

import React from 'react';
import { act, fireEvent, render } from '@testing-library/react';
import { BehaviorSubject } from 'rxjs';
import { act, fireEvent, render, waitFor } from '@testing-library/react';
import { I18nProvider } from '@osd/i18n/react';

import { coreMock } from '../../../../../../src/core/public/mocks';
import { HttpStart } from '../../../../../../src/core/public';

import * as useChatStateExports from '../../../hooks/use_chat_state';
import * as chatContextExports from '../../../contexts/chat_context';
import * as coreContextExports from '../../../contexts/core_context';
import { SessionsService } from '../../../services/sessions_service';

import { ChatHistoryPage } from '../chat_history_page';

const setup = () => {
const mockGetSessionsHttp = () => {
const http = coreMock.createStart().http;
http.get.mockImplementation(async () => ({
objects: [
{
id: '1',
title: 'foo',
},
],
total: 1,
}));
return http;
};

const setup = ({
http = mockGetSessionsHttp(),
chatContext = {},
}: {
http?: HttpStart;
chatContext?: { flyoutFullScreen?: boolean };
} = {}) => {
const useCoreMock = {
services: {
notifications: {
toasts: {
addSuccess: jest.fn(),
addDanger: jest.fn(),
addError: jest.fn(),
},
},
sessions: {
sessions$: new BehaviorSubject({
objects: [
{
id: '1',
title: 'foo',
},
],
total: 1,
}),
status$: new BehaviorSubject('idle'),
load: jest.fn(),
},
...coreMock.createStart(),
http,
sessions: new SessionsService(http),
sessionLoad: {},
},
};
Expand All @@ -47,6 +53,8 @@ const setup = () => {
sessionId: '1',
setSessionId: jest.fn(),
setTitle: jest.fn(),
setSelectedTabId: jest.fn(),
...chatContext,
};
jest.spyOn(coreContextExports, 'useCore').mockReturnValue(useCoreMock);
jest.spyOn(useChatStateExports, 'useChatState').mockReturnValue(useChatStateMock);
Expand All @@ -68,7 +76,13 @@ const setup = () => {

describe('<ChatHistoryPage />', () => {
it('should clear old session data after current session deleted', async () => {
const { renderResult, useChatStateMock, useChatContextMock } = setup();
const { renderResult, useChatStateMock, useChatContextMock } = setup({
http: mockGetSessionsHttp(),
});

await waitFor(() => {
expect(renderResult.getByLabelText('Delete conversation')).toBeTruthy();
});

act(() => {
fireEvent.click(renderResult.getByLabelText('Delete conversation'));
Expand All @@ -86,4 +100,154 @@ describe('<ChatHistoryPage />', () => {
expect(useChatContextMock.setTitle).toHaveBeenLastCalledWith(undefined);
expect(useChatStateMock.chatStateDispatch).toHaveBeenLastCalledWith({ type: 'reset' });
});

it('should render empty screen', async () => {
const http = coreMock.createStart().http;
http.get.mockImplementation(async () => {
return {
objects: [],
total: 0,
};
});
const { renderResult } = setup({
http,
});

await waitFor(async () => {
expect(
renderResult.getByText(
'No conversation has been recorded. Start a conversation in the assistant to have it saved.'
)
).toBeTruthy();
});
});

it('should render full screen back icon button instead of back', async () => {
const { renderResult } = setup({
chatContext: {
flyoutFullScreen: true,
},
});
await waitFor(async () => {
expect(renderResult.getByLabelText('full screen back')).toBeTruthy();
expect(renderResult.queryByRole('button', { name: 'Back' })).toBeFalsy();
});
});

it('should render back button and history list', async () => {
const { renderResult } = setup();
await waitFor(async () => {
expect(renderResult.getByRole('button', { name: 'Back' })).toBeTruthy();
expect(renderResult.getByText('foo')).toBeTruthy();
});
});

it('should call get sessions with search text', async () => {
const { renderResult, useCoreMock } = setup();
await waitFor(async () => {
expect(renderResult.getByPlaceholderText('Search by conversation name')).toBeTruthy();
});
act(() => {
fireEvent.change(renderResult.getByPlaceholderText('Search by conversation name'), {
target: {
value: 'bar',
},
});
});
await waitFor(() => {
expect(useCoreMock.services.http.get).toHaveBeenLastCalledWith(
expect.any(String),
expect.objectContaining({
query: expect.objectContaining({
search: 'bar',
page: 1,
}),
})
);
});
});

it('should call get sessions with new page size', async () => {
const { renderResult, useCoreMock } = setup();
act(() => {
fireEvent.click(renderResult.getByTestId('tablePaginationPopoverButton'));
});
act(() => {
fireEvent.click(renderResult.getByTestId('tablePagination-50-rows'));
});
await waitFor(() => {
expect(useCoreMock.services.http.get).toHaveBeenLastCalledWith(
expect.any(String),
expect.objectContaining({
query: expect.objectContaining({
page: 1,
perPage: 50,
}),
})
);
});
});

it('should call setSelectedTabId with "chat" after back button click', async () => {
const { renderResult, useChatContextMock } = setup();

expect(useChatContextMock.setSelectedTabId).not.toHaveBeenCalled();
act(() => {
fireEvent.click(renderResult.getByRole('button', { name: 'Back' }));
});
await waitFor(() => {
expect(useChatContextMock.setSelectedTabId).toHaveBeenLastCalledWith('chat');
});
});

it('should call setSelectedTabId with "chat" after full screen back button click', async () => {
const { renderResult, useChatContextMock } = setup({
chatContext: {
flyoutFullScreen: true,
},
});

expect(useChatContextMock.setSelectedTabId).not.toHaveBeenCalled();
act(() => {
fireEvent.click(renderResult.getByLabelText('full screen back'));
});
await waitFor(() => {
expect(useChatContextMock.setSelectedTabId).toHaveBeenLastCalledWith('chat');
});
});

it('should call sessions.reload after shouldRefresh change', async () => {
const { renderResult, useCoreMock } = setup();

jest.spyOn(useCoreMock.services.sessions, 'reload');

expect(useCoreMock.services.sessions.reload).not.toHaveBeenCalled();

renderResult.rerender(
<I18nProvider>
<ChatHistoryPage shouldRefresh={true} />
</I18nProvider>
);

await waitFor(() => {
expect(useCoreMock.services.sessions.reload).toHaveBeenCalled();
});
});

it('should call sessions.abortController.abort after unmount', async () => {
const { renderResult, useCoreMock } = setup();

await waitFor(() => {
expect(useCoreMock.services.sessions.abortController).toBeTruthy();
});
const abortMock = jest.spyOn(useCoreMock.services.sessions.abortController!, 'abort');

expect(abortMock).not.toHaveBeenCalled();

renderResult.unmount();

await waitFor(() => {
expect(abortMock).toHaveBeenCalled();
});
});
});
4 changes: 2 additions & 2 deletions public/tabs/history/chat_history_page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ export const ChatHistoryPage: React.FC<ChatHistoryPageProps> = React.memo((props
} = useChatContext();
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(10);
const [searchName, setSearchName] = useState<string>();
const [debouncedSearchName, setDebouncedSearchName] = useState<string>();
const [searchName, setSearchName] = useState<string>('');
const [debouncedSearchName, setDebouncedSearchName] = useState<string>('');
const bulkGetOptions = useMemo(
() => ({
page: pageIndex + 1,
Expand Down

0 comments on commit b9f07db

Please sign in to comment.