diff --git a/jest-setup.js b/jest-setup.js index 2379beb83..4e791c2ad 100644 --- a/jest-setup.js +++ b/jest-setup.js @@ -1,4 +1,5 @@ import './__mocks__/intersectionObserverMock'; +import '@testing-library/jest-dom'; const { JSDOM } = require('jsdom'); diff --git a/src/lib/Sendbird/__tests__/SendbirdContext.spec.tsx b/src/lib/Sendbird/__tests__/SendbirdContext.spec.tsx new file mode 100644 index 000000000..58b8ad2c5 --- /dev/null +++ b/src/lib/Sendbird/__tests__/SendbirdContext.spec.tsx @@ -0,0 +1,31 @@ +import React, { useContext } from 'react'; +import { render } from '@testing-library/react'; +import { SendbirdContext, createSendbirdContextStore } from '../context/SendbirdContext'; + +describe('SendbirdContext', () => { + it('should initialize with null by default', () => { + const TestComponent = () => { + const context = useContext(SendbirdContext); + return
{context ? 'Not Null' : 'Null'}
; + }; + + const { getByText } = render(); + expect(getByText('Null')).toBeInTheDocument(); + }); + + it('should provide a valid context to child components', () => { + const mockStore = createSendbirdContextStore(); + const TestComponent = () => { + const context = useContext(SendbirdContext); + return
{context ? 'Not Null' : 'Null'}
; + }; + + const { getByText } = render( + + + , + ); + + expect(getByText('Not Null')).toBeInTheDocument(); + }); +}); diff --git a/src/lib/Sendbird/__tests__/SendbirdProvider.spec.tsx b/src/lib/Sendbird/__tests__/SendbirdProvider.spec.tsx new file mode 100644 index 000000000..fbe631e48 --- /dev/null +++ b/src/lib/Sendbird/__tests__/SendbirdProvider.spec.tsx @@ -0,0 +1,73 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { SendbirdContextProvider } from '../context/SendbirdProvider'; +import useSendbird from '../context/hooks/useSendbird'; + +const mockState = { + stores: { sdkStore: { initialized: false } }, + config: { logger: console, groupChannel: { enableVoiceMessage: false } }, +}; +const mockActions = { connect: jest.fn(), disconnect: jest.fn() }; + +jest.mock('../context/hooks/useSendbird', () => ({ + __esModule: true, + default: jest.fn(() => ({ state: mockState, actions: mockActions })), + useSendbird: jest.fn(() => ({ state: mockState, actions: mockActions })), +})); + +describe('SendbirdProvider', () => { + beforeEach(() => { + // Reset mock functions before each test + jest.clearAllMocks(); + + // Mock MediaRecorder.isTypeSupported + global.MediaRecorder = { + isTypeSupported: jest.fn((type) => { + const supportedMimeTypes = ['audio/webm', 'audio/wav']; + return supportedMimeTypes.includes(type); + }), + }; + + // Mock useSendbird return value + useSendbird.mockReturnValue({ + state: mockState, + actions: mockActions, + }); + }); + + it('should render child components', () => { + const { getByTestId } = render( + +
Child Component
+
, + ); + + expect(getByTestId('child')).toBeInTheDocument(); + }); + + it('should call connect when mounted', () => { + render( + +
Child Component
+
, + ); + + expect(mockActions.connect).toHaveBeenCalledWith( + expect.objectContaining({ + appId: 'mockAppId', + userId: 'mockUserId', + }), + ); + }); + + it('should call disconnect on unmount', () => { + const { unmount } = render( + +
Child Component
+
, + ); + + unmount(); + expect(mockActions.disconnect).toHaveBeenCalled(); + }); +}); diff --git a/src/lib/Sendbird/__tests__/index.spec.tsx b/src/lib/Sendbird/__tests__/index.spec.tsx new file mode 100644 index 000000000..9c76d3bdc --- /dev/null +++ b/src/lib/Sendbird/__tests__/index.spec.tsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; + +import { SendbirdProvider, withSendBird } from '../index'; + +jest.mock('@sendbird/uikit-tools', () => ({ + UIKitConfigProvider: jest.fn(({ children }) =>
{children}
), +})); +jest.mock('../context/SendbirdProvider', () => ({ + SendbirdContextProvider: jest.fn(({ children }) =>
{children}
), +})); +jest.mock('../context/hooks/useSendbird', () => jest.fn(() => ({ + state: { someState: 'testState' }, + actions: { someAction: jest.fn() }, +}))); +jest.mock('../../utils/uikitConfigMapper', () => ({ + uikitConfigMapper: jest.fn(() => ({ + common: {}, + groupChannel: {}, + openChannel: {}, + })), +})); +jest.mock('../../utils/uikitConfigStorage', () => ({})); + +describe('SendbirdProvider/index', () => { + it('renders UIKitConfigProvider with correct localConfigs', () => { + const props = { + replyType: 'threaded', + isMentionEnabled: true, + isReactionEnabled: true, + disableUserProfile: false, + isVoiceMessageEnabled: true, + isTypingIndicatorEnabledOnChannelList: false, + isMessageReceiptStatusEnabledOnChannelList: false, + showSearchIcon: true, + uikitOptions: {}, + }; + + render(); + + expect(screen.getByTestId('UIKitConfigProvider')).toBeInTheDocument(); + expect(screen.getByTestId('SendbirdContextProvider')).toBeInTheDocument(); + }); +}); + +describe('withSendbirdContext', () => { + let consoleWarnSpy: jest.SpyInstance; + + beforeEach(() => { + consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); + }); + + afterEach(() => { + consoleWarnSpy.mockRestore(); + }); + + it('logs a warning if mapStoreToProps is not a function', () => { + const MockComponent = jest.fn(() =>
); + const invalidMapStoreToProps = 'invalidValue'; + + const WrappedComponent = withSendBird(MockComponent, invalidMapStoreToProps); + + render(); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + 'Second parameter to withSendbirdContext must be a pure function', + ); + }); + + it('renders OriginalComponent with merged props', () => { + const MockComponent = jest.fn((props) =>
{props.testProp}
); + const mapStoreToProps = (context: any) => ({ + mappedProp: context.someState, + }); + + const WrappedComponent = withSendBird(MockComponent, mapStoreToProps); + + render(); + + expect(screen.getByTestId('MockComponent')).toHaveTextContent('additionalValue'); + + expect(MockComponent).toHaveBeenCalledWith( + expect.objectContaining({ + mappedProp: 'testState', + testProp: 'additionalValue', + }), + {}, + ); + }); +}); diff --git a/src/lib/Sendbird/__tests__/initialState.spec.ts b/src/lib/Sendbird/__tests__/initialState.spec.ts new file mode 100644 index 000000000..a99e49195 --- /dev/null +++ b/src/lib/Sendbird/__tests__/initialState.spec.ts @@ -0,0 +1,47 @@ +import { initialState } from '../context/initialState'; + +describe('initialState', () => { + it('should match the expected structure', () => { + expect(initialState).toMatchObject({ + config: expect.any(Object), + stores: expect.any(Object), + utils: expect.any(Object), + eventHandlers: expect.any(Object), + }); + }); + + it('should have default values', () => { + expect(initialState.stores.sdkStore).toEqual({ + sdk: {}, + initialized: false, + loading: false, + error: undefined, + }); + expect(initialState.stores.userStore).toEqual({ + user: {}, + initialized: false, + loading: false, + }); + expect(initialState.stores.appInfoStore).toEqual({ + messageTemplatesInfo: undefined, + waitingTemplateKeysMap: {}, + }); + }); + + it('should have correct config values', () => { + expect(initialState.config.theme).toBe('light'); + expect(initialState.config.replyType).toBe('NONE'); + expect(initialState.config.uikitUploadSizeLimit).toBeDefined(); + expect(initialState.config.uikitMultipleFilesMessageLimit).toBeDefined(); + }); + + it('should have all eventHandlers initialized', () => { + expect(initialState.eventHandlers.reaction.onPressUserProfile).toBeInstanceOf(Function); + expect(initialState.eventHandlers.connection.onConnected).toBeInstanceOf(Function); + expect(initialState.eventHandlers.connection.onFailed).toBeInstanceOf(Function); + expect(initialState.eventHandlers.modal.onMounted).toBeInstanceOf(Function); + expect(initialState.eventHandlers.message.onSendMessageFailed).toBeInstanceOf(Function); + expect(initialState.eventHandlers.message.onUpdateMessageFailed).toBeInstanceOf(Function); + expect(initialState.eventHandlers.message.onFileUploadFailed).toBeInstanceOf(Function); + }); +}); diff --git a/src/lib/Sendbird/__tests__/useSendbird.spec.tsx b/src/lib/Sendbird/__tests__/useSendbird.spec.tsx new file mode 100644 index 000000000..ddcb777fd --- /dev/null +++ b/src/lib/Sendbird/__tests__/useSendbird.spec.tsx @@ -0,0 +1,410 @@ +import React from 'react'; +import { renderHook, act } from '@testing-library/react-hooks'; +import useSendbird from '../context/hooks/useSendbird'; +import { SendbirdContext, createSendbirdContextStore } from '../context/SendbirdContext'; + +jest.mock('../utils', () => { + const actualUtils = jest.requireActual('../utils'); + return { + ...actualUtils, + initSDK: jest.fn(() => ({ + connect: jest.fn().mockResolvedValue({ userId: 'mockUserId' }), + updateCurrentUserInfo: jest.fn().mockResolvedValue({}), + })), + setupSDK: jest.fn(), + }; +}); + +describe('useSendbird', () => { + let mockStore; + const mockLogger = { error: jest.fn(), info: jest.fn() }; + + beforeEach(() => { + mockStore = createSendbirdContextStore(); + }); + + const wrapper = ({ children }) => ( + {children} + ); + + describe('General behavior', () => { + it('should throw an error if used outside SendbirdProvider', () => { + const { result } = renderHook(() => useSendbird()); + expect(result.error).toBeDefined(); + expect(result.error.message).toBe('No sendbird state value available. Make sure you are rendering `` at the top of your app.'); + }); + + it('should return state and actions when used within SendbirdProvider', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + expect(result.current.state).toBeDefined(); + expect(result.current.actions).toBeDefined(); + }); + }); + + describe('SDK actions', () => { + it('should update state when initSdk is called', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + result.current.actions.initSdk('mockSdk'); + }); + + expect(mockStore.getState().stores.sdkStore.sdk).toBe('mockSdk'); + expect(mockStore.getState().stores.sdkStore.initialized).toBe(true); + }); + + it('should reset SDK state when resetSdk is called', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + result.current.actions.initSdk('mockSdk'); + }); + + act(() => { + result.current.actions.resetSdk(); + }); + + const sdkStore = mockStore.getState().stores.sdkStore; + expect(sdkStore.sdk).toBeNull(); + expect(sdkStore.initialized).toBe(false); + expect(sdkStore.loading).toBe(false); + }); + + it('should set SDK loading state correctly', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + result.current.actions.setSdkLoading(true); + }); + + expect(mockStore.getState().stores.sdkStore.loading).toBe(true); + + act(() => { + result.current.actions.setSdkLoading(false); + }); + + expect(mockStore.getState().stores.sdkStore.loading).toBe(false); + }); + + it('should handle SDK errors correctly', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + result.current.actions.sdkError(); + }); + + const sdkStore = mockStore.getState().stores.sdkStore; + expect(sdkStore.error).toBe(true); + expect(sdkStore.loading).toBe(false); + expect(sdkStore.initialized).toBe(false); + }); + }); + + describe('User actions', () => { + it('should update user state when initUser is called', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const mockUser = { id: 'mockUserId', name: 'mockUserName' }; + act(() => { + result.current.actions.initUser(mockUser); + }); + + const userStore = mockStore.getState().stores.userStore; + expect(userStore.user).toEqual(mockUser); + expect(userStore.initialized).toBe(true); + }); + + it('should reset user state when resetUser is called', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const mockUser = { id: 'mockUserId', name: 'mockUserName' }; + act(() => { + result.current.actions.initUser(mockUser); + }); + + act(() => { + result.current.actions.resetUser(); + }); + + const userStore = mockStore.getState().stores.userStore; + expect(userStore.user).toBeNull(); + expect(userStore.initialized).toBe(false); + }); + + it('should update user info when updateUserInfo is called', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const initialUser = { id: 'mockUserId', name: 'oldName' }; + const updatedUser = { id: 'mockUserId', name: 'newName' }; + + act(() => { + result.current.actions.initUser(initialUser); + }); + + act(() => { + result.current.actions.updateUserInfo(updatedUser); + }); + + const userStore = mockStore.getState().stores.userStore; + expect(userStore.user).toEqual(updatedUser); + }); + }); + + describe('AppInfo actions', () => { + it('should initialize message templates info with initMessageTemplateInfo', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const mockPayload = { templatesMap: { key1: 'template1', key2: 'template2' } }; + + act(() => { + result.current.actions.initMessageTemplateInfo({ payload: mockPayload }); + }); + + const appInfoStore = mockStore.getState().stores.appInfoStore; + expect(appInfoStore.messageTemplatesInfo).toEqual(mockPayload); + expect(appInfoStore.waitingTemplateKeysMap).toEqual({}); + }); + + it('should upsert message templates with upsertMessageTemplates', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + mockStore.setState((state) => ({ + ...state, + stores: { + ...state.stores, + appInfoStore: { + ...state.stores.appInfoStore, + messageTemplatesInfo: { templatesMap: {} }, + waitingTemplateKeysMap: { key1: {}, key2: {} }, + }, + }, + })); + }); + + act(() => { + result.current.actions.upsertMessageTemplates({ + payload: [ + { key: 'key1', template: 'templateContent1' }, + { key: 'key2', template: 'templateContent2' }, + ], + }); + }); + + const appInfoStore = mockStore.getState().stores.appInfoStore; + expect(appInfoStore.messageTemplatesInfo.templatesMap).toEqual({ + key1: 'templateContent1', + key2: 'templateContent2', + }); + expect(appInfoStore.waitingTemplateKeysMap).toEqual({}); + }); + + it('should upsert waiting template keys with upsertWaitingTemplateKeys', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const mockPayload = { + keys: ['key1', 'key2'], + requestedAt: Date.now(), + }; + + act(() => { + result.current.actions.upsertWaitingTemplateKeys({ payload: mockPayload }); + }); + + const appInfoStore = mockStore.getState().stores.appInfoStore; + expect(appInfoStore.waitingTemplateKeysMap.key1).toEqual({ + erroredMessageIds: [], + requestedAt: mockPayload.requestedAt, + }); + expect(appInfoStore.waitingTemplateKeysMap.key2).toEqual({ + erroredMessageIds: [], + requestedAt: mockPayload.requestedAt, + }); + }); + + it('should mark error waiting template keys with markErrorWaitingTemplateKeys', () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + mockStore.setState((state) => ({ + ...state, + stores: { + ...state.stores, + appInfoStore: { + ...state.stores.appInfoStore, + waitingTemplateKeysMap: { + key1: { erroredMessageIds: [] }, + key2: { erroredMessageIds: ['existingErrorId'] }, + }, + }, + }, + })); + }); + + act(() => { + result.current.actions.markErrorWaitingTemplateKeys({ + payload: { + keys: ['key1', 'key2'], + messageId: 'newErrorId', + }, + }); + }); + + const appInfoStore = mockStore.getState().stores.appInfoStore; + expect(appInfoStore.waitingTemplateKeysMap.key1.erroredMessageIds).toContain('newErrorId'); + expect(appInfoStore.waitingTemplateKeysMap.key2.erroredMessageIds).toContain('newErrorId'); + expect(appInfoStore.waitingTemplateKeysMap.key2.erroredMessageIds).toContain('existingErrorId'); + }); + + }); + + describe('Connection actions', () => { + it('should connect and initialize SDK correctly', async () => { + const mockStore = createSendbirdContextStore(); + const wrapper = ({ children }) => ( + {children} + ); + + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const mockActions = result.current.actions; + + await act(async () => { + await mockActions.connect({ + logger: mockLogger, + userId: 'mockUserId', + appId: 'mockAppId', + accessToken: 'mockAccessToken', + nickname: 'mockNickname', + profileUrl: 'mockProfileUrl', + isMobile: false, + sdkInitParams: {}, + customApiHost: '', + customWebSocketHost: '', + customExtensionParams: {}, + eventHandlers: { + connection: { + onConnected: jest.fn(), + onFailed: jest.fn(), + }, + }, + initializeMessageTemplatesInfo: jest.fn(), + initDashboardConfigs: jest.fn(), + configureSession: jest.fn(), + }); + }); + + const sdkStore = mockStore.getState().stores.sdkStore; + const userStore = mockStore.getState().stores.userStore; + + expect(sdkStore.initialized).toBe(true); + expect(sdkStore.sdk).toBeDefined(); + expect(userStore.user).toEqual({ userId: 'mockUserId' }); + }); + + it('should disconnect and reset SDK correctly', async () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + act(() => { + result.current.actions.initSdk('mockSdk'); + }); + + await act(async () => { + await result.current.actions.disconnect({ logger: mockLogger }); + }); + + const sdkStore = mockStore.getState().stores.sdkStore; + const userStore = mockStore.getState().stores.userStore; + + expect(sdkStore.sdk).toBeNull(); + expect(userStore.user).toBeNull(); + }); + + it('should trigger onConnected event handler after successful connection', async () => { + const mockOnConnected = jest.fn(); + const { result } = renderHook(() => useSendbird(), { wrapper }); + + await act(async () => { + await result.current.actions.connect({ + logger: mockLogger, + userId: 'mockUserId', + appId: 'mockAppId', + accessToken: 'mockAccessToken', + eventHandlers: { + connection: { + onConnected: mockOnConnected, + }, + }, + }); + }); + + expect(mockOnConnected).toHaveBeenCalledWith({ userId: 'mockUserId' }); + }); + + it('should call initSDK and setupSDK with correct parameters during connect', async () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + const mockInitSDK = jest.requireMock('../utils').initSDK; + const mockSetupSDK = jest.requireMock('../utils').setupSDK; + + await act(async () => { + await result.current.actions.connect({ + logger: mockLogger, + userId: 'mockUserId', + appId: 'mockAppId', + accessToken: 'mockAccessToken', + sdkInitParams: {}, + }); + }); + + expect(mockInitSDK).toHaveBeenCalledWith({ + appId: 'mockAppId', + customApiHost: undefined, + customWebSocketHost: undefined, + sdkInitParams: {}, + }); + + expect(mockSetupSDK).toHaveBeenCalled(); + }); + + it('should handle connection failure and trigger onFailed event handler', async () => { + const { result } = renderHook(() => useSendbird(), { wrapper }); + + const mockOnFailed = jest.fn(); + const mockLogger = { error: jest.fn(), info: jest.fn() }; + + const mockSdk = { + connect: jest.fn(() => { + throw new Error('Mock connection error'); + }), + }; + jest.requireMock('../utils').initSDK.mockReturnValue(mockSdk); + + await act(async () => { + await result.current.actions.connect({ + logger: mockLogger, + userId: 'mockUserId', + appId: 'mockAppId', + accessToken: 'mockAccessToken', + eventHandlers: { + connection: { + onFailed: mockOnFailed, + }, + }, + }); + }); + + const sdkStore = mockStore.getState().stores.sdkStore; + const userStore = mockStore.getState().stores.userStore; + + expect(sdkStore.sdk).toBeNull(); + expect(userStore.user).toBeNull(); + + expect(mockLogger.error).toHaveBeenCalledWith( + 'SendbirdProvider | useSendbird/connect failed', + expect.any(Error), + ); + + expect(mockOnFailed).toHaveBeenCalledWith(expect.any(Error)); + }); + }); +}); diff --git a/src/lib/Sendbird/__tests__/utils.spec.ts b/src/lib/Sendbird/__tests__/utils.spec.ts new file mode 100644 index 000000000..1b0ee5ec8 --- /dev/null +++ b/src/lib/Sendbird/__tests__/utils.spec.ts @@ -0,0 +1,154 @@ +import SendbirdChat from '@sendbird/chat'; + +import type { SendbirdState, SdkStore, UserStore, AppInfoStore, SendbirdStateConfig } from '../types'; +import { updateAppInfoStore, updateSdkStore, updateUserStore, initSDK, setupSDK } from '../utils'; + +jest.mock('@sendbird/chat', () => ({ + init: jest.fn(), + GroupChannelModule: jest.fn(), + OpenChannelModule: jest.fn(), + DeviceOsPlatform: { + MOBILE_WEB: 'mobile_web', + WEB: 'web', + }, + SendbirdPlatform: { + JS: 'js', + }, + SendbirdProduct: { + UIKIT_CHAT: 'uikit_chat', + }, +})); + +describe('State Update Functions', () => { + const initialState: SendbirdState = { + config: { + appId: 'testAppId', + } as SendbirdStateConfig, + stores: { + appInfoStore: { + waitingTemplateKeysMap: {}, + messageTemplatesInfo: undefined, + }, + sdkStore: { + error: false, + initialized: false, + loading: false, + sdk: {} as any, + }, + userStore: { + initialized: false, + loading: false, + user: {} as any, + }, + }, + eventHandlers: undefined, + emojiManager: {} as any, + utils: {} as any, + }; + + test('updateAppInfoStore merges payload with existing appInfoStore', () => { + const payload: Partial = { messageTemplatesInfo: { templateKey: 'templateValue' } }; + const updatedState = updateAppInfoStore(initialState, payload); + + expect(updatedState.stores.appInfoStore).toEqual({ + waitingTemplateKeysMap: {}, + messageTemplatesInfo: { templateKey: 'templateValue' }, + }); + }); + + test('updateSdkStore merges payload with existing sdkStore', () => { + const payload: Partial = { initialized: true, error: true }; + const updatedState = updateSdkStore(initialState, payload); + + expect(updatedState.stores.sdkStore).toEqual({ + error: true, + initialized: true, + loading: false, + sdk: {} as any, + }); + }); + + test('updateUserStore merges payload with existing userStore', () => { + const payload: Partial = { initialized: true, loading: true }; + const updatedState = updateUserStore(initialState, payload); + + expect(updatedState.stores.userStore).toEqual({ + initialized: true, + loading: true, + user: {} as any, + }); + }); +}); + +describe('initSDK', () => { + it('initializes SendbirdChat with required parameters', () => { + const params = { appId: 'testAppId' }; + initSDK(params); + + expect(SendbirdChat.init).toHaveBeenCalledWith( + expect.objectContaining({ + appId: 'testAppId', + modules: expect.any(Array), + localCacheEnabled: true, + }), + ); + }); + + it('includes customApiHost and customWebSocketHost if provided', () => { + const params = { + appId: 'testAppId', + customApiHost: 'https://custom.api', + customWebSocketHost: 'wss://custom.websocket', + }; + initSDK(params); + + expect(SendbirdChat.init).toHaveBeenCalledWith( + expect.objectContaining({ + customApiHost: 'https://custom.api', + customWebSocketHost: 'wss://custom.websocket', + }), + ); + }); +}); + +const mockSdk = { + addExtension: jest.fn(), + addSendbirdExtensions: jest.fn(), + setSessionHandler: jest.fn(), +}; +const mockLogger = { + info: jest.fn(), +}; + +describe('setupSDK', () => { + it('sets up SDK with extensions and session handler', () => { + const params = { + logger: mockLogger, + sessionHandler: { onSessionExpired: jest.fn() }, + isMobile: false, + customExtensionParams: { customKey: 'customValue' }, + }; + + setupSDK(mockSdk, params); + + expect(mockLogger.info).toHaveBeenCalledWith( + 'SendbirdProvider | useConnect/setupConnection/setVersion', + expect.any(Object), + ); + expect(mockSdk.addExtension).toHaveBeenCalledWith('sb_uikit', expect.any(String)); + expect(mockSdk.addSendbirdExtensions).toHaveBeenCalledWith( + expect.any(Array), + expect.any(Object), + { customKey: 'customValue' }, + ); + expect(mockSdk.setSessionHandler).toHaveBeenCalledWith(params.sessionHandler); + }); + + it('does not set session handler if not provided', () => { + const params = { logger: mockLogger }; + + setupSDK(mockSdk, params); + + expect(mockSdk.setSessionHandler).not.toHaveBeenCalled(); + }); +}); diff --git a/src/lib/Sendbird/context/hooks/useSendbird.tsx b/src/lib/Sendbird/context/hooks/useSendbird.tsx index 908242fe3..34844183a 100644 --- a/src/lib/Sendbird/context/hooks/useSendbird.tsx +++ b/src/lib/Sendbird/context/hooks/useSendbird.tsx @@ -224,7 +224,7 @@ export const useSendbird = () => { actions.resetUser(); logger.info?.('SendbirdProvider | useSendbird/disconnect completed'); }, - }), [store]); + }), [store, state.stores.appInfoStore]); return { state, actions }; }; diff --git a/src/lib/Sendbird/index.tsx b/src/lib/Sendbird/index.tsx index ef8d1fe57..f071aa83e 100644 --- a/src/lib/Sendbird/index.tsx +++ b/src/lib/Sendbird/index.tsx @@ -57,7 +57,7 @@ const withSendbirdContext = (OriginalComponent: any, mapStoreToProps: (props: an const ContextAwareComponent = (props) => { const { state, actions } = useSendbird(); const context = { ...state, ...actions }; - if (mapStoreToProps && typeof mapStoreToProps !== 'function') { + if (!mapStoreToProps || typeof mapStoreToProps !== 'function') { // eslint-disable-next-line no-console console.warn('Second parameter to withSendbirdContext must be a pure function'); }