Skip to content

Commit

Permalink
Add unit test for the new provider and hook
Browse files Browse the repository at this point in the history
  • Loading branch information
git-babel committed Oct 17, 2024
1 parent c123ebc commit 0ded427
Show file tree
Hide file tree
Showing 5 changed files with 417 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/lib/UserProfileContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useContext } from 'react';
import type { GroupChannel } from '@sendbird/chat/groupChannel';
import type { RenderUserProfileProps } from '../types';
import { useSendbirdStateContext } from './Sendbird';
import useSendbirdStateContext from '../hooks/useSendbirdStateContext';

interface UserProfileContextInterface {
isOpenChannel: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { fireEvent, render, screen } from '@testing-library/react';
import GroupChannelListUI from '../index';
import '@testing-library/jest-dom/extend-expect';
import React from 'react';
import { useGroupChannelList as useGroupChannelListModule } from '../../../context/useGroupChannelList';
import { LocalizationContext } from '../../../../../lib/LocalizationContext';

jest.mock('../../../../../hooks/useSendbirdStateContext', () => ({
__esModule: true,
default: jest.fn(() => ({
stores: {
userStore: {
user: {
userId: ' test-user-id',
},
},
sdkStore: {
sdk: {
currentUser: {
userId: 'test-user-id',
},
},
initialized: true,
},
},
config: {
logger: console,
userId: 'test-user-id',
groupChannel: {
enableMention: true,
},
isOnline: true,
},
})),
}));
jest.mock('../../../context/useGroupChannelList');
jest.mock('@sendbird/uikit-tools', () => ({
useGroupChannelList: jest.fn(() => ({
refreshing: false,
initialized: false,
groupChannels: [],
refresh: null,
loadMore: null,
})),
useGroupChannelHandler: jest.fn(() => {}),
usePreservedCallback: jest.requireActual('@sendbird/uikit-tools').usePreservedCallback,
}));

const mockStringSet = {
PLACE_HOLDER__NO_CHANNEL: 'No channels',
TYPING_INDICATOR__IS_TYPING: 'is typing...',
TYPING_INDICATOR__AND: 'and',
TYPING_INDICATOR__ARE_TYPING: 'are typing...',
TYPING_INDICATOR__MULTIPLE_TYPING: 'Several people are typing...',
};

const defaultMockState = {
className: '',
selectedChannelUrl: '',
disableAutoSelect: false,
allowProfileEdit: false,
isTypingIndicatorEnabled: false,
isMessageReceiptStatusEnabled: false,
onChannelSelect: undefined,
onChannelCreated: undefined,
onThemeChange: undefined,
onCreateChannelClick: undefined,
onBeforeCreateChannel: undefined,
onUserProfileUpdated: undefined,
typingChannelUrls: [],
refreshing: false,
initialized: false,
groupChannels: [],
refresh: null,
loadMore: null,
};

describe('GroupChannelListUI Integration Tests', () => {

const renderComponent = (mockState = {}) => {
const mockUseGroupChannelList = useGroupChannelListModule as jest.Mock;

mockUseGroupChannelList.mockReturnValue({
state: { ...defaultMockState, ...mockState },
});

return render(
<LocalizationContext.Provider value={{ stringSet: mockStringSet } as any}>
<GroupChannelListUI />,
</LocalizationContext.Provider>,
);
};

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

it('display loader if not initialized', () => {
const { container } = renderComponent();

expect(container.getElementsByClassName('sendbird-loader')[0]).toBeInTheDocument();
});

it('display results when available', () => {
renderComponent({
groupChannels: [
{ name: 'test-group-channel-1' },
{ name: 'test-group-channel-2' },
],
initialized: true,
});

expect(screen.getByText('test-group-channel-1')).toBeInTheDocument();
expect(screen.getByText('test-group-channel-2')).toBeInTheDocument();
});

it('handle no result', () => {
renderComponent({
groupChannels: [],
initialized: true,
});

expect(screen.getByText(mockStringSet.PLACE_HOLDER__NO_CHANNEL)).toBeInTheDocument();
});

it('handle selectedChannelUrl', () => {
const { container } = renderComponent({
groupChannels: [
{ name: 'test-group-channel-1', url: 'test-group-channel-url-1' },
{ name: 'test-group-channel-2', url: 'test-group-channel-url-2' },
],
selectedChannelUrl: 'test-group-channel-url-2',
initialized: true,
});

const selected = container.getElementsByClassName('sendbird-channel-preview--active')[0];

expect(selected).toBeInTheDocument();
expect(selected.getAttribute('tabindex')).toEqual('1');
});

it('handle disableAutoSelect', () => {
const { container } = renderComponent({
groupChannels: [
{ name: 'test-group-channel-1', url: 'test-group-channel-url-1' },
{ name: 'test-group-channel-2', url: 'test-group-channel-url-2' },
],
disableAutoSelect: true,
initialized: true,
});

const selected = container.getElementsByClassName('sendbird-channel-preview--active')[0];

expect(selected).toBeUndefined();
});

it('handle allowProfileEdit', () => {
const { container } = renderComponent({
allowProfileEdit: true,
initialized: true,
});

expect(container.getElementsByClassName('sendbird-channel-header--allow-edit')[0]).toBeInTheDocument();
});

it('handle isTypingIndicatorEnabled', () => {
renderComponent({
groupChannels: [
{ name: 'test-group-channel-1', url: 'test-group-channel-url-1', getTypingUsers: () => ['test-user-1', 'test-user-2'] },
{ name: 'test-group-channel-2', url: 'test-group-channel-url-2', getTypingUsers: () => ['test-user-2'] },
],
typingChannelUrls: ['test-group-channel-url-1', 'test-group-channel-url-2'],
selectedChannelUrl: 'test-group-channel-url-1',
isTypingIndicatorEnabled: true,
initialized: true,
});

expect(screen.getByText(mockStringSet.TYPING_INDICATOR__IS_TYPING, { exact: false })).toBeInTheDocument();
expect(screen.getByText(mockStringSet.TYPING_INDICATOR__ARE_TYPING, { exact: false })).toBeInTheDocument();
});

it('handle onChannelSelect', () => {
const onChannelSelect = jest.fn();

renderComponent({
groupChannels: [
{ name: 'test-group-channel-1', url: 'test-group-channel-url-1' },
{ name: 'test-group-channel-2', url: 'test-group-channel-url-2' },
],
initialized: true,
onChannelSelect,
});

const channel = screen.getByText('test-group-channel-1');
fireEvent.click(channel);

expect(onChannelSelect).toHaveBeenCalledTimes(1);
});

});
68 changes: 35 additions & 33 deletions src/modules/GroupChannelList/context/GroupChannelListProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useEffect, useRef, useState } from 'react';
import React, { useContext, useEffect, useRef } from 'react';

import type { User } from '@sendbird/chat';
import type { GroupChannel, GroupChannelCreateParams, GroupChannelFilterParams } from '@sendbird/chat/groupChannel';
Expand All @@ -11,7 +11,6 @@ import { UserProfileProvider } from '../../../lib/UserProfileContext';
import type { UserProfileProviderProps } from '../../../lib/UserProfileContext';
import { useMarkAsDeliveredScheduler } from '../../../lib/hooks/useMarkAsDeliveredScheduler';
import useOnlineStatus from '../../../lib/hooks/useOnlineStatus';
import { noop } from '../../../utils/utils';
import type { SdkStore } from '../../../lib/types';
import { PartialRequired } from '../../../utils/typeHelpers/partialRequired';
import { createStore } from '../../../utils/storeManager';
Expand Down Expand Up @@ -66,12 +65,12 @@ const initialState: GroupChannelListState = {
allowProfileEdit: false,
isTypingIndicatorEnabled: false,
isMessageReceiptStatusEnabled: false,
onChannelSelect: noop,
onChannelCreated: noop,
onThemeChange: noop,
onCreateChannelClick: noop,
onBeforeCreateChannel: null,
onUserProfileUpdated: noop,
onChannelSelect: undefined,
onChannelCreated: undefined,
onThemeChange: undefined,
onCreateChannelClick: undefined,
onBeforeCreateChannel: undefined,
onUserProfileUpdated: undefined,
typingChannelUrls: [],
refreshing: false,
initialized: false,
Expand All @@ -87,27 +86,26 @@ const useGroupChannelListStore = () => {
return useStore(GroupChannelListContext, state => state, initialState);
};

export const GroupChannelListManager: React.FC<GroupChannelListProviderProps> = (props: GroupChannelListProviderProps) => {
const {
className = '',
selectedChannelUrl,
export const GroupChannelListManager: React.FC<GroupChannelListProviderProps> = ({
className = '',
selectedChannelUrl = '',

disableAutoSelect = false,
allowProfileEdit,
isTypingIndicatorEnabled,
isMessageReceiptStatusEnabled,
disableAutoSelect = false,
allowProfileEdit = false,
isTypingIndicatorEnabled = false,
isMessageReceiptStatusEnabled = false,

channelListQueryParams,
onThemeChange,
onChannelSelect = noop,
onChannelCreated = noop,
onCreateChannelClick,
onBeforeCreateChannel,
onUserProfileUpdated,
} = props;
channelListQueryParams,
onThemeChange,
onChannelSelect,
onChannelCreated,
onCreateChannelClick,
onBeforeCreateChannel,
onUserProfileUpdated,
}: GroupChannelListProviderProps) => {

const { config, stores } = useSendbirdStateContext();
const { updateState } = useGroupChannelListStore();
const { state, updateState } = useGroupChannelListStore();
const { sdkStore } = stores;

const sdk = sdkStore.sdk;
Expand Down Expand Up @@ -135,21 +133,25 @@ export const GroupChannelListManager: React.FC<GroupChannelListProviderProps> =

// Recreates the GroupChannelCollection when `channelListQueryParams` change
useEffect(() => {
refresh();
refresh?.();
}, [
Object.keys(channelListQueryParams ?? {}).sort()
.map((key: string) => `${key}=${encodeURIComponent(JSON.stringify(channelListQueryParams[key]))}`)
.join('&'),
]);

const [typingChannelUrls, setTypingChannelUrls] = useState<string[]>([]);
const { typingChannelUrls } = state;
useGroupChannelHandler(sdk, {
onTypingStatusUpdated: (channel) => {
const channelList = typingChannelUrls.filter((channelUrl) => channelUrl !== channel.url);
if (channel.getTypingUsers()?.length > 0) {
setTypingChannelUrls(channelList.concat(channel.url));
updateState({
typingChannelUrls: (channelList.concat(channel.url)),
});
} else {
setTypingChannelUrls(channelList);
updateState({
typingChannelUrls: (channelList),
});
}
},
});
Expand Down Expand Up @@ -191,7 +193,7 @@ export const GroupChannelListManager: React.FC<GroupChannelListProviderProps> =
typingChannelUrls,
refreshing,
initialized,
groupChannels,
// groupChannels,
refresh,
loadMore,
]);
Expand All @@ -200,7 +202,7 @@ export const GroupChannelListManager: React.FC<GroupChannelListProviderProps> =
};

const createGroupChannelListStore = () => createStore(initialState);
const InternalGroupChannelListStoreProvider = ({ children }) => {
const InternalGroupChannelListProvider = ({ children }) => {
const storeRef = useRef(createGroupChannelListStore());
return (
<GroupChannelListContext.Provider value={storeRef.current}>
Expand All @@ -213,12 +215,12 @@ export const GroupChannelListProvider = (props: GroupChannelListProviderProps) =
const { children, className } = props;

return (
<InternalGroupChannelListStoreProvider>
<InternalGroupChannelListProvider>
<GroupChannelListManager {...props} />
<UserProfileProvider {...props}>
<div className={`sendbird-channel-list ${className}`}>{children}</div>
</UserProfileProvider>
</InternalGroupChannelListStoreProvider>
</InternalGroupChannelListProvider>
);
};

Expand Down
Loading

0 comments on commit 0ded427

Please sign in to comment.