-
Notifications
You must be signed in to change notification settings - Fork 139
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic test cases for the new
ThreadProvider
(#1274)
### Changelog This PR will add the test cases for the new `ThreadProvider` and its related hooks. #### Before <img width="1163" alt="스크린샷 2024-12-09 오전 11 29 53" src="https://github.com/user-attachments/assets/40ffc29e-0774-4ac7-973d-15af55bc88fd"> #### After <img width="1199" alt="스크린샷 2024-12-09 오전 11 28 39" src="https://github.com/user-attachments/assets/ea3366c0-2f11-4e1f-af6f-a423d006ab42"> --------- Co-authored-by: Irene Ryu <[email protected]>
- Loading branch information
1 parent
5b35b02
commit b9e7602
Showing
22 changed files
with
3,817 additions
and
204 deletions.
There are no files selected for viewing
392 changes: 392 additions & 0 deletions
392
src/modules/Thread/components/ThreadUI/__test__/ThreadUI.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,392 @@ | ||
import * as useThreadModule from '../../../context/useThread'; | ||
import { ChannelStateTypes, ParentMessageStateTypes, ThreadListStateTypes } from '../../../types'; | ||
import { EmojiContainer } from '@sendbird/chat'; | ||
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'; | ||
import { LocalizationContext } from '../../../../../lib/LocalizationContext'; | ||
import ThreadUI from '../index'; | ||
import React from 'react'; | ||
import '@testing-library/jest-dom/extend-expect'; | ||
|
||
const mockSendUserMessage = jest.fn(); | ||
|
||
const mockChannel = { | ||
url: 'test-channel', | ||
members: [{ userId: 'test-user-id', nickname: 'user1' }], | ||
updateUserMessage: jest.fn().mockImplementation(async (message) => mockNewMessage(message)), | ||
sendUserMessage: mockSendUserMessage, | ||
isGroupChannel: jest.fn().mockImplementation(() => true), | ||
}; | ||
|
||
const mockNewMessage = (message) => ({ | ||
messageId: 42, | ||
message: message ?? 'new message', | ||
}); | ||
|
||
const mockMessage = { | ||
messageId: 1, | ||
message: 'first message', | ||
}; | ||
|
||
const mockGetMessage = jest.fn().mockResolvedValue(mockMessage); | ||
const mockGetChannel = jest.fn().mockResolvedValue(mockChannel); | ||
|
||
const mockState = { | ||
stores: { | ||
sdkStore: { | ||
sdk: { | ||
getMessage: mockGetMessage, | ||
groupChannel: { | ||
getChannel: mockGetChannel, | ||
}, | ||
}, | ||
initialized: true, | ||
}, | ||
userStore: { user: { userId: 'test-user-id' } }, | ||
}, | ||
config: { | ||
logger: console, | ||
isOnline: true, | ||
pubSub: { | ||
publish: jest.fn(), | ||
}, | ||
groupChannel: { | ||
enableMention: true, | ||
enableReactions: true, | ||
replyType: 'THREAD', | ||
}, | ||
}, | ||
}; | ||
|
||
jest.mock('../../../../../lib/Sendbird/context/hooks/useSendbird', () => ({ | ||
__esModule: true, | ||
default: jest.fn(() => ({ state: mockState })), | ||
useSendbird: jest.fn(() => ({ state: mockState })), | ||
})); | ||
|
||
jest.mock('../../../context/useThread'); | ||
|
||
const mockStringSet = { | ||
DATE_FORMAT__MESSAGE_CREATED_AT: 'p', | ||
}; | ||
|
||
const mockLocalizationContext = { | ||
stringSet: mockStringSet, | ||
}; | ||
|
||
const defaultMockState = { | ||
channelUrl: '', | ||
message: null, | ||
onHeaderActionClick: undefined, | ||
onMoveToParentMessage: undefined, | ||
onBeforeSendUserMessage: undefined, | ||
onBeforeSendFileMessage: undefined, | ||
onBeforeSendVoiceMessage: undefined, | ||
onBeforeSendMultipleFilesMessage: undefined, | ||
onBeforeDownloadFileMessage: undefined, | ||
isMultipleFilesMessageEnabled: undefined, | ||
filterEmojiCategoryIds: undefined, | ||
currentChannel: undefined, | ||
allThreadMessages: [ | ||
{ | ||
messageId: 2, | ||
message: 'threaded message 1', | ||
isUserMessage: () => true, | ||
}, | ||
], | ||
localThreadMessages: [], | ||
parentMessage: null, | ||
channelState: ChannelStateTypes.INITIALIZED, | ||
parentMessageState: ParentMessageStateTypes.INITIALIZED, | ||
threadListState: ThreadListStateTypes.INITIALIZED, | ||
hasMorePrev: false, | ||
hasMoreNext: false, | ||
emojiContainer: {} as EmojiContainer, | ||
isMuted: false, | ||
isChannelFrozen: false, | ||
currentUserId: '', | ||
typingMembers: [], | ||
nicknamesMap: null, | ||
}; | ||
|
||
const defaultMockActions = { | ||
fetchPrevThreads: jest.fn((callback) => { | ||
callback(); | ||
}), | ||
fetchNextThreads: jest.fn((callback) => { | ||
callback(); | ||
}), | ||
}; | ||
|
||
describe('CreateChannelUI Integration Tests', () => { | ||
const mockUseThread = useThreadModule.default as jest.Mock; | ||
|
||
const renderComponent = (mockState = {}, mockActions = {}) => { | ||
mockUseThread.mockReturnValue({ | ||
state: { ...defaultMockState, ...mockState }, | ||
actions: { ...defaultMockActions, ...mockActions }, | ||
}); | ||
|
||
return render( | ||
<LocalizationContext.Provider value={mockLocalizationContext as any}> | ||
<ThreadUI/> | ||
</LocalizationContext.Provider>, | ||
); | ||
}; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('display initial state correctly', async () => { | ||
await act(async () => { | ||
renderComponent( | ||
{ | ||
parentMessage: { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
}, | ||
}, | ||
); | ||
}); | ||
|
||
expect(screen.getByText('parent message')).toBeInTheDocument(); | ||
expect(screen.getByText('threaded message 1')).toBeInTheDocument(); | ||
}); | ||
|
||
it('fetchPrevThread is correctly called when scroll is top', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
getThreadedMessagesByTimestamp: () => ({ | ||
parentMessage, | ||
threadedMessages: [ | ||
{ messageId: 3, message: 'threaded message -1', isUserMessage: () => true }, | ||
{ messageId: 4, message: 'threaded message 0', isUserMessage: () => true }, | ||
], | ||
}), | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
hasMorePrev: true, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
const scrollContainer = container.getElementsByClassName('sendbird-thread-ui--scroll')[0]; | ||
fireEvent.scroll(scrollContainer, { target: { scrollY: -1 } }); | ||
|
||
await waitFor(() => { | ||
expect(defaultMockActions.fetchPrevThreads).toBeCalledTimes(1); | ||
}); | ||
}); | ||
|
||
it('fetchNextThreads is correctly called when scroll is bottom', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
getThreadedMessagesByTimestamp: () => ({ | ||
parentMessage, | ||
threadedMessages: [ | ||
{ messageId: 3, message: 'threaded message -1', isUserMessage: () => true }, | ||
{ messageId: 4, message: 'threaded message 0', isUserMessage: () => true }, | ||
], | ||
}), | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
hasMoreNext: true, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
const scrollContainer = container.getElementsByClassName('sendbird-thread-ui--scroll')[0]; | ||
fireEvent.scroll(scrollContainer, { target: { scrollY: scrollContainer.scrollHeight + 1 } }); | ||
|
||
await waitFor(() => { | ||
expect(defaultMockActions.fetchNextThreads).toBeCalledTimes(1); | ||
}); | ||
}); | ||
|
||
it('show proper placeholder when ParentMessageStateTypes is NIL', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
parentMessageState: ParentMessageStateTypes.NIL, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
await waitFor(() => { | ||
const placeholder = container.getElementsByClassName('placeholder-nil')[0]; | ||
expect(placeholder).not.toBe(undefined); | ||
}); | ||
|
||
}); | ||
|
||
it('show proper placeholder when ParentMessageStateTypes is LOADING', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
parentMessageState: ParentMessageStateTypes.LOADING, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
await waitFor(() => { | ||
const placeholder = container.getElementsByClassName('placeholder-loading')[0]; | ||
expect(placeholder).not.toBe(undefined); | ||
}); | ||
|
||
}); | ||
|
||
it('show proper placeholder when ParentMessageStateTypes is INVALID', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
parentMessageState: ParentMessageStateTypes.INVALID, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
await waitFor(() => { | ||
const placeholder = container.getElementsByClassName('placeholder-invalid')[0]; | ||
expect(placeholder).not.toBe(undefined); | ||
}); | ||
}); | ||
|
||
it('show proper placeholder when ThreadListState is LOADING', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
threadListState: ThreadListStateTypes.LOADING, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
await waitFor(() => { | ||
const placeholder = container.getElementsByClassName('placeholder-loading')[0]; | ||
expect(placeholder).not.toBe(undefined); | ||
}); | ||
}); | ||
|
||
it('show proper placeholder when ThreadListState is INVALID', async () => { | ||
let container; | ||
const parentMessage = { | ||
messageId: 1, | ||
message: 'parent message', | ||
isUserMessage: () => true, | ||
isTextMessage: true, | ||
createdAt: 0, | ||
sender: { | ||
userId: 'test-user-id', | ||
}, | ||
}; | ||
|
||
await act(async () => { | ||
const result = renderComponent( | ||
{ | ||
parentMessage, | ||
threadListState: ThreadListStateTypes.INVALID, | ||
}, | ||
); | ||
|
||
container = result.container; | ||
}); | ||
|
||
await waitFor(() => { | ||
const placeholder = container.getElementsByClassName('placeholder-invalid')[0]; | ||
expect(placeholder).not.toBe(undefined); | ||
}); | ||
}); | ||
|
||
}); |
Oops, something went wrong.