-
Notifications
You must be signed in to change notification settings - Fork 138
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[CLNP-5043] Add unit tests for improved MessageSearch module (#1228)
https://sendbird.atlassian.net/browse/CLNP-5043 Two things are handled based on what I mentioned in https://sendbird.atlassian.net/wiki/spaces/UIKitreact/pages/2511765635/UIKit+React+new+State+Management+Method+Proposal#4.1-Unit-Test - [x] Added unit tests for `useMessageSearch` hook and new `MessageSearchProvider` - [x] Added integration tests for `MessageSearchUI` component So the MessageSearch module test coverage has been changed **from** File --------------------------------------------------| % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s <img width="814" alt="Screenshot 2024-10-08 at 2 36 55 PM" src="https://github.com/user-attachments/assets/c0fef6fe-0fc1-4f37-b74f-0486d70352a7"> **to** <img width="941" alt="after" src="https://github.com/user-attachments/assets/7fc19fb8-810c-4256-8230-3884d11e109a"> note that it used to be like 0%, but now the test coverage of the newly added files is almost 100%; green 🟩. ### Checklist Put an `x` in the boxes that apply. You can also fill these out after creating the PR. If unsure, ask the members. This is a reminder of what we look for before merging your code. - [x] **All tests pass locally with my changes** - [x] **I have added tests that prove my fix is effective or that my feature works** - [ ] **Public components / utils / props are appropriately exported** - [ ] I have added necessary documentation (if appropriate)
- Loading branch information
1 parent
77f5395
commit 4689d79
Showing
9 changed files
with
570 additions
and
134 deletions.
There are no files selected for viewing
151 changes: 151 additions & 0 deletions
151
src/modules/MessageSearch/__test__/MessageSearchUI.integration.test.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
import React from 'react'; | ||
import { render, screen, fireEvent } from '@testing-library/react'; | ||
import '@testing-library/jest-dom/extend-expect'; | ||
import MessageSearchUI from '../components/MessageSearchUI'; | ||
import { LocalizationContext } from '../../../lib/LocalizationContext'; | ||
import * as useMessageSearchModule from '../context/hooks/useMessageSearch'; | ||
|
||
jest.mock('../context/hooks/useMessageSearch'); | ||
|
||
const mockStringSet = { | ||
SEARCH_IN: 'Search in', | ||
SEARCH_PLACEHOLDER: 'Search', | ||
SEARCHING: 'Searching...', | ||
NO_SEARCHED_MESSAGE: 'No results found', | ||
NO_TITLE: 'No title', | ||
PLACE_HOLDER__RETRY_TO_CONNECT: 'Retry', | ||
}; | ||
|
||
const mockLocalizationContext = { | ||
stringSet: mockStringSet, | ||
}; | ||
|
||
const defaultMockState = { | ||
isQueryInvalid: false, | ||
searchString: '', | ||
requestString: '', | ||
currentChannel: null, | ||
loading: false, | ||
scrollRef: { current: null }, | ||
hasMoreResult: false, | ||
onScroll: jest.fn(), | ||
allMessages: [], | ||
onResultClick: jest.fn(), | ||
selectedMessageId: null, | ||
}; | ||
|
||
const defaultMockActions = { | ||
setSelectedMessageId: jest.fn(), | ||
handleRetryToConnect: jest.fn(), | ||
}; | ||
|
||
describe('MessageSearchUI Integration Tests', () => { | ||
const mockUseMessageSearch = useMessageSearchModule.default as jest.Mock; | ||
|
||
const renderComponent = (mockState = {}, mockActions = {}) => { | ||
mockUseMessageSearch.mockReturnValue({ | ||
state: { ...defaultMockState, ...mockState }, | ||
actions: { ...defaultMockActions, ...mockActions }, | ||
}); | ||
|
||
return render( | ||
<LocalizationContext.Provider value={mockLocalizationContext as any}> | ||
<MessageSearchUI /> | ||
</LocalizationContext.Provider>, | ||
); | ||
}; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('renders initial state correctly', () => { | ||
renderComponent(); | ||
expect(screen.getByText('Search in')).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays loading state when search is in progress', () => { | ||
renderComponent({ loading: true, searchString: 'test query', requestString: 'test query' }); | ||
expect(screen.getByText(mockStringSet.SEARCHING)).toBeInTheDocument(); | ||
}); | ||
|
||
it('displays search results when available', () => { | ||
renderComponent({ | ||
allMessages: [ | ||
{ messageId: 1, message: 'Message 1', sender: { nickname: 'Sender 1' } }, | ||
{ messageId: 2, message: 'Message 2', sender: { nickname: 'Sender 2' } }, | ||
], | ||
searchString: 'test query', | ||
}); | ||
expect(screen.getByText('Message 1')).toBeInTheDocument(); | ||
expect(screen.getByText('Message 2')).toBeInTheDocument(); | ||
}); | ||
|
||
it('handles no results state', () => { | ||
renderComponent({ allMessages: [], searchString: 'no results query', requestString: 'no results query' }); | ||
expect(screen.getByText(mockStringSet.NO_SEARCHED_MESSAGE)).toBeInTheDocument(); | ||
}); | ||
|
||
it('handles error state and retry', async () => { | ||
const handleRetryToConnect = jest.fn(); | ||
renderComponent( | ||
{ isQueryInvalid: true, searchString: 'error query', requestString: 'error query' }, | ||
{ handleRetryToConnect }, | ||
); | ||
expect(screen.getByText(mockStringSet.PLACE_HOLDER__RETRY_TO_CONNECT)).toBeInTheDocument(); | ||
|
||
const retryButton = screen.getByText('Retry'); | ||
fireEvent.click(retryButton); | ||
|
||
expect(handleRetryToConnect).toHaveBeenCalled(); | ||
}); | ||
|
||
it('triggers loading more messages when scrolled near bottom', async () => { | ||
const onScroll = jest.fn(); | ||
const loadMoreMessages = jest.fn(); | ||
const { container } = renderComponent({ | ||
allMessages: [{ messageId: 1, message: 'Message 1' }], | ||
hasMoreResult: true, | ||
onScroll, | ||
}); | ||
|
||
const scrollContainer = container.firstChild as Element; | ||
|
||
// define scroll container properties | ||
Object.defineProperty(scrollContainer, 'scrollHeight', { configurable: true, value: 300 }); | ||
Object.defineProperty(scrollContainer, 'clientHeight', { configurable: true, value: 500 }); | ||
Object.defineProperty(scrollContainer, 'scrollTop', { configurable: true, value: 450 }); | ||
|
||
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => { | ||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; | ||
|
||
if (scrollTop + clientHeight >= scrollHeight - 1) { | ||
loadMoreMessages(); | ||
} | ||
}; | ||
|
||
fireEvent.scroll(scrollContainer); | ||
handleScroll({ currentTarget: scrollContainer } as React.UIEvent<HTMLDivElement>); | ||
|
||
expect(loadMoreMessages).toHaveBeenCalled(); | ||
}); | ||
|
||
it('handles message click', () => { | ||
const setSelectedMessageId = jest.fn(); | ||
const onResultClick = jest.fn(); | ||
renderComponent( | ||
{ | ||
allMessages: [{ messageId: 1, message: 'Message 1', sender: { nickname: 'Sender 1' } }], | ||
searchString: 'Message 1', | ||
onResultClick, | ||
}, | ||
{ setSelectedMessageId }, | ||
); | ||
|
||
const message = screen.getByText('Message 1'); | ||
fireEvent.click(message); | ||
|
||
expect(setSelectedMessageId).toHaveBeenCalledWith(1); | ||
expect(onResultClick).toHaveBeenCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.