Skip to content

Commit

Permalink
Chat history test cases added
Browse files Browse the repository at this point in the history
  • Loading branch information
Roopan-Microsoft committed Nov 21, 2024
1 parent 6aa4457 commit afc8e07
Show file tree
Hide file tree
Showing 9 changed files with 1,335 additions and 341 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react'
import { renderWithContext, screen, waitFor, fireEvent, act } from '../../test/test.utils';
import { ChatHistoryList } from './ChatHistoryList'
import {groupByMonth} from '../../helpers/helpers';

// Mock the groupByMonth function
jest.mock('../../helpers/helpers', () => ({
groupByMonth: jest.fn(),
}));

// Mock ChatHistoryListItemGroups component
jest.mock('./ChatHistoryListItem', () => ({
ChatHistoryListItemGroups: jest.fn(() => <div>Mocked ChatHistoryListItemGroups</div>),
}));

describe('ChatHistoryList', () => {

beforeEach(() => {
global.fetch = jest.fn();
});

afterEach(() => {
jest.clearAllMocks();
});

it('should display "No chat history." when chatHistory is empty', () => {
renderWithContext(<ChatHistoryList />);

expect(screen.getByText('No chat history.')).toBeInTheDocument();
});

it('should call groupByMonth with chatHistory when chatHistory is present', () => {
const mockstate = {
chatHistory : [{
id: '1',
title: 'Sample chat message',
messages:[],
date:new Date().toISOString(),
updatedAt: new Date().toISOString(),
}]
};
(groupByMonth as jest.Mock).mockReturnValue([]);
renderWithContext(<ChatHistoryList /> , mockstate);

expect(groupByMonth).toHaveBeenCalledWith(mockstate.chatHistory);
});

it('should render ChatHistoryListItemGroups with grouped chat history when chatHistory is present', () => {
const mockstate = {
chatHistory : [{
id: '1',
title: 'Sample chat message',
messages:[],
date:new Date().toISOString(),
updatedAt: new Date().toISOString(),
}]
};
(groupByMonth as jest.Mock).mockReturnValue([]);
renderWithContext(<ChatHistoryList /> , mockstate);

expect(screen.getByText('Mocked ChatHistoryListItemGroups')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,69 +1,21 @@
import React, { useContext } from 'react'
import React, { useContext,useEffect } from 'react'
import { Stack, StackItem, Text } from '@fluentui/react'

import { Conversation } from '../../api/models'
import { Conversation , GroupedChatHistory } from '../../api/models'
import {groupByMonth} from '../../helpers/helpers';
import { AppStateContext } from '../../state/AppProvider'

import { ChatHistoryListItemGroups } from './ChatHistoryListItem'

interface ChatHistoryListProps {}

export interface GroupedChatHistory {
month: string
entries: Conversation[]
}

const groupByMonth = (entries: Conversation[]) => {
const groups: GroupedChatHistory[] = [{ month: 'Recent', entries: [] }]
const currentDate = new Date()

entries.forEach(entry => {
const date = new Date(entry.date)
const daysDifference = (currentDate.getTime() - date.getTime()) / (1000 * 60 * 60 * 24)
const monthYear = date.toLocaleString('default', { month: 'long', year: 'numeric' })
const existingGroup = groups.find(group => group.month === monthYear)

if (daysDifference <= 7) {
groups[0].entries.push(entry)
} else {
if (existingGroup) {
existingGroup.entries.push(entry)
} else {
groups.push({ month: monthYear, entries: [entry] })
}
}
})

groups.sort((a, b) => {
// Check if either group has no entries and handle it
if (a.entries.length === 0 && b.entries.length === 0) {
return 0 // No change in order
} else if (a.entries.length === 0) {
return 1 // Move 'a' to a higher index (bottom)
} else if (b.entries.length === 0) {
return -1 // Move 'b' to a higher index (bottom)
}
const dateA = new Date(a.entries[0].date)
const dateB = new Date(b.entries[0].date)
return dateB.getTime() - dateA.getTime()
})

groups.forEach(group => {
group.entries.sort((a, b) => {
const dateA = new Date(a.date)
const dateB = new Date(b.date)
return dateB.getTime() - dateA.getTime()
})
})

return groups
}

const ChatHistoryList: React.FC<ChatHistoryListProps> = () => {
export const ChatHistoryList: React.FC<ChatHistoryListProps> = () => {
const appStateContext = useContext(AppStateContext)
const chatHistory = appStateContext?.state.chatHistory

React.useEffect(() => {}, [appStateContext?.state.chatHistory])
useEffect(() => {}, [appStateContext?.state.chatHistory])

let groupedChatHistory
if (chatHistory && chatHistory.length > 0) {
Expand All @@ -83,4 +35,4 @@ const ChatHistoryList: React.FC<ChatHistoryListProps> = () => {
return <ChatHistoryListItemGroups groupedChatHistory={groupedChatHistory} />
}

export default ChatHistoryList

Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { renderWithContext, screen, waitFor, fireEvent, act } from '../../test/test.utils';
import { ChatHistoryListItemGroups } from './ChatHistoryListItem';
import { historyList } from '../../api';

jest.mock('../../api', () => ({
historyList: jest.fn(),
}));

const mockDispatch = jest.fn();
const handleFetchHistory = jest.fn();

// Mock the ChatHistoryListItemCell component
jest.mock('./ChatHistoryListItemCell', () => ({
ChatHistoryListItemCell: jest.fn(({ item, onSelect }) => (
<div data-testid={`mock-cell-${item.id}`} onClick={() => onSelect(item)}>
{item?.title}
</div>
)),
}));

const mockGroupedChatHistory = [
{
month: '2023-09',
entries: [
{ id: '1', title: 'Chat 1', messages: [], date: new Date().toISOString(), updatedAt: new Date().toISOString() },
{ id: '2', title: 'Chat 2', messages: [], date: new Date().toISOString(), updatedAt: new Date().toISOString() },
],
},
{
month: '2023-08',
entries: [
{ id: '3', title: 'Chat 3', messages: [], date: new Date().toISOString(), updatedAt: new Date().toISOString() },
],
},
];

describe('ChatHistoryListItemGroups Component', () => {
beforeEach(() => {
global.fetch = jest.fn();

jest.spyOn(console, 'error').mockImplementation(() => { });
});

afterEach(() => {
jest.clearAllMocks();
//(console.error as jest.Mock).mockRestore();
});

it('should call handleFetchHistory with the correct offset when the observer is triggered', async () => {
const responseMock = [{ id: '4', title: 'Chat 4', messages: [], date: new Date().toISOString(), updatedAt: new Date().toISOString() }];
(historyList as jest.Mock).mockResolvedValue([...responseMock]);
await act(async () => {
renderWithContext(<ChatHistoryListItemGroups groupedChatHistory={mockGroupedChatHistory} />);
});

const scrollElms = await screen.findAllByRole('scrollDiv');
const lastElem = scrollElms[scrollElms.length - 1];

await act(async () => {
fireEvent.scroll(lastElem, { target: { scrollY: 100 } });
//await waitFor(() => expect(historyList).toHaveBeenCalled());
});

await act(async () => {
await waitFor(() => {
expect(historyList).toHaveBeenCalled();
});
});
});

it('displays spinner while loading more history', async () => {
const responseMock = [{ id: '4', title: 'Chat 4', messages: [], date: new Date().toISOString(), updatedAt: new Date().toISOString() }];
(historyList as jest.Mock).mockResolvedValue([...responseMock]);
await act(async () => {
renderWithContext(<ChatHistoryListItemGroups groupedChatHistory={mockGroupedChatHistory} />);
});

const scrollElms = await screen.findAllByRole('scrollDiv');
const lastElem = scrollElms[scrollElms.length - 1];

await act(async () => {
fireEvent.scroll(lastElem, { target: { scrollY: 100 } });
});

await act(async () => {
await waitFor(() => {
expect(screen.queryByLabelText(/loading/i)).not.toBeInTheDocument();
});
});
});

it('should render the grouped chat history', () => {
renderWithContext(<ChatHistoryListItemGroups groupedChatHistory={mockGroupedChatHistory} />);

// Check if each group is rendered
expect(screen.getByText('2023-09')).toBeInTheDocument();
expect(screen.getByText('2023-08')).toBeInTheDocument();

// Check if entries are rendered
expect(screen.getByText('Chat 1')).toBeInTheDocument();
expect(screen.getByText('Chat 2')).toBeInTheDocument();
expect(screen.getByText('Chat 3')).toBeInTheDocument();
});

it('calls onSelect with the correct item when a ChatHistoryListItemCell is clicked', async () => {
const handleSelectMock = jest.fn();

// Render the component
renderWithContext(<ChatHistoryListItemGroups groupedChatHistory={mockGroupedChatHistory} />);

// Simulate clicks on each ChatHistoryListItemCell
const cells = screen.getAllByTestId(/mock-cell-/);

// Click on the first cell
fireEvent.click(cells[0]);

// Wait for the mock function to be called with the correct item
// await waitFor(() => {
// expect(handleSelectMock).toHaveBeenCalledWith(mockGroupedChatHistory[0].entries[0]);
// });

});

it('handles API failure gracefully', async () => {
// Mock the API to reject with an error
(historyList as jest.Mock).mockResolvedValue(undefined);

renderWithContext(<ChatHistoryListItemGroups groupedChatHistory={mockGroupedChatHistory} />);

// Simulate triggering the scroll event that loads more history
const scrollElms = await screen.findAllByRole('scrollDiv');
const lastElem = scrollElms[scrollElms.length - 1];

await act(async () => {
fireEvent.scroll(lastElem, { target: { scrollY: 100 } });
});
// Check that the spinner is hidden after the API call
await waitFor(() => {
expect(screen.queryByLabelText(/loading/i)).not.toBeInTheDocument();
});
});

});
Loading

0 comments on commit afc8e07

Please sign in to comment.