Skip to content

chore: migrate test suite from JavaScript to TypeScript#3057

Merged
oliverlaz merged 33 commits intomasterfrom
chore/test-typescript-migration
Mar 25, 2026
Merged

chore: migrate test suite from JavaScript to TypeScript#3057
oliverlaz merged 33 commits intomasterfrom
chore/test-typescript-migration

Conversation

@oliverlaz
Copy link
Member

@oliverlaz oliverlaz commented Mar 25, 2026

🎯 Goal

Migrate the entire test suite from JavaScript/JSX to TypeScript/TSX to improve IDE autocompletion, refactoring safety, and catch type errors at write time. Also replace ad-hoc as any casts with proper SDK types and typed test helpers.

πŸ›  Implementation details

Scope: 248 files changed, 3725 insertions, 2411 deletions

Infrastructure

  • Install @total-typescript/shoehorn for fromPartial<T>() in test mocks
  • Update tsconfig.test.json with proper include patterns, path aliases, and 4 strict flags (strictBindCallApply, noImplicitThis, alwaysStrict, useUnknownInCatchVariables)
  • Update eslint.config.mjs with test-specific rule relaxations; enable disallowTypeAnnotations: false for typeof import() in vi.mock
  • Add @vitest/expect module augmentation for jest-dom + vitest-axe matchers (vitest 4.x compatibility)
  • Add yarn types:tests script for test type-checking
  • Exclude test files from i18next translation extraction (i18next.config.ts)

Mock-builders (45 JS β†’ TS)

  • All generators typed with SDK types (UserResponse, Attachment, ReactionResponse, ChannelMemberResponse, LocalMessage, PollResponse, ChannelAPIResponse, etc.)
  • generateMessage() returns LocalMessage (matches what components consume) and accepts Date | string for date fields
  • generateChannel() returns ChannelAPIResponse (renamed pinnedMessages β†’ pinned_messages)
  • mockClient typed with StreamChat, uses vi.spyOn for public methods
  • MockClientOverrides interface using StreamChat['getAppSettings'] and StreamChat['queryReactions']
  • TokenManager typed with fromPartial<TokenManager>()
  • getOrCreateChannelApi typed with ChannelAPIResponse, queryChannelsApi with ChannelAPIResponse[]
  • sendMessageApi typed with MessageResponse | LocalMessage
  • Event dispatchers use fromPartial<Event>(), accept Channel | ChannelResponse and MessageResponse | LocalMessage
  • TDateTimeParser from i18n/types used for translation mock
  • ResizeObserverCallback DOM type, Partial<BlobEvent>, Partial<File>, Action from SDK
  • 9 typed context builder helpers (mockChatContext, mockChannelStateContext, mockTranslationContextValue, etc.)

Test files (134 JS/JSX β†’ TS/TSX)

  • All 1,720 type errors fixed
  • as any reduced from ~1,107 to ~435 (61% reduction)
  • Record<string, any> reduced to 0 β€” all replaced with typed interfaces
  • Provider value as any replaced with context helpers (mockChatContext(), etc.)
  • vi.spyOn(obj as any) replaced with @ts-expect-error + proper spy
  • {} as any replaced with fromPartial<Type>({})
  • importOriginal() as any replaced with importOriginal<typeof import('...')>()
  • Private property access uses bracket notation (obj['prop']) instead of (obj as any).prop
  • renderComponent params typed with per-file interfaces using SDK types
  • Variables typed as StreamChat, Channel, LocalMessage instead of any
  • { children }: any replaced with React.PropsWithChildren
  • Removed no-op amplitudesCount prop from WaveProgressBar tests
  • Deleted 19 orphan .test.jsx.snap snapshot files

Remaining as any (~435, intentionally kept)

Category Count Reason
Partial objects in component/hook tests ~149 Test data does not satisfy full type; fromPartial used where possible
TFunction mock ($TFunctionBrand) ~35 i18next brand symbol cannot be satisfied by mock functions
LocalMessage[] β†’ MessageResponse[] ~42 Date vs string field mismatch in generateChannel calls
window/navigator/globalThis ~17 Read-only DOM globals
@ts-expect-error (explicit) ~18 Mock impl signature mismatches, properly documented
Mouse event mocks ~16 Partial event objects
.next(value) on state/subjects ~12 BehaviorSubject type constraints
Misc (channel, client, renderText) ~146 Scattered partials and legacy patterns

🎨 UI Changes

No UI changes β€” test infrastructure only.

- Install @total-typescript/shoehorn for pragmatic test typing
- Update tsconfig.test.json: broaden include, add jest-dom/vitest types
- Update eslint.config.mjs: relax no-explicit-any and no-non-null-assertion
  for tests and mock-builders
- Add @vitest/expect module augmentation for jest-dom + vitest-axe matchers
  (vitest 4.x moved Assertion interface to @vitest/expect)

Phase 1 β€” Mock-builders (45 JS β†’ TS):
- Generators: typed with UserResponse, Attachment, ReactionResponse,
  ChannelMemberResponse, MessageResponse, PollResponse from stream-chat
- API mocks: typed with StreamChat, MockedApiResponse interface
- Event dispatchers: typed with StreamChat, Event types
- Browser mocks: typed class properties and method signatures
- Root files (index, utils, translator): typed with StreamChat, UserResponse

Phase 2 Tier 1 β€” Simple tests (19 files):
- 9 JSX β†’ TSX: Avatar, Tooltip, EmptyStateIndicator, SafeAnchor,
  LoadingChannel, LoadingIndicator, LoadingChannels, InfiniteScroll,
  LoadMorePaginator
- 10 JS β†’ TS: DialogsManager, BehaviorSubject, Subject, useIsMounted,
  NotificationTranslationBuilder, TranslationBuilder, audioSampling,
  Attachment/utils, AudioPlayerPool, AudioPlayerNotificationsPlugin
- Fixed type errors with pragmatic as-any casts for partial mocks,
  private property access, and missing required props
Convert all 109 remaining test files to TypeScript extensions:
- 96 .test.jsx β†’ .test.tsx
- 13 .test.js β†’ .test.ts

Add // @ts-nocheck to 108 files that have pre-existing type errors
from the JS era. These can be removed incrementally as type errors
are fixed per-file. 33 test files are already fully type-checked.

Allow @typescript-eslint/ban-ts-comment in test files to support
the incremental @ts-nocheck removal strategy.

Zero .js/.jsx test files remain in the repository.
Remove @ts-nocheck and fix type errors in test files with 1-4 errors
each. Fixes are pragmatic as-any casts for partial mock objects,
incomplete context values, and missing required props.

Files fixed: File, WithAudioPlayback, LoadMoreButton, MessageDeleted,
CommandItem, EmoticonItem, UserItem, BaseImage, ChannelListUI,
LoadingErrorIndicator, ThreadStart, Window, GalleryContext, GalleryUI,
Image, ModalGallery, SuggestPollOptionForm.

Files that needed only @ts-nocheck removal (already type-safe):
GlobalModal, SendButton, CooldownTimer, useCooldownTimer,
ConnectionStatus, MessageNotification, ReminderNotification,
useDebouncedTypingActive, Dropdown, ThreadList.

81 files with @ts-nocheck remaining.
Fix type errors in files with 3-8 errors each using pragmatic as-any
casts for partial mocks, incomplete context values, and mock function
signatures.

72 files with @ts-nocheck remaining (down from 108).
69 test files now fully type-checked.
Fix type errors in files with 9-18 errors each. Same pragmatic
approach: as-any casts for partial mocks, context values, private
property access, and mock function signatures.

41 files with @ts-nocheck remaining (down from 108).
100 test files now fully type-checked.
Fix type errors in files with 1-30 errors each. Includes fixes for
partial context values, mock function signatures, missing props,
private property access, and type-incompatible arguments.

12 files with @ts-nocheck remaining (the largest/most complex tests).
129 test files now fully type-checked.
Remove @ts-nocheck from the largest/most complex test files and fix
all type errors:

- Channel.test.tsx (112 errors)
- Card.test.tsx (123 errors)
- MessageInput.test.tsx (70 errors)
- Message.test.tsx (59 errors)
- VirtualizedMessageListComponents.test.tsx (46 errors)
- AudioRecorder.test.tsx (41 errors)
- useMarkRead.test.tsx (30 errors)
- ChannelList.test.tsx (29 errors)
- AttachmentPreviewList.test.tsx (28 errors)
- PollActions.test.tsx (28 errors)
- MessageList.test.tsx (22 errors)
- useUnreadMessagesNotificationVirtualized.test.tsx (10 errors)

All 141 test files are now fully type-checked with zero @ts-nocheck
directives remaining. Zero JS/JSX test files in the repository.
Phase 1 of as-any elimination:

- Create src/mock-builders/context.ts with typed context builder
  helpers (mockChatContext, mockChannelStateContext, etc.) using
  fromPartial from @total-typescript/shoehorn
- Fix generateMessage to accept Date | string for created_at/updated_at
  fields, eliminating ~64 date coercion casts in test files
- Replace all as-any in 19 event dispatcher files with fromPartial<Event>
  (zero as-any remaining in mock-builders/event/)
- Export context helpers from mock-builders barrel

The context helpers and generateMessage fix prepare for Phase 2-6
where test files will adopt these instead of as-any casts.
Replace ~350 as-any casts across 41 test files with typed context
helper functions from mock-builders/context.ts:

- mockChatContext() replaces `<ChatProvider value={{...} as any}>`
- mockChannelStateContext() replaces ChannelStateProvider as-any
- mockChannelActionContext() replaces ChannelActionProvider as-any
- mockTranslationContextValue() replaces TranslationProvider as-any
- mockComponentContext() replaces ComponentProvider as-any
- mockMessageContext() replaces MessageProvider as-any
- mockTypingContext() replaces TypingProvider as-any

Also removes ~64 created_at date casts since generateMessage now
accepts Date | string natively (Omit-based intersection type fix).

Context helpers use Record<string, unknown> for overrides param to
accept any partial shape while returning properly typed context values
via fromPartial.

~753 as-any remaining (down from ~1,107).
- Replace 37 `new Poll({ client: {} as any })` with
  `fromPartial<StreamChat>({})` across 5 Poll test files
- Replace mock return value as-any with `fromPartial()` or `undefined\!`
  in Search, Thread, Notification, MediaRecorder, MessageComposer tests
- Revert importOriginal<typeof import(...)>() back to as-any due to
  consistent-type-imports ESLint rule blocking dynamic import() types
- Use Record<string, unknown> for context helper overrides to handle
  complex function types (TFunction, ComponentType) that fromPartial
  can't deeply partial

~661 as-any remaining (down from ~1,107 original).
Replace as-any casts with proper types in Search, i18n, and Message
test files:

- Use vi.mocked() instead of (hook as any) for mock return values
- Use fromPartial<SearchSource>() for search source mocks
- Use fromPartial<i18n>() for i18next mocks
- Use fromPartial<Mute>() for mute object mocks
- Type function params (key: string) instead of (key: any)
- Use @ts-expect-error for intentional type mismatch tests
- Create event/utils.ts with ChannelOrResponse type to accept both
  Channel instances and ChannelResponse objects in event dispatchers

Event dispatchers now accept Channel | ChannelResponse, eliminating
channel-as-any casts at call sites.
Replace ~88 as-any/: any with proper types across 9 files:

- Type renderComponent/setup helper params with Record<string, any>
- Type wrapper components with { children?: React.ReactNode }
- Use fromPartial<React.BaseSyntheticEvent>() for mock events
- Use StreamChat/Channel types for client/channel variables
- Use importOriginal<Record<string, unknown>>() where ESLint allows
- Type file drop helpers, message helpers, and container params
- Add VirtuosoContext type for virtualized list context objects
- Use TranslationContextValue['t'] for translation function types
…r tests

Replace : any on function params with proper types in 4 test files:

- Channel.test.tsx: type renderComponent, initClient, runTest helpers,
  ActiveChannelSetter props, getMessageIds array type
- ChannelListItem.test.tsx: type PreviewUIComponent props, helper
  functions, client/channel variables with StreamChat/Channel
- ChannelList.test.tsx: type renderUI client param as StreamChat
- ChannelHeader.test.tsx: type renderComponent helpers with
  Channel, StreamChat, ChannelHeaderProps

Also remove unnecessary as-any from dispatchUserUpdatedEvent calls
where the spread objects already satisfy Partial<UserResponse>.
Replace ~39 as-any/: any with proper types:

- MediaRecorderController: type expectRegistersError params, use
  fromPartial<File>, fromPartial<BlobEvent>, LocalVoiceRecordingAttachment
- AudioRecorder: use fromPartial<DOMRect>, ChannelActionContextValue
- AudioPlayer: type overrides as Partial<AudioPlayerOptions>,
  use fromPartial<AudioPlayerPool>, fromPartial<DOMRect>
- AmplitudeRecorder: use fromPartial<MediaStream>,
  AmplitudeRecorderConfig type
- useMessageDeliveryStatus: type renderComponent with Channel/StreamChat
- Thread/ThreadHeader: type function params with string types
…cess

Replace ~107 private/protected property access patterns from
(obj as any).prop to obj['prop'] which bypasses TypeScript's
private/protected checks without requiring as-any casts.

Bracket notation works for TS private/protected keywords because
they're compile-time only. Reverted to (obj as any) for cases where
bracket notation doesn't help:
- window/navigator property assignments (read-only or missing)
- Type-incompatible assignments (string to TFunction, etc.)
- globalThis/module property manipulation

~506 as-any remaining (down from ~1,107 original, 54% reduction).
…ponse

Change generateMessage() return type from MessageResponse to LocalMessage,
matching what components actually consume (all context values use LocalMessage).

This eliminates as-any casts where generateMessage() results are passed
to MessageContext, ChannelStateContext, and component props that expect
LocalMessage.

Also widen event dispatcher message params to accept
MessageResponse | LocalMessage, and threadRepliesApi to accept both.

Some generateChannel({ messages: [...] }) calls now need as-any since
generateChannel's DeepPartial<ChannelAPIResponse> expects MessageResponse[].
…script

- Delete 19 orphan .test.jsx.snap files left from JS→TS rename
- Remove ban-ts-comment eslint relaxation (no more @ts-nocheck)
- Add "types:tests" script for test type-checking via
  tsc --project tsconfig.test.json --noEmit
Enable three strict-mode flags in tsconfig.test.json that pass
cleanly with zero errors:

- strictBindCallApply: type-check bind/call/apply arguments
- alwaysStrict: emit "use strict" in all files
- useUnknownInCatchVariables: type catch variables as unknown

Not yet enabled (require further fixes):
- strictNullChecks: ~420 errors (possibly-undefined access)
- strictFunctionTypes: ~38 errors (component prop type mismatches)
- noImplicitThis: ~4 errors (this in regular functions)
- noImplicitAny: ~1,200 errors (implicit any in untyped code)
- Enable noImplicitThis in tsconfig.test.json β€” fix 4 implicit this
  errors in MessageList scrollTo/scrollBy mock functions by adding
  explicit `this: HTMLElement` annotation
- Narrow Record<string, any> to Record<string, unknown> in
  mock-builders where the values aren't destructured (getTestClient,
  context helpers, generators, dataavailable event)
- Keep Record<string, any> in test helper functions where values are
  destructured (renderComponent params etc.) since unknown values
  can't be destructured without narrowing
The i18next-cli extract config scanned ./src/**/*.{tsx,ts} which now
includes test files after the JS→TS migration. This caused t('abc')
from Streami18n.test.ts to be extracted into all language JSON files
with empty values, failing validate-translations.

Fix: add \!./src/**/__tests__/** and \!./src/mock-builders/** exclusions
to the i18next extraction input, and remove the erroneously extracted
"abc" key from all 12 language files.
The ! negation patterns in input array don't work with i18next-cli.
Use the ignore config option instead to exclude test files and
mock-builders from translation key extraction.

Also remove the abc key that was re-extracted into JSON files.
@codecov
Copy link

codecov bot commented Mar 25, 2026

Codecov Report

βœ… All modified and coverable lines are covered by tests.
βœ… Project coverage is 78.92%. Comparing base (30ddab0) to head (a5dbc92).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #3057   +/-   ##
=======================================
  Coverage   78.92%   78.92%           
=======================================
  Files         426      426           
  Lines       12064    12064           
  Branches     3853     3853           
=======================================
  Hits         9522     9522           
  Misses       2542     2542           

β˜” View full report in Codecov by Sentry.
πŸ“’ Have feedback on the report? Share it here.

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

renderComponent({ attachments: [generateStaticLocationResponse({})] });
waitFor(() => {
expect(screen.getByTestId(testId)).toBeInTheDocument();
expect(screen.getByTestId('geolocation-attachment')).toBeInTheDocument();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a bad experience with hardcoding the same value everywhere. Variables are a bit easier to maintain - as there was before the testId var.

const renderComponent = (props) =>
render(
<ChannelStateProvider value={{}}>
<ChannelStateProvider value={{} as any}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe another iteration to reduce the count of these using shoehorn?

@github-actions
Copy link

github-actions bot commented Mar 25, 2026

Size Change: 0 B

Total Size: 769 kB

ℹ️ View Unchanged
Filename Size
./dist/cjs/audioProcessing.js 1.32 kB
./dist/cjs/emojis.js 2.96 kB
./dist/cjs/index.js 236 kB
./dist/cjs/mp3-encoder.js 1.27 kB
./dist/cjs/WithAudioPlayback.js 89.1 kB
./dist/css/index.css 46.9 kB
./dist/css/v2/emoji-mart.css 1.84 kB
./dist/css/v2/emoji-replacement.css 300 B
./dist/css/v2/index.css 39.4 kB
./dist/css/v2/index.layout.css 22.8 kB
./dist/es/audioProcessing.mjs 1.31 kB
./dist/es/emojis.mjs 2.48 kB
./dist/es/index.mjs 234 kB
./dist/es/mp3-encoder.mjs 756 B
./dist/es/WithAudioPlayback.mjs 88.8 kB

compressed-size-action

Rename useLastOwnMessage.test.js and CommandChip.test.jsx that were
added on master after the migration branch was created.
* api - /channels/{type}/{id}/message
*/
export const sendMessageApi = (
message: MessageResponse | Record<string, unknown> = {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it be dangerous to add Record<string, unknown>?

* @param {*} channels Array of channel objects.
*/
export const queryChannelsApi = (channels = []) => {
export const queryChannelsApi = (channels: unknown[] = []) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we use correct type for channels instead of unknown?

- Extract GEOLOCATION_TEST_ID constant instead of hardcoding testId
  string (Martin's comment on Attachment.test.tsx:267)
- Replace {}\as any on ChannelStateProvider with mockChannelStateContext()
  (Anton's comment on Attachment.test.tsx:59)
- Type sendMessageApi param as MessageResponse | LocalMessage instead
  of Record<string, unknown> (Martin's comment on sendMessage.ts:11)
- Type queryChannelsApi param as DeepPartial<ChannelAPIResponse>[]
  instead of unknown[] (Martin's comment on queryChannels.ts:8)
…thods

- Type connectUser and mockClient params as StreamChat instead of any
- Use bracket notation for internal properties (_user, connectionId,
  userToken) that are on the StreamChat type but not public API
- Replace direct method assignments (client.muteUser = mock) with
  vi.spyOn(client, 'muteUser').mockImplementation() in Message.test.tsx
  (12 occurrences) β€” proper spy pattern for public methods
- Type ThreadStart client variable as StreamChat
- Type mockClient mocks param with MockClientOverrides interface using
  StreamChat['getAppSettings'] and StreamChat['queryReactions']
- Type generateChannel return as ChannelAPIResponse, rename
  pinnedMessages to pinned_messages to match the SDK type
- Type getOrCreateChannelApi param as ChannelAPIResponse
- Type queryChannelsApi param as ChannelAPIResponse[]
- Type generateAttachmentAction with Action type from stream-chat
- Type generateFile/generateImageFile with Partial<File>
- Type channelData as Partial<ChannelResponse> in createClientWithChannel
- Type initChannelFromData params with GenerateChannelOptions
- Use ChannelConfigWithInfo for getConfig mock return
- Use GetDraftResponse for getDraft mock return
- Fix ReadResponse.last_read to use string (toISOString) instead of Date
- Use TDateTimeParser from i18n/types in mockTranslationContextValue
  instead of as any cast
- Use standard ResizeObserverCallback DOM type in ResizeObserverMock
  instead of custom (...args: any[]) => void
- Use Partial<BlobEvent> for dataavailable event overrides
- Use unknown instead of any for EventEmitterMock data params
- Remove as any from reminder.ts channel (ChannelResponse is compatible)
- Add comment explaining why context helpers use Record<string, unknown>
  (TFunction.$TFunctionBrand prevents Partial<ContextValue>)
unread_messages: 0,
user,
},
} as any,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this in purpose?

client['connectUser'] = connectUser.bind(null, client) as any;
});
vi.spyOn(client, 'connectUser').mockImplementation(
(_user) => connectUser(client, _user) as any,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this on purpose?

generateMessage({ user: users[i % memberCount] as any }),
) as any),
} as DeepPartial<ChannelAPIResponse>);
.map((_v, i) => generateMessage({ user: users[i % memberCount] })) as any),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this as any on purpose?

- Type chatClient/channel variables as StreamChat/Channel (Card, ChannelListItem)
- Type message/previousMessage/nextMessage as LocalMessage (utils.test)
- Type noGroupByUser as boolean (utils.test)
- Replace { children }: any with React.PropsWithChildren (4 files)
- Fix muteStatus mock to return proper { createdAt, expiresAt, muted }
- Fix message_text_updated_at to use toISOString() (string, not Date)
- Use LocalMessage cast for custom message types (customType, date)
Enable disallowTypeAnnotations: false for consistent-type-imports in
test files, allowing typeof import() syntax in generic positions.

Replace all importOriginal() as any and importOriginal<Record<string, any>>()
with properly typed importOriginal<typeof import('module/path')>()
across 11 test files.
Three categories fixed across 37 test files:

1. {} as any β†’ fromPartial<Type>({}) (11 files)
   Use fromPartial for empty mock objects (ChannelActionContextValue,
   LocalMessage, AudioPlayer, etc.) and Record<string, any> for
   destructured default params.

2. vi.spyOn().mockImplementation(fn as any) β†’ @ts-expect-error (5 files)
   Replace as-any on mock implementations with @ts-expect-error
   comments that explicitly document the type mismatch between mock
   and real function signatures.

3. Provider value as any β†’ context helpers (21 files)
   Replace TranslationProvider/ChatProvider/ComponentProvider/
   MessageProvider/ChannelStateProvider value as-any casts with
   mockTranslationContextValue(), mockChatContext(),
   mockComponentContext(), mockMessageContext(),
   mockChannelStateContext() from mock-builders.

~399 as-any remaining (down from ~1,107 original, 64% reduction).
Replace all 46 Record<string, any> annotations in test files with
proper interfaces using SDK types:

- renderComponent params typed with Partial<ChannelProps>,
  Partial<MessageListProps>, Channel, StreamChat, etc.
- channelData typed as Partial<ChannelResponse> or GenerateChannelOptions
- channelConfig typed as Partial<ChannelConfigWithInfo>
- componentOverrides typed as Partial<ComponentContextValue>
- channelStateOpts/channelActionOverrides typed as Partial<ContextValue>
- PreviewUIComponent props typed as ChannelListItemUIProps
- mockT options typed as Record<string, unknown>
- channelListProps typed as Partial<ChannelListProps>

Zero Record<string, any> remaining in test files.
amplitudesCount is not a prop on WaveProgressBar β€” the component
calculates amplitude count internally from container width. The prop
was silently ignored by React. Pass progress directly instead of
through an as-any spread.
@MartinCupela MartinCupela self-requested a review March 25, 2026 15:05
@oliverlaz oliverlaz merged commit 9877da5 into master Mar 25, 2026
10 checks passed
@oliverlaz oliverlaz deleted the chore/test-typescript-migration branch March 25, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants