diff --git a/.github/workflows/scheduled_test.yml b/.github/workflows/scheduled_test.yml index 1dd239d76..628276e3d 100644 --- a/.github/workflows/scheduled_test.yml +++ b/.github/workflows/scheduled_test.yml @@ -14,6 +14,9 @@ jobs: - uses: ./.github/actions/setup-node + - name: Build project + run: yarn build + - name: Run test env: API_KEY: ${{ secrets.TS_TEST_API_KEY }} diff --git a/package.json b/package.json index e4398aea9..1ee33627b 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,9 @@ "eslint-fix": "yarn run eslint --fix", "test": "yarn test-unit", "testwatch": "NODE_ENV=test nodemon ./node_modules/.bin/mocha --timeout 20000 --require test-entry.js test/test.js", - "test-types": "node test/typescript/index.js && tsc --esModuleInterop true --noEmit true --strictNullChecks true --noImplicitAny true --strict true test/typescript/*.ts", + "test-types": "yarn run-test-types && yarn run-types-gen", + "run-test-types": "node test/typescript/index.js", + "run-types-gen": "tsc --esModuleInterop true --noEmit true --strictNullChecks true --noImplicitAny true --strict true test/typescript/*.ts", "test-unit": "vitest", "test-coverage": "vitest run --coverage", "fix-staged": "lint-staged --config .lintstagedrc.fix.json --concurrent 1", diff --git a/src/client.ts b/src/client.ts index c2cbe8703..fac0de161 100644 --- a/src/client.ts +++ b/src/client.ts @@ -220,6 +220,7 @@ import type { UpdatePollOptionAPIResponse, UpdateReminderOptions, UpdateSegmentData, + UpdateUsersAPIResponse, UpsertPushPreferencesResponse, UserCustomEvent, UserFilters, @@ -2396,11 +2397,9 @@ export class StreamChat { userMap[userObject.id] = userObject; } - return await this.post< - APIResponse & { - users: { [key: string]: UserResponse }; - } - >(this.baseURL + '/users', { users: userMap }); + return await this.post(this.baseURL + '/users', { + users: userMap, + }); } /** @@ -2448,11 +2447,7 @@ export class StreamChat { } } - return await this.patch< - APIResponse & { - users: { [key: string]: UserResponse }; - } - >(this.baseURL + '/users', { users }); + return await this.patch(this.baseURL + '/users', { users }); } async deleteUser( diff --git a/src/types.ts b/src/types.ts index ec597d8d3..6e2495aaa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -71,6 +71,7 @@ export type Unpacked = T extends (infer U)[] export type APIResponse = { duration: string; + blocklist?: BlockListResponse; }; export type TranslateResponse = { @@ -80,6 +81,8 @@ export type TranslateResponse = { export type AppSettingsAPIResponse = APIResponse & { app?: { + id?: string | number; + allow_multi_user_devices?: boolean; // TODO // eslint-disable-next-line @typescript-eslint/no-explicit-any call_types: any; @@ -108,6 +111,9 @@ export type AppSettingsAPIResponse = APIResponse & { read_events?: boolean; replies?: boolean; search?: boolean; + shared_locations?: boolean; + skip_last_msg_update_for_system_msgs?: boolean; + count_messages?: boolean; typing_events?: boolean; updated_at?: string; uploads?: boolean; @@ -140,12 +146,28 @@ export type AppSettingsAPIResponse = APIResponse & { type: string; }>; grants?: Record; + guest_user_creation_disabled?: boolean; image_moderation_enabled?: boolean; + image_moderation_labels?: string[]; image_upload_config?: FileUploadConfig; + allowed_flag_reasons?: string[]; + max_aggregated_activities_length?: number; + moderation_bulk_submit_action_enabled?: boolean; + moderation_dashboard_preferences?: Record | null; + moderation_enabled?: boolean; + moderation_llm_configurability_enabled?: boolean; + moderation_multitenant_blocklist_enabled?: boolean; + moderation_webhook_url?: string; multi_tenant_enabled?: boolean; name?: string; organization?: string; permission_version?: string; + /** + * The placement of the app in the form of `${region}.${shard}`. + * Examples: "us-east.c1", "dublin.c3", "singapore.c2" + * Note: The backend may add/remove regions or shards occasionally. + */ + placement?: string; policies?: Record; poll_enabled?: boolean; push_notifications?: { @@ -167,6 +189,8 @@ export type AppSettingsAPIResponse = APIResponse & { sqs_url?: string; suspended?: boolean; suspended_explanation?: string; + use_hook_v2?: boolean; + user_response_time_enabled?: boolean; user_search_disallowed_roles?: string[] | null; video_provider?: string; webhook_events?: Array; @@ -788,6 +812,7 @@ export type OwnUserBase = { privacy_settings?: PrivacySettings; push_preferences?: PushPreference; roles?: string[]; + total_unread_count_by_team?: Record | null; }; export type OwnUserResponse = UserResponse & OwnUserBase; @@ -884,10 +909,12 @@ export type UpdateMessageAPIResponse = APIResponse & { export type UsersAPIResponse = APIResponse & { users: Array; + membership_deletion_task_id?: string; }; export type UpdateUsersAPIResponse = APIResponse & { users: { [key: string]: UserResponse }; + membership_deletion_task_id?: string; }; export type UserResponse = CustomUserData & { @@ -910,7 +937,7 @@ export type UserResponse = CustomUserData & { role?: string; shadow_banned?: boolean; teams?: string[]; - teams_role?: TeamsRole; + teams_role?: TeamsRole | null; updated_at?: string; username?: string; avg_response_time?: number; @@ -1034,11 +1061,13 @@ export type CreateChannelOptions = { reminders?: boolean; replies?: boolean; search?: boolean; + shared_locations?: boolean; skip_last_msg_update_for_system_msgs?: boolean; typing_events?: boolean; uploads?: boolean; url_enrichment?: boolean; user_message_reminders?: boolean; + count_messages?: boolean; }; export type CreateCommandOptions = { @@ -1066,9 +1095,8 @@ export type DeactivateUsersOptions = { export type NewMemberPayload = CustomMemberData & Pick; -export type Thresholds = Record< - 'explicit' | 'spam' | 'toxic', - Partial<{ block: number; flag: number }> +export type Thresholds = Partial< + Record<'explicit' | 'spam' | 'toxic', Partial<{ block: number; flag: number }>> >; export type BlockListOptions = { @@ -1172,6 +1200,7 @@ export type UpdateChannelTypeResponse = { reminders: boolean; replies: boolean; search: boolean; + shared_locations: boolean; skip_last_msg_update_for_system_msgs: boolean; typing_events: boolean; updated_at: string; @@ -1182,9 +1211,11 @@ export type UpdateChannelTypeResponse = { blocklist?: string; blocklist_behavior?: BlocklistBehavior; blocklists?: BlockListOptions[]; + message_retention?: string; partition_size?: number; partition_ttl?: string; count_messages?: boolean; + user_message_reminders?: boolean; }; export type GetChannelTypeResponse = { @@ -1210,6 +1241,7 @@ export type GetChannelTypeResponse = { reminders: boolean; replies: boolean; search: boolean; + shared_locations: boolean; skip_last_msg_update_for_system_msgs: boolean; typing_events: boolean; updated_at: string; @@ -1220,9 +1252,11 @@ export type GetChannelTypeResponse = { blocklist?: string; blocklist_behavior?: BlocklistBehavior; blocklists?: BlockListOptions[]; + message_retention?: string; partition_size?: number; partition_ttl?: string; count_messages?: boolean; + user_message_reminders?: boolean; }; export type UpdateChannelOptions = Partial<{ @@ -2049,7 +2083,7 @@ export type UserFilters = QueryFilters< } >; -export type InviteStatus = 'pending' | 'accepted' | 'rejected'; +export type InviteStatus = 'pending' | 'accepted' | 'rejected' | 'member'; // https://getstream.io/chat/docs/react/channel_member/#update-channel-members export type MemberFilters = QueryFilters< @@ -2373,6 +2407,8 @@ export type BlockList = { team?: string; type?: string; validate?: boolean; + is_leet_check_enabled?: boolean; + is_plural_check_enabled?: boolean; }; export type ChannelConfig = ChannelConfigFields & diff --git a/src/utils.ts b/src/utils.ts index ef255ccb7..433be7128 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -104,6 +104,7 @@ export function isOwnUserBaseProperty(property: string) { privacy_settings: true, roles: true, push_preferences: true, + total_unread_count_by_team: true, }; return ownUserBaseProperties[property as keyof OwnUserBase]; diff --git a/test/typescript/index.js b/test/typescript/index.js index fa6d4cfbd..9ece4aa06 100644 --- a/test/typescript/index.js +++ b/test/typescript/index.js @@ -10,94 +10,94 @@ const executables = [ { f: rg.acceptInvite, imports: ['Channel', 'Unpacked'], - type: "Unpacked['acceptInvite']>>", + type: "Unpacked>", }, { f: rg.addDevice, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['addDevice']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.addMembers, imports: ['Channel', 'Unpacked'], - type: "Unpacked['addMembers']>>", + type: "Unpacked>", }, { f: rg.addFilterTags, imports: ['Channel', 'Unpacked'], - type: "Unpacked['addFilterTags']>>", + type: "Unpacked>", }, { f: rg.addModerators, imports: ['Channel', 'Unpacked'], - type: "Unpacked['addModerators']>>", + type: "Unpacked>", }, { f: rg.banUsers, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['banUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.channelSearch, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['search']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, // TODO: Fix the issue "WS failed with code 5 and reason - anon auth token must have user_id claim equal to '!anon'" // { // f: rg.connect, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - // type: "Unpacked['connect']>>", + // imports: ['StreamChat', 'Unpacked'], + // type: "Unpacked>", // }, // { // f: rg.connectAnonymousUser, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - // type: "Unpacked['connectAnonymousUser']>>", + // imports: ['StreamChat', 'Unpacked'], + // type: "Unpacked>", // }, { f: rg.connectUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['connectUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.create, imports: ['Channel', 'Unpacked'], - type: "Unpacked['create']>>", + type: "Unpacked>", }, { f: rg.createBlockList, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['createBlockList']>>", + type: "Unpacked>", }, // createChannelType has a limit. So only run this when needed. // { // f: rg.createChannelType, // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked['createChannelType']>>", + // type: "Unpacked>", // }, { f: rg.createCommand, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['createCommand']>>", + type: "Unpacked>", }, { f: rg.createPermission, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['createPermission']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.deactivateUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['deactivateUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.deleteBlockList, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['deleteBlockList']>>", + type: "Unpacked>", }, { f: rg.deleteChannel, imports: ['Channel', 'Unpacked'], - type: "Unpacked['delete']>>", + type: "Unpacked>", }, // TODO: Fix the error which results from deleteChannelType api call:Add commentMore actions // `deleteChannelType failed with error: { Error: StreamChat error code 16: DeleteChannelType failed with error: "bc0b09df-2cfd-4e80-93e7-1f0091e6a435 is not a defined channel type"` @@ -105,412 +105,412 @@ const executables = [ // { // f: rg.deleteChannelType, // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked['deleteChannelType']>>", + // type: "Unpacked>", { f: rg.deleteCommand, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['deleteCommand']>>", + type: "Unpacked>", }, { f: rg.deleteFile, imports: ['Channel', 'Unpacked'], - type: "Unpacked['deleteFile']>>", + type: "Unpacked>", }, { f: rg.deleteImage, imports: ['Channel', 'Unpacked'], - type: "Unpacked['deleteImage']>>", + type: "Unpacked>", }, { f: rg.deleteMessage, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['deleteMessage']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.deletePermission, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['deletePermission']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.deleteReaction, imports: ['Channel', 'Unpacked'], - type: "Unpacked['deleteReaction']>>", + type: "Unpacked>", }, { f: rg.deleteUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['deleteUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.demoteModerators, imports: ['Channel', 'Unpacked'], - type: "Unpacked['demoteModerators']>>", + type: "Unpacked>", }, // { // f: rg.disconnect, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - // type: "Unpacked['disconnect']>>", + // imports: ['StreamChat', 'Unpacked'], + // type: "Unpacked>", // }, { f: rg.exportUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['exportUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.flagMessage, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['flagMessage']>>", + type: "Unpacked>", }, { f: rg.flagUser, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['flagUser']>>", + type: "Unpacked>", }, { f: rg.getAppSettings, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['getAppSettings']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.getBlockList, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['getBlockList']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.getChannelType, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['getChannelType']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.getCommand, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getCommand']>>", + type: "Unpacked>", }, { f: rg.getConfig, imports: ['Channel', 'Unpacked'], - type: "Unpacked['getConfig']>>", + type: "Unpacked>", }, { f: rg.getDevices, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['getDevices']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.getMessage, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['getMessage']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.getMessagesById, imports: ['Channel', 'Unpacked'], - type: "Unpacked['getMessagesById']>>", + type: "Unpacked>", }, { f: rg.getMessageWithReply, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getMessage']>>", + type: "Unpacked>", }, { f: rg.getPermission, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['getPermission']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.getReactions, imports: ['Channel', 'Unpacked'], - type: "Unpacked['getReactions']>>", + type: "Unpacked>", }, { f: rg.getReplies, imports: ['Channel', 'Unpacked'], - type: "Unpacked['getReplies']>>", + type: "Unpacked>", }, { f: rg.hide, imports: ['Channel', 'Unpacked'], - type: "Unpacked['hide']>>", + type: "Unpacked>", }, { f: rg.inviteMembers, imports: ['Channel', 'Unpacked'], - type: "Unpacked['inviteMembers']>>", + type: "Unpacked>", }, { f: rg.keystroke, imports: ['Channel', 'Unpacked'], - type: "Unpacked['keystroke']>>", + type: "Unpacked>", }, { f: rg.lastMessage, imports: ['Channel', 'FormatMessageResponse', 'Unpacked'], - type: "Omit, 'created_at' | 'updated_at'> & { created_at?: string; updated_at?: string } | undefined", + type: "Omit & { created_at?: string; updated_at?: string } | undefined", }, { f: rg.listBlockLists, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['listBlockLists']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.listChannelTypes, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['listChannelTypes']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.listCommands, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['listCommands']>>", + type: "Unpacked>", }, { f: rg.listPermissions, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['listPermissions']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.markAllRead, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['markAllRead']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.markRead, imports: ['Channel', 'Unpacked'], - type: "Unpacked['markRead']>>", + type: "Unpacked>", }, { f: rg.mute, imports: ['Channel', 'Unpacked'], - type: "Unpacked['mute']>>", + type: "Unpacked>", }, { f: rg.muteStatus, imports: ['Channel', 'Unpacked'], - type: "Unpacked['muteStatus']>>", + type: "Unpacked>", }, { f: rg.muteUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['muteUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.partialUpdateUser, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['partialUpdateUser']>>", + type: "Unpacked>", }, { f: rg.partialUpdateUsers, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['partialUpdateUsers']>>", + type: "Unpacked>", }, { f: rg.query, imports: ['Channel', 'Unpacked'], - type: "Unpacked['query']>>", + type: "Unpacked>", }, // TODO: Add this back in when queryBannedUsers is deployed to all shards for testing // { // f: rg.queryBannedUsers, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + // imports: ['StreamChat', 'Unpacked'], // type: - // "Unpacked['queryBannedUsers']>>", + // "Unpacked>", // }, { f: rg.queryMembers, imports: ['Channel', 'Unpacked'], - type: "Unpacked['queryMembers']>>", + type: "Unpacked>", }, { f: rg.queryUsers, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['queryUsers']>>", + type: "Unpacked>", }, { f: rg.reactivateUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['reactivateUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.rejectInvite, imports: ['Channel', 'Unpacked'], - type: "Unpacked['rejectInvite']>>", + type: "Unpacked>", }, { f: rg.removeMembers, imports: ['Channel', 'Unpacked'], - type: "Unpacked['removeMembers']>>", + type: "Unpacked>", }, { f: rg.removeFilterTags, imports: ['Channel', 'Unpacked'], - type: "Unpacked['removeFilterTags']>>", + type: "Unpacked>", }, { f: rg.removeShadowBan, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['removeShadowBan']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.sendAction, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendAction']>>", + type: "Unpacked>", }, { f: rg.sendFile, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendFile']>>", + type: "Unpacked>", }, { f: rg.sendImage, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendImage']>>", + type: "Unpacked>", }, { f: rg.sendMessage, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendMessage']>>", + type: "Unpacked>", }, { f: rg.sendMessageReadEvent, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendEvent']>>", + type: "Unpacked>", }, { f: rg.sendReaction, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendReaction']>>", + type: "Unpacked>", }, { f: rg.setGuestUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['setGuestUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.shadowBan, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['shadowBan']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.show, imports: ['Channel', 'Unpacked'], - type: "Unpacked['show']>>", + type: "Unpacked>", }, { f: rg.stopTyping, imports: ['Channel', 'Unpacked'], - type: "Unpacked['stopTyping']>>", + type: "Unpacked>", }, { f: rg.stopWatching, imports: ['Channel', 'Unpacked'], - type: "Unpacked['stopWatching']>>", + type: "Unpacked>", }, { f: rg.sync, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['sync']>>", + type: "Unpacked>", }, { f: rg.syncTeam, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['sync']>>", + type: "Unpacked>", }, // Need translation on the account to run this test // { // f: rg.translateMessage, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + // imports: ['StreamChat', 'Unpacked'], // type: - // "Unpacked['translateMessage']>>", + // "Unpacked>", // }, { f: rg.truncateChannel, imports: ['Channel', 'Unpacked'], - type: "Unpacked['truncate']>>", + type: "Unpacked>", }, { f: rg.unbanUsers, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['unbanUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.unmute, imports: ['Channel', 'Unpacked'], - type: "Unpacked['unmute']>>", + type: "Unpacked>", }, { f: rg.unmuteUser, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['unmuteUser']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.updateAppSettings, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['updateAppSettings']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.updateBlockList, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['updateBlockList']>>", + type: "Unpacked>", }, { f: rg.updateChannel, imports: ['Channel', 'Unpacked'], - type: "Unpacked['update']>>", + type: "Unpacked>", }, { f: rg.updateChannelFromOriginal, imports: ['Channel', 'Unpacked'], - type: "Unpacked['update']>>", + type: "Unpacked>", }, { f: rg.updateChannelType, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['updateChannelType']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.updateCommand, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['updateCommand']>>", + type: "Unpacked>", }, { f: rg.updateMessage, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['updateMessage']>>", + type: "Unpacked>", }, { f: rg.updatePermission, - imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - type: "Unpacked['updatePermission']>>", + imports: ['StreamChat', 'Unpacked'], + type: "Unpacked>", }, { f: rg.upsertUsers, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['upsertUsers']>>", + type: "Unpacked>", }, { f: rg.upsertUser, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['upsertUser']>>", + type: "Unpacked>", }, { f: rg.watch, imports: ['Channel', 'Unpacked'], - type: "Unpacked['watch']>>", + type: "Unpacked>", }, // Currently roles do not return // { // f: rg.createRole, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - // type: "Unpacked['createRole']>>", + // imports: ['StreamChat', 'Unpacked'], + // type: "Unpacked>", // }, // { // f: rg.deleteRole, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - // type: "Unpacked['deleteRole']>>", + // imports: ['StreamChat', 'Unpacked'], + // type: "Unpacked>", // }, // { // f: rg.listRoles, - // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], - // type: "Unpacked['listRoles']>>", + // imports: ['StreamChat', 'Unpacked'], + // type: "Unpacked>", // }, ]; @@ -530,8 +530,54 @@ const run = async () => { imports = uniqueTypes.join(', '); imports = `import { ${imports} } from '../..';`; + + // Module augmentation to add custom properties used in tests + const moduleAugmentation = ` +// Module augmentation to add custom properties for testing +declare module '../..' { + interface CustomUserData { + testString?: string; + instrument?: string; + song?: string; + gender?: string; + unique?: string; + work?: string; + status?: string; + image?: string; + devices?: unknown[]; + invisible?: boolean; + last_engaged_at?: string; + mutes?: unknown[]; + channel_mutes?: unknown[]; + unread_count?: number; + total_unread_count?: number; + unread_channels?: number; + unread_threads?: number; + nickname?: string; + } + interface CustomChannelData { + description?: string; + image?: string; + smallTitle?: string; + shared_locations?: boolean; + } + interface CustomCommandData { + testCreateCommand?: unknown; + testCreateCommand_set?: unknown; + testDeleteCommand?: unknown; + testDeleteCommand_set?: unknown; + testGetCommand?: unknown; + testGetCommand_set?: unknown; + testListCommand?: unknown; + testListCommand_set?: unknown; + testUpdateCommand?: unknown; + testUpdateCommand_set?: unknown; + testUpdateCommand_set_two?: unknown; + } +}`; + tsFileName = `${__dirname}/data.ts`; - fs.writeFile(tsFileName, `${imports} \n\n`, function (err) { + fs.writeFile(tsFileName, `${imports} \n${moduleAugmentation}\n\n`, function (err) { if (err) { return console.log(err); } diff --git a/test/typescript/response-generators/channel-search.js b/test/typescript/response-generators/channel-search.js index 3b6d1eb2b..408fe1a69 100644 --- a/test/typescript/response-generators/channel-search.js +++ b/test/typescript/response-generators/channel-search.js @@ -1,4 +1,6 @@ -const { v4: uuid4 } = require('uuid'); +// External uuid package not available, using test UUID for test data +const uuid4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); const johnID = `john-${uuid4()}`; diff --git a/test/typescript/response-generators/channel-type.js b/test/typescript/response-generators/channel-type.js index c383421e4..c2af5e912 100644 --- a/test/typescript/response-generators/channel-type.js +++ b/test/typescript/response-generators/channel-type.js @@ -1,4 +1,6 @@ -const { generateUUIDv4: uuidv4 } = require('../../../src/utils'); +// generateUUIDv4 is not exported from the public API, using test UUID for test data +const uuidv4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); async function createChannelType() { diff --git a/test/typescript/response-generators/channel.js b/test/typescript/response-generators/channel.js index af7c364a1..9296bf4e4 100644 --- a/test/typescript/response-generators/channel.js +++ b/test/typescript/response-generators/channel.js @@ -1,4 +1,6 @@ -const { generateUUIDv4: uuidv4 } = require('../../../src/utils'); +// generateUUIDv4 is not exported from the public API, using test UUID for test data +const uuidv4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); const fs = require('fs'); const url = require('url'); @@ -28,15 +30,31 @@ async function addMembers() { } async function addFilterTags() { - const channel = await utils.createTestChannelForUser(uuidv4(), johnID); - await channel.watch(); + // Use server-side client for filter tag operations (required by backend) + const serverClient = utils.getServerTestClient(); + const channelId = uuidv4(); + await utils.createUsers([johnID]); + const channel = serverClient.channel('messaging', channelId, { + members: [johnID], + created_by_id: johnID, // Required for server-side auth + }); + await channel.create(); return await channel.addFilterTags(['tag1', 'tag2']); } async function removeFilterTags() { - const channel = await utils.createTestChannelForUser(uuidv4(), johnID); - await channel.watch(); + // Use server-side client for filter tag operations (required by backend) + const serverClient = utils.getServerTestClient(); + const channelId = uuidv4(); + await utils.createUsers([johnID]); + const channel = serverClient.channel('messaging', channelId, { + members: [johnID], + created_by_id: johnID, // Required for server-side auth + }); + await channel.create(); + // Add tags first, then remove them + await channel.addFilterTags(['tag1', 'tag2']); return await channel.removeFilterTags(['tag1']); } diff --git a/test/typescript/response-generators/client.js b/test/typescript/response-generators/client.js index 1a38dab6f..cddc8f3d1 100644 --- a/test/typescript/response-generators/client.js +++ b/test/typescript/response-generators/client.js @@ -1,4 +1,6 @@ -const { generateUUIDv4: uuidv4 } = require('../../../src/utils'); +// generateUUIDv4 is not exported from the public API, using test UUID for test data +const uuidv4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); async function addDevice() { @@ -113,9 +115,14 @@ async function markAllRead() { async function queryUsers() { const user1 = uuidv4(); const user2 = uuidv4(); - await utils.createUsers([user1, user2], { nickname: user2 }); + const nickname = `test-nickname-${Date.now()}`; + + // Create users with different properties + await utils.createUsers([user1], {}); // user1 without nickname + await utils.createUsers([user2], { nickname: nickname }); // user2 with nickname + const client = await utils.getTestClientForUser(user1); - return await client.queryUsers({ nickname: { $eq: user2 } }); + return await client.queryUsers({ nickname: { $eq: nickname } }); } async function queryBannedUsers() { diff --git a/test/typescript/response-generators/event.js b/test/typescript/response-generators/event.js index 8546c1f9f..67b3a2f96 100644 --- a/test/typescript/response-generators/event.js +++ b/test/typescript/response-generators/event.js @@ -1,4 +1,6 @@ -const { generateUUIDv4: uuidv4 } = require('../../../src/utils'); +// generateUUIDv4 is not exported from the public API, using test UUID for test data +const uuidv4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); const johnID = `john-${uuidv4()}`; diff --git a/test/typescript/response-generators/message.js b/test/typescript/response-generators/message.js index d811d1047..1be1cbd7b 100644 --- a/test/typescript/response-generators/message.js +++ b/test/typescript/response-generators/message.js @@ -1,4 +1,6 @@ -const { generateUUIDv4: uuidv4 } = require('../../../src/utils'); +// generateUUIDv4 is not exported from the public API, using test UUID for test data +const uuidv4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); const johnID = `john-${uuidv4()}`; diff --git a/test/typescript/response-generators/moderation.js b/test/typescript/response-generators/moderation.js index a0163e989..6958c7184 100644 --- a/test/typescript/response-generators/moderation.js +++ b/test/typescript/response-generators/moderation.js @@ -1,4 +1,6 @@ -const { generateUUIDv4: uuidv4 } = require('../../../src/utils'); +// generateUUIDv4 is not exported from the public API, using test UUID for test data +const uuidv4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); const { sleep } = require('../utils'); @@ -73,16 +75,16 @@ async function deleteBlockList() { await cleanupBlockList(client, name); await cleanupBlockList(client, name2); - try { - await client.createBlockList({ name, words: ['F*!k'] }); - await client.createBlockList({ name: name2, words: ['S!*t'] }); - } catch (err) { - // in case the blocklist already exists - // do nothing - } + // Create the blocklists - if creation fails, the test should fail + await client.createBlockList({ name, words: ['F*!k'] }); + await client.createBlockList({ name: name2, words: ['S!*t'] }); + // Delete the first blocklist and return its response const returnValue = await client.deleteBlockList(name); + + // Clean up the second blocklist await client.deleteBlockList(name2); + return returnValue; } diff --git a/test/typescript/response-generators/reaction.js b/test/typescript/response-generators/reaction.js index 536e00f8f..b138fd1ff 100644 --- a/test/typescript/response-generators/reaction.js +++ b/test/typescript/response-generators/reaction.js @@ -1,4 +1,6 @@ -const { v4: uuid4 } = require('uuid'); +// External uuid package not available, using test UUID for test data +const uuid4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); const johnID = `john-${uuid4()}`; diff --git a/test/typescript/response-generators/update-users.js b/test/typescript/response-generators/update-users.js index 313bc0b7d..5ee55f269 100644 --- a/test/typescript/response-generators/update-users.js +++ b/test/typescript/response-generators/update-users.js @@ -1,4 +1,6 @@ -const { v4: uuid4 } = require('uuid'); +// External uuid package not available, using test UUID for test data +const uuid4 = () => + 'test-' + Math.random().toString(36).substring(2, 15) + '-' + Date.now(); const utils = require('../utils'); async function deactivateUser() { diff --git a/test/typescript/unit-test.ts b/test/typescript/unit-test.ts index 308b837fb..61f11e118 100644 --- a/test/typescript/unit-test.ts +++ b/test/typescript/unit-test.ts @@ -20,8 +20,22 @@ import { UpdateChannelAPIResponse, PartialUserUpdate, PermissionObject, + PolicyRequest, ConnectAPIResponse, } from '../../dist/types'; + +// Module augmentation to add custom properties for testing +declare module '../../dist/types' { + interface CustomUserData { + example?: number; + phone?: number; + name?: string; + } + interface CustomChannelData { + color?: string; + name?: string; + } +} const apiKey = 'apiKey'; type UserType = { @@ -58,81 +72,21 @@ type StreamTypes = { let voidReturn: void | { unsubscribe: () => void }; let voidPromise: Promise; -const client: StreamChat = new StreamChat(apiKey, undefined, { +const client: StreamChat = new StreamChat(apiKey, undefined, { timeout: 3000, logger: (logLevel: string, msg: string, extraData?: Record) => {}, }); -const clientWithoutSecret: StreamChat<{ - attachmentType: {}; - channelType: ChannelType; - commandType: string & {}; - eventType: {}; - messageType: {}; - reactionType: {}; - userType: UserType; - pollType: {}; - pollOptionType: {}; -}> = new StreamChat<{ - attachmentType: {}; - channelType: ChannelType; - commandType: string & {}; - eventType: {}; - messageType: {}; - reactionType: {}; - userType: UserType; - pollType: {}; - pollOptionType: {}; -}>(apiKey, { +const clientWithoutSecret: StreamChat = new StreamChat(apiKey, { timeout: 3000, - logger: (logLevel: string, msg: string, extraData?: Record) => {}, }); +const singletonClient = StreamChat.getInstance(apiKey); -const singletonClient = StreamChat.getInstance(apiKey); - -const singletonClient1: StreamChat<{ - attachmentType: {}; - channelType: ChannelType; - commandType: string & {}; - eventType: {}; - messageType: {}; - reactionType: {}; - userType: UserType; - pollType: {}; - pollOptionType: {}; -}> = StreamChat.getInstance<{ - attachmentType: {}; - channelType: ChannelType; - commandType: string & {}; - eventType: {}; - messageType: {}; - reactionType: {}; - userType: UserType; - pollType: {}; - pollOptionType: {}; -}>(apiKey); +const singletonClient1: StreamChat = StreamChat.getInstance(apiKey); -const singletonClient2: StreamChat<{ - attachmentType: {}; - channelType: ChannelType; - commandType: string & {}; - eventType: {}; - messageType: {}; - reactionType: {}; - userType: UserType; - pollType: {}; - pollOptionType: {}; -}> = StreamChat.getInstance<{ - attachmentType: {}; - channelType: ChannelType; - commandType: string & {}; - eventType: {}; - messageType: {}; - reactionType: {}; - userType: UserType; - pollType: {}; - pollOptionType: {}; -}>(apiKey, '', {}); +const singletonClient2: StreamChat = StreamChat.getInstance(apiKey, { + timeout: 3000, +}); const devToken: string = client.devToken('joshua'); const token: string = client.createToken('james', 3600); @@ -143,7 +97,7 @@ const settingsPromise: Promise = client.updateAppSettings({}); const appPromise: Promise = client.getAppSettings(); voidPromise = client.disconnectUser(); -const updateRequest: PartialUserUpdate = { +const updateRequest: PartialUserUpdate = { id: 'vishal', set: { name: 'Awesome', @@ -152,14 +106,14 @@ const updateRequest: PartialUserUpdate = { }; const updateUser: Promise<{ - users: { [key: string]: UserResponse }; + users: { [key: string]: UserResponse }; }> = client.partialUpdateUser(updateRequest); const updateUsers: Promise<{ - users: { [key: string]: UserResponse }; + users: { [key: string]: UserResponse }; }> = client.partialUpdateUsers([updateRequest]); const updateUsersWithSingletonClient: Promise<{ - users: { [key: string]: UserResponse }; + users: { [key: string]: UserResponse }; }> = singletonClient.partialUpdateUsers([updateRequest]); const eventHandler = (event: Event) => {}; @@ -168,7 +122,7 @@ voidReturn = client.off(eventHandler); voidReturn = client.on('message.new', eventHandler); voidReturn = client.off('message.new', eventHandler); -let userReturn: ConnectAPIResponse; +let userReturn: ConnectAPIResponse | undefined; userReturn = client.connectUser({ id: 'john', phone: 2 }, devToken); userReturn = client.connectUser({ id: 'john', phone: 2 }, async () => 'token'); userReturn = client.setUser({ id: 'john', phone: 2 }, devToken); @@ -195,7 +149,7 @@ const file: Promise = client.sendFile( ); const type: EventTypes = 'user.updated'; -const event: Event = { +const event: Event = { type, cid: 'channelid', message: { @@ -224,26 +178,23 @@ const event: Event = { voidReturn = client.dispatchEvent(event); voidPromise = client.recoverState(); -const channels: Promise[]> = client.queryChannels({}, {}, {}); +const channels: Promise = client.queryChannels({}, {}, {}); channels.then((response) => { const type: string = response[0].type; const cid: string = response[0].cid; }); -const channel: Channel = client.channel('messaging', 'channelName', { +const channel: Channel = client.channel('messaging', 'channelName', { color: 'green', }); -const channelState: ChannelState = channel.state; -const chUser1: ChannelMemberResponse = channelState.members.someUser12433222; -const chUser2: ChannelMemberResponse = - channelState.members.someUser124332221; +const channelState: ChannelState = channel.state; +const chUser1: ChannelMemberResponse = channelState.members.someUser12433222; +const chUser2: ChannelMemberResponse = channelState.members.someUser124332221; -const chUser3: UserResponse = channelState.read.someUserId.user; -const typing: Event = channelState.typing['someUserId']; +const chUser3: UserResponse = channelState.read.someUserId.user; +const typing: Event = channelState.typing['someUserId']; -const acceptInvite: Promise> = channel.acceptInvite( - {}, -); +const acceptInvite: Promise = channel.acceptInvite({}); voidReturn = channel.on(eventHandler); voidReturn = channel.off(eventHandler); @@ -290,7 +241,7 @@ const permissions = [ ]; client.updateChannelType('messaging', { permissions }).then((response) => { - const permissions: PermissionObject[] = response.permissions || []; + const permissions: PolicyRequest[] = response.permissions || []; const permissionName: string = permissions[0].name || ''; const permissionRoles: string[] = permissions[0].roles || []; }); diff --git a/test/typescript/utils.js b/test/typescript/utils.js index fee87fdf2..3025f7e92 100644 --- a/test/typescript/utils.js +++ b/test/typescript/utils.js @@ -1,4 +1,4 @@ -const { StreamChat } = require('../../dist'); +const { StreamChat } = require('../../dist/cjs/index.node.js'); require('dotenv').config({ path: `${process.cwd()}/test/typescript/.env` });