Skip to content

Commit 7bd650d

Browse files
committed
refactor(mock-builders,tests): return LocalMessage from generateMessage
The mock-builder `generateMessage` produces `LocalMessage`-shaped data at runtime (Date objects for `created_at`/`updated_at`/`pinned_at`, `deleted_at: null`), so typing the return as `MessageResponse` was a lie that forced test files to paper over it with `as unknown as LocalMessage` everywhere. - Mock-builders now return `LocalMessage`: - generator/message: returns `LocalMessage`, adds `deleted_at: null`, `pinned_at: null`, `status: 'received'`; uses Date objects for the date fields (matches `LocalMessage` type); `message_text_ updated_at` stays ISO string (still on `MessageResponseBase`). - API mocks and event dispatchers accept `MessageResponse | LocalMessage`: - api/{sendMessage,deleteMessage,sendReaction,deleteReaction, threadReplies,getOrCreateChannel}.ts - event/{messageNew,messageDeleted,messageUpdated,reactionNew, reactionDeleted,reactionUpdated}.ts (cast to `MessageResponse` at the Event payload boundary — that's where the wire-shape mismatch actually happens). - generator/channel: `GeneratedChannelResponseCustomValues.messages` widened to accept both. - Test files cleaned up: - Removed 33 `as unknown as LocalMessage` / `as unknown as LocalMessage[]` casts. The message value flows through without rewrapping. - Removed `toLocalMessage` / `toLocalMessages` wrapper helpers (57 call sites across 3 files) — no longer needed. - Replaced 13 `{...({...} as unknown as ComponentProps<typeof X>)}` spread+cast patterns with direct prop passing. The spread was hiding props that weren't actually accepted by the target component (dead props from pre-migration JS). Dead props removed (all verified by reading each component's prop type): - `MessageAuthor`: `alignment`, `groupStyles` (live on `MessageContextValue` but `MessageAuthor` doesn't pick them). - `MessageReplies`: `groupStyles`, `MessageRepliesAvatars` (component override, comes via `ComponentsContext`), `openThread` (typo — actual prop is `onOpenThread`). - `Message`: `reactionsEnabled`, `MessageFooter` (override, goes through `WithComponents`). - `ScrollToBottomButton`: `t` (supplied via `TranslationProvider`). - `ChannelPreviewView`: `client`, `latestMessagePreview`, `watchers`, `latestMessage`, `latestMessageLength` (none are real props). - `Giphy` test helper: tightened `props: Record<string, unknown>` to `props: ComponentProps<typeof Giphy>` so callers are type-checked. Other fixups: - `ownCapabilities.test.tsx`: helper arg `targetMessage: MessageResponse` -> `LocalMessage`. - `useMessageListPagination.test.tsx`: `initialMessages` and `channelUnreadState` / `targetedMessageId` callback args retyped to `LocalMessage[]`. - `optimistic-update.tsx`: `allMessages: LocalMessage[]`. - `isAttachmentEqualHandler.test.tsx`: drop stale `as unknown as string` on `updated_at: new Date()`. - `useMessageListPagination.test.tsx`: drop stale `as unknown as string` on `created_at`. - Thread.test.tsx snapshot updated: missing optional `quoted_message: null` / `reaction_groups: null` keys were artifacts of the SDK's old `formatMessage` normalization. The current `LocalMessage` type declares them optional; same runtime data. test:typecheck: 0 errors. Full suite: same pre-existing SQLite flake, no regressions.
1 parent 1466189 commit 7bd650d

39 files changed

Lines changed: 227 additions & 345 deletions

package/src/__tests__/offline-support/optimistic-update.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { act, cleanup, render, screen, waitFor } from '@testing-library/react-na
66
import type {
77
ChannelAPIResponse,
88
ChannelMemberResponse,
9-
MessageResponse,
9+
LocalMessage,
1010
ReactionResponse,
1111
} from 'stream-chat';
1212
import { v4 as uuidv4 } from 'uuid';
@@ -50,7 +50,7 @@ export const OptimisticUpdates = () => {
5050
const getRandomInt = (lower, upper) => Math.floor(lower + Math.random() * (upper - lower + 1));
5151
const createChannel = () => {
5252
const allUsers = Array(20).fill(1).map(generateUser);
53-
const allMessages: MessageResponse[] = [];
53+
const allMessages: LocalMessage[] = [];
5454
const allMembers: ChannelMemberResponse[] = [];
5555
const allReactions: ReactionResponse[] = [];
5656
const allReads: Array<{

package/src/components/Attachment/__tests__/Attachment.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { ComponentProps } from 'react';
22

33
import { render, waitFor } from '@testing-library/react-native';
4-
import type { LocalMessage } from 'stream-chat';
54
import { v4 as uuidv4 } from 'uuid';
65

76
import type { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
@@ -28,7 +27,7 @@ jest.mock('../../../native.ts', () => ({
2827
}));
2928

3029
const getAttachmentComponent = (props: ComponentProps<typeof Attachment>) => {
31-
const message = generateMessage() as unknown as LocalMessage;
30+
const message = generateMessage();
3231
return (
3332
<ThemeProvider>
3433
<MessagesProvider

package/src/components/Attachment/__tests__/Giphy.test.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@ import {
99
userEvent,
1010
waitFor,
1111
} from '@testing-library/react-native';
12-
import type {
13-
Channel as ChannelType,
14-
ChannelResponse,
15-
LocalMessage,
16-
StreamChat,
17-
} from 'stream-chat';
12+
import type { Channel as ChannelType, ChannelResponse, StreamChat } from 'stream-chat';
1813

1914
import type { MessageContextValue } from '../../../contexts/messageContext/MessageContext';
2015
import { MessageProvider } from '../../../contexts/messageContext/MessageContext';
@@ -47,10 +42,10 @@ describe('Giphy', () => {
4742
const lightTheme = mergeThemes({ scheme: 'light' });
4843

4944
const getAttachmentComponent = (
50-
props: Record<string, unknown>,
45+
props: ComponentProps<typeof Giphy>,
5146
messageContextValue: Partial<MessageContextValue> = {},
5247
) => {
53-
const message = generateMessage() as unknown as LocalMessage;
48+
const message = generateMessage();
5449
return (
5550
<ThemeProvider>
5651
<MessagesProvider
@@ -64,7 +59,7 @@ describe('Giphy', () => {
6459
<MessageProvider
6560
value={{ message, ...messageContextValue } as unknown as MessageContextValue}
6661
>
67-
<Giphy {...(props as unknown as ComponentProps<typeof Giphy>)} />
62+
<Giphy {...props} />
6863
</MessageProvider>
6964
</MessagesProvider>
7065
</ThemeProvider>

package/src/components/Channel/__tests__/isAttachmentEqualHandler.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ describe('isAttachmentEqualHandler', () => {
110110
attachments: [
111111
{ customField: 'custom-field-2', type: 'test' } as AttachmentWithCustomField,
112112
],
113-
updated_at: new Date() as unknown as string,
113+
updated_at: new Date(),
114114
},
115115
channel,
116116
);

package/src/components/Channel/__tests__/ownCapabilities.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FlatList } from 'react-native';
44
import { SafeAreaProvider } from 'react-native-safe-area-context';
55

66
import { act, fireEvent, render, waitFor } from '@testing-library/react-native';
7-
import type { Channel as ChannelType, MessageResponse, StreamChat } from 'stream-chat';
7+
import type { Channel as ChannelType, LocalMessage, StreamChat } from 'stream-chat';
88

99
import { OverlayProvider } from '../../../contexts/overlayContext/OverlayProvider';
1010
import { allOwnCapabilities } from '../../../contexts/ownCapabilitiesContext/OwnCapabilitiesContext';
@@ -73,7 +73,7 @@ describe('Own capabilities', () => {
7373
};
7474

7575
const renderChannelAndOpenMessageActionsList = async (
76-
targetMessage: MessageResponse,
76+
targetMessage: LocalMessage,
7777
props: Partial<React.ComponentProps<typeof Channel>> = {},
7878
) => {
7979
const { findByTestId, queryByLabelText, queryByText, unmount } = render(getComponent(props));

package/src/components/Channel/__tests__/useMessageListPagination.test.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { act, cleanup, renderHook, waitFor } from '@testing-library/react-native';
2-
import type { Channel as ChannelType, MessageResponse, StreamChat } from 'stream-chat';
2+
import type { Channel as ChannelType, LocalMessage, StreamChat } from 'stream-chat';
33

44
import { getOrCreateChannelApi } from '../../../mock-builders/api/getOrCreateChannel';
55
import { useMockedApis } from '../../../mock-builders/api/useMockedApis';
@@ -393,15 +393,15 @@ describe('useMessageListPagination', () => {
393393
};
394394

395395
type TestCase = {
396-
channelUnreadState: (messages: MessageResponse[]) => TestCaseUnreadState;
396+
channelUnreadState: (messages: LocalMessage[]) => TestCaseUnreadState;
397397
expectedCalls: {
398398
jumpToMessageFinishedCalls: number;
399399
loadMessageIntoStateCalls: number;
400400
setChannelUnreadStateCalls: number;
401401
setTargetedMessageIdCalls: number;
402-
targetedMessageId: (messages: MessageResponse[]) => string;
402+
targetedMessageId: (messages: LocalMessage[]) => string;
403403
};
404-
initialMessages: MessageResponse[];
404+
initialMessages: LocalMessage[];
405405
name: string;
406406
setupLoadMessageIntoState: ((channel: ChannelType) => jest.Mock) | null;
407407
};
@@ -569,7 +569,7 @@ describe('useMessageListPagination', () => {
569569

570570
const messages = Array.from({ length: 20 }, (_, i) =>
571571
generateMessage({
572-
created_at: new Date('2021-09-01T00:00:00.000Z') as unknown as string,
572+
created_at: new Date('2021-09-01T00:00:00.000Z'),
573573
id: String(i),
574574
text: `message-${i}`,
575575
}),

package/src/components/ChannelPreview/__tests__/ChannelPreviewView.test.tsx

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,25 +21,7 @@ describe('ChannelPreviewView', () => {
2121

2222
const getComponent = (props: Partial<React.ComponentProps<typeof ChannelPreviewView>> = {}) => (
2323
<Chat client={chatClient}>
24-
<ChannelPreviewView
25-
{...({
26-
channel,
27-
client: chatClient,
28-
latestMessagePreview: {
29-
created_at: '',
30-
messageObject: generateMessage(),
31-
previews: [
32-
{
33-
bold: true,
34-
text: 'This is the message preview text',
35-
},
36-
],
37-
status: 1, // read states of latest message.
38-
},
39-
onSelect: jest.fn(),
40-
} as unknown as React.ComponentProps<typeof ChannelPreviewView>)}
41-
{...props}
42-
/>
24+
<ChannelPreviewView channel={channel as ChannelType} onSelect={jest.fn()} {...props} />
4325
</Chat>
4426
);
4527

@@ -63,14 +45,7 @@ describe('ChannelPreviewView', () => {
6345
const onSelect = jest.fn();
6446
await initializeChannel(generateChannelResponse());
6547

66-
render(
67-
getComponent({
68-
onSelect,
69-
...({ watchers: {} } as unknown as Partial<
70-
React.ComponentProps<typeof ChannelPreviewView>
71-
>),
72-
}),
73-
);
48+
render(getComponent({ onSelect }));
7449

7550
await waitFor(() => screen.getByTestId('channel-preview-button'));
7651

@@ -115,14 +90,7 @@ describe('ChannelPreviewView', () => {
11590
const message = generateMessage();
11691
await initializeChannel(generateChannelResponse());
11792

118-
render(
119-
getComponent({
120-
...({
121-
latestMessage: message,
122-
latestMessageLength: 6,
123-
} as unknown as Partial<React.ComponentProps<typeof ChannelPreviewView>>),
124-
}),
125-
);
93+
render(getComponent());
12694

12795
const expectedMessagePreview = truncate(message.text, { length: 6 });
12896
await waitFor(() => screen.queryByText(expectedMessagePreview));

package/src/components/ImageGallery/__tests__/ImageGallery.test.tsx

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,13 @@ describe('ImageGallery', () => {
8888
it('render image gallery component', async () => {
8989
render(
9090
<ImageGalleryComponent
91-
message={
92-
generateMessage({
93-
attachments: [
94-
generateImageAttachment(),
95-
generateGiphyAttachment(),
96-
generateVideoAttachment({ type: 'video' }),
97-
],
98-
}) as unknown as LocalMessage
99-
}
91+
message={generateMessage({
92+
attachments: [
93+
generateImageAttachment(),
94+
generateGiphyAttachment(),
95+
generateVideoAttachment({ type: 'video' }),
96+
],
97+
})}
10098
/>,
10199
);
102100

@@ -111,11 +109,9 @@ describe('ImageGallery', () => {
111109

112110
render(
113111
<ImageGalleryComponent
114-
message={
115-
generateMessage({
116-
attachments: [generateImageAttachment()],
117-
}) as unknown as LocalMessage
118-
}
112+
message={generateMessage({
113+
attachments: [generateImageAttachment()],
114+
})}
119115
/>,
120116
);
121117

package/src/components/ImageGallery/__tests__/ImageGalleryFooter.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { SharedValue } from 'react-native-reanimated';
44

55
import { render, screen, userEvent, waitFor } from '@testing-library/react-native';
66

7-
import { Attachment, LocalMessage } from 'stream-chat';
7+
import { Attachment } from 'stream-chat';
88

99
import { WithComponents } from '../../../contexts/componentsContext/ComponentsContext';
1010
import {
@@ -53,7 +53,7 @@ const ImageGalleryComponentVideo = (props: ImageGalleryProps) => {
5353
messages: [
5454
generateMessage({
5555
attachments: [attachment],
56-
}) as unknown as LocalMessage,
56+
}),
5757
],
5858
selectedAttachmentUrl: attachment.asset_url,
5959
});
@@ -95,7 +95,7 @@ const ImageGalleryComponentImage = (
9595
messages: [
9696
generateMessage({
9797
attachments: [props.attachment],
98-
}) as unknown as LocalMessage,
98+
}),
9999
],
100100
selectedAttachmentUrl: props.attachment.image_url as string,
101101
});

package/src/components/ImageGallery/__tests__/ImageGalleryGrid.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ describe('ImageGalleryGrid', () => {
5555
it('should render ImageGalleryGrid', async () => {
5656
const message = generateMessage({
5757
attachments: [generateImageAttachment(), generateImageAttachment()],
58-
}) as unknown as LocalMessage;
58+
});
5959

6060
render(<ImageGalleryGridComponent message={message} />);
6161

@@ -67,7 +67,7 @@ describe('ImageGalleryGrid', () => {
6767
it('should render ImageGalleryGrid individual images', async () => {
6868
const message = generateMessage({
6969
attachments: [generateImageAttachment(), generateVideoAttachment({ type: 'video' })],
70-
}) as unknown as LocalMessage;
70+
});
7171

7272
render(<ImageGalleryGridComponent message={message} />);
7373

@@ -82,7 +82,7 @@ describe('ImageGalleryGrid', () => {
8282

8383
const message = generateMessage({
8484
attachments: [generateImageAttachment(), generateVideoAttachment({ type: 'video' })],
85-
}) as unknown as LocalMessage;
85+
});
8686

8787
render(<ImageGalleryGridComponent closeGridView={closeGridViewMock} message={message} />);
8888

0 commit comments

Comments
 (0)