@@ -2,122 +2,223 @@ import React from 'react';
22import { render , fireEvent , screen , waitFor } from '@testing-library/react' ;
33import { AppStateContext } from '../../state/AppProvider' ;
44import { ChatHistoryPanel } from './ChatHistoryPanel' ;
5- import { ChatHistoryLoadingState , ChatMessage , Conversation , CosmosDBStatus , Feedback } from '../../api' ;
5+ import { ChatHistoryLoadingState , ChatMessage , Conversation , CosmosDBStatus , Feedback , historyDeleteAll , historyList } from '../../api' ;
66import * as api from '../../api' ;
7-
7+
88// Mock the API function
99jest . 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
1920const 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
2931const 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
3639const 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+
5457const mockDispatch = jest . fn ( ) ;
5558const 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+
6466describe ( '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 ( / C l e a r a l l c h a t h i s t o r y / i) ) ;
9597 expect ( screen . getByText ( / A r e y o u s u r e y o u w a n t t o c l e a r a l l c h a t h i s t o r y / 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 ( / C l e a r a l l c h a t h i s t o r y / i) ) ;
103-
104104 fireEvent . click ( screen . getByRole ( 'button' , { name : / C l e a r A l l / 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 ( / C l e a r a l l c h a t h i s t o r y / i) ) ;
119+ fireEvent . click ( screen . getByRole ( 'button' , { name : / C l e a r A l l / i } ) ) ;
120+
121+ await waitFor ( ( ) => {
122+ expect ( screen . getByText ( / E r r o r d e l e t i n g a l l o f c h a t h i s t o r y / 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 ( / C l e a r a l l c h a t h i s t o r y / i) ) ;
134+
135+ // Confirm the action to clear history
136+ fireEvent . click ( screen . getByRole ( 'button' , { name : / C l e a r A l l / 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 ( / C l e a r a l l c h a t h i s t o r y / 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 ( / C a n c e l / 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 : / h i d e b u t t o n / 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 : / c l e a r a l l c h a t h i s t o r y / 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