refactor(tests): migrate test suite and mock-builders to TypeScript#3562
Open
refactor(tests): migrate test suite and mock-builders to TypeScript#3562
Conversation
Migrates 44 mock-builders, 36 test files, 2 offline-support helpers, 1 test utility (BetterSqlite), and jest-setup from JS to TS. Adds tsconfig.test.json so tests and mock-builders are type-checked (base tsconfig still excludes them from the published build) and a test:typecheck script. Dependency changes: - @types/jest: ^29.5.14 -> ^30.0.0 - jest: ^30.0.0 -> ^30.3.0 - @total-typescript/shoehorn: new devDependency (fromPartial<T>() for type-safe partial mocks, replacing `as any` / `Record<string, any>`) Caveats: - tsconfig.test.json keeps noImplicitAny: false as a starting point; tightening is follow-up work (Phase 5 of the migration plan). - yarn test:typecheck exists but is not wired into CI yet. Verification: - yarn build passes - yarn lint passes - yarn test:unit shows only the pre-existing SQLite-isolation flake in offline-support/index.test.ts (baseline had 5 failures; now 2 β no regressions).
Contributor
SDK Size
|
β¦erence With tsconfig.test.json widening the include scope to cover tests and mock-builders, several `const queries = []` / `const whereClause = []` initializers in source files start erroring because TS infers `never[]` and the subsequent `.push(X)` call cannot accept X. These were latent type issues hidden by the base tsconfig's `**/__tests__` exclude; the runtime behavior is unchanged. - addPendingTask/deleteMessage: `queries: PreparedQueries[]` - upsertDraft: `messagesToUpsert: MessageResponseBase[]` (matches the actual type of `draft.quoted_message` / `draft.parent_message`) - appendOrderByClause/appendWhereCluase: `whereClause: string[]` etc. - createCreateTableQuery: explicit `PreparedQueries` return on the `.map` callback so the tuple shape is preserved (was inferring `string[][]`) - useMessageList: `newMessageList: LocalMessage[]` - renderText: drop an unused `// @ts-expect-error` directive (the `react-native-markdown-package` import no longer needs it) Takes the test:typecheck error count from 568 β 556.
β¦chers
- generator/channel: narrow `id`/`type` to `string` via `??` defaults
before composing the object; add missing `reminders` on default
config; tag `commands[0].name/set` as `'giphy' as const` / `'fun_set'
as const` to satisfy `CommandVariants`; drop `type` from nested
`config` (not on `ChannelConfigFields`).
- generator/attachment: drop `id` and `description` fields that aren't
on `Attachment` (verified no caller uses them).
- generator/message, generator/reaction, event/messageRead,
event/notificationMarkUnread: keep `Date` objects at runtime (a
handful of components call `.toDateString()` on them) but silence
the `Date` vs `string` mismatch with `as unknown as string` where
the underlying `MessageResponse`/`Event` type demands a string.
Converting to ISO strings would have been cleaner but broke
runtime; see NOTE comment in generator/message.
- mock: type `user`/`_user` as `OwnUserResponse` so `mutes: []` type-
checks; replace the `as { mutes?: unknown[] }` workaround.
- api/channelMocks: drop bogus `message: {}` field from
`FORMATTED_MESSAGE` (not on `LocalMessage`); add required
`deleted_at: null`.
- api/initiateClientWithChannels: pass the top-level `id`/`type` from
the generated channel to `client.channel(...)` (both `string`)
instead of the nested `channel.id`/`channel.type` (`string |
undefined`).
- DB/mock: narrow `db.pragma()`'s `unknown` return to `unknown[]`.
Takes test:typecheck from 556 β 544. Full test suite: same pre-
existing SQLite-isolation flake in offline-support; no regressions.
β¦est.json
These test files were always `.test.ts`/`.test.tsx` but errors were
hidden because the base `tsconfig.json` excludes `**/__tests__`. The
migration added `tsconfig.test.json` which now checks them. Behavior
unchanged β only type annotations and casts.
- state-store/__tests__/image-gallery-state-store.test.ts: add local
`toLocalMessage` / `toLocalMessages` helpers that cast `MessageResponse`
mocks to `LocalMessage` at assignment sites (store expects
`LocalMessage[]`). Convert numeric `id` fields to strings (matches the
`MessageResponse['id']: string` signature). Convert Giphy
`height`/`width` to strings (`GiphyVersionInfo` types them as string).
Narrow `tDateTimeParser()` call results with `as Dayjs.Dayjs` (the
returned union type `TDateTimeParserOutput` doesn't expose `.locale`/
`.localeData`). Fixes all 82 errors.
- utils/__tests__/Streami18n.test.ts: import `Moment` type; narrow
`tDateTimeParser()` outputs with `as Dayjs.Dayjs` / `as Moment` before
calling `.locale()`/`.format()`; widen inline options types where the
test-only `abc` translation key collides with the `Partial<
enTranslations>` type; access private `i18n.init()` via a minimal
`{ init: () => Promise<void> }` cast. Fixes all 19 errors.
- store/apis/__tests__/updatePendingTask.test.ts: import `PendingTask`
from `stream-chat`; cast the test payload shape to `PendingTask` (the
local union doesn't include a `send-message` variant that matches
`[MessageResponse, {}]`). Type `BetterSqlite.selectFromTable` rows via
its generic arg. Narrow `updatedTask.payload[0].text` access with a
tuple cast. Fixes all 6 errors.
Test suite: all three files still pass (unchanged assertions). Total
test:typecheck count: 544 β 437 (β107).
Type annotations for 21 component test files. No behavior changes β
only type imports, describe-scope `let` declarations, context-value
casts via the established `as unknown as ContextValue` pattern, and
narrow casts at SDK-type access points.
- Channel/__tests__/{Channel,ownCapabilities,useMessageListPagination,
isAttachmentEqualHandler}.test.tsx
- ChannelList/__tests__/{ChannelList,ChannelListView}.test.tsx
- ChannelPreview/__tests__/ChannelPreviewView.test.tsx
- Chat/__tests__/Chat.test.tsx
- Message/MessageItemView/__tests__/{Message,MessageAuthor,
MessageContent,MessageItemView,MessagePinnedHeader,MessageReplies,
MessageStatus,MessageTextContainer,ReactionListBottom,
ReactionListTop}.test.tsx
- MessageList/__tests__/{MessageList,ScrollToBottomButton}.test.tsx
- Thread/__tests__/Thread.test.tsx
Conventions applied:
- `let client: StreamChat; let channel: Channel;` at describe scope.
- `{...} as unknown as ChannelContextValue` (or `ChatContextValue`,
`MessagesContextValue`, etc.) for partial context mocks β matches
`components/MessageList/__tests__/useMessageList.test.tsx:35,43`.
- `MessageResponse` β `LocalMessage` via small local `toLocalMessage`
helper where needed; SDK's `formatMessage` isn't used because it
requires a configured Channel.
- `ComponentProps<typeof X>` for `renderComponent({ props })` args.
- Numeric `id`s in mock-builder calls fixed to string.
Pre-existing latent test bug noted: `MessageStatus.test.tsx` used
`it.each('string', fn)` (malformed β string iterated as chars).
Converted to `it.skip` to preserve pre-migration runtime behavior
(the test body never executed) and filed as future work to fix the
test properly.
Takes test:typecheck 437 β 227 (β210). Full test suite: same
pre-existing SQLite-isolation flake in offline-support; no
regressions.
Type annotations for ~32 remaining test files across attachment,
message-input, message-menu, channel-preview, image-gallery, hooks,
and context folders. No behavior changes.
Patterns applied (same as the previous commit):
- `let client: StreamChat; let channel: Channel;` at describe scope.
- `{...} as unknown as XContextValue` for partial context mocks.
- `renderComponent` helpers typed with `ComponentProps<typeof X>`.
- `MessageResponse` β `LocalMessage` via local wrapper casts.
- Replace bogus `as unknown as FileUpload` (never imported β leftover
silent bug from .js) with the real component prop type:
`LocalAudioAttachment` / `LocalVoiceRecordingAttachment` from
`stream-chat` (see `AudioAttachmentUploadPreview.tsx:5`).
- Attachment mocks with `localMetadata` typed as `LocalAttachment`
(from `stream-chat`) rather than `Partial<Attachment>`.
Notable: `AudioAttachmentUploadPreviewNative.test.tsx` and
`*Expo.test.tsx` already used `describe.skip` at the top (pre-migration
behavior: their mock props don't match the current component API).
Kept the skip and loosened helper-arg types to `unknown` so the
(unreachable) test bodies type-check. Filing these as follow-up to
properly un-skip and update.
Takes test:typecheck 227 β 69 (β158). All 69 remaining errors are in
the two offline-support helpers (`offline-feature.tsx`,
`optimistic-update.tsx`) β handled in the next commit.
Full test suite: 752 passed, 14 skipped; only the pre-existing
SQLite-isolation flake in `offline-support/index.test.ts` fails.
No regressions.
Type annotations for the two large integration-test helpers. No
behavior changes.
- offline-feature.tsx: narrow `_fiber` access via
`as unknown as { _fiber: { pendingProps: { testID: string } } }` at
13 call sites; rename `userId` β `user_id` in `generateMessage`
options (snake_case per SDK); type `ChannelSort` literals properly
(`last_updated: 1`); narrow `tablesInDb`, `JSON.parse` args, and
`new Date(...)` args from `unknown`; import `ChannelMemberResponse`,
`ChannelSort`, `Event`, `MessageResponse`, `ReactionResponse`,
`ChannelAPIResponse` as `type` imports.
- optimistic-update.tsx: local wrapper cast
`Channel as unknown as ComponentType<... & { initialValue?: string }>`
to preserve the legacy `initialValue` prop the test passes; typed
empty accumulators (`const newLatestReactions: ReactionResponse[]`);
non-null assertion on `newReaction.user!.id`; cast
`doUpdateMessageRequest` callbacks whose test-return shape doesn't
include SDK-required `duration` etc.
Latent test bugs preserved (passing undeclared fields at runtime
that the SDK types don't include β `cid` on `ChannelMemberResponse`
and `GeneratedChannelResponseCustomValues`, `last_read` instead of
`last_read_at` on Event). Widened locally with
`as unknown as T & { cid: string }` etc. rather than changing test
data. These are follow-up items.
Takes test:typecheck 69 β 0. Full test suite: 752 passed, 14
skipped; only the pre-existing SQLite-isolation flake in
offline-support/index.test.ts fails. No regressions.
Adds a `Typecheck tests` step to check-pr.yml that runs `yarn test:typecheck` (tsc --noEmit -p tsconfig.test.json) in the package/ workspace between lint and test. Tests and mock-builders are now type-gated at PR time. Leaves `noImplicitAny: false` in tsconfig.test.json with a TODO comment. Flipping it exposes ~630 mechanical implicit-any errors (destructured-parameter and `let`-variable annotations across ~60 test files). Filing as separate follow-up rather than ballooning this PR further. Full error count at this commit: 0.
- src/components/Message/MessageItemView/utils/renderText.tsx: restore the directive for the untyped `react-native-markdown-package` import, but use `@ts-ignore` instead of `@ts-expect-error`. The base tsconfig (with noImplicitAny: true) needs the suppression for bob's `yarn build` to succeed; the test tsconfig (with noImplicitAny: false) doesn't report the import as an error, so `@ts-expect-error` would read as "unused". `@ts-ignore` is tolerant of both. - prettier auto-formatting across a handful of recently edited test files.
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.
Second pass of the dead-props sweep now that the low-hanging
spread+cast pattern is gone.
- MessageList.test.tsx: remove \`channelUnreadState\` prop from
\`<MessageList {...messageListProps} />\`. Not on \`MessageList\`'s
type β it's an internal state derived from
\`channelUnreadStateStore\`. The test was silently passing it
hoping to override something; nothing consumed it. Also drop the
now-unused \`messageListProps\` cast.
- Thread.test.tsx: drop \`client={chatClient}\` from \`<Channel>\`.
\`Channel\` doesn't declare \`client\` as a prop β it reads it from
\`ChatContext\`. The prop was silently accepted via structural
typing and ignored at runtime.
- ImageGalleryHeader.test.tsx: drop two \`// @ts-ignore\` lines
above JSX props. The underlying issue was that
\`sharedValueOpacity\`/\`sharedValueVisible\` were \`SharedValue<
number> | undefined\` (uninitialized let). Added \`!\` non-null
assertion instead β the \`renderHook\` synchronously assigns them.
- MessageUserReactionsAvatar.test.tsx: drop
\`as unknown as ComponentProps<typeof Chat>['style']\` on the
\`style\` prop. Use the direct
\`defaultTheme as DeepPartial<Theme>\` cast (same pattern used by
other tests in the repo).
test:typecheck: 0 errors. Full test suite: same pre-existing
SQLite-isolation flake.
Zero \`@ts-ignore\` / \`@ts-expect-error\` suppressions remain under
src/**/*.test.*.
- getResizedImageUrl.test.ts: typed the mocked URL object as
\`as unknown as URL\` at the return value instead of silencing the
search-params shape mismatch inline.
- renderText.test.tsx:
- Dropped \`@ts-ignore\` on the \`simple-markdown\` import
(\`ASTNode\`/\`SingleASTNode\` are declared in the bundled
\`simple-markdown.d.ts\` β the ignore was stale).
- Replaced \`<Text>{node}</Text>\` with \`<Text>{JSON.stringify(node)}</Text>\`
so the \`ASTNode\` value is a valid React child; the test only
asserts \`getByText\`, the content shape is incidental.
Flips \`noImplicitAny: true\` in \`tsconfig.test.json\` (removes the
override β now inherited from the base config) and fixes the ~630
resulting errors across ~20 test files. All annotations use SDK types
from \`stream-chat\` where applicable; \`as unknown as T\` casts reserved
for spots where the SDK type doesn't fit cleanly at the test boundary.
Patterns applied:
- \`let client\`, \`let channel\`, \`let chatClient\` β \`StreamChat\` /
\`Channel\` / \`StreamChat\`.
- \`renderComponent\` / \`Wrapper\` / \`renderMessage\` helper signatures
typed via \`ComponentProps<typeof X>\`, \`Partial<ChannelProps>\`,
\`ComponentOverrides\`, or \`PropsWithChildren<Partial<...>>\`.
- Context-consumer arrows (\`({ fn }) => ...\`) typed with
\`ChatContextValue\` / \`TranslationContextValue\`.
- Translation \`obj[key]\` β \`obj[key as keyof typeof obj]\`.
- Uninitialized locals typed with a definite-assigned cast
(\`{} as ChatContextValue\`) to match the established pattern.
- \`_fiber\` and \`offlineDb.syncManager.invokeSyncStatusListeners\`
accesses handled through narrow local shim types (\`TestOfflineDb\`,
\`TestSyncManager\`) rather than widening the real SDK types.
- \`updatedMessage\` \`Array.find\` results narrowed with \`!\` at
assertion sites.
Notable scope decisions (kept out of scope):
- \`Channel\`'s \`doUpdateMessageRequest\` callback signature mismatches
the SDK β cast the arg to \`Awaited<ReturnType<typeof
channel.messageComposer.compose>>\` at the mock site to preserve
runtime behavior.
- \`messageComposer.compose\` mock resolves with \`{ localMessage,
message }\` (no \`options\`) β dropped the dead \`options\` key; the
SDK's \`MessageComposerMiddlewareState\` doesn't include it.
- Removed unused \`TestWsConnection\` type in \`offline-feature.tsx\`
and the unused \`MessageInputContextValue\` / \`MessagesContextValue\`
type-imports in \`optimistic-update.tsx\` to satisfy
\`@typescript-eslint/no-unused-vars\`.
test:typecheck: 636 β 0. Full test suite: same pre-existing
SQLite-isolation flake in \`offline-support/index.test.ts\`; no
regressions. \`yarn lint\` / \`yarn build\` clean.
Closes #3563.
Cuts `Record<string, unknown>` uses from 57 β 22. The remaining 22 are
legitimate: generic parameter defaults on public API types
(`CustomLocalMetadata`), the `UnknownType` export, `Message.additionalInfo`
(genuine bag-of-data prop), `useStateStore` / `usePollStateStore` generic
constraints, `topologicalResolution` plain-object type guards,
`BetterSqlite.selectFromTable` default row type, the i18n formatter
`options` (matches i18next API shape), and the one remaining
`exception_fields` field on `mock-builders/api/error.ts` which matches
the real Stream API error shape.
**Source / mock-builders:**
- mock-builders/api/initiateClientWithChannels.ts: alias
`ChannelData` as `Parameters<typeof generateChannel>[0]`.
- mock-builders/generator/channel.ts: drop unused `_client` field (only
ever set to `{}`, no consumers).
- mock-builders/attachments.ts: narrow override arg to
`Partial<LocalAttachmentData & { file: Partial<FileReference> }>`
(both callers pass no overrides anyway).
- mock-builders/mock.ts: replace two `as unknown as Record<string,
unknown>` + `as never` casts with a single `type WithPrivates` cast
for `_setToken` / `_setupConnection` spies.
- components/UIComponents/SwipableWrapper.tsx: `Content` prop typed as
bare `React.ComponentType` (rendered as `<Content />` with no props).
**Tests:**
- Channel.test.tsx: 9 uses replaced β `RenderComponentProps` becomes
`Partial<ComponentProps<typeof Channel>>` (with `channel?: unknown` to
allow the one test that passes an ad-hoc shape); context-cast sites
use `typeof mockContext` so test mocks can mix fields from
`ChatContextValue` + `MessagesContextValue`; `T extends object`
generic; `typeof channel.state.messages`; `Partial<ReturnType<typeof
MessageListPaginationHooks.useMessageListPagination>>`; `typeof
channel.state.read`.
- Giphy.test.tsx: 10 uses replaced with `ComponentProps<typeof Image>`
+ `StyleProp<ImageStyle>` / `ImageStyle` from `react-native`.
- ChannelList.test.tsx: `filters` arg typed as
`Parameters<typeof chatClient.queryChannels>[0]`.
- MessageContent.test.tsx: override-component prop types switched to
`MessageHeaderProps` / `MessageFooterProps` from the component
sources.
- useMessageListPagination.test.tsx: `mockedHook` args typed as
`Partial<typeof channelInitialState>` and `Partial<ReturnType<
typeof ChannelStateHooks.useChannelMessageDataState>>`.
- ChannelDetailsBottomSheet.test.tsx: 3 uses replaced with
`ComponentProps<typeof StreamBottomSheetModalFlatList>`.
- ChannelSwipableWrapper.test.tsx: `ComponentProps<typeof
SwipableWrapper>`.
- ChannelPreview.test.tsx: override-map types from
`Parameters<typeof generateChannelResponse>[0]` and `Partial<Channel>`.
test:typecheck: 0 errors. Full test suite: same pre-existing
SQLite-isolation flake; no regressions.
Focused pass to reduce redundant casts across the test suite. Cuts
\`as unknown as\` occurrences from 359 β 283 (-76, ~21%) without
changing runtime behavior.
**Highlights:**
- \`channelMocks.tsx\`: introduce local \`mockMessage\` / \`mockUser\`
helpers so 17 per-literal \`as unknown as MessageResponse\` /
\`as unknown as UserResponse\` casts collapse into one internal
narrow cast inside each helper.
- \`useMessageListPagination.test.tsx\` / \`Channel.test.tsx\` /
\`MessageList.test.tsx\`: drop many \`as unknown as typeof
channel.state.messages\` casts on \`generateMessage()\` outputs β
the generator now returns \`LocalMessage\`, which already matches.
- \`Channel.test.tsx\`: tighten \`ChannelContext as unknown as
React.Context<unknown>\` β \`as React.Context<unknown>\` (React's
\`Context<T>\` is invariant, so a full cast isn't needed, just the
single \`as\`).
- Dropped \`{} as unknown as X\` β \`{} as X\` for empty provider
values where the double-cast was overkill.
- Dropped the \`client={chatClient}\` dead prop still lingering on
one of the two \`<Channel>\` renders in \`Thread.test.tsx\`
(missed in the earlier dead-prop sweep).
- Simplified \`as unknown as X\` to \`as X\` at ~10 miscellaneous
sites (e.g. \`Parameters<T>\`, \`jest.Mocked<T>\`, context-prop
indexers).
**Intentionally kept:**
- \`_fiber\` internal-access casts in offline-support helpers.
- Private-member access casts (\`syncManager\`, \`_sendMessage\`,
\`_setToken\`, etc.).
- \`Streami18n\` \`ConstructorParameters\` double-casts where test
fixtures violate the stricter option types.
- \`channel.state = {...} as unknown as typeof channel.state\` where
a partial spread genuinely doesn't satisfy the full ChannelState
class instance shape.
test:typecheck: 0 errors. Full test suite: same pre-existing
SQLite-isolation flake; no regressions.
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Migrates the remaining JS test infrastructure to TypeScript, then type-tightens the whole test suite with full
noImplicitAny: truestrictness and wiresyarn test:typecheckinto CI as a required check.Migration (baseline)
package/src/mock-builders/): API response builders, event dispatchers, data generators, and the coremock.tsclient helper.test.jsfiles β.test.ts/.test.tsxacross components and utils.tsxtest-utils/BetterSqlite.jsβ.tsjest-setup.jsβ.tsx(contains JSX for theBottomSheetModalmock)package/tsconfig.test.jsonso tests and mock-builders are type-checked (basetsconfig.jsonstill excludes them from the published build)yarn test:typecheckscript, wired into.github/workflows/check-pr.ymlType-tightening
const queries = []was inferringnever[]under the broader include scope. Real latent issues; no behavior change.stream-chat; drop fields not onAttachment; add missingreminderson default channel config; narrowclient.channel(...)args.generateMessagenow returnsLocalMessage. Previously typed asMessageResponsebut at runtime producedDateobjects forcreated_at/updated_at/pinned_atβ theLocalMessageshape. Making the type honest eliminated 33as unknown as LocalMessagecasts and 57toLocalMessage(β¦)wrapper calls across the suite. API mocks and event dispatchers that legitimately need the wire shape acceptMessageResponse | LocalMessageat their boundary.{...} as unknown as XContextValuepattern for partial context mocks. Replaces every bogusas unknown as FileUpload(leftover silent bug βFileUploadwas never imported) with the correct SDK type (LocalAudioAttachment/LocalVoiceRecordingAttachment). UsesLocalAttachment(notPartial<Attachment>) for attachment mocks that setlocalMetadata. UsesComponentProps<typeof X>forrenderComponent({ props })typings.noImplicitAny: trueand annotate the ~630 resulting errors across ~20 test files.let chatClient: StreamChat, typed destructured params, typedjest.fn()callbacks,ComponentProps<typeof X>for helper prop shapes. Zeroany/as anyadded.Dead-prop cleanup
Removing spread+cast and
@ts-ignoreescape hatches made TypeScript surface a slew of props tests were passing that target components don't actually accept. 15+ dead props removed across 16 test files. Notable:MessageAuthor:alignment,groupStyles(live onMessageContextValue;MessageAuthordoesn't pick them).MessageReplies:groupStyles,MessageRepliesAvatars,openThread(typo β real prop isonOpenThread; silently dropped).Message:reactionsEnabled,MessageFooteroverride.ScrollToBottomButton:t(supplied viaTranslationProvider).ChannelPreviewView:client,latestMessagePreview,watchers,latestMessage,latestMessageLength.MessageList:channelUnreadState(internal state, never a prop).Channel:client(comes fromChatContext, caught onThread.test.tsx).Giphytest helper: widenedRecord<string, unknown>βComponentProps<typeof Giphy>.Also: zero
@ts-ignore/@ts-expect-errordirectives remain in any*.test.*file.Dependency changes
@types/jest:^29.5.14β^30.0.0(matches installedjest@30)jest:^30.0.0β^30.3.0@total-typescript/shoehorn: new devDependency βfromPartial<T>()for type-safe partial mocks, replacingas any/Record<string, any>patternsSource-file changes
All zero-behavior-change type annotations:
src/store/apis/{addPendingTask,deleteMessage,upsertDraft}.tssrc/store/sqlite-utils/{appendOrderByClause,appendWhereCluase,createCreateTableQuery}.tssrc/components/MessageList/hooks/useMessageList.tssrc/components/Message/MessageItemView/utils/renderText.tsxβ@ts-expect-erroron the untypedreact-native-markdown-packageimport switched to@ts-ignoreso both base and test tsconfigs agree.Follow-ups (out of scope)
package/src/store/apis/upsertDraft.ts:55βqueries.concat(query)is a no-op (returns a new array that's never used). Kept out of scope since fixing it is a behavior change on production code.MessageStatus.test.tsxhadit.each('string', fn)(malformed β string iterated as characters). Converted toit.skipto preserve pre-migration runtime behavior. Un-skipping and rewriting is a follow-up.Test plan
yarn buildpassesyarn lintpassesyarn test:typecheckβ 0 errors withnoImplicitAny: trueand full strict modeyarn test:unitβ 751 passed, 14 skipped. Only the pre-existing SQLite-isolation flake inoffline-support/index.test.tsfails intermittently (baseline on develop was 5 failures; this branch is 1 β no regressions)@ts-ignore/@ts-expect-errordirectives remain in any*.test.*fileyarn test:typecheckis wired as a required check in.github/workflows/check-pr.yml