From 8c8348ea0bbf9da50070451a6871a058a019b4a4 Mon Sep 17 00:00:00 2001 From: Joseph Baylon Date: Thu, 15 Jul 2021 09:10:08 -0700 Subject: [PATCH] MM-35681 Detox/E2E: Add e2e for bot account, mention badges, at mention (#5539) * MM-35681 Detox/E2E: Add e2e for bot account, mention badges, at mention * Fix return for apiUpdateUserActiveStatus * Fix param comment for apiCreateBot * Move it blocks to the top --- .../post_list/post/header/tag/index.tsx | 1 + detox/e2e/support/server_api/bot.js | 58 +++++++++ detox/e2e/support/server_api/channel.js | 4 +- detox/e2e/support/server_api/index.js | 2 + detox/e2e/support/server_api/team.js | 4 +- detox/e2e/support/server_api/user.js | 24 +++- .../e2e/support/ui/component/autocomplete.js | 6 +- detox/e2e/support/ui/component/post.js | 3 + detox/e2e/support/ui/component/post_list.js | 2 + detox/e2e/support/ui/screen/channel.js | 7 + .../support/ui/screen/search_result_post.js | 2 + detox/e2e/support/ui/screen/user_profile.js | 4 +- detox/e2e/test/autocomplete/at_mention.e2e.js | 73 ++++++++--- detox/e2e/test/bot_account/bot_account.e2e.js | 78 +++++++++++ .../test/notifications/mention_badges.e2e.js | 121 ++++++++++++++++++ 15 files changed, 364 insertions(+), 25 deletions(-) create mode 100644 detox/e2e/support/server_api/bot.js create mode 100644 detox/e2e/test/bot_account/bot_account.e2e.js create mode 100644 detox/e2e/test/notifications/mention_badges.e2e.js diff --git a/app/components/post_list/post/header/tag/index.tsx b/app/components/post_list/post/header/tag/index.tsx index 0f0da24f6..a47672246 100644 --- a/app/components/post_list/post/header/tag/index.tsx +++ b/app/components/post_list/post/header/tag/index.tsx @@ -31,6 +31,7 @@ const HeaderTag = ({ return ( ); diff --git a/detox/e2e/support/server_api/bot.js b/detox/e2e/support/server_api/bot.js new file mode 100644 index 000000000..1bcde16c5 --- /dev/null +++ b/detox/e2e/support/server_api/bot.js @@ -0,0 +1,58 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {capitalize, getRandomId} from '@support/utils'; + +import client from './client'; +import {getResponseFromError} from './common'; + +// **************************************************************** +// Bots +// See https://api.mattermost.com/#tag/bots +// +// Exported API function should have the following: +// - documented using JSDoc +// - meaningful description +// - match the referenced API endpoints +// - parameter/s defined by `@param` +// - return value defined by `@return` +// **************************************************************** + +/** + * Create a bot. + * See https://api.mattermost.com/#operation/CreateBot + * @param {string} option.prefix - prefix to username and display name + * @param {Object} option.bot - bot object to be created + * @return {Object} returns {bot} on success or {error, status} on error + */ +export const apiCreateBot = async ({prefix = 'bot', bot = null} = {}) => { + try { + const newBot = bot || generateRandomBot({prefix}); + + const response = await client.post( + '/api/v4/bots', + newBot, + ); + + return {bot: response.data}; + } catch (err) { + return getResponseFromError(err); + } +}; + +export const generateRandomBot = ({prefix = 'bot', randomIdLength = 6} = {}) => { + const randomId = getRandomId(randomIdLength); + + return { + username: `${prefix}-${randomId}`, + display_name: `${capitalize(prefix)} ${randomId}`, + description: `Test bot description ${randomId}`, + }; +}; + +export const Bot = { + apiCreateBot, + generateRandomBot, +}; + +export default Bot; diff --git a/detox/e2e/support/server_api/channel.js b/detox/e2e/support/server_api/channel.js index 0d65a55e2..57f6990e5 100644 --- a/detox/e2e/support/server_api/channel.js +++ b/detox/e2e/support/server_api/channel.js @@ -43,8 +43,8 @@ export const apiAddUserToChannel = async (userId, channelId) => { * See https://api.mattermost.com/#operation/CreateChannel * @param {string} option.teamId - The team ID of the team to create the channel on * @param {string} option.type - 'O' (default) for a public channel, 'P' for a private channel - * @param {string} option.prefix - option to add prefix to name and display name - * @param {Object} option.channel - fix channel object to be created + * @param {string} option.prefix - prefix to name, display name, purpose, and header + * @param {Object} option.channel - channel object to be created * @return {Object} returns {channel} on success or {error, status} on error */ export const apiCreateChannel = async ({teamId = null, type = 'O', prefix = 'channel', channel = null} = {}) => { diff --git a/detox/e2e/support/server_api/index.js b/detox/e2e/support/server_api/index.js index ed46c7a7b..576ca0677 100644 --- a/detox/e2e/support/server_api/index.js +++ b/detox/e2e/support/server_api/index.js @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import Bot from './bot'; import Channel from './channel'; import Ldap from './ldap'; import Plugin from './plugin'; @@ -13,6 +14,7 @@ import Team from './team'; import User from './user'; export { + Bot, Channel, Ldap, Plugin, diff --git a/detox/e2e/support/server_api/team.js b/detox/e2e/support/server_api/team.js index c011a1014..316102383 100644 --- a/detox/e2e/support/server_api/team.js +++ b/detox/e2e/support/server_api/team.js @@ -43,8 +43,8 @@ export const apiAddUserToTeam = async (userId, teamId) => { * Create a team. * See https://api.mattermost.com/#operation/CreateTeam * @param {string} option.type - 'O' (default) for open, 'I' for invite only - * @param {string} option.prefix - option to add prefix to name and display name - * @param {Object} team - fix team object to be created + * @param {string} option.prefix - prefix to name and display name + * @param {Object} option.team - team object to be created * @return {Object} returns {team} on success or {error, status} on error */ export const apiCreateTeam = async ({type = 'O', prefix = 'team', team = null} = {}) => { diff --git a/detox/e2e/support/server_api/user.js b/detox/e2e/support/server_api/user.js index 89530e4c0..3df8640ee 100644 --- a/detox/e2e/support/server_api/user.js +++ b/detox/e2e/support/server_api/user.js @@ -33,7 +33,8 @@ export const apiAdminLogin = () => { /** * Create a user. * See https://api.mattermost.com/#operation/CreateUser - * @param {Object} user - user object to be created + * @param {string} option.prefix - prefix to email and username + * @param {Object} option.user - user object to be created * @return {Object} returns {user} on success or {error, status} on error */ export const apiCreateUser = async ({prefix = 'user', user = null} = {}) => { @@ -194,6 +195,26 @@ export const apiPatchUser = async (userId, userData) => { } }; +/** + * Update user active status. + * See https://api.mattermost.com/#operation/UpdateUserActive + * @param {string} userId - the user ID + * @param {boolean} active - use true to set the user active, false for inactive + * @return {Object} returns {status} on success or {error, status} on error + */ +export const apiUpdateUserActiveStatus = async (userId, active) => { + try { + const response = await client.put( + `/api/v4/users/${userId}/active`, + {active}, + ); + + return {status: response.status}; + } catch (err) { + return getResponseFromError(err); + } +}; + export const generateRandomUser = ({prefix = 'user', randomIdLength = 6} = {}) => { const randomId = getRandomId(randomIdLength); @@ -220,6 +241,7 @@ export const User = { apiLogout, apiPatchMe, apiPatchUser, + apiUpdateUserActiveStatus, generateRandomUser, }; diff --git a/detox/e2e/support/ui/component/autocomplete.js b/detox/e2e/support/ui/component/autocomplete.js index aa4b4edbb..0505c5873 100644 --- a/detox/e2e/support/ui/component/autocomplete.js +++ b/detox/e2e/support/ui/component/autocomplete.js @@ -1,13 +1,15 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import ProfilePicture from './profile_picture'; + class Autocomplete { testID = { atMentionItemPrefix: 'autocomplete.at_mention.item.', + atMentionItemProfilePicturePrefix: 'at_mention_item.profile_picture.', channelMentionItemPrefix: 'autocomplete.channel_mention.item.', autocomplete: 'autocomplete', atMentionItemName: 'at_mention_item.name', - atMentionItemProfilePicture: 'at_mention_item.profile_picture', atMentionItemUsername: 'at_mention_item.username', atMentionSuggestionList: 'at_mention_suggestion.list', channelMentionSuggestionList: 'channel_mention_suggestion.list', @@ -26,7 +28,7 @@ class Autocomplete { getAtMentionItem = (userId) => { const atMentionItemMatcher = by.id(`${this.testID.atMentionItemPrefix}${userId}`); const atMentionItemNameMatcher = by.id(this.testID.atMentionItemName).withAncestor(atMentionItemMatcher); - const atMentionItemProfilePictureMatcher = by.id(this.testID.atMentionItemProfilePicture).withAncestor(atMentionItemMatcher); + const atMentionItemProfilePictureMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.atMentionItemProfilePicturePrefix, userId).withAncestor(atMentionItemMatcher); const atMentionItemUsernameMatcher = by.id(this.testID.atMentionItemUsername).withAncestor(atMentionItemMatcher); return { diff --git a/detox/e2e/support/ui/component/post.js b/detox/e2e/support/ui/component/post.js index e56a7cb6a..6dc326926 100644 --- a/detox/e2e/support/ui/component/post.js +++ b/detox/e2e/support/ui/component/post.js @@ -13,6 +13,7 @@ class Post { postHeaderCommentedOn: 'post_header.commented_on', postHeaderDateTime: 'post_header.date_time', postHeaderDisplayName: 'post_header.display_name', + postHeaderBotTag: 'post_header.bot_tag', postHeaderGuestTag: 'post_header.guest_tag', postHeaderReply: 'post_header.reply', postHeaderReplyCount: 'post_header.reply.count', @@ -58,6 +59,7 @@ class Post { const postItemHeaderCommentedOnMatcher = by.id(this.testID.postHeaderCommentedOn).withAncestor(postItemMatcher); const postItemHeaderDateTimeMatcher = by.id(this.testID.postHeaderDateTime).withAncestor(postItemMatcher); const postItemHeaderDisplayNameMatcher = by.id(this.testID.postHeaderDisplayName).withAncestor(postItemMatcher); + const postItemHeaderBotTagMatcher = by.id(this.testID.postHeaderBotTag).withAncestor(postItemMatcher); const postItemHeaderGuestTagMatcher = by.id(this.testID.postHeaderGuestTag).withAncestor(postItemMatcher); const postItemHeaderReplyMatcher = by.id(this.testID.postHeaderReply).withAncestor(postItemMatcher); const postItemHeaderReplyCountMatcher = by.id(this.testID.postHeaderReplyCount).withAncestor(postItemMatcher); @@ -66,6 +68,7 @@ class Post { postItemHeaderCommentedOn: element(postItemHeaderCommentedOnMatcher), postItemHeaderDateTime: element(postItemHeaderDateTimeMatcher), postItemHeaderDisplayName: element(postItemHeaderDisplayNameMatcher), + postItemHeaderBotTag: element(postItemHeaderBotTagMatcher), postItemHeaderGuestTag: element(postItemHeaderGuestTagMatcher), postItemHeaderReply: element(postItemHeaderReplyMatcher), postItemHeaderReplyCount: element(postItemHeaderReplyCountMatcher), diff --git a/detox/e2e/support/ui/component/post_list.js b/detox/e2e/support/ui/component/post_list.js index bf21bcd5c..b686db1de 100644 --- a/detox/e2e/support/ui/component/post_list.js +++ b/detox/e2e/support/ui/component/post_list.js @@ -28,6 +28,7 @@ class PostList { postItemHeaderCommentedOn, postItemHeaderDateTime, postItemHeaderDisplayName, + postItemHeaderBotTag, postItemHeaderGuestTag, postItemHeaderReply, postItemHeaderReplyCount, @@ -50,6 +51,7 @@ class PostList { postListPostItemHeaderCommentedOn: postItemHeaderCommentedOn, postListPostItemHeaderDateTime: postItemHeaderDateTime, postListPostItemHeaderDisplayName: postItemHeaderDisplayName, + postListPostItemHeaderBotTag: postItemHeaderBotTag, postListPostItemHeaderGuestTag: postItemHeaderGuestTag, postListPostItemHeaderReply: postItemHeaderReply, postListPostItemHeaderReplyCount: postItemHeaderReplyCount, diff --git a/detox/e2e/support/ui/screen/channel.js b/detox/e2e/support/ui/screen/channel.js index 87fb8bea9..bc17c200f 100644 --- a/detox/e2e/support/ui/screen/channel.js +++ b/detox/e2e/support/ui/screen/channel.js @@ -10,6 +10,7 @@ import { PostDraft, PostList, PostOptions, + ProfilePicture, SendButton, SettingsSidebar, } from '@support/ui/component'; @@ -23,6 +24,7 @@ import {isAndroid} from '@support/utils'; class ChannelScreen { testID = { channelScreenPrefix: 'channel.', + channelIntroProfilePicturePrefix: 'channel_intro.profile_picture.', channelScreen: 'channel.screen', channelPostList: 'channel.post_list', mainSidebarDrawerButton: 'main_sidebar_drawer.button', @@ -68,6 +70,11 @@ class ChannelScreen { postList = new PostList(this.testID.channelScreenPrefix); + getChannelIntroProfilePicture = (userId) => { + const profilePictureItemMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.channelIntroProfilePicturePrefix, userId); + return element(profilePictureItemMatcher); + } + getMoreMessagesButton = () => { return this.postList.getMoreMessagesButton(); } diff --git a/detox/e2e/support/ui/screen/search_result_post.js b/detox/e2e/support/ui/screen/search_result_post.js index 14f153379..d6c212212 100644 --- a/detox/e2e/support/ui/screen/search_result_post.js +++ b/detox/e2e/support/ui/screen/search_result_post.js @@ -15,6 +15,7 @@ class SearchResultPostScreen { postItemEmoji, postItemHeaderDateTime, postItemHeaderDisplayName, + postItemHeaderBotTag, postItemHeaderGuestTag, postItemHeaderReply, postItemImage, @@ -34,6 +35,7 @@ class SearchResultPostScreen { searchResultPostItemEmoji: postItemEmoji, searchResultPostItemHeaderDateTime: postItemHeaderDateTime, searchResultPostItemHeaderDisplayName: postItemHeaderDisplayName, + searchResultPostItemHeaderBotTag: postItemHeaderBotTag, searchResultPostItemHeaderGuestTag: postItemHeaderGuestTag, searchResultPostItemHeaderReply: postItemHeaderReply, searchResultPostItemImage: postItemImage, diff --git a/detox/e2e/support/ui/screen/user_profile.js b/detox/e2e/support/ui/screen/user_profile.js index 20b251314..d7a468aab 100644 --- a/detox/e2e/support/ui/screen/user_profile.js +++ b/detox/e2e/support/ui/screen/user_profile.js @@ -11,13 +11,12 @@ class UserProfileScreen { userProfilePicturePrefix: 'user_profile.profile_picture.', userProfileScreen: 'user_profile.screen', customStatus: 'user_profile.custom_status', - profilePicture: 'user_profile.profile_picture', userProfileScrollView: 'user_profile.scroll_view', closeSettingsButton: 'close.settings.button', editButton: 'user_profile.edit.button', userProfileBotTag: 'user_profile.bot_tag', userProfileDisplayName: 'user_profile.display_name', - userProfileGuestTag: 'user_profile.bot_tag', + userProfileGuestTag: 'user_profile.guest_tag', userProfileUsername: 'user_profile.username', emailLabel: 'user_profile.display_block.email.label', emailValue: 'user_profile.display_block.email.value', @@ -38,7 +37,6 @@ class UserProfileScreen { userProfileScreen = element(by.id(this.testID.userProfileScreen)); userProfileScrollView = element(by.id(this.testID.userProfileScrollView)); customStatus = element(by.id(this.testID.customStatus)); - profilePicture = element(by.id(this.testID.profilePicture)) closeSettingsButton = element(by.id(this.testID.closeSettingsButton)); editButton = element(by.id(this.testID.editButton)); userProfileBotTag = element(by.id(this.testID.userProfileBotTag)); diff --git a/detox/e2e/test/autocomplete/at_mention.e2e.js b/detox/e2e/test/autocomplete/at_mention.e2e.js index 432ec2241..3ef59c1b8 100644 --- a/detox/e2e/test/autocomplete/at_mention.e2e.js +++ b/detox/e2e/test/autocomplete/at_mention.e2e.js @@ -9,20 +9,31 @@ import {Autocomplete} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; -import {Setup} from '@support/server_api'; +import { + Setup, + Team, + User, +} from '@support/server_api'; describe('Autocomplete', () => { - let user; const {postInput} = ChannelScreen; const {atMentionSuggestionList} = Autocomplete; + let testUser; + let testOtherUser; let userAtMentionAutocomplete; + let otherUserAtMentionAutocomplete; beforeAll(async () => { - ({user} = await Setup.apiInit()); - ({atMentionItem: userAtMentionAutocomplete} = Autocomplete.getAtMentionItem(user.id)); + const {team, user} = await Setup.apiInit(); + testUser = user; + ({atMentionItem: userAtMentionAutocomplete} = Autocomplete.getAtMentionItem(testUser.id)); + + ({user: testOtherUser} = await User.apiCreateUser()); + await Team.apiAddUserToTeam(testOtherUser.id, team.id); + ({atMentionItem: otherUserAtMentionAutocomplete} = Autocomplete.getAtMentionItem(testOtherUser.id)); // # Open channel screen - await ChannelScreen.open(user); + await ChannelScreen.open(testUser); }); beforeEach(async () => { @@ -42,7 +53,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type username - await postInput.typeText(user.username); + await postInput.typeText(testUser.username); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -55,7 +66,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type nickname - await postInput.typeText(user.nickname); + await postInput.typeText(testUser.nickname); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -68,7 +79,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type first name - await postInput.typeText(user.first_name); + await postInput.typeText(testUser.first_name); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -81,7 +92,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type last name - await postInput.typeText(user.last_name); + await postInput.typeText(testUser.last_name); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -94,7 +105,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type lowercase first name - await postInput.typeText(user.first_name.toLowerCase()); + await postInput.typeText(testUser.first_name.toLowerCase()); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -107,7 +118,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type lowercase last name - await postInput.typeText(user.last_name.toLowerCase()); + await postInput.typeText(testUser.last_name.toLowerCase()); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -120,7 +131,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type full name - await postInput.typeText(`${user.first_name} ${user.last_name}`); + await postInput.typeText(`${testUser.first_name} ${testUser.last_name}`); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -133,7 +144,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type partial full name - await postInput.typeText(`${user.first_name} ${user.last_name.substring(0, user.last_name.length - 6)}`); + await postInput.typeText(`${testUser.first_name} ${testUser.last_name.substring(0, testUser.last_name.length - 6)}`); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -146,7 +157,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type full name - await postInput.typeText(`${user.first_name} ${user.last_name}`); + await postInput.typeText(`${testUser.first_name} ${testUser.last_name}`); // * Expect at mention autocomplete to contain associated user suggestion await expect(userAtMentionAutocomplete).toExist(); @@ -178,7 +189,7 @@ describe('Autocomplete', () => { await expect(atMentionSuggestionList).toExist(); // # Type username - await postInput.typeText(user.username); + await postInput.typeText(testUser.username); // # Tap user await userAtMentionAutocomplete.tap(); @@ -190,4 +201,36 @@ describe('Autocomplete', () => { await postInput.typeText('@'); await expect(atMentionSuggestionList).toExist(); }); + + it('MM-T511 should not be able to autocomplete deactivated user', async () => { + // # Deactivate user + await User.apiDeactivateUser(testOtherUser.id); + + // # Type "@" to activate at mention autocomplete + await postInput.typeText('@'); + await Autocomplete.toBeVisible(); + await expect(atMentionSuggestionList).toExist(); + + // # Type username + await postInput.typeText(testOtherUser.username); + + // * Expect at mention autocomplete to not contain associated user suggestion + await expect(otherUserAtMentionAutocomplete).not.toExist(); + + // # Reactivate user + await User.apiUpdateUserActiveStatus(testOtherUser.id, true); + + // # Type "@" to activate at mention autocomplete + await postInput.clearText(); + await Autocomplete.toBeVisible(false); + await postInput.typeText('@'); + await Autocomplete.toBeVisible(); + await expect(atMentionSuggestionList).toExist(); + + // # Type username + await postInput.typeText(testOtherUser.username); + + // * Expect at mention autocomplete to contain associated user suggestion + await expect(otherUserAtMentionAutocomplete).toExist(); + }); }); diff --git a/detox/e2e/test/bot_account/bot_account.e2e.js b/detox/e2e/test/bot_account/bot_account.e2e.js new file mode 100644 index 000000000..89ef627dc --- /dev/null +++ b/detox/e2e/test/bot_account/bot_account.e2e.js @@ -0,0 +1,78 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// ******************************************************************* +// - [#] indicates a test step (e.g. # Go to a screen) +// - [*] indicates an assertion (e.g. * Check the title) +// - Use element testID when selecting an element. Create one if none. +// ******************************************************************* + +import { + ChannelScreen, + MoreDirectMessagesScreen, + UserProfileScreen, +} from '@support/ui/screen'; +import { + Bot, + Setup, + Team, +} from '@support/server_api'; + +describe('Bot Account', () => { + const { + getChannelIntroProfilePicture, + openMainSidebar, + } = ChannelScreen; + let testBot; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + + ({bot: testBot} = await Bot.apiCreateBot()); + await Team.apiAddUserToTeam(testBot.user_id, team.id); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T1824 should be able to show bot profile', async () => { + const { + getUserAtIndex, + searchInput, + startButton, + } = MoreDirectMessagesScreen; + const { + additionalOptionsAction, + getProfilePicture, + localTimeLabel, + sendMessageAction, + userProfileBotTag, + } = UserProfileScreen; + + // # Create a DM with the other user + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await searchInput.typeText(testBot.username); + await getUserAtIndex(0).tap(); + await startButton.tap(); + + // # Open bot profile from channel intro + await getChannelIntroProfilePicture(testBot.user_id).tap(); + + // * Verify bot profile + await UserProfileScreen.toBeVisible(); + await expect(element(by.text(`@${testBot.username}`))).toBeVisible(); + await expect(getProfilePicture(testBot.user_id)).toBeVisible(); + await expect(userProfileBotTag).toBeVisible(); + await expect(sendMessageAction.atIndex(0)).toBeVisible(); + await expect(additionalOptionsAction).not.toBeVisible(); + await expect(localTimeLabel).not.toBeVisible(); + + // # Go back to channel + await UserProfileScreen.close(); + }); +}); diff --git a/detox/e2e/test/notifications/mention_badges.e2e.js b/detox/e2e/test/notifications/mention_badges.e2e.js new file mode 100644 index 000000000..85fd39c5c --- /dev/null +++ b/detox/e2e/test/notifications/mention_badges.e2e.js @@ -0,0 +1,121 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// ******************************************************************* +// - [#] indicates a test step (e.g. # Go to a screen) +// - [*] indicates an assertion (e.g. * Check the title) +// - Use element testID when selecting an element. Create one if none. +// ******************************************************************* + +import {MainSidebar} from '@support/ui/component'; +import {ChannelScreen} from '@support/ui/screen'; +import { + Channel, + Post, + Team, + User, +} from '@support/server_api'; + +describe('Mention Badges', () => { + const { + channelNavBarTitle, + closeMainSidebar, + goToChannel, + mainSidebarDrawerButtonBadgeUnreadCount, + openTeamSidebar, + } = ChannelScreen; + const { + closeTeamSidebar, + getTeamByDisplayName, + switchTeamsButtonBadgeUnreadCount, + } = MainSidebar; + let testChannel; + let testPrivateChannel; + let testPublicChannel; + let testTeam1; + let testTeam2; + let testUser1; + let testUser2; + + beforeAll(async () => { + ({user: testUser1} = await User.apiCreateUser()); + ({team: testTeam1} = await Team.apiCreateTeam({prefix: 'team-a'})); + ({channel: testChannel} = await Channel.apiGetChannelByName(testTeam1.id, 'town-square')); + + ({user: testUser2} = await User.apiCreateUser()); + ({team: testTeam2} = await Team.apiCreateTeam({prefix: 'team-b'})); + ({channel: testPublicChannel} = await Channel.apiGetChannelByName(testTeam2.id, 'town-square')); + + await Team.apiAddUserToTeam(testUser1.id, testTeam1.id); + await Team.apiAddUserToTeam(testUser1.id, testTeam2.id); + await Team.apiAddUserToTeam(testUser2.id, testTeam1.id); + await Team.apiAddUserToTeam(testUser2.id, testTeam2.id); + + ({channel: testPrivateChannel} = await Channel.apiCreateChannel({type: 'P', teamId: testTeam2.id})); + + await Channel.apiAddUserToChannel(testUser1.id, testPrivateChannel.id); + await Channel.apiAddUserToChannel(testUser2.id, testPrivateChannel.id); + + // # Open channel screen + await ChannelScreen.open(testUser1); + + // # Clear unreads and go back to original team and channel + await openTeamSidebar(); + await getTeamByDisplayName(testTeam2.display_name).tap(); + await goToChannel(testPrivateChannel.display_name); + await openTeamSidebar(); + await getTeamByDisplayName(testTeam1.display_name).tap(); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T520_1 should display mention badges for public channel', async () => { + await expectMentionBadges(testPublicChannel); + }); + + it('MM-T520_2 should display mention badges for private channel', async () => { + await expectMentionBadges(testPrivateChannel); + }); + + const expectMentionBadges = async (messagePostedChannel) => { + // * Verify team 1 main sidebar drawer button badge does not exist + await expect(mainSidebarDrawerButtonBadgeUnreadCount).not.toExist(); + + // # Post an at-mention message to user 1 by user 2 on a channel + const testMessage = `Mention @${testUser1.username}`; + await User.apiLogin(testUser2); + await Post.apiCreatePost({ + channelId: messagePostedChannel.id, + message: testMessage, + }); + + // * Verify team 1 main sidebar drawer button badge count is 1 + await expect(channelNavBarTitle).toHaveText(testChannel.display_name); + await expect(mainSidebarDrawerButtonBadgeUnreadCount).toHaveText('1'); + + // * Verify team 2 item badge count is 1 + await openTeamSidebar(); + const {teamItemBadgeUnreadCount: team2ItemBadgeUnreadCount} = await MainSidebar.getTeam(testTeam2.id); + await expect(team2ItemBadgeUnreadCount).toHaveText('1'); + + // * Verify team 1 item badge count does not exist + const {teamItemBadgeUnreadCount: team1ItemBadgeUnreadCount} = await MainSidebar.getTeam(testTeam1.id); + await expect(team1ItemBadgeUnreadCount).not.toExist(); + + // * Verify switch teams sidebar button badge count is 1 + await closeTeamSidebar(); + await expect(switchTeamsButtonBadgeUnreadCount).toHaveText('1'); + + // # Close main sidebar + await closeMainSidebar(); + + // # Clear unreads and go back to original team and channel + await openTeamSidebar(); + await getTeamByDisplayName(testTeam2.display_name).tap(); + await goToChannel(messagePostedChannel.display_name); + await openTeamSidebar(); + await getTeamByDisplayName(testTeam1.display_name).tap(); + }; +});