From 4fedb846bca708031f89c200c9ad2b7d1b1cc3f0 Mon Sep 17 00:00:00 2001 From: Joram Wilander Date: Tue, 7 Jan 2025 08:23:28 -0500 Subject: [PATCH] MM-59683 + more - Add tests to client/rest (#8447) * Add tests to client/rest/users * Add tests to client/rest/channels * Add tests to client/rest/posts * Add tests to client/rest/teams * Add tests to client/rest/general * Minor clean-up * Add tests to client/rest/file * Add tests to client/rest/threads * Add tests to client/rest/emojis * Add tests to client/rest/integrations * Add tests to client/rest/categories * Add tests to client/rest/groups * Add tests to client/rest/channel_bookmark * Add tests to client/rest/preferences * Add tests to client/rest/tos * Add tests to client/rest/nps and client/rest/plugins * Minor clean-up * Fix type error with proper client.searchFiles definition * add more test to cover 100% * add teamId test * improve users test --------- Co-authored-by: Rahim Rahman --- app/actions/remote/search.ts | 2 +- app/client/rest/categories.test.ts | 62 +++ app/client/rest/channel_bookmark.test.ts | 92 +++++ app/client/rest/channels.test.ts | 499 ++++++++++++++++++++++ app/client/rest/emojis.test.ts | 100 +++++ app/client/rest/files.test.ts | 165 ++++++++ app/client/rest/files.ts | 4 +- app/client/rest/general.test.ts | 168 ++++++++ app/client/rest/groups.test.ts | 103 +++++ app/client/rest/integrations.test.ts | 97 +++++ app/client/rest/nps.test.ts | 28 ++ app/client/rest/plugins.test.ts | 27 ++ app/client/rest/posts.test.ts | 313 +++++++++++++- app/client/rest/preferences.test.ts | 59 +++ app/client/rest/teams.test.ts | 238 +++++++++++ app/client/rest/threads.test.ts | 129 ++++++ app/client/rest/tos.test.ts | 41 ++ app/client/rest/users.test.ts | 500 +++++++++++++++++++++++ 18 files changed, 2614 insertions(+), 13 deletions(-) create mode 100644 app/client/rest/categories.test.ts create mode 100644 app/client/rest/channel_bookmark.test.ts create mode 100644 app/client/rest/channels.test.ts create mode 100644 app/client/rest/emojis.test.ts create mode 100644 app/client/rest/files.test.ts create mode 100644 app/client/rest/general.test.ts create mode 100644 app/client/rest/groups.test.ts create mode 100644 app/client/rest/integrations.test.ts create mode 100644 app/client/rest/nps.test.ts create mode 100644 app/client/rest/plugins.test.ts create mode 100644 app/client/rest/preferences.test.ts create mode 100644 app/client/rest/teams.test.ts create mode 100644 app/client/rest/threads.test.ts create mode 100644 app/client/rest/tos.test.ts create mode 100644 app/client/rest/users.test.ts diff --git a/app/actions/remote/search.ts b/app/actions/remote/search.ts index 2a5783eac..4628f890d 100644 --- a/app/actions/remote/search.ts +++ b/app/actions/remote/search.ts @@ -139,7 +139,7 @@ export const searchFiles = async (serverUrl: string, teamId: string, params: Fil currentTeamId = await getCurrentTeamId(database); } const client = NetworkManager.getClient(serverUrl); - const result = await client.searchFiles(currentTeamId, params.terms); + const result = await client.searchFiles(currentTeamId, params.terms, false); const files = result?.file_infos ? Object.values(result.file_infos) : []; const [allChannelIds, allPostIds] = files.reduce<[Set, Set]>((acc, f) => { if (f.channel_id) { diff --git a/app/client/rest/categories.test.ts b/app/client/rest/categories.test.ts new file mode 100644 index 000000000..f7020a21c --- /dev/null +++ b/app/client/rest/categories.test.ts @@ -0,0 +1,62 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; + +import type ClientBase from './base'; +import type {ClientCategoriesMix} from './categories'; + +describe('ClientCategories', () => { + let client: ClientCategoriesMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getCategories', async () => { + const userId = 'user_id'; + const teamId = 'team_id'; + await client.getCategories(userId, teamId); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getCategoriesRoute(userId, teamId), + {method: 'get', groupLabel: undefined}, + ); + }); + + test('getCategoriesOrder', async () => { + const userId = 'user_id'; + const teamId = 'team_id'; + await client.getCategoriesOrder(userId, teamId); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getCategoriesOrderRoute(userId, teamId), + {method: 'get'}, + ); + }); + + test('getCategory', async () => { + const userId = 'user_id'; + const teamId = 'team_id'; + const categoryId = 'category_id'; + await client.getCategory(userId, teamId, categoryId); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getCategoryRoute(userId, teamId, categoryId), + {method: 'get'}, + ); + }); + + test('updateChannelCategories', async () => { + const userId = 'user_id'; + const teamId = 'team_id'; + const categories = [{id: 'category_id', channel_ids: ['channel_id']}] as CategoryWithChannels[]; + await client.updateChannelCategories(userId, teamId, categories); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getCategoriesRoute(userId, teamId), + {method: 'put', body: categories}, + ); + }); +}); diff --git a/app/client/rest/channel_bookmark.test.ts b/app/client/rest/channel_bookmark.test.ts new file mode 100644 index 000000000..c5be4d52d --- /dev/null +++ b/app/client/rest/channel_bookmark.test.ts @@ -0,0 +1,92 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import type ClientBase from './base'; +import type {ClientChannelBookmarksMix} from './channel_bookmark'; + +describe('ClientChannelBookmarks', () => { + let client: ClientChannelBookmarksMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('createChannelBookmark', async () => { + const channelId = 'channel_id'; + const bookmark = {id: 'bookmark_id', display_name: 'bookmark_name'} as ChannelBookmark; + const expectedUrl = client.getChannelBookmarksRoute(channelId); + const expectedOptions = { + method: 'post', + body: bookmark, + headers: {'Connection-Id': ''}, + }; + + await client.createChannelBookmark(channelId, bookmark); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateChannelBookmark', async () => { + const channelId = 'channel_id'; + const bookmark = {id: 'bookmark_id', display_name: 'new_bookmark_name'} as ChannelBookmark; + const connectionId = 'connection_id'; + const expectedUrl = client.getChannelBookmarkRoute(channelId, bookmark.id); + const expectedOptions = { + method: 'patch', + body: bookmark, + headers: {'Connection-Id': connectionId}, + }; + + await client.updateChannelBookmark(channelId, bookmark, connectionId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateChannelBookmarkSortOrder', async () => { + const channelId = 'channel_id'; + const bookmarkId = 'bookmark_id'; + const newSortOrder = 1; + const connectionId = 'connection_id'; + const expectedUrl = `${client.getChannelBookmarkRoute(channelId, bookmarkId)}/sort_order`; + const expectedOptions = { + method: 'post', + body: newSortOrder, + headers: {'Connection-Id': connectionId}, + }; + + await client.updateChannelBookmarkSortOrder(channelId, bookmarkId, newSortOrder, connectionId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('deleteChannelBookmark', async () => { + const channelId = 'channel_id'; + const bookmarkId = 'bookmark_id'; + const connectionId = 'connection_id'; + const expectedUrl = client.getChannelBookmarkRoute(channelId, bookmarkId); + const expectedOptions = { + method: 'delete', + headers: {'Connection-Id': connectionId}, + }; + + await client.deleteChannelBookmark(channelId, bookmarkId, connectionId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelBookmarksForChannel', async () => { + const channelId = 'channel_id'; + const since = 123456; + const groupLabel = 'group_label'; + const expectedUrl = `${client.getChannelBookmarksRoute(channelId)}${buildQueryString({bookmarks_since: since})}`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getChannelBookmarksForChannel(channelId, since, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/channels.test.ts b/app/client/rest/channels.test.ts new file mode 100644 index 000000000..38c6aae84 --- /dev/null +++ b/app/client/rest/channels.test.ts @@ -0,0 +1,499 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientChannelsMix} from './channels'; + +describe('ClientChannels', () => { + let client: ClientChannelsMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getAllChannels', async () => { + const page = 1; + const perPage = 10; + const notAssociatedToGroup = 'group1'; + const excludeDefaultChannels = true; + const includeTotalCount = true; + const expectedUrl = `${client.getChannelsRoute()}?page=${page}&per_page=${perPage}¬_associated_to_group=${notAssociatedToGroup}&exclude_default_channels=${excludeDefaultChannels}&include_total_count=${includeTotalCount}`; + const expectedOptions = {method: 'get'}; + + await client.getAllChannels(page, perPage, notAssociatedToGroup, excludeDefaultChannels, includeTotalCount); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getAllChannels(); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getChannelsRoute()}?page=0&per_page=${PER_PAGE_DEFAULT}¬_associated_to_group=&exclude_default_channels=false&include_total_count=false`, expectedOptions); + }); + + test('createChannel', async () => { + const channel = {id: 'channel1', name: 'testchannel'} as Channel; + const expectedUrl = client.getChannelsRoute(); + const expectedOptions = {method: 'post', body: channel}; + + await client.createChannel(channel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('createDirectChannel', async () => { + const userIds = ['user1', 'user2']; + const expectedUrl = `${client.getChannelsRoute()}/direct`; + const expectedOptions = {method: 'post', body: userIds}; + + await client.createDirectChannel(userIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('createGroupChannel', async () => { + const userIds = ['user1', 'user2', 'user3']; + const expectedUrl = `${client.getChannelsRoute()}/group`; + const expectedOptions = {method: 'post', body: userIds}; + + await client.createGroupChannel(userIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('deleteChannel', async () => { + const channelId = 'channel1'; + const expectedUrl = client.getChannelRoute(channelId); + const expectedOptions = {method: 'delete'}; + + await client.deleteChannel(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('unarchiveChannel', async () => { + const channelId = 'channel1'; + const expectedUrl = `${client.getChannelRoute(channelId)}/restore`; + const expectedOptions = {method: 'post'}; + + await client.unarchiveChannel(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateChannel', async () => { + const channel = {id: 'channel1', name: 'updatedchannel'} as Channel; + const expectedUrl = client.getChannelRoute(channel.id); + const expectedOptions = {method: 'put', body: channel}; + + await client.updateChannel(channel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('convertChannelToPrivate', async () => { + const channelId = 'channel1'; + const expectedUrl = `${client.getChannelRoute(channelId)}/privacy`; + const expectedOptions = {method: 'put', body: {privacy: 'P'}}; + + await client.convertChannelToPrivate(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateChannelPrivacy', async () => { + const channelId = 'channel1'; + const privacy = 'P'; + const expectedUrl = `${client.getChannelRoute(channelId)}/privacy`; + const expectedOptions = {method: 'put', body: {privacy}}; + + await client.updateChannelPrivacy(channelId, privacy); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('patchChannel', async () => { + const channelId = 'channel1'; + const channelPatch = {name: 'patchedchannel'}; + const expectedUrl = `${client.getChannelRoute(channelId)}/patch`; + const expectedOptions = {method: 'put', body: channelPatch}; + + await client.patchChannel(channelId, channelPatch); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateChannelNotifyProps', async () => { + const props = {channel_id: 'channel1', user_id: 'user1', mark_unread: 'all'} as ChannelNotifyProps & {channel_id: string; user_id: string}; + const expectedUrl = `${client.getChannelMemberRoute(props.channel_id, props.user_id)}/notify_props`; + const expectedOptions = {method: 'put', body: props}; + + await client.updateChannelNotifyProps(props); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannel', async () => { + const channelId = 'channel1'; + const expectedUrl = client.getChannelRoute(channelId); + const expectedOptions = {method: 'get'}; + + await client.getChannel(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelByName', async () => { + const teamId = 'team1'; + const channelName = 'channelname'; + const includeDeleted = true; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels/name/${channelName}?include_deleted=${includeDeleted}`; + const expectedOptions = {method: 'get'}; + + await client.getChannelByName(teamId, channelName, includeDeleted); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getChannelByName(teamId, channelName); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getTeamRoute(teamId)}/channels/name/${channelName}?include_deleted=false`, expectedOptions); + }); + + test('getChannelByNameAndTeamName', async () => { + const teamName = 'teamname'; + const channelName = 'channelname'; + const includeDeleted = true; + const expectedUrl = `${client.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=${includeDeleted}`; + const expectedOptions = {method: 'get'}; + + await client.getChannelByNameAndTeamName(teamName, channelName, includeDeleted); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getChannelByNameAndTeamName(teamName, channelName); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getTeamNameRoute(teamName)}/channels/name/${channelName}?include_deleted=false`, expectedOptions); + }); + + test('getChannels', async () => { + const teamId = 'team1'; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels?page=${page}&per_page=${perPage}`; + const expectedOptions = {method: 'get'}; + + await client.getChannels(teamId, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getChannels(teamId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getTeamRoute(teamId)}/channels?page=0&per_page=${PER_PAGE_DEFAULT}`, expectedOptions); + }); + + test('getArchivedChannels', async () => { + const teamId = 'team1'; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels/deleted?page=${page}&per_page=${perPage}`; + const expectedOptions = {method: 'get'}; + + await client.getArchivedChannels(teamId, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getArchivedChannels(teamId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getTeamRoute(teamId)}/channels/deleted?page=0&per_page=${PER_PAGE_DEFAULT}`, expectedOptions); + }); + + test('getSharedChannels', async () => { + const teamId = 'team1'; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getSharedChannelsRoute()}/${teamId}?page=${page}&per_page=${perPage}`; + const expectedOptions = {method: 'get'}; + + await client.getSharedChannels(teamId, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getSharedChannels(teamId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getSharedChannelsRoute()}/${teamId}?page=0&per_page=${PER_PAGE_DEFAULT}`, expectedOptions); + }); + + test('getMyChannels', async () => { + const teamId = 'team1'; + const includeDeleted = true; + const since = 123456; + const expectedUrl = `${client.getUserRoute('me')}/teams/${teamId}/channels?include_deleted=${includeDeleted}&last_delete_at=${since}`; + const expectedOptions = {method: 'get'}; + + await client.getMyChannels(teamId, includeDeleted, since); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getMyChannels(teamId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getUserRoute('me')}/teams/${teamId}/channels?include_deleted=false&last_delete_at=0`, expectedOptions); + }); + + test('getMyChannelMember', async () => { + const channelId = 'channel1'; + const expectedUrl = client.getChannelMemberRoute(channelId, 'me'); + const expectedOptions = {method: 'get'}; + + await client.getMyChannelMember(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getMyChannelMembers', async () => { + const teamId = 'team1'; + const expectedUrl = `${client.getUserRoute('me')}/teams/${teamId}/channels/members`; + const expectedOptions = {method: 'get'}; + + await client.getMyChannelMembers(teamId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelMembers', async () => { + const channelId = 'channel1'; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getChannelMembersRoute(channelId)}?page=${page}&per_page=${perPage}`; + const expectedOptions = {method: 'get'}; + + await client.getChannelMembers(channelId, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getChannelMembers(channelId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getChannelMembersRoute(channelId)}?page=0&per_page=${PER_PAGE_DEFAULT}`, expectedOptions); + }); + + test('getChannelTimezones', async () => { + const channelId = 'channel1'; + const expectedUrl = `${client.getChannelRoute(channelId)}/timezones`; + const expectedOptions = {method: 'get'}; + + await client.getChannelTimezones(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelMember', async () => { + const channelId = 'channel1'; + const userId = 'user1'; + const expectedUrl = client.getChannelMemberRoute(channelId, userId); + const expectedOptions = {method: 'get'}; + + await client.getChannelMember(channelId, userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelMembersByIds', async () => { + const channelId = 'channel1'; + const userIds = ['user1', 'user2']; + const expectedUrl = `${client.getChannelMembersRoute(channelId)}/ids`; + const expectedOptions = {method: 'post', body: userIds}; + + await client.getChannelMembersByIds(channelId, userIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('addToChannel', async () => { + const userId = 'user1'; + const channelId = 'channel1'; + const postRootId = 'post1'; + const member = {user_id: userId, channel_id: channelId, post_root_id: postRootId}; + const expectedUrl = client.getChannelMembersRoute(channelId); + const expectedOptions = {method: 'post', body: member}; + + await client.addToChannel(userId, channelId, postRootId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test without postRootId + await client.addToChannel(userId, channelId); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, {...expectedOptions, body: {user_id: userId, channel_id: channelId, post_root_id: ''}}); + }); + + test('removeFromChannel', async () => { + const userId = 'user1'; + const channelId = 'channel1'; + const expectedUrl = client.getChannelMemberRoute(channelId, userId); + const expectedOptions = {method: 'delete'}; + + await client.removeFromChannel(userId, channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelStats', async () => { + const channelId = 'channel1'; + const expectedUrl = `${client.getChannelRoute(channelId)}/stats`; + const expectedOptions = {method: 'get'}; + + await client.getChannelStats(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getChannelMemberCountsByGroup', async () => { + const channelId = 'channel1'; + const includeTimezones = true; + const expectedUrl = `${client.getChannelRoute(channelId)}/member_counts_by_group?include_timezones=${includeTimezones}`; + const expectedOptions = {method: 'get'}; + + await client.getChannelMemberCountsByGroup(channelId, includeTimezones); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('viewMyChannel', async () => { + const channelId = 'channel1'; + const prevChannelId = 'channel2'; + const data = {channel_id: channelId, prev_channel_id: prevChannelId, collapsed_threads_supported: true}; + const expectedUrl = `${client.getChannelsRoute()}/members/me/view`; + const expectedOptions = {method: 'post', body: data}; + + await client.viewMyChannel(channelId, prevChannelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('autocompleteChannels', async () => { + const teamId = 'team1'; + const name = 'channelname'; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels/autocomplete?name=${name}`; + const expectedOptions = {method: 'get'}; + + await client.autocompleteChannels(teamId, name); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('autocompleteChannelsForSearch', async () => { + const teamId = 'team1'; + const name = 'channelname'; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels/search_autocomplete?name=${name}`; + const expectedOptions = {method: 'get'}; + + await client.autocompleteChannelsForSearch(teamId, name); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('searchChannels', async () => { + const teamId = 'team1'; + const term = 'searchterm'; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels/search`; + const expectedOptions = {method: 'post', body: {term}}; + + await client.searchChannels(teamId, term); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('searchArchivedChannels', async () => { + const teamId = 'team1'; + const term = 'searchterm'; + const expectedUrl = `${client.getTeamRoute(teamId)}/channels/search_archived`; + const expectedOptions = {method: 'post', body: {term}}; + + await client.searchArchivedChannels(teamId, term); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('searchAllChannels', async () => { + const term = 'searchterm'; + const teamIds = ['team1', 'team2']; + const archivedOnly = true; + const queryParams = {include_deleted: false, system_console: false, exclude_default_channels: false}; + const body = { + term, + team_ids: teamIds, + deleted: archivedOnly, + exclude_default_channels: true, + exclude_group_constrained: true, + public: true, + private: false, + }; + const expectedUrl = `${client.getChannelsRoute()}/search${buildQueryString(queryParams)}`; + const expectedOptions = {method: 'post', body}; + + await client.searchAllChannels(term, teamIds, archivedOnly); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultExpectedOptions = {method: 'post', body: {...body, deleted: false}}; + await client.searchAllChannels(term, teamIds); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getChannelsRoute()}/search${buildQueryString(queryParams)}`, defaultExpectedOptions); + }); + + test('updateChannelMemberSchemeRoles', async () => { + const channelId = 'channel1'; + const userId = 'user1'; + const isSchemeUser = true; + const isSchemeAdmin = false; + const body = {scheme_user: isSchemeUser, scheme_admin: isSchemeAdmin}; + const expectedUrl = `${client.getChannelMembersRoute(channelId)}/${userId}/schemeRoles`; + const expectedOptions = {method: 'put', body}; + + await client.updateChannelMemberSchemeRoles(channelId, userId, isSchemeUser, isSchemeAdmin); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getMemberInChannel', async () => { + const channelId = 'channel1'; + const userId = 'user1'; + const expectedUrl = `${client.getChannelMembersRoute(channelId)}/${userId}`; + const expectedOptions = {method: 'get'}; + + await client.getMemberInChannel(channelId, userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getGroupMessageMembersCommonTeams', async () => { + const channelId = 'channel1'; + const expectedUrl = `${client.getChannelRoute(channelId)}/common_teams`; + const expectedOptions = {method: 'get'}; + + await client.getGroupMessageMembersCommonTeams(channelId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('convertGroupMessageToPrivateChannel', async () => { + const channelId = 'channel1'; + const teamId = 'team1'; + const displayName = 'Private Channel'; + const name = 'private-channel'; + const body = { + channel_id: channelId, + team_id: teamId, + display_name: displayName, + name, + }; + const expectedUrl = `${client.getChannelRoute(channelId)}/convert_to_channel?team-id=${teamId}`; + const expectedOptions = {method: 'post', body}; + + await client.convertGroupMessageToPrivateChannel(channelId, teamId, displayName, name); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/emojis.test.ts b/app/client/rest/emojis.test.ts new file mode 100644 index 000000000..537cbc0fe --- /dev/null +++ b/app/client/rest/emojis.test.ts @@ -0,0 +1,100 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientEmojisMix} from './emojis'; + +describe('ClientEmojis', () => { + let client: ClientEmojisMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getCustomEmoji', async () => { + const id = 'emoji_id'; + await client.getCustomEmoji(id); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}/${id}`, + {method: 'get'}, + ); + }); + + test('getCustomEmojiByName', async () => { + const name = 'emoji_name'; + await client.getCustomEmojiByName(name); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}/name/${name}`, + {method: 'get'}, + ); + }); + + test('getCustomEmojis', async () => { + const page = 1; + const perPage = 10; + const sort = 'create_at'; + await client.getCustomEmojis(page, perPage, sort); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}${buildQueryString({page, per_page: perPage, sort})}`, + {method: 'get'}, + ); + + // Test with default values + await client.getCustomEmojis(); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}${buildQueryString({page: 0, per_page: PER_PAGE_DEFAULT, sort: ''})}`, + {method: 'get'}, + ); + }); + + test('getSystemEmojiImageUrl', () => { + const filename = 'emoji_filename'; + const result = client.getSystemEmojiImageUrl(filename); + + expect(result).toBe(`${client.apiClient.baseUrl}static/emoji/${filename}.png`); + }); + + test('getCustomEmojiImageUrl', () => { + const id = 'emoji_id'; + const result = client.getCustomEmojiImageUrl(id); + + expect(result).toBe(`${client.apiClient.baseUrl}${client.getEmojiRoute(id)}/image`); + }); + + test('searchCustomEmoji', async () => { + const term = 'search_term'; + const options = {option1: 'value1'}; + await client.searchCustomEmoji(term, options); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}/search`, + {method: 'post', body: {term, ...options}}, + ); + + // Test with default values + await client.searchCustomEmoji(term); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}/search`, + {method: 'post', body: {term}}, + ); + }); + + test('autocompleteCustomEmoji', async () => { + const name = 'emoji_name'; + await client.autocompleteCustomEmoji(name); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getEmojisRoute()}/autocomplete${buildQueryString({name})}`, + {method: 'get'}, + ); + }); +}); diff --git a/app/client/rest/files.test.ts b/app/client/rest/files.test.ts new file mode 100644 index 000000000..0bd4f48ca --- /dev/null +++ b/app/client/rest/files.test.ts @@ -0,0 +1,165 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; + +import type ClientBase from './base'; +import type {ClientFilesMix} from './files'; + +let client: ClientFilesMix & ClientBase; + +beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + client.apiClient.upload = jest.fn(); +}); + +test('getFileUrl', () => { + const fileId = 'file_id'; + const timestamp = 123456; + const expectedBaseUrl = `${client.apiClient.baseUrl}${client.getFileRoute(fileId)}`; + const expectedUrl = `${expectedBaseUrl}?${timestamp}`; + + const result = client.getFileUrl(fileId, timestamp); + + expect(result).toBe(expectedUrl); + + // Test with zero timestamp + const resultZero = client.getFileUrl(fileId, 0); + expect(resultZero).toBe(expectedBaseUrl); +}); + +test('getFileThumbnailUrl', () => { + const fileId = 'file_id'; + const timestamp = 123456; + const expectedBaseUrl = `${client.apiClient.baseUrl}${client.getFileRoute(fileId)}/thumbnail`; + const expectedUrl = `${expectedBaseUrl}?${timestamp}`; + + const result = client.getFileThumbnailUrl(fileId, timestamp); + + expect(result).toBe(expectedUrl); + + // Test with zero timestamp + const resultZero = client.getFileThumbnailUrl(fileId, 0); + expect(resultZero).toBe(expectedBaseUrl); +}); + +test('getFilePreviewUrl', () => { + const fileId = 'file_id'; + const timestamp = 123456; + const expectedBaseUrl = `${client.apiClient.baseUrl}${client.getFileRoute(fileId)}/preview`; + const expectedUrl = `${expectedBaseUrl}?${timestamp}`; + + const result = client.getFilePreviewUrl(fileId, timestamp); + + expect(result).toBe(expectedUrl); + + // Test with zero timestamp + const resultZero = client.getFilePreviewUrl(fileId, 0); + expect(resultZero).toBe(expectedBaseUrl); +}); + +test('getFilePublicLink', async () => { + const fileId = 'file_id'; + const expectedUrl = `${client.getFileRoute(fileId)}/link`; + const expectedOptions = {method: 'get'}; + + await client.getFilePublicLink(fileId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); +}); + +test('uploadAttachment', () => { + const file = {localPath: '/path/to/file'} as FileInfo; + const channelId = 'channel_id'; + const onProgress = jest.fn(); + const onComplete = jest.fn(); + const onError = jest.fn(); + const skipBytes = 1; + const isBookmark = true; + const expectedUrl = `${client.getFilesRoute()}?bookmark=true`; + const expectedOptions = { + skipBytes, + method: 'POST', + multipart: { + data: { + channel_id: channelId, + }, + }, + timeoutInterval: 180000, + }; + + (client.apiClient.upload as jest.Mock).mockReturnValue({ + progress: jest.fn().mockReturnThis(), + then: jest.fn().mockReturnThis(), + catch: jest.fn().mockReturnThis(), + cancel: jest.fn(), + }); + + client.uploadAttachment(file, channelId, onProgress, onComplete, onError, skipBytes, isBookmark); + + expect(client.apiClient.upload).toHaveBeenCalledWith(expectedUrl, file.localPath, expectedOptions); + + // Test with default values + const expectedDefaultOptions = { + skipBytes: 0, + method: 'POST', + multipart: { + data: { + channel_id: channelId, + }, + }, + timeoutInterval: 180000, + }; + client.uploadAttachment(file, channelId, onProgress, onComplete, onError); + expect(client.apiClient.upload).toHaveBeenCalledWith(client.getFilesRoute(), file.localPath, expectedDefaultOptions); +}); + +test('uploadAttachment throws error when file has no localPath', () => { + const file = {} as FileInfo; + const channelId = 'channel_id'; + + expect(() => { + client.uploadAttachment( + file, + channelId, + jest.fn(), + jest.fn(), + jest.fn(), + ); + }).toThrow('file does not have local path defined'); +}); + +test('searchFilesWithParams', async () => { + const teamId = 'team_id'; + const params = {terms: 'search terms', is_or_search: true}; + const expectedUrl = `${client.getTeamRoute(teamId)}/files/search`; + const expectedOptions = {method: 'post', body: params}; + + await client.searchFilesWithParams(teamId, params); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); +}); + +test('searchFilesWithParams without teamId', async () => { + const params = {terms: 'search terms', is_or_search: true}; + const expectedUrl = `${client.getFilesRoute()}/search`; + const expectedOptions = {method: 'post', body: params}; + + await client.searchFilesWithParams('', params); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); +}); + +test('searchFiles', async () => { + const teamId = 'team_id'; + const terms = 'search terms'; + const isOrSearch = true; + const params = {terms, is_or_search: isOrSearch}; + const expectedUrl = `${client.getTeamRoute(teamId)}/files/search`; + const expectedOptions = {method: 'post', body: params}; + + await client.searchFiles(teamId, terms, isOrSearch); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); +}); diff --git a/app/client/rest/files.ts b/app/client/rest/files.ts index d899acdf0..a5608f800 100644 --- a/app/client/rest/files.ts +++ b/app/client/rest/files.ts @@ -20,8 +20,8 @@ export interface ClientFilesMix { skipBytes?: number, isBookmark?: boolean, ) => () => void; - searchFiles: (teamId: string, terms: string) => Promise; - searchFilesWithParams: (teamId: string, FileSearchParams: string) => Promise; + searchFiles: (teamId: string, terms: string, isOrSearch: boolean) => Promise; + searchFilesWithParams: (teamId: string, FileSearchParams: FileSearchParams) => Promise; } const ClientFiles = >(superclass: TBase) => class extends superclass { diff --git a/app/client/rest/general.test.ts b/app/client/rest/general.test.ts new file mode 100644 index 000000000..86af1068c --- /dev/null +++ b/app/client/rest/general.test.ts @@ -0,0 +1,168 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; +import ClientError from './error'; + +import type ClientBase from './base'; +import type {ClientGeneralMix} from './general'; + +describe('ClientGeneral', () => { + let client: ClientGeneralMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('ping', async () => { + const deviceId = 'device1'; + const timeoutInterval = 1000; + const groupLabel = 'group1'; + const expectedUrl = `${client.urlVersion}/system/ping?time=${Date.now()}&device_id=${deviceId}`; + const expectedOptions = {method: 'get', timeoutInterval, groupLabel}; + + await client.ping(deviceId, timeoutInterval, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions, false); + }); + + test('logClientError', async () => { + client.enableLogging = true; + const message = 'debug message'; + const level = 'DEBUG'; + const expectedUrl = `${client.urlVersion}/logs`; + const expectedOptions = {method: 'post', body: {message, level}}; + + await client.logClientError(message, level); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default level + await client.logClientError(message); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, {method: 'post', body: {message, level: 'ERROR'}}); + }); + + test('logClientError should throw error if logging is disabled', async () => { + client.enableLogging = false; + const message = 'error message'; + const level = 'ERROR'; + const expectedUrl = `${client.urlVersion}/logs`; + + await expect(client.logClientError(message, level)).rejects.toThrow(new ClientError(client.apiClient.baseUrl, { + message: 'Logging disabled.', + url: expectedUrl, + })); + }); + + test('getClientConfigOld', async () => { + const groupLabel = 'group1'; + const expectedUrl = `${client.urlVersion}/config/client?format=old`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getClientConfigOld(groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getClientLicenseOld', async () => { + const groupLabel = 'group1'; + const expectedUrl = `${client.urlVersion}/license/client?format=old`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getClientLicenseOld(groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTimezones', async () => { + const expectedUrl = client.getTimezonesRoute(); + const expectedOptions = {method: 'get'}; + + await client.getTimezones(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getGlobalDataRetentionPolicy', async () => { + const groupLabel = 'group1'; + const expectedUrl = `${client.getGlobalDataRetentionRoute()}/policy`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getGlobalDataRetentionPolicy(groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamDataRetentionPolicies', async () => { + const userId = 'user1'; + const page = 1; + const perPage = 10; + const groupLabel = 'group1'; + const expectedUrl = `${client.getGranularDataRetentionRoute(userId)}/team_policies${buildQueryString({page, per_page: perPage})}`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getTeamDataRetentionPolicies(userId, page, perPage, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getTeamDataRetentionPolicies(userId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getGranularDataRetentionRoute(userId)}/team_policies${buildQueryString({page: 0, per_page: PER_PAGE_DEFAULT})}`, {method: 'get', groupLabel: undefined}); + }); + + test('getChannelDataRetentionPolicies', async () => { + const userId = 'user1'; + const page = 1; + const perPage = 10; + const groupLabel = 'group1'; + const expectedUrl = `${client.getGranularDataRetentionRoute(userId)}/channel_policies${buildQueryString({page, per_page: perPage})}`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getChannelDataRetentionPolicies(userId, page, perPage, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getChannelDataRetentionPolicies(userId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getGranularDataRetentionRoute(userId)}/channel_policies${buildQueryString({page: 0, per_page: PER_PAGE_DEFAULT})}`, {method: 'get', groupLabel: undefined}); + }); + + test('getRolesByNames', async () => { + const rolesNames = ['role1', 'role2']; + const groupLabel = 'group1'; + const expectedUrl = `${client.getRolesRoute()}/names`; + const expectedOptions = {method: 'post', body: rolesNames, groupLabel}; + + await client.getRolesByNames(rolesNames, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getRedirectLocation', async () => { + const urlParam = 'http://example.com'; + const expectedUrl = `${client.getRedirectLocationRoute()}${buildQueryString({url: urlParam})}`; + const expectedOptions = {method: 'get'}; + + // Test with empty url + await client.getRedirectLocation(''); + expect(client.doFetch).not.toHaveBeenCalled(); + + // Test with non-empty url + await client.getRedirectLocation(urlParam); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('sendPerformanceReport', async () => { + const report = {start: 0, end: 1} as PerformanceReport; + const expectedUrl = client.getPerformanceRoute(); + const expectedOptions = {method: 'post', body: report}; + + await client.sendPerformanceReport(report); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/groups.test.ts b/app/client/rest/groups.test.ts new file mode 100644 index 000000000..6985f651a --- /dev/null +++ b/app/client/rest/groups.test.ts @@ -0,0 +1,103 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientGroupsMix} from './groups'; + +describe('ClientGroups', () => { + let client: ClientGroupsMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getGroups', async () => { + const params = { + query: 'test', + filterAllowReference: true, + page: 1, + perPage: 10, + since: 0, + includeMemberCount: true, + }; + + await client.getGroups(params); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/groups${buildQueryString({ + q: params.query, + filter_allow_reference: params.filterAllowReference, + page: params.page, + per_page: params.perPage, + since: params.since, + include_member_count: params.includeMemberCount, + })}`, + {method: 'get'}, + ); + + // Test with default values + await client.getGroups({}); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/groups${buildQueryString({ + q: '', + filter_allow_reference: true, + page: 0, + per_page: PER_PAGE_DEFAULT, + since: 0, + include_member_count: false, + })}`, + {method: 'get'}, + ); + }); + + test('getAllGroupsAssociatedToChannel', async () => { + const channelId = 'channel1'; + const groupLabel = 'testGroup'; + + await client.getAllGroupsAssociatedToChannel(channelId, undefined, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/channels/${channelId}/groups${buildQueryString({ + paginate: false, + filter_allow_reference: false, + include_member_count: true, + })}`, + {method: 'get', groupLabel}, + ); + }); + + test('getAllGroupsAssociatedToTeam', async () => { + const teamId = 'team1'; + + await client.getAllGroupsAssociatedToTeam(teamId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/teams/${teamId}/groups${buildQueryString({ + paginate: false, + filter_allow_reference: false, + })}`, + {method: 'get'}, + ); + }); + + test('getAllGroupsAssociatedToMembership', async () => { + const userId = 'user1'; + const groupLabel = 'testGroup'; + + await client.getAllGroupsAssociatedToMembership(userId, undefined, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/users/${userId}/groups${buildQueryString({ + paginate: false, + filter_allow_reference: false, + })}`, + {method: 'get', groupLabel}, + ); + }); +}); diff --git a/app/client/rest/integrations.test.ts b/app/client/rest/integrations.test.ts new file mode 100644 index 000000000..e73f01f6a --- /dev/null +++ b/app/client/rest/integrations.test.ts @@ -0,0 +1,97 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientIntegrationsMix} from './integrations'; + +describe('ClientIntegrations', () => { + let client: ClientIntegrationsMix & ClientBase; + const teamId = 'team_id'; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getCommandsList', async () => { + await client.getCommandsList(teamId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getCommandsRoute()}?team_id=${teamId}`, + {method: 'get'}, + ); + }); + + test('getCommandAutocompleteSuggestionsList', async () => { + const userInput = 'input'; + const channelId = 'channel_id'; + const rootId = 'root_id'; + await client.getCommandAutocompleteSuggestionsList(userInput, teamId, channelId, rootId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getTeamRoute(teamId)}/commands/autocomplete_suggestions${buildQueryString({user_input: userInput, team_id: teamId, channel_id: channelId, root_id: rootId})}`, + {method: 'get'}, + ); + }); + + test('getAutocompleteCommandsList', async () => { + const page = 1; + const perPage = 10; + await client.getAutocompleteCommandsList(teamId, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getTeamRoute(teamId)}/commands/autocomplete${buildQueryString({page, per_page: perPage})}`, + {method: 'get'}, + ); + + // Test with default values + await client.getAutocompleteCommandsList(teamId); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getTeamRoute(teamId)}/commands/autocomplete${buildQueryString({page: 0, per_page: PER_PAGE_DEFAULT})}`, + {method: 'get'}, + ); + }); + + test('executeCommand', async () => { + const command = '/command'; + const commandArgs = {channel_id: 'channel_id'} as CommandArgs; + await client.executeCommand(command, commandArgs); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getCommandsRoute()}/execute`, + {method: 'post', body: {command, ...commandArgs}}, + ); + + // Test without commandArgs + await client.executeCommand(command); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getCommandsRoute()}/execute`, + {method: 'post', body: {command}}, + ); + }); + + test('addCommand', async () => { + const command = {id: 'command_id', trigger: 'trigger'} as Command; + await client.addCommand(command); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getCommandsRoute(), + {method: 'post', body: command}, + ); + }); + + test('submitInteractiveDialog', async () => { + const data = {url: 'data'} as DialogSubmission; + await client.submitInteractiveDialog(data); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/actions/dialogs/submit`, + {method: 'post', body: data}, + ); + }); +}); diff --git a/app/client/rest/nps.test.ts b/app/client/rest/nps.test.ts new file mode 100644 index 000000000..59363c77a --- /dev/null +++ b/app/client/rest/nps.test.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {General} from '@constants'; +import TestHelper from '@test/test_helper'; + +import type ClientBase from './base'; +import type {ClientNPSMix} from './nps'; + +describe('ClientNPS', () => { + let client: ClientNPSMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('npsGiveFeedbackAction', async () => { + const expectedUrl = `${client.getPluginRoute(General.NPS_PLUGIN_ID)}/api/v1/give_feedback`; + const expectedOptions = { + method: 'post', + }; + + await client.npsGiveFeedbackAction(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/plugins.test.ts b/app/client/rest/plugins.test.ts new file mode 100644 index 000000000..d4b594156 --- /dev/null +++ b/app/client/rest/plugins.test.ts @@ -0,0 +1,27 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; + +import type ClientBase from './base'; +import type {ClientPluginsMix} from './plugins'; + +describe('ClientPlugins', () => { + let client: ClientPluginsMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getPluginsManifests', async () => { + const expectedUrl = `${client.getPluginsRoute()}/webapp`; + const expectedOptions = { + method: 'get', + }; + + await client.getPluginsManifests(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/posts.test.ts b/app/client/rest/posts.test.ts index 3fc8b4b99..9eb72a1bf 100644 --- a/app/client/rest/posts.test.ts +++ b/app/client/rest/posts.test.ts @@ -1,18 +1,311 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Client} from '.'; +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; -import type {APIClientInterface} from '@mattermost/react-native-network-client'; +import {PER_PAGE_DEFAULT} from './constants'; -const mockAPIClient = { - post: jest.fn(), -} as any as APIClientInterface; +import type ClientBase from './base'; +import type {ClientPostsMix} from './posts'; -describe('sendTestNotification', () => { - it('fetch the correct url with the correct method', () => { - const client = new Client(mockAPIClient, 'serverUrl'); - client.sendTestNotification(); - expect(mockAPIClient.post).toHaveBeenCalledWith('/api/v4/notifications/test', expect.anything()); +describe('ClientPosts', () => { + let client: ClientPostsMix & ClientBase; + + beforeEach(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('createPost', async () => { + const post = {id: 'post_id', message: 'message'} as Post; + await client.createPost(post); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getPostsRoute(), + {method: 'post', body: post, noRetry: true}, + ); + }); + + test('updatePost', async () => { + const post = {id: 'post_id', message: 'updated message'} as Post; + await client.updatePost(post); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getPostRoute(post.id), + {method: 'put', body: post}, + ); + }); + + test('getPost', async () => { + const postId = 'post_id'; + await client.getPost(postId); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getPostRoute(postId), + {method: 'get', groupLabel: undefined}, + ); + }); + + test('patchPost', async () => { + const postPatch = {id: 'post_id', message: 'patched message'}; + await client.patchPost(postPatch); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postPatch.id)}/patch`, + {method: 'put', body: postPatch}, + ); + }); + + test('deletePost', async () => { + const postId = 'post_id'; + await client.deletePost(postId); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getPostRoute(postId), + {method: 'delete'}, + ); + }); + + test('getPostThread', async () => { + const postId = 'post_id'; + const options = {fetchThreads: true, collapsedThreads: false, collapsedThreadsExtended: false, direction: 'up'} as FetchPaginatedThreadOptions; + await client.getPostThread(postId, options); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/thread${buildQueryString({skipFetchThreads: !options.fetchThreads, collapsedThreads: options.collapsedThreads, collapsedThreadsExtended: options.collapsedThreadsExtended, direction: options.direction, perPage: PER_PAGE_DEFAULT})}`, + {method: 'get', groupLabel: undefined}, + ); + + // Test with default options + await client.getPostThread(postId, {fetchAll: true}); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/thread${buildQueryString({skipFetchThreads: false, collapsedThreads: false, collapsedThreadsExtended: false, direction: 'up', perPage: PER_PAGE_DEFAULT})}`, + {method: 'get', groupLabel: undefined}, + ); + }); + + test('getPosts', async () => { + const channelId = 'channel_id'; + await client.getPosts(channelId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getChannelRoute(channelId)}/posts${buildQueryString({page: 0, per_page: PER_PAGE_DEFAULT, collapsedThreads: false, collapsedThreadsExtended: false})}`, + {method: 'get', groupLabel: undefined}, + ); + }); + + test('getPostsSince', async () => { + const channelId = 'channel_id'; + const since = 123456789; + await client.getPostsSince(channelId, since); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getChannelRoute(channelId)}/posts${buildQueryString({since, collapsedThreads: false, collapsedThreadsExtended: false})}`, + {method: 'get', groupLabel: undefined}, + ); + }); + + test('getPostsBefore', async () => { + const channelId = 'channel_id'; + const postId = 'post_id'; + await client.getPostsBefore(channelId, postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getChannelRoute(channelId)}/posts${buildQueryString({before: postId, page: 0, per_page: PER_PAGE_DEFAULT, collapsedThreads: false, collapsedThreadsExtended: false})}`, + {method: 'get'}, + ); + }); + + test('getPostsAfter', async () => { + const channelId = 'channel_id'; + const postId = 'post_id'; + await client.getPostsAfter(channelId, postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getChannelRoute(channelId)}/posts${buildQueryString({after: postId, page: 0, per_page: PER_PAGE_DEFAULT, collapsedThreads: false, collapsedThreadsExtended: false})}`, + {method: 'get'}, + ); + }); + + test('getFileInfosForPost', async () => { + const postId = 'post_id'; + await client.getFileInfosForPost(postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/files/info`, + {method: 'get'}, + ); + }); + + test('getSavedPosts', async () => { + const userId = 'user_id'; + await client.getSavedPosts(userId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getUserRoute(userId)}/posts/flagged${buildQueryString({channel_id: '', team_id: '', page: 0, per_page: PER_PAGE_DEFAULT})}`, + {method: 'get'}, + ); + }); + + test('getPinnedPosts', async () => { + const channelId = 'channel_id'; + await client.getPinnedPosts(channelId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getChannelRoute(channelId)}/pinned`, + {method: 'get'}, + ); + }); + + test('markPostAsUnread', async () => { + const userId = 'user_id'; + const postId = 'post_id'; + await client.markPostAsUnread(userId, postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getUserRoute(userId)}/posts/${postId}/set_unread`, + {method: 'post', body: JSON.stringify({collapsed_threads_supported: true})}, + ); + }); + + test('pinPost', async () => { + const postId = 'post_id'; + await client.pinPost(postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/pin`, + {method: 'post'}, + ); + }); + + test('unpinPost', async () => { + const postId = 'post_id'; + await client.unpinPost(postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/unpin`, + {method: 'post'}, + ); + }); + + test('addReaction', async () => { + const userId = 'user_id'; + const postId = 'post_id'; + const emojiName = 'emoji_name'; + await client.addReaction(userId, postId, emojiName); + + expect(client.doFetch).toHaveBeenCalledWith( + client.getReactionsRoute(), + {method: 'post', body: {user_id: userId, post_id: postId, emoji_name: emojiName}}, + ); + }); + + test('removeReaction', async () => { + const userId = 'user_id'; + const postId = 'post_id'; + const emojiName = 'emoji_name'; + await client.removeReaction(userId, postId, emojiName); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getUserRoute(userId)}/posts/${postId}/reactions/${emojiName}`, + {method: 'delete'}, + ); + }); + + test('getReactionsForPost', async () => { + const postId = 'post_id'; + await client.getReactionsForPost(postId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/reactions`, + {method: 'get'}, + ); + }); + + test('searchPostsWithParams', async () => { + const teamId = 'team_id'; + const params = {terms: 'search terms', is_or_search: false}; + + // Test with teamId + await client.searchPostsWithParams(teamId, params); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getTeamRoute(teamId)}/posts/search`, + {method: 'post', body: params}, + ); + + // Test without teamId + await client.searchPostsWithParams('', params); + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostsRoute()}/search`, + {method: 'post', body: params}, + ); + }); + + test('searchPosts', async () => { + const teamId = 'team_id'; + const terms = 'search terms'; + const isOrSearch = false; + const spy = jest.spyOn(client, 'searchPostsWithParams').mockResolvedValue({} as SearchPostResponse); + + await client.searchPosts(teamId, terms, isOrSearch); + + expect(spy).toHaveBeenCalledWith(teamId, {terms, is_or_search: isOrSearch}); + }); + + test('doPostAction', async () => { + const postId = 'post_id'; + const actionId = 'action_id'; + const selectedOption = 'selected_option'; + const spy = jest.spyOn(client, 'doPostActionWithCookie').mockResolvedValue({}); + + await client.doPostAction(postId, actionId, selectedOption); + + expect(spy).toHaveBeenCalledWith(postId, actionId, '', selectedOption); + }); + + test('doPostActionWithCookie', async () => { + const postId = 'post_id'; + const actionId = 'action_id'; + const actionCookie = 'action_cookie'; + const selectedOption = 'selected_option'; + await client.doPostActionWithCookie(postId, actionId, actionCookie, selectedOption); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getPostRoute(postId)}/actions/${encodeURIComponent(actionId)}`, + {method: 'post', body: {selected_option: selectedOption, cookie: actionCookie}}, + ); + }); + + test('acknowledgePost', async () => { + const postId = 'post_id'; + const userId = 'user_id'; + await client.acknowledgePost(postId, userId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getUserRoute(userId)}/posts/${postId}/ack`, + {method: 'post'}, + ); + }); + + test('unacknowledgePost', async () => { + const postId = 'post_id'; + const userId = 'user_id'; + await client.unacknowledgePost(postId, userId); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.getUserRoute(userId)}/posts/${postId}/ack`, + {method: 'delete'}, + ); + }); + + test('sendTestNotification', async () => { + await client.sendTestNotification(); + + expect(client.doFetch).toHaveBeenCalledWith( + `${client.urlVersion}/notifications/test`, + {method: 'post'}, + ); }); }); diff --git a/app/client/rest/preferences.test.ts b/app/client/rest/preferences.test.ts new file mode 100644 index 000000000..1993172a5 --- /dev/null +++ b/app/client/rest/preferences.test.ts @@ -0,0 +1,59 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; + +import type ClientBase from './base'; +import type {ClientPreferencesMix} from './preferences'; + +describe('ClientPreferences', () => { + let client: ClientPreferencesMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('savePreferences', async () => { + const userId = 'user_id'; + const preferences = [{category: 'category1', name: 'name1', value: 'value1'}] as PreferenceType[]; + const groupLabel = 'group_label'; + const expectedUrl = client.getPreferencesRoute(userId); + const expectedOptions = { + method: 'put', + body: preferences, + groupLabel, + }; + + await client.savePreferences(userId, preferences, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getMyPreferences', async () => { + const groupLabel = 'group_label'; + const expectedUrl = client.getPreferencesRoute('me'); + const expectedOptions = { + method: 'get', + groupLabel, + }; + + await client.getMyPreferences(groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('deletePreferences', async () => { + const userId = 'user_id'; + const preferences = [{category: 'category1', name: 'name1', value: 'value1'}] as PreferenceType[]; + const expectedUrl = `${client.getPreferencesRoute(userId)}/delete`; + const expectedOptions = { + method: 'post', + body: preferences, + }; + + await client.deletePreferences(userId, preferences); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/teams.test.ts b/app/client/rest/teams.test.ts new file mode 100644 index 000000000..e1c1da09f --- /dev/null +++ b/app/client/rest/teams.test.ts @@ -0,0 +1,238 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientTeamsMix} from './teams'; + +describe('ClientTeams', () => { + let client: ClientTeamsMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('createTeam', async () => { + const team = {id: 'team1', name: 'testteam'} as Team; + const expectedUrl = client.getTeamsRoute(); + const expectedOptions = {method: 'post', body: team}; + + await client.createTeam(team); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('deleteTeam', async () => { + const teamId = 'team1'; + const expectedUrl = client.getTeamRoute(teamId); + const expectedOptions = {method: 'delete'}; + + await client.deleteTeam(teamId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateTeam', async () => { + const team = {id: 'team1', name: 'testteam'} as Team; + const expectedUrl = client.getTeamRoute(team.id); + const expectedOptions = {method: 'put', body: team}; + + await client.updateTeam(team); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('patchTeam', async () => { + const team = {id: 'team1', name: 'patchedteam'} as Partial & {id: string}; + const expectedUrl = `${client.getTeamRoute(team.id)}/patch`; + const expectedOptions = {method: 'put', body: team}; + + await client.patchTeam(team); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeams', async () => { + const page = 1; + const perPage = 10; + const includeTotalCount = true; + const expectedUrl = `${client.getTeamsRoute()}?page=${page}&per_page=${perPage}&include_total_count=${includeTotalCount}`; + const expectedOptions = {method: 'get'}; + + await client.getTeams(page, perPage, includeTotalCount); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getTeams(); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getTeamsRoute()}?page=0&per_page=${PER_PAGE_DEFAULT}&include_total_count=false`, expectedOptions); + }); + + test('getTeam', async () => { + const teamId = 'team1'; + const expectedUrl = client.getTeamRoute(teamId); + const expectedOptions = {method: 'get'}; + + await client.getTeam(teamId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamByName', async () => { + const teamName = 'team_name'; + const expectedUrl = client.getTeamNameRoute(teamName); + const expectedOptions = {method: 'get'}; + + await client.getTeamByName(teamName); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getMyTeams', async () => { + const expectedUrl = `${client.getUserRoute('me')}/teams`; + const expectedOptions = {method: 'get'}; + + await client.getMyTeams(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamsForUser', async () => { + const userId = 'user1'; + const expectedUrl = `${client.getUserRoute(userId)}/teams`; + const expectedOptions = {method: 'get'}; + + await client.getTeamsForUser(userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getMyTeamMembers', async () => { + const expectedUrl = `${client.getUserRoute('me')}/teams/members`; + const expectedOptions = {method: 'get'}; + + await client.getMyTeamMembers(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamMembers', async () => { + const teamId = 'team1'; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getTeamMembersRoute(teamId)}?page=${page}&per_page=${perPage}`; + const expectedOptions = {method: 'get'}; + + await client.getTeamMembers(teamId, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + await client.getTeamMembers(teamId); + expect(client.doFetch).toHaveBeenCalledWith(`${client.getTeamMembersRoute(teamId)}?page=0&per_page=${PER_PAGE_DEFAULT}`, expectedOptions); + }); + + test('getTeamMember', async () => { + const teamId = 'team1'; + const userId = 'user1'; + const expectedUrl = client.getTeamMemberRoute(teamId, userId); + const expectedOptions = {method: 'get'}; + + await client.getTeamMember(teamId, userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamMembersByIds', async () => { + const teamId = 'team1'; + const userIds = ['user1', 'user2']; + const expectedUrl = `${client.getTeamMembersRoute(teamId)}/ids`; + const expectedOptions = {method: 'post', body: userIds}; + + await client.getTeamMembersByIds(teamId, userIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('addToTeam', async () => { + const teamId = 'team1'; + const userId = 'user1'; + const member = {user_id: userId, team_id: teamId}; + const expectedUrl = `${client.getTeamMembersRoute(teamId)}`; + const expectedOptions = {method: 'post', body: member}; + + await client.addToTeam(teamId, userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('addUsersToTeamGracefully', async () => { + const teamId = 'team1'; + const userIds = ['user1', 'user2']; + const members = userIds.map((id) => ({team_id: teamId, user_id: id})); + const expectedUrl = `${client.getTeamMembersRoute(teamId)}/batch?graceful=true`; + const expectedOptions = {method: 'post', body: members}; + + await client.addUsersToTeamGracefully(teamId, userIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('sendEmailInvitesToTeamGracefully', async () => { + const teamId = 'team1'; + const emails = ['test1@example.com', 'test2@example.com']; + const expectedUrl = `${client.getTeamRoute(teamId)}/invite/email?graceful=true`; + const expectedOptions = {method: 'post', body: emails}; + + await client.sendEmailInvitesToTeamGracefully(teamId, emails); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('joinTeam', async () => { + const inviteId = 'invite1'; + const query = buildQueryString({invite_id: inviteId}); + const expectedUrl = `${client.getTeamsRoute()}/members/invite${query}`; + const expectedOptions = {method: 'post'}; + + await client.joinTeam(inviteId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('removeFromTeam', async () => { + const teamId = 'team1'; + const userId = 'user1'; + const expectedUrl = client.getTeamMemberRoute(teamId, userId); + const expectedOptions = {method: 'delete'}; + + await client.removeFromTeam(teamId, userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamStats', async () => { + const teamId = 'team1'; + const expectedUrl = `${client.getTeamRoute(teamId)}/stats`; + const expectedOptions = {method: 'get'}; + + await client.getTeamStats(teamId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTeamIconUrl', () => { + const teamId = 'team1'; + const lastTeamIconUpdate = 123456; + const expectedUrl = `${client.getTeamRoute(teamId)}/image?_=${lastTeamIconUpdate}`; + + const result = client.getTeamIconUrl(teamId, lastTeamIconUpdate); + + expect(result).toBe(expectedUrl); + }); +}); diff --git a/app/client/rest/threads.test.ts b/app/client/rest/threads.test.ts new file mode 100644 index 000000000..b28ada6be --- /dev/null +++ b/app/client/rest/threads.test.ts @@ -0,0 +1,129 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; +import {buildQueryString} from '@utils/helpers'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientThreadsMix} from './threads'; + +const userId = 'user_id'; +const teamId = 'team_id'; +const threadId = 'thread_id'; + +describe('ClientThreads', () => { + let client: ClientThreadsMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('getThreads', async () => { + const before = 'before'; + const after = 'after'; + const pageSize = 10; + const deleted = true; + const unread = true; + const since = 123456; + const totalsOnly = true; + const serverVersion = '6.0.0'; + const excludeDirect = true; + const groupLabel = 'group1'; + const queryStringObj = { + extended: 'true', + before, + after, + deleted, + unread, + since, + totalsOnly, + excludeDirect, + per_page: pageSize, + }; + const expectedUrl = `${client.getThreadsRoute(userId, teamId)}${buildQueryString(queryStringObj)}`; + const expectedOptions = {method: 'get', groupLabel}; + + await client.getThreads(userId, teamId, before, after, pageSize, deleted, unread, since, totalsOnly, serverVersion, excludeDirect, groupLabel); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultQueryStringObj = { + extended: 'true', + before: '', + after: '', + deleted: false, + unread: false, + since: 0, + totalsOnly: false, + excludeDirect: false, + pageSize: PER_PAGE_DEFAULT, + }; + const expectedUrlDefault = `${client.getThreadsRoute(userId, teamId)}${buildQueryString(defaultQueryStringObj)}`; + await client.getThreads(userId, teamId); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrlDefault, {method: 'get', groupLabel: undefined}); + }); + + test('getThread', async () => { + const extended = false; + const expectedUrl = `${client.getThreadRoute(userId, teamId, threadId)}${buildQueryString({extended})}`; + const expectedOptions = {method: 'get'}; + + await client.getThread(userId, teamId, threadId, extended); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const expectedUrlDefault = `${client.getThreadRoute(userId, teamId, threadId)}${buildQueryString({extended: true})}`; + await client.getThread(userId, teamId, threadId); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrlDefault, expectedOptions); + }); + + test('markThreadAsRead', async () => { + const timestamp = 123456; + const expectedUrl = `${client.getThreadRoute(userId, teamId, threadId)}/read/${timestamp}`; + const expectedOptions = {method: 'put', body: {}}; + + await client.markThreadAsRead(userId, teamId, threadId, timestamp); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('markThreadAsUnread', async () => { + const postId = 'post_id'; + const expectedUrl = `${client.getThreadRoute(userId, teamId, threadId)}/set_unread/${postId}`; + const expectedOptions = {method: 'post'}; + + await client.markThreadAsUnread(userId, teamId, threadId, postId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateTeamThreadsAsRead', async () => { + const expectedUrl = `${client.getThreadsRoute(userId, teamId)}/read`; + const expectedOptions = {method: 'put', body: {}}; + + await client.updateTeamThreadsAsRead(userId, teamId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateThreadFollow', async () => { + const state = true; + const expectedUrl = `${client.getThreadRoute(userId, teamId, threadId)}/following`; + const expectedOptions = {method: 'put', body: {}}; + + await client.updateThreadFollow(userId, teamId, threadId, state); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with state = false + const expectedOptionsDelete = {method: 'delete', body: {}}; + await client.updateThreadFollow(userId, teamId, threadId, false); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptionsDelete); + }); +}); diff --git a/app/client/rest/tos.test.ts b/app/client/rest/tos.test.ts new file mode 100644 index 000000000..54ffca2a7 --- /dev/null +++ b/app/client/rest/tos.test.ts @@ -0,0 +1,41 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; + +import type ClientBase from './base'; +import type {ClientTosMix} from './tos'; + +describe('ClientTos', () => { + let client: ClientTosMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('updateMyTermsOfServiceStatus', async () => { + const termsOfServiceId = 'tos_id'; + const accepted = true; + const expectedUrl = `${client.getUserRoute('me')}/terms_of_service`; + const expectedOptions = { + method: 'post', + body: {termsOfServiceId, accepted}, + }; + + await client.updateMyTermsOfServiceStatus(termsOfServiceId, accepted); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getTermsOfService', async () => { + const expectedUrl = `${client.urlVersion}/terms_of_service`; + const expectedOptions = { + method: 'get', + }; + + await client.getTermsOfService(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +}); diff --git a/app/client/rest/users.test.ts b/app/client/rest/users.test.ts new file mode 100644 index 000000000..4fbc0cb1c --- /dev/null +++ b/app/client/rest/users.test.ts @@ -0,0 +1,500 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import TestHelper from '@test/test_helper'; + +import {PER_PAGE_DEFAULT} from './constants'; + +import type ClientBase from './base'; +import type {ClientUsersMix} from './users'; + +describe('ClientUsers', () => { + let client: ClientUsersMix & ClientBase; + + beforeAll(() => { + client = TestHelper.createClient(); + client.doFetch = jest.fn(); + }); + + test('createUser', async () => { + const user = {id: 'user1', username: 'testuser'} as UserProfile; + const token = 'token'; + const inviteId = 'inviteId'; + const expectedUrl = `${client.getUsersRoute()}?t=${token}&iid=${inviteId}`; + const expectedOptions = {method: 'post', body: user}; + + await client.createUser(user, token, inviteId); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + await client.createUser(user, '', ''); + expect(client.doFetch).toHaveBeenCalledWith(client.getUsersRoute(), expectedOptions); + }); + + test('patchMe', async () => { + const userPatch = {username: 'newusername'}; + const expectedUrl = `${client.getUserRoute('me')}/patch`; + const expectedOptions = {method: 'put', body: userPatch}; + + await client.patchMe(userPatch); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('patchUser', async () => { + const userPatch = {id: 'user1', username: 'newusername'}; + const expectedUrl = `${client.getUserRoute(userPatch.id)}/patch`; + const expectedOptions = {method: 'put', body: userPatch}; + + await client.patchUser(userPatch); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateUser', async () => { + const user = {id: 'user1', username: 'updateduser'} as UserProfile; + const expectedUrl = client.getUserRoute(user.id); + const expectedOptions = {method: 'put', body: user}; + + await client.updateUser(user); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('demoteUserToGuest', async () => { + const userId = 'user1'; + const expectedUrl = `${client.getUserRoute(userId)}/demote`; + const expectedOptions = {method: 'post'}; + + await client.demoteUserToGuest(userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getKnownUsers', async () => { + const expectedUrl = `${client.getUsersRoute()}/known`; + const expectedOptions = {method: 'get'}; + + await client.getKnownUsers(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('sendPasswordResetEmail', async () => { + const email = 'test@example.com'; + const expectedUrl = `${client.getUsersRoute()}/password/reset/send`; + const expectedOptions = {method: 'post', body: {email}}; + + await client.sendPasswordResetEmail(email); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('setDefaultProfileImage', async () => { + const userId = 'user1'; + const expectedUrl = `${client.getUserRoute(userId)}/image`; + const expectedOptions = {method: 'delete'}; + + await client.setDefaultProfileImage(userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('login', async () => { + const loginId = 'testuser'; + const password = 'password'; + const token = 'token'; + const deviceId = 'deviceId'; + const ldapOnly = true; + const expectedUrl = `${client.getUsersRoute()}/login`; + const expectedOptions = { + method: 'post', + body: { + device_id: deviceId, + login_id: loginId, + password, + token, + ldap_only: 'true', + }, + headers: {'Cache-Control': 'no-store'}, + }; + + await client.login(loginId, password, token, deviceId, ldapOnly); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions, false); + + // Test with default values + const defaultExpectedOptions = { + method: 'post', + body: { + device_id: '', + login_id: loginId, + password, + token: '', + }, + headers: {'Cache-Control': 'no-store'}, + }; + + await client.login(loginId, password); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, defaultExpectedOptions, false); + }); + + test('loginById', async () => { + const id = 'user1'; + const password = 'password'; + const token = 'token'; + const deviceId = 'deviceId'; + const expectedUrl = `${client.getUsersRoute()}/login`; + const expectedOptions = { + method: 'post', + body: { + device_id: deviceId, + id, + password, + token, + }, + headers: {'Cache-Control': 'no-store'}, + }; + + await client.loginById(id, password, token, deviceId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions, false); + + // Test with default values + const defaultExpectedOptions = { + method: 'post', + body: { + device_id: '', + id, + password, + token: '', + }, + headers: {'Cache-Control': 'no-store'}, + }; + + await client.loginById(id, password); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, defaultExpectedOptions, false); + }); + + test('logout', async () => { + const expectedUrl = `${client.getUsersRoute()}/logout`; + const expectedOptions = {method: 'post'}; + + await client.logout(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getProfiles', async () => { + const page = 1; + const perPage = 10; + const options = {active: true}; + const expectedUrl = `${client.getUsersRoute()}?page=${page}&per_page=${perPage}&active=true`; + const expectedOptions = {method: 'get'}; + + await client.getProfiles(page, perPage, options); + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultExpectedUrl = `${client.getUsersRoute()}?page=0&per_page=${PER_PAGE_DEFAULT}`; + await client.getProfiles(); + expect(client.doFetch).toHaveBeenCalledWith(defaultExpectedUrl, expectedOptions); + }); + + test('getProfilesByIds', async () => { + const userIds = ['user1', 'user2']; + const options = {active: true}; + const expectedUrl = `${client.getUsersRoute()}/ids?active=true`; + const expectedOptions = {method: 'post', body: userIds}; + + await client.getProfilesByIds(userIds, options); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getProfilesByUsernames', async () => { + const usernames = ['user1', 'user2']; + const expectedUrl = `${client.getUsersRoute()}/usernames`; + const expectedOptions = {method: 'post', body: usernames}; + + await client.getProfilesByUsernames(usernames); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getProfilesInTeam', async () => { + const teamId = 'team1'; + const page = 1; + const perPage = 10; + const sort = 'username'; + const options = {active: true}; + const expectedUrl = `${client.getUsersRoute()}?active=true&in_team=${teamId}&page=${page}&per_page=${perPage}&sort=${sort}`; + const expectedOptions = {method: 'get'}; + + await client.getProfilesInTeam(teamId, page, perPage, sort, options); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultExpectedUrl = `${client.getUsersRoute()}?in_team=${teamId}&page=0&per_page=${PER_PAGE_DEFAULT}&sort=`; + await client.getProfilesInTeam(teamId); + expect(client.doFetch).toHaveBeenCalledWith(defaultExpectedUrl, expectedOptions); + }); + + test('getProfilesNotInTeam', async () => { + const teamId = 'team1'; + const groupConstrained = true; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getUsersRoute()}?not_in_team=${teamId}&page=${page}&per_page=${perPage}&group_constrained=true`; + const expectedOptions = {method: 'get'}; + + await client.getProfilesNotInTeam(teamId, groupConstrained, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultExpectedUrl = `${client.getUsersRoute()}?not_in_team=${teamId}&page=0&per_page=${PER_PAGE_DEFAULT}`; + await client.getProfilesNotInTeam(teamId, false); + expect(client.doFetch).toHaveBeenCalledWith(defaultExpectedUrl, expectedOptions); + }); + + test('getProfilesWithoutTeam', async () => { + const page = 1; + const perPage = 10; + const options = {active: true}; + const expectedUrl = `${client.getUsersRoute()}?active=true&without_team=1&page=${page}&per_page=${perPage}`; + const expectedOptions = {method: 'get'}; + + await client.getProfilesWithoutTeam(page, perPage, options); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultExpectedUrl = `${client.getUsersRoute()}?without_team=1&page=0&per_page=${PER_PAGE_DEFAULT}`; + await client.getProfilesWithoutTeam(); + expect(client.doFetch).toHaveBeenCalledWith(defaultExpectedUrl, expectedOptions); + }); + + test('getProfilesInChannel', async () => { + const channelId = 'channel1'; + const options = {active: true}; + const expectedUrl = `${client.getUsersRoute()}?in_channel=${channelId}&active=true`; + const expectedOptions = {method: 'get'}; + + await client.getProfilesInChannel(channelId, options); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getProfilesInGroupChannels', async () => { + const channelsIds = ['channel1', 'channel2']; + const expectedUrl = `${client.getUsersRoute()}/group_channels`; + const expectedOptions = {method: 'post', body: channelsIds}; + + await client.getProfilesInGroupChannels(channelsIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getProfilesNotInChannel', async () => { + const teamId = 'team1'; + const channelId = 'channel1'; + const groupConstrained = true; + const page = 1; + const perPage = 10; + const expectedUrl = `${client.getUsersRoute()}?in_team=${teamId}¬_in_channel=${channelId}&page=${page}&per_page=${perPage}&group_constrained=true`; + const expectedOptions = {method: 'get'}; + + await client.getProfilesNotInChannel(teamId, channelId, groupConstrained, page, perPage); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + + // Test with default values + const defaultExpectedUrl = `${client.getUsersRoute()}?in_team=${teamId}¬_in_channel=${channelId}&page=0&per_page=${PER_PAGE_DEFAULT}`; + await client.getProfilesNotInChannel(teamId, channelId, false); + expect(client.doFetch).toHaveBeenCalledWith(defaultExpectedUrl, expectedOptions); + }); + + test('getMe', async () => { + const expectedUrl = client.getUserRoute('me'); + const expectedOptions = {method: 'get'}; + + await client.getMe(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getUser', async () => { + const userId = 'user1'; + const expectedUrl = client.getUserRoute(userId); + const expectedOptions = {method: 'get'}; + + await client.getUser(userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getUserByUsername', async () => { + const username = 'testuser'; + const expectedUrl = `${client.getUsersRoute()}/username/${username}`; + const expectedOptions = {method: 'get'}; + + await client.getUserByUsername(username); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getUserByEmail', async () => { + const email = 'test@example.com'; + const expectedUrl = `${client.getUsersRoute()}/email/${email}`; + const expectedOptions = {method: 'get'}; + + await client.getUserByEmail(email); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getProfilePictureUrl', () => { + const userId = 'user1'; + + // Test with non-zero lastPictureUpdate + const lastPictureUpdate = 123456; + let url = client.getProfilePictureUrl(userId, lastPictureUpdate); + expect(url).toBe(`${client.getUserRoute(userId)}/image?_=${lastPictureUpdate}`); + + // Test with zero lastPictureUpdate + url = client.getProfilePictureUrl(userId, 0); + expect(url).toBe(`${client.getUserRoute(userId)}/image`); + }); + + test('getDefaultProfilePictureUrl', () => { + const userId = 'user1'; + const expectedUrl = `${client.getUserRoute(userId)}/image/default`; + + const result = client.getDefaultProfilePictureUrl(userId); + + expect(result).toBe(expectedUrl); + }); + + test('autocompleteUsers', async () => { + const name = 'test'; + const teamId = 'team1'; + const channelId = 'channel1'; + const options = {limit: 10}; + const expectedUrl = `${client.getUsersRoute()}/autocomplete?in_team=${teamId}&name=${name}&in_channel=${channelId}&limit=${options.limit}`; + const expectedOptions = {method: 'get'}; + + await client.autocompleteUsers(name, teamId, channelId, options); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getSessions', async () => { + const userId = 'user1'; + const expectedUrl = `${client.getUserRoute(userId)}/sessions`; + const expectedOptions = {method: 'get', headers: {'Cache-Control': 'no-store'}}; + + await client.getSessions(userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('checkUserMfa', async () => { + const loginId = 'testuser'; + const expectedUrl = `${client.getUsersRoute()}/mfa`; + const expectedOptions = {method: 'post', body: {login_id: loginId}, headers: {'Cache-Control': 'no-store'}}; + + await client.checkUserMfa(loginId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('setExtraSessionProps', async () => { + const deviceId = 'device1'; + const deviceNotificationDisabled = true; + const version = '1.0.0'; + const expectedUrl = `${client.getUsersRoute()}/sessions/device`; + const expectedOptions = { + method: 'put', + body: { + device_id: deviceId, + device_notification_disabled: 'true', + mobile_version: version, + }, + }; + + await client.setExtraSessionProps(deviceId, deviceNotificationDisabled, version); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('searchUsers', async () => { + const term = 'test'; + const options = {team_id: 'team_id'}; + const expectedUrl = `${client.getUsersRoute()}/search`; + const expectedOptions = {method: 'post', body: {term, ...options}}; + + await client.searchUsers(term, options); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getStatusesByIds', async () => { + const userIds = ['user1', 'user2']; + const expectedUrl = `${client.getUsersRoute()}/status/ids`; + const expectedOptions = {method: 'post', body: userIds}; + + await client.getStatusesByIds(userIds); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('getStatus', async () => { + const userId = 'user1'; + const expectedUrl = `${client.getUserRoute(userId)}/status`; + const expectedOptions = {method: 'get'}; + + await client.getStatus(userId); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateStatus', async () => { + const status = {user_id: 'user1', status: 'online'} as UserStatus; + const expectedUrl = `${client.getUserRoute(status.user_id)}/status`; + const expectedOptions = {method: 'put', body: status}; + + await client.updateStatus(status); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('updateCustomStatus', async () => { + const customStatus = {emoji: 'smile', text: 'Happy'}; + const expectedUrl = `${client.getUserRoute('me')}/status/custom`; + const expectedOptions = {method: 'put', body: customStatus}; + + await client.updateCustomStatus(customStatus); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('unsetCustomStatus', async () => { + const expectedUrl = `${client.getUserRoute('me')}/status/custom`; + const expectedOptions = {method: 'delete'}; + + await client.unsetCustomStatus(); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); + + test('removeRecentCustomStatus', async () => { + const customStatus = {emoji: 'smile', text: 'Happy'}; + const expectedUrl = `${client.getUserRoute('me')}/status/custom/recent/delete`; + const expectedOptions = {method: 'post', body: customStatus}; + + await client.removeRecentCustomStatus(customStatus); + + expect(client.doFetch).toHaveBeenCalledWith(expectedUrl, expectedOptions); + }); +});