Skip to content

Commit aed0051

Browse files
authored
refactor(tests): migrate test suite and mock-builders to TypeScript (#3562)
## Summary Migrates the remaining JS test infrastructure to TypeScript, then type-tightens the whole test suite with full `noImplicitAny: true` strictness and wires `yarn test:typecheck` into CI as a required check. ### Migration (baseline) - 44 mock-builders (`package/src/mock-builders/`): API response builders, event dispatchers, data generators, and the core `mock.ts` client helper - 36 `.test.js` files → `.test.ts` / `.test.tsx` across components and utils - 2 offline-support helpers → `.tsx` - `test-utils/BetterSqlite.js` → `.ts` - `jest-setup.js` → `.tsx` (contains JSX for the `BottomSheetModal` mock) - New `package/tsconfig.test.json` so tests and mock-builders are type-checked (base `tsconfig.json` still excludes them from the published build) - New `yarn test:typecheck` script, wired into `.github/workflows/check-pr.yml` ### Type-tightening - **fix(store,messagelist):** type-annotate empty arrays in 8 source files where `const queries = []` was inferring `never[]` under the broader include scope. Real latent issues; no behavior change. - **refactor(mock-builders):** tighten types in generators and event dispatchers using SDK types from `stream-chat`; drop fields not on `Attachment`; add missing `reminders` on default channel config; narrow `client.channel(...)` args. - **refactor(mock-builders): `generateMessage` now returns `LocalMessage`.** Previously typed as `MessageResponse` but at runtime produced `Date` objects for `created_at`/`updated_at`/`pinned_at` — the `LocalMessage` shape. Making the type honest eliminated 33 `as unknown as LocalMessage` casts and 57 `toLocalMessage(…)` wrapper calls across the suite. API mocks and event dispatchers that legitimately need the wire shape accept `MessageResponse | LocalMessage` at their boundary. - **refactor(tests):** annotate all test bodies. Uses the established `{...} as unknown as XContextValue` pattern for partial context mocks. Replaces every bogus `as unknown as FileUpload` (leftover silent bug — `FileUpload` was never imported) with the correct SDK type (`LocalAudioAttachment` / `LocalVoiceRecordingAttachment`). Uses `LocalAttachment` (not `Partial<Attachment>`) for attachment mocks that set `localMetadata`. Uses `ComponentProps<typeof X>` for `renderComponent({ props })` typings. - **refactor(tests): flip `noImplicitAny: true`** and annotate the ~630 resulting errors across ~20 test files. `let chatClient: StreamChat`, typed destructured params, typed `jest.fn()` callbacks, `ComponentProps<typeof X>` for helper prop shapes. Zero `any` / `as any` added. ### Dead-prop cleanup Removing spread+cast and `@ts-ignore` escape 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 on `MessageContextValue`; `MessageAuthor` doesn't pick them). - `MessageReplies`: `groupStyles`, `MessageRepliesAvatars`, `openThread` (typo — real prop is `onOpenThread`; silently dropped). - `Message`: `reactionsEnabled`, `MessageFooter` override. - `ScrollToBottomButton`: `t` (supplied via `TranslationProvider`). - `ChannelPreviewView`: `client`, `latestMessagePreview`, `watchers`, `latestMessage`, `latestMessageLength`. - `MessageList`: `channelUnreadState` (internal state, never a prop). - `Channel`: `client` (comes from `ChatContext`, caught on `Thread.test.tsx`). - `Giphy` test helper: widened `Record<string, unknown>` → `ComponentProps<typeof Giphy>`. Also: zero `@ts-ignore` / `@ts-expect-error` directives remain in any `*.test.*` file. ### Dependency changes - `@types/jest`: `^29.5.14` → `^30.0.0` (matches installed `jest@30`) - `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>` patterns ### Source-file changes All zero-behavior-change type annotations: - `src/store/apis/{addPendingTask,deleteMessage,upsertDraft}.ts` - `src/store/sqlite-utils/{appendOrderByClause,appendWhereCluase,createCreateTableQuery}.ts` - `src/components/MessageList/hooks/useMessageList.ts` - `src/components/Message/MessageItemView/utils/renderText.tsx` — `@ts-expect-error` on the untyped `react-native-markdown-package` import switched to `@ts-ignore` so 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.tsx` had `it.each('string', fn)` (malformed — string iterated as characters). Converted to `it.skip` to preserve pre-migration runtime behavior. Un-skipping and rewriting is a follow-up. ## Test plan - [x] `yarn build` passes - [x] `yarn lint` passes - [x] `yarn test:typecheck` — **0 errors with `noImplicitAny: true` and full strict mode** - [x] `yarn test:unit` — 751 passed, 14 skipped. Only the pre-existing SQLite-isolation flake in `offline-support/index.test.ts` fails intermittently (baseline on develop was 5 failures; this branch is 1 — no regressions) - [x] Zero `@ts-ignore` / `@ts-expect-error` directives remain in any `*.test.*` file - [x] `yarn test:typecheck` is wired as a required check in `.github/workflows/check-pr.yml` - [x] CI green on push
1 parent 563dcdb commit aed0051

182 files changed

Lines changed: 3346 additions & 2058 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/check-pr.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,7 @@ jobs:
2929
uses: ./.github/actions/install-and-build-sdk
3030
- name: Lint
3131
run: yarn lerna-workspaces run lint
32+
- name: Typecheck tests
33+
run: cd package && yarn test:typecheck
3234
- name: Test
3335
run: yarn test:coverage
Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* global require */
2-
import rn, { FlatList, View } from 'react-native';
2+
import type { ReactNode } from 'react';
3+
import { FlatList, View } from 'react-native';
34

45
import mockRNCNetInfo from '@react-native-community/netinfo/jest/netinfo-mock.js';
56
import mockSafeAreaContext from 'react-native-safe-area-context/jest/mock';
@@ -36,12 +37,18 @@ registerNativeHandlers({
3637

3738
jest.mock('react-native-reanimated', () => {
3839
const RNReanimatedmock = require('react-native-reanimated/mock');
39-
return { ...RNReanimatedmock, runOnUI: (fn) => fn };
40+
return { ...RNReanimatedmock, runOnUI: (fn: () => unknown) => fn };
4041
});
4142

4243
jest.mock('@react-native-community/netinfo', () => mockRNCNetInfo);
4344

44-
const BottomSheetMock = ({ handleComponent, children }) => (
45+
const BottomSheetMock = ({
46+
handleComponent,
47+
children,
48+
}: {
49+
handleComponent: () => ReactNode;
50+
children: ReactNode;
51+
}) => (
4552
<View>
4653
{handleComponent()}
4754
{children}

package/jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module.exports = {
99
setupFiles: ['./node_modules/react-native-gesture-handler/jestSetup.js'],
1010
setupFilesAfterEnv: [
1111
'@testing-library/jest-native/extend-expect',
12-
require.resolve('./jest-setup.js'),
12+
require.resolve('./jest-setup.tsx'),
1313
],
1414
testEnvironment: 'node',
1515
testPathIgnorePatterns: ['/node_modules/', '/examples/', '__snapshots__', '/lib/'],

package/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
"prettier": "prettier --list-different '**/*.{js,ts,tsx,md,json}' eslint.config.mjs ../.prettierrc babel.config.js",
3838
"prettier-fix": "prettier --write '**/*.{js,ts,tsx,md,json}' eslint.config.mjs ../.prettierrc babel.config.js",
3939
"test:coverage": "yarn test:unit --coverage",
40+
"test:typecheck": "tsc --noEmit -p tsconfig.test.json",
4041
"test:unit": "TZ=UTC jest",
4142
"validate-translations": "node bin/validate-translations.js",
4243
"get-version": "echo $npm_package_version",
@@ -127,9 +128,10 @@
127128
"@shopify/flash-list": "^2.1.0",
128129
"@testing-library/jest-native": "^5.4.3",
129130
"@testing-library/react-native": "13.2.0",
131+
"@total-typescript/shoehorn": "^0.1.2",
130132
"@types/better-sqlite3": "^7.6.13",
131133
"@types/eslint": "9.6.1",
132-
"@types/jest": "^29.5.14",
134+
"@types/jest": "^30.0.0",
133135
"@types/linkify-it": "5.0.0",
134136
"@types/lodash": "4.17.16",
135137
"@types/mime-types": "2.1.4",
@@ -154,7 +156,7 @@
154156
"eslint-plugin-react-hooks": "^5.2.0",
155157
"eslint-plugin-react-native": "^5.0.0",
156158
"i18next-cli": "^1.31.0",
157-
"jest": "^30.0.0",
159+
"jest": "^30.3.0",
158160
"moment-timezone": "^0.6.0",
159161
"prettier": "^3.5.3",
160162
"react": "19.1.0",

0 commit comments

Comments
 (0)