Skip to content

Commit 7dd5d28

Browse files
UT - ChatHistoryPanel, FeatureCard
1 parent 273129b commit 7dd5d28

File tree

4 files changed

+252
-126
lines changed

4 files changed

+252
-126
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ __pycache__/
77
static
88
scripts/config.json
99
venv
10-
myenv
10+
myenv
11+
frontend/coverage

frontend/src/components/ChatHistory/ChatHistoryPanel.test.tsx

Lines changed: 171 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -2,122 +2,223 @@ import React from 'react';
22
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
33
import { AppStateContext } from '../../state/AppProvider';
44
import { ChatHistoryPanel } from './ChatHistoryPanel';
5-
import { ChatHistoryLoadingState, ChatMessage, Conversation, CosmosDBStatus, Feedback } from '../../api';
5+
import { ChatHistoryLoadingState, ChatMessage, Conversation, CosmosDBStatus, Feedback, historyDeleteAll,historyList } from '../../api';
66
import * as api from '../../api';
7-
7+
88
// Mock the API function
99
jest.mock('../../api/api', () => ({
10-
historyDeleteAll: jest.fn(),
11-
historyDelete: jest.fn(),
12-
historyGenerate: jest.fn(),
13-
historyUpdate: jest.fn(),
14-
historyClear: jest.fn(),
15-
historyEnsure: jest.fn()
16-
17-
}));
10+
historyList: jest.fn(() => Promise.resolve('mocked data')),
11+
historyDeleteAll: jest.fn(),
12+
historyDelete: jest.fn(),
13+
historyGenerate: jest.fn(),
14+
historyUpdate: jest.fn(),
15+
historyClear: jest.fn(),
16+
historyEnsure: jest.fn()
17+
}));
18+
1819
// Define a mock ChatMessage
1920
const mockMessage: ChatMessage = {
20-
id: 'msg1',
21-
role: 'user',
22-
content: 'This is a mock message for testing purposes.',
23-
end_turn: true,
24-
date: '2024-01-01T12:00:00Z',
25-
feedback: Feedback.Positive,
26-
context: 'Previous messages or context information',
27-
};
21+
id: 'msg1',
22+
role: 'user',
23+
content: 'This is a mock message for testing purposes.',
24+
end_turn: true,
25+
date: '2024-01-01T12:00:00Z',
26+
feedback: Feedback.Positive,
27+
context: 'Previous messages or context information',
28+
};
29+
2830
// Define a mock Conversation
2931
const mockConversation: Conversation = {
30-
id: '1',
31-
title: 'Mock Conversation 1',
32-
messages: [mockMessage],
33-
date: '2024-01-01T00:00:00Z',
34-
};
32+
id: '1',
33+
title: 'Mock Conversation 1',
34+
messages: [mockMessage],
35+
date: '2024-01-01T00:00:00Z',
36+
};
37+
3538
// Mock initial state for the context
3639
const mockState = {
37-
chatHistoryLoadingState: ChatHistoryLoadingState.Success,
38-
isCosmosDBAvailable: { cosmosDB: true, status: CosmosDBStatus.NotConfigured },
39-
chatHistory: [],
40-
isChatHistoryOpen: true,
41-
filteredChatHistory: [],
42-
currentChat: null,
43-
browseChat: mockConversation, // This should be a valid Conversation[]
44-
generateChat: null, // Adjust as necessary
45-
frontendSettings: {}, // Adjust as necessary
46-
feedbackState: {}, // Adjust as necessary
47-
draftedDocument: null, // Adjust as necessary
48-
draftedDocumentTitles: [], // Adjust as necessary
49-
draftedDocumentTitle: 'Some Title', // Ensure this is included
50-
isGenerating: false,
40+
chatHistoryLoadingState: ChatHistoryLoadingState.Success,
41+
isCosmosDBAvailable: { cosmosDB: true, status: CosmosDBStatus.NotConfigured },
42+
chatHistory: [mockConversation], // Added mock chat history here
43+
isChatHistoryOpen: true,
44+
filteredChatHistory: [],
45+
currentChat: null,
46+
browseChat: mockConversation,
47+
generateChat: null,
48+
frontendSettings: {},
49+
feedbackState: {},
50+
draftedDocument: null,
51+
draftedDocumentTitles: [],
52+
draftedDocumentTitle: 'Some Title',
53+
isGenerating: false,
5154
isRequestInitiated: false,
52-
};
53-
55+
};
56+
5457
const mockDispatch = jest.fn();
5558
const renderComponent = (state = mockState) => {
56-
return render(
57-
<AppStateContext.Provider value={{ state, dispatch: mockDispatch }}>
58-
<ChatHistoryPanel />
59-
</AppStateContext.Provider>
60-
);
61-
};
62-
63-
59+
return render(
60+
<AppStateContext.Provider value={{ state, dispatch: mockDispatch }}>
61+
<ChatHistoryPanel />
62+
</AppStateContext.Provider>
63+
);
64+
};
65+
6466
describe('ChatHistoryPanel', () => {
6567
afterEach(() => {
6668
jest.clearAllMocks();
6769
});
68-
70+
6971
test('renders the chat history panel header', () => {
7072
renderComponent();
7173
expect(screen.getByRole('heading', { level: 2 })).toHaveTextContent('Template history');
7274
});
73-
75+
7476
test('shows loading spinner when loading chat history', () => {
7577
const loadingState = { ...mockState, chatHistoryLoadingState: ChatHistoryLoadingState.Loading };
7678
renderComponent(loadingState);
7779
expect(screen.getByText('Loading chat history')).toBeInTheDocument();
7880
});
79-
81+
8082
test('shows error message when loading fails', () => {
81-
const errorState = { ...mockState, chatHistoryLoadingState: ChatHistoryLoadingState.Fail, isCosmosDBAvailable: { cosmosDB: false,status: CosmosDBStatus.NotConfigured} };
83+
const errorState = { ...mockState, chatHistoryLoadingState: ChatHistoryLoadingState.Fail, isCosmosDBAvailable: { cosmosDB: false, status: CosmosDBStatus.NotConfigured } };
8284
renderComponent(errorState);
8385
expect(screen.getByText("Template history can't be saved at this time")).toBeInTheDocument();
8486
});
85-
87+
8688
test('displays chat history when loaded successfully', () => {
87-
const successState = { ...mockState, chatHistoryLoadingState: ChatHistoryLoadingState.Success, isCosmosDBAvailable: { cosmosDB: true,status: CosmosDBStatus.Working} };
89+
const successState = { ...mockState, chatHistoryLoadingState: ChatHistoryLoadingState.Success, isCosmosDBAvailable: { cosmosDB: true, status: CosmosDBStatus.Working } };
8890
renderComponent(successState);
8991
expect(screen.getByText('Template history')).toBeInTheDocument(); // Adjust according to what ChatHistoryList renders
9092
});
91-
93+
9294
test('opens clear all chat history dialog when button is clicked', () => {
9395
renderComponent();
9496
fireEvent.click(screen.getByText(/Clear all chat history/i));
9597
expect(screen.getByText(/Are you sure you want to clear all chat history/i)).toBeInTheDocument();
9698
});
97-
99+
98100
test('calls the clear all history function when confirmed', async () => {
99101
(api.historyDeleteAll as jest.Mock).mockResolvedValueOnce({ ok: true });
100102
renderComponent();
101-
102103
fireEvent.click(screen.getByText(/Clear all chat history/i));
103-
104104
fireEvent.click(screen.getByRole('button', { name: /Clear All/i }));
105-
106105
await waitFor(() => {
106+
// Ensure the `historyDeleteAll` function was called
107107
expect(api.historyDeleteAll).toHaveBeenCalled();
108+
109+
// Ensure the dispatch action for deleting chat history is called
108110
expect(mockDispatch).toHaveBeenCalledWith({ type: 'DELETE_CHAT_HISTORY' });
109111
});
110112
});
111-
112-
// test('shows an error message if clearing chat history fails', async () => {
113-
// (api.historyDeleteAll as jest.Mock).mockResolvedValueOnce({ ok: false });
114-
// renderComponent();
115-
116-
// fireEvent.click(screen.getByText(/Clear all chat history/i));
117-
// fireEvent.click(screen.getByRole('button', { name: /Clear All/i }));
118-
119-
// await waitFor(() => {
120-
// expect(screen.getByText(/Error deleting all of chat history/i)).toBeInTheDocument();
121-
// });
122-
// });
113+
114+
test('shows an error message if clearing chat history fails', async () => {
115+
(api.historyDeleteAll as jest.Mock).mockResolvedValueOnce({ ok: false });
116+
renderComponent();
117+
118+
fireEvent.click(screen.getByText(/Clear all chat history/i));
119+
fireEvent.click(screen.getByRole('button', { name: /Clear All/i }));
120+
121+
await waitFor(() => {
122+
expect(screen.getByText(/Error deleting all of chat history/i)).toBeInTheDocument();
123+
});
124+
});
125+
test('shows loading state while chat history is being cleared', async () => {
126+
// Mock the API response for clearing all chat history
127+
(api.historyDeleteAll as jest.Mock).mockResolvedValueOnce({ ok: true });
128+
129+
// Render the component
130+
renderComponent();
131+
132+
// Trigger the clear all chat history dialog
133+
fireEvent.click(screen.getByText(/Clear all chat history/i));
134+
135+
// Confirm the action to clear history
136+
fireEvent.click(screen.getByRole('button', { name: /Clear All/i }));
137+
138+
// Wait for the loading spinner to appear
139+
expect(screen.getByLabelText('loading more chat history')).toBeInTheDocument(); // Assuming "Loading..." is part of your UI
140+
141+
// Wait for the API response to be completed
142+
await waitFor(() => {
143+
// Ensure the `historyDeleteAll` function was called and the spinner disappears
144+
expect(api.historyDeleteAll).toHaveBeenCalled();
145+
expect(screen.queryByText('loading more chat history')).not.toBeInTheDocument(); // Make sure loading disappears
146+
});
147+
});
148+
test('calls toggleClearAllDialog when clearing is false', async () => {
149+
renderComponent();
150+
// Trigger the dialog
151+
fireEvent.click(screen.getByText('Clear all chat history'));
152+
153+
// Wait for the dialog to appear and assert visibility
154+
await waitFor(() => {
155+
const dialog = screen.getByRole('alertdialog');
156+
expect(dialog).not.toBeVisible();
157+
});
158+
159+
// Simulate the cancel button click and check the dialog closes
160+
fireEvent.click(screen.getByText('Cancel'));
161+
await waitFor(() => {
162+
expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument();
163+
});
164+
});
165+
166+
167+
test('does not close the dialog if clearing is in progress', () => {
168+
// Render the component with clearing set to true
169+
renderComponent();
170+
171+
// Open the dialog
172+
fireEvent.click(screen.getByText(/Clear all chat history/i));
173+
174+
// Ensure the dialog is visible
175+
expect(screen.getByText('Are you sure you want to clear all chat history?')).toBeInTheDocument();
176+
177+
// Mock the toggleClearAllDialog function
178+
const toggleClearAllDialog = jest.fn();
179+
180+
// Try to click Cancel or Close while clearing is true
181+
fireEvent.click(screen.getByText(/Cancel/i)); // or screen.getByText(/Close/i)
182+
183+
// Ensure that toggleClearAllDialog is not called because clearing is in progress
184+
expect(toggleClearAllDialog).not.toHaveBeenCalled();
185+
186+
// Ensure the dialog is still visible
187+
expect(screen.getByText('Are you sure you want to clear all chat history?')).toBeInTheDocument();
188+
});
189+
it('dispatches TOGGLE_CHAT_HISTORY when handleHistoryClick is called', () => {
190+
renderComponent();
191+
192+
// Locate the correct button by its aria-label or title
193+
const toggleButton = screen.getByRole('button', { name: /hide button/i }); // Update the name based on the actual label
194+
fireEvent.click(toggleButton);
195+
196+
// Assert that dispatch was called with the correct action
197+
expect(mockDispatch).toHaveBeenCalledWith({ type: 'TOGGLE_CHAT_HISTORY' });
198+
});
199+
it('sets showContextualMenu to true when CommandBarButton is clicked', () => {
200+
const mockSetShowContextualMenu = jest.fn();
201+
202+
// Mock useState to return the mock function
203+
jest.spyOn(React, 'useState').mockImplementation(() => {
204+
return [false, mockSetShowContextualMenu]; // Return `false` for the initial state, and the mock setter function
123205
});
206+
207+
// Render the component
208+
renderComponent();
209+
210+
// Locate the CommandBarButton by its role and aria-label
211+
const moreButton = screen.getByRole('button', { name: /clear all chat history/i });
212+
213+
// Assert that the button is present in the DOM
214+
expect(moreButton).toBeInTheDocument();
215+
216+
// Simulate a click event on the button
217+
fireEvent.click(moreButton);
218+
219+
// Assert that setShowContextualMenu is called with `true`
220+
expect(mockSetShowContextualMenu).toHaveBeenCalledTimes(16);
221+
expect(mockSetShowContextualMenu).toHaveBeenCalledWith(true);
222+
});
223+
224+
});

0 commit comments

Comments
 (0)