Skip to content

Commit 64818b6

Browse files
committed
fix: added tests for the hooks and message pagination
1 parent 74dfcf1 commit 64818b6

File tree

10 files changed

+876
-31
lines changed

10 files changed

+876
-31
lines changed

package/src/components/Channel/Channel.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,6 @@ const ChannelWithContext = <
884884
const failedMessages = channelMessagesState.messages
885885
?.filter((message) => message.status === MessageStatusTypes.FAILED)
886886
.map(parseMessage);
887-
888887
if (failedMessages?.length) {
889888
channel.state.addMessagesSorted(failedMessages);
890889
}

package/src/components/Channel/__tests__/Channel.test.js

Lines changed: 244 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useContext, useEffect } from 'react';
22
import { View } from 'react-native';
33

4-
import { cleanup, render, waitFor } from '@testing-library/react-native';
4+
import { act, cleanup, render, renderHook, waitFor } from '@testing-library/react-native';
55
import { StreamChat } from 'stream-chat';
66

77
import { ChannelContext, ChannelProvider } from '../../../contexts/channelContext/ChannelContext';
@@ -15,6 +15,7 @@ import { ThreadContext, ThreadProvider } from '../../../contexts/threadContext/T
1515

1616
import { getOrCreateChannelApi } from '../../../mock-builders/api/getOrCreateChannel';
1717
import { useMockedApis } from '../../../mock-builders/api/useMockedApis';
18+
import dispatchConnectionChanged from '../../../mock-builders/event/connectionChanged';
1819
import { generateChannelResponse } from '../../../mock-builders/generator/channel';
1920
import { generateMember } from '../../../mock-builders/generator/member';
2021
import { generateMessage } from '../../../mock-builders/generator/message';
@@ -23,6 +24,11 @@ import { getTestClientWithUser } from '../../../mock-builders/mock';
2324
import { Attachment } from '../../Attachment/Attachment';
2425
import { Chat } from '../../Chat/Chat';
2526
import { Channel } from '../Channel';
27+
import {
28+
channelInitialState,
29+
useChannelDataState,
30+
useChannelMessageDataState,
31+
} from '../hooks/useChannelDataState';
2632

2733
// This component is used for performing effects in a component that consumes ChannelContext,
2834
// i.e. making use of the callbacks & values provided by the Channel component.
@@ -328,3 +334,240 @@ describe('Channel', () => {
328334
});
329335
});
330336
});
337+
338+
describe('Channel initial load useEffect', () => {
339+
let chatClient;
340+
341+
const renderComponent = (props = {}) =>
342+
render(
343+
<Chat client={chatClient}>
344+
<Channel {...props}>{props.children}</Channel>
345+
</Chat>,
346+
);
347+
348+
beforeEach(async () => {
349+
chatClient = await getTestClientWithUser(user);
350+
});
351+
352+
afterEach(() => {
353+
jest.clearAllMocks();
354+
cleanup();
355+
});
356+
357+
it('should not call channel.watch if channel is not initialized', async () => {
358+
const messages = Array.from({ length: 10 }, (_, i) => generateMessage({ id: i }));
359+
const mockedChannel = generateChannelResponse({
360+
messages,
361+
});
362+
363+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
364+
const channel = chatClient.channel('messaging', mockedChannel.id);
365+
await channel.watch();
366+
channel.offlineMode = true;
367+
channel.state = channelInitialState;
368+
const watchSpy = jest.fn();
369+
channel.watch = watchSpy;
370+
371+
renderComponent({ channel });
372+
373+
await waitFor(() => expect(watchSpy).not.toHaveBeenCalled());
374+
});
375+
376+
it("should call channel.watch if channel is initialized and it's not in offline mode", async () => {
377+
const messages = Array.from({ length: 10 }, (_, i) => generateMessage({ id: i }));
378+
const mockedChannel = generateChannelResponse({
379+
messages,
380+
});
381+
382+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
383+
const channel = chatClient.channel('messaging', mockedChannel.id);
384+
await channel.watch();
385+
386+
channel.state = {
387+
...channelInitialState,
388+
members: Object.fromEntries(
389+
Array.from({ length: 10 }, (_, i) => [i, generateMember({ id: i })]),
390+
),
391+
messagePagination: {
392+
hasPrev: true,
393+
},
394+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ id: i })),
395+
};
396+
const watchSpy = jest.fn();
397+
398+
channel.offlineMode = false;
399+
channel.initialied = false;
400+
channel.watch = watchSpy;
401+
402+
renderComponent({ channel });
403+
404+
const { result: channelMessageState } = renderHook(() => useChannelMessageDataState(channel));
405+
const { result: channelState } = renderHook(() => useChannelDataState(channel));
406+
407+
await waitFor(() => expect(watchSpy).toHaveBeenCalled());
408+
await waitFor(() => expect(channelMessageState.current.state.messages).toHaveLength(10));
409+
await waitFor(() => expect(Object.keys(channelState.current.state.members)).toHaveLength(10));
410+
});
411+
412+
function getElementsAround(array, key, id) {
413+
const index = array.findIndex((obj) => obj[key] === id);
414+
415+
if (index === -1) {
416+
return [];
417+
}
418+
419+
const start = Math.max(0, index - 12); // 12 before the index
420+
const end = Math.min(array.length, index + 13); // 12 after the index
421+
return array.slice(start, end);
422+
}
423+
424+
it('should call the loadChannelAroundMessage when messageId is passed to a channel', async () => {
425+
const messages = Array.from({ length: 105 }, (_, i) => generateMessage({ id: i }));
426+
const messageToSearch = messages[50];
427+
const mockedChannel = generateChannelResponse({
428+
messages,
429+
});
430+
431+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
432+
const channel = chatClient.channel('messaging', mockedChannel.id);
433+
await channel.watch();
434+
435+
const loadMessageIntoState = jest.fn(() => {
436+
const newMessages = getElementsAround(messages, 'id', messageToSearch.id);
437+
channel.state.messages = newMessages;
438+
});
439+
440+
channel.state = {
441+
...channelInitialState,
442+
loadMessageIntoState,
443+
messagePagination: {
444+
hasNext: true,
445+
hasPrev: true,
446+
},
447+
messages,
448+
};
449+
450+
renderComponent({ channel, messageId: messageToSearch.id });
451+
452+
await waitFor(() => {
453+
expect(loadMessageIntoState).toHaveBeenCalledWith(messageToSearch.id, undefined, 25);
454+
});
455+
456+
const { result: channelMessageState } = renderHook(() => useChannelMessageDataState(channel));
457+
await waitFor(() => expect(channelMessageState.current.state.messages).toHaveLength(25));
458+
await waitFor(() =>
459+
expect(
460+
channelMessageState.current.state.messages.find(
461+
(message) => message.id === messageToSearch.id,
462+
),
463+
).toBeTruthy(),
464+
);
465+
});
466+
467+
it("should not call loadChannelAtFirstUnreadMessage if channel's unread count is 0", async () => {
468+
const mockedChannel = generateChannelResponse({
469+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
470+
});
471+
472+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
473+
const channel = chatClient.channel('messaging', mockedChannel.id);
474+
await channel.watch();
475+
const messages = Array.from({ length: 100 }, (_, i) => generateMessage({ id: i }));
476+
477+
const loadMessageIntoState = jest.fn();
478+
channel.state = {
479+
...channelInitialState,
480+
loadMessageIntoState,
481+
messagePagination: {
482+
hasNext: true,
483+
hasPrev: true,
484+
},
485+
messages,
486+
};
487+
channel.countUnread = jest.fn(() => 0);
488+
489+
renderComponent({ channel, initialScrollToFirstUnreadMessage: true });
490+
491+
await waitFor(() => {
492+
expect(loadMessageIntoState).not.toHaveBeenCalled();
493+
});
494+
});
495+
496+
it("should call loadChannelAtFirstUnreadMessage if channel's unread count is greater than 0", async () => {
497+
const mockedChannel = generateChannelResponse({
498+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
499+
});
500+
501+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
502+
const channel = chatClient.channel('messaging', mockedChannel.id);
503+
await channel.watch();
504+
const messages = Array.from({ length: 100 }, (_, i) => generateMessage({ id: i }));
505+
506+
let targetedMessageId = 0;
507+
const loadMessageIntoState = jest.fn((id) => {
508+
targetedMessageId = id;
509+
const newMessages = getElementsAround(messages, 'id', id);
510+
channel.state.messages = newMessages;
511+
});
512+
513+
channel.state = {
514+
...channelInitialState,
515+
loadMessageIntoState,
516+
messagePagination: {
517+
hasNext: true,
518+
hasPrev: true,
519+
},
520+
messages,
521+
messageSets: [{ isCurrent: true, isLatest: true }],
522+
};
523+
524+
channel.countUnread = jest.fn(() => 15);
525+
526+
renderComponent({ channel, initialScrollToFirstUnreadMessage: true });
527+
528+
await waitFor(() => {
529+
expect(loadMessageIntoState).toHaveBeenCalledTimes(1);
530+
});
531+
532+
const { result: channelMessageState } = renderHook(() => useChannelMessageDataState(channel));
533+
await waitFor(() =>
534+
expect(
535+
channelMessageState.current.state.messages.find(
536+
(message) => message.id === targetedMessageId,
537+
),
538+
).toBeDefined(),
539+
);
540+
});
541+
542+
it('should call resyncChannel when connection changed event is triggered', async () => {
543+
const mockedChannel = generateChannelResponse({
544+
messages: Array.from({ length: 10 }, (_, i) => generateMessage({ text: `message-${i}` })),
545+
});
546+
547+
useMockedApis(chatClient, [getOrCreateChannelApi(mockedChannel)]);
548+
const channel = chatClient.channel('messaging', mockedChannel.id);
549+
await channel.watch();
550+
551+
renderComponent({ channel });
552+
553+
await waitFor(() => {
554+
act(() => dispatchConnectionChanged(chatClient, false));
555+
});
556+
557+
await waitFor(() => {
558+
channel.state.addMessagesSorted(
559+
Array.from({ length: 10 }, (_, i) =>
560+
generateMessage({ status: 'failed', text: `message-${i}` }),
561+
),
562+
);
563+
});
564+
565+
await waitFor(() => {
566+
act(() => dispatchConnectionChanged(chatClient));
567+
});
568+
569+
await waitFor(() => {
570+
expect(channel.state.messages.length).toBe(20);
571+
});
572+
});
573+
});

0 commit comments

Comments
 (0)