From eed5b41292e3315b7f1bcff582779319ca725509 Mon Sep 17 00:00:00 2001 From: Joseph Baylon Date: Fri, 9 Jul 2021 03:53:57 -0700 Subject: [PATCH] MM-35679 Detox/E2E: Add e2e for DMs, GMs, main sidebar (#5521) --- .../sidebars/main/channels_list/list/list.js | 1 + .../__snapshots__/selected_users.test.js.snap | 4 + .../more_dms/selected_users/selected_user.js | 16 +- .../more_dms/selected_users/selected_users.js | 1 + detox/e2e/support/server_api/preference.js | 18 +++ detox/e2e/support/server_api/user.js | 2 +- .../support/ui/screen/channel_add_members.js | 4 +- detox/e2e/support/ui/screen/channel_info.js | 6 + .../e2e/support/ui/screen/channel_members.js | 4 +- .../support/ui/screen/more_direct_messages.js | 20 ++- .../e2e/test/channels/direct_messages.e2e.js | 100 +++++++++++++ detox/e2e/test/channels/group_messages.e2e.js | 141 ++++++++++++++++++ .../e2e/test/main_sidebar/main_sidebar.e2e.js | 73 +++++++++ .../test/main_sidebar/select_channel.e2e.js | 47 ------ 14 files changed, 381 insertions(+), 56 deletions(-) create mode 100644 detox/e2e/test/channels/direct_messages.e2e.js create mode 100644 detox/e2e/test/channels/group_messages.e2e.js create mode 100644 detox/e2e/test/main_sidebar/main_sidebar.e2e.js delete mode 100644 detox/e2e/test/main_sidebar/select_channel.e2e.js diff --git a/app/components/sidebars/main/channels_list/list/list.js b/app/components/sidebars/main/channels_list/list/list.js index ff34f1ff7..3432c6a20 100644 --- a/app/components/sidebars/main/channels_list/list/list.js +++ b/app/components/sidebars/main/channels_list/list/list.js @@ -260,6 +260,7 @@ export default class List extends PureComponent { leftButtons: [{ id: 'close-dms', icon: this.closeButton, + testID: 'close.more_direct_messages.button', }], }, }; diff --git a/app/screens/more_dms/selected_users/__snapshots__/selected_users.test.js.snap b/app/screens/more_dms/selected_users/__snapshots__/selected_users.test.js.snap index c0b80dd7e..01e7a0517 100644 --- a/app/screens/more_dms/selected_users/__snapshots__/selected_users.test.js.snap +++ b/app/screens/more_dms/selected_users/__snapshots__/selected_users.test.js.snap @@ -21,6 +21,7 @@ exports[`SelectedUsers should match snapshot 1`] = ` { @@ -46,13 +51,20 @@ export default class SelectedUser extends React.PureComponent { const style = getStyleFromTheme(this.props.theme); return ( - - + + {displayUsername(this.props.user, this.props.teammateNameDisplay)} , ); } diff --git a/detox/e2e/support/server_api/preference.js b/detox/e2e/support/server_api/preference.js index 94a1cd2aa..e6c8c02fe 100644 --- a/detox/e2e/support/server_api/preference.js +++ b/detox/e2e/support/server_api/preference.js @@ -33,6 +33,23 @@ export const apiSaveFavoriteChannelPreference = (userId, channelId) => { return apiSaveUserPreferences(userId, [preference]); }; +/** + * Save the user's teammate name display preference. + * @param {string} userId - the user ID + * @param {string} nameFormat - one of "username" (default), "nickname_full_name" or "full_name" + * @returns + */ +export const apiSaveTeammateNameDisplayPreference = (userId, nameFormat = 'username') => { + const preference = { + user_id: userId, + category: 'display_settings', + name: 'name_format', + value: nameFormat, + }; + + return apiSaveUserPreferences(userId, [preference]); +}; + /** * Save the user's teams order preference. * @param {string} userId - the user ID @@ -72,6 +89,7 @@ export const apiSaveUserPreferences = async (userId, preferences = []) => { export const Preference = { apiSaveFavoriteChannelPreference, + apiSaveTeammateNameDisplayPreference, apiSaveTeamsOrderPreference, apiSaveUserPreferences, }; diff --git a/detox/e2e/support/server_api/user.js b/detox/e2e/support/server_api/user.js index 84a06a622..89530e4c0 100644 --- a/detox/e2e/support/server_api/user.js +++ b/detox/e2e/support/server_api/user.js @@ -38,7 +38,7 @@ export const apiAdminLogin = () => { */ export const apiCreateUser = async ({prefix = 'user', user = null} = {}) => { try { - const newUser = user || generateRandomUser(prefix); + const newUser = user || generateRandomUser({prefix}); const response = await client.post( '/api/v4/users', diff --git a/detox/e2e/support/ui/screen/channel_add_members.js b/detox/e2e/support/ui/screen/channel_add_members.js index 20337374f..0dec09c41 100644 --- a/detox/e2e/support/ui/screen/channel_add_members.js +++ b/detox/e2e/support/ui/screen/channel_add_members.js @@ -32,11 +32,11 @@ class ChannelAddMembersScreen { const userItemTestID = `${this.testID.userItem}.${userId}`; const baseMatcher = by.id(userItemTestID); const userItemMatcher = diplayUsername ? baseMatcher.withDescendant(by.text(diplayUsername)) : baseMatcher; - const userItemUsernameDisplayMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher); + const userItemDisplayUsernameMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher); return { userItem: element(userItemMatcher), - userItemUsernameDisplay: element(userItemUsernameDisplayMatcher), + userItemDisplayUsername: element(userItemDisplayUsernameMatcher), }; } diff --git a/detox/e2e/support/ui/screen/channel_info.js b/detox/e2e/support/ui/screen/channel_info.js index 47943d501..3db5ed11e 100644 --- a/detox/e2e/support/ui/screen/channel_info.js +++ b/detox/e2e/support/ui/screen/channel_info.js @@ -110,6 +110,12 @@ class ChannelInfoScreen { } } + closeDirectOrGroupMessage = async () => { + await this.channelInfoScrollView.scrollTo('bottom'); + await this.leaveAction.tap(); + await expect(this.channelInfoScreen).not.toBeVisible(); + } + leaveChannel = async ({confirm = true, publicChannel = true, description = null} = {}) => { await this.channelInfoScrollView.scrollTo('bottom'); await this.leaveAction.tap(); diff --git a/detox/e2e/support/ui/screen/channel_members.js b/detox/e2e/support/ui/screen/channel_members.js index 0c39da3ba..5b5df0cd6 100644 --- a/detox/e2e/support/ui/screen/channel_members.js +++ b/detox/e2e/support/ui/screen/channel_members.js @@ -34,11 +34,11 @@ class ChannelMembersScreen { const userItemTestID = `${this.testID.userItem}.${userId}`; const baseMatcher = by.id(userItemTestID); const userItemMatcher = diplayUsername ? baseMatcher.withDescendant(by.text(diplayUsername)) : baseMatcher; - const userItemUsernameDisplayMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher); + const userItemDisplayUsernameMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher); return { userItem: element(userItemMatcher), - userItemUsernameDisplay: element(userItemUsernameDisplayMatcher), + userItemDisplayUsername: element(userItemDisplayUsernameMatcher), }; } diff --git a/detox/e2e/support/ui/screen/more_direct_messages.js b/detox/e2e/support/ui/screen/more_direct_messages.js index 8adec04aa..fce835a6c 100644 --- a/detox/e2e/support/ui/screen/more_direct_messages.js +++ b/detox/e2e/support/ui/screen/more_direct_messages.js @@ -10,13 +10,16 @@ class MoreDirectMessagesScreen { testID = { moreDirectMessagesScreenPrefix: 'more_direct_messages.', moreDirectMessagesScreen: 'more_direct_messages.screen', + closeMoreDirectMessagesButton: 'close.more_direct_messages.button', startButton: 'more_direct_messages.start.button', + selectedUser: 'more_direct_messages.selected_user', usersList: 'more_direct_messages.custom_list', userItem: 'more_direct_messages.custom_list.user_item', userItemDisplayUsername: 'more_direct_messages.custom_list.user_item.display_username', } moreDirectMessagesScreen = element(by.id(this.testID.moreDirectMessagesScreen)); + closeMoreDirectMessagesButton = element(by.id(this.testID.closeMoreDirectMessagesButton)); startButton = element(by.id(this.testID.startButton)); usersList = element(by.id(this.testID.usersList)); @@ -26,15 +29,28 @@ class MoreDirectMessagesScreen { cancelButton = SearchBar.getCancelButton(this.testID.moreDirectMessagesScreenPrefix); clearButton = SearchBar.getClearButton(this.testID.moreDirectMessagesScreenPrefix); + getSelectedUser = (userId) => { + const selectedUserTestID = `${this.testID.selectedUser}.${userId}`; + const selectedUserMatcher = by.id(selectedUserTestID); + const selectedUserDisplayUsernameMatcher = by.id(`${selectedUserTestID}.display_username`).withAncestor(selectedUserMatcher); + const selectedUserRemoveButtonMatcher = by.id(`${selectedUserTestID}.remove.button`).withAncestor(selectedUserMatcher); + + return { + selectedUser: element(selectedUserMatcher), + selectedUserDisplayUsername: element(selectedUserDisplayUsernameMatcher), + selectedUserRemoveButton: element(selectedUserRemoveButtonMatcher), + }; + } + getUser = (userId, diplayUsername) => { const userItemTestID = `${this.testID.userItem}.${userId}`; const baseMatcher = by.id(userItemTestID); const userItemMatcher = diplayUsername ? baseMatcher.withDescendant(by.text(diplayUsername)) : baseMatcher; - const userItemUsernameDisplayMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher); + const userItemDisplayUsernameMatcher = by.id(this.testID.userItemDisplayUsername).withAncestor(userItemMatcher); return { userItem: element(userItemMatcher), - userItemUsernameDisplay: element(userItemUsernameDisplayMatcher), + userItemDisplayUsername: element(userItemDisplayUsernameMatcher), }; } diff --git a/detox/e2e/test/channels/direct_messages.e2e.js b/detox/e2e/test/channels/direct_messages.e2e.js new file mode 100644 index 000000000..5d636b739 --- /dev/null +++ b/detox/e2e/test/channels/direct_messages.e2e.js @@ -0,0 +1,100 @@ +// 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 { + ChannelInfoScreen, + ChannelScreen, + MoreDirectMessagesScreen, +} from '@support/ui/screen'; +import { + Preference, + Setup, + Team, + User, +} from '@support/server_api'; + +describe('Direct Messages', () => { + const { + closeMainSidebar, + goToChannel, + openMainSidebar, + } = ChannelScreen; + const { + closeMoreDirectMessagesButton, + getUserAtIndex, + searchInput, + startButton, + } = MoreDirectMessagesScreen; + const {getChannelByDisplayName} = MainSidebar; + let testUser; + let testOtherUser; + + beforeAll(async () => { + const {user, team} = await Setup.apiInit(); + testUser = user; + + ({user: testOtherUser} = await User.apiCreateUser()); + await Team.apiAddUserToTeam(testOtherUser.id, team.id); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T1800 should remove closed DMs from main sidebar', async () => { + // # Create a DM with the other user + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await searchInput.typeText(testOtherUser.username); + await getUserAtIndex(0).tap(); + await startButton.tap(); + + // # Close DM channel + await goToChannel(testOtherUser.username); + await ChannelInfoScreen.open(); + await ChannelInfoScreen.closeDirectOrGroupMessage(); + + // * Verify DM channel is removed + await openMainSidebar(); + await expect(getChannelByDisplayName(testOtherUser.username)).not.toBeVisible(); + + // # Go back to channel + await closeMainSidebar(); + }); + + it('MM-T442 should display full name / nickname on DM search result', async () => { + // # Set teammate name display to full name + await Preference.apiSaveTeammateNameDisplayPreference(testUser.id, 'full_name'); + + // # DM search other user + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await searchInput.typeText(testOtherUser.username); + + // * Verify search result contains other user's full name + await expect(element(by.text(`${testOtherUser.first_name} ${testOtherUser.last_name}`))).toBeVisible(); + + // # Set teammate name display to nickname + await Preference.apiSaveTeammateNameDisplayPreference(testUser.id, 'nickname_full_name'); + + // # DM search other user again + await searchInput.clearText(); + await searchInput.typeText(testOtherUser.username); + + // * Verify search result contains other user's nickname + await expect(element(by.text(testOtherUser.nickname))).toBeVisible(); + + // # Go back to channel + await closeMoreDirectMessagesButton.tap(); + }); +}); diff --git a/detox/e2e/test/channels/group_messages.e2e.js b/detox/e2e/test/channels/group_messages.e2e.js new file mode 100644 index 000000000..fb0110220 --- /dev/null +++ b/detox/e2e/test/channels/group_messages.e2e.js @@ -0,0 +1,141 @@ +// 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, +} from '@support/ui/screen'; +import { + Setup, + Team, + User, +} from '@support/server_api'; + +describe('Group Messages', () => { + const {openMainSidebar} = ChannelScreen; + const { + closeMoreDirectMessagesButton, + getSelectedUser, + getUserAtIndex, + searchInput, + startButton, + } = MoreDirectMessagesScreen; + const testOtherUsers = new Map(); + + beforeAll(async () => { + const {user, team} = await Setup.apiInit(); + + [...Array(8).keys()].forEach(async (key) => { + const {user: testOtherUser} = await User.apiCreateUser({prefix: `user-${key}-`}); + await Team.apiAddUserToTeam(testOtherUser.id, team.id); + testOtherUsers.set(`user-${key}`, testOtherUser); + }); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T462 should be able to add and remove users while creating new GM', async () => { + const testOtherUser1 = testOtherUsers.get('user-0'); + const testOtherUser2 = testOtherUsers.get('user-1'); + const testOtherUser3 = testOtherUsers.get('user-2'); + const testOtherUser4 = testOtherUsers.get('user-3'); + + // # Add 4 users + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await searchInput.typeText(testOtherUser1.username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUser2.username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUser3.username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUser4.username); + await getUserAtIndex(0).tap(); + + // * Verify 4 users are added + const {selectedUserRemoveButton: selectedUserRemoveButton1} = getSelectedUser(testOtherUser1.id); + await expect(selectedUserRemoveButton1).toBeVisible(); + const {selectedUserRemoveButton: selectedUserRemoveButton2} = getSelectedUser(testOtherUser2.id); + await expect(selectedUserRemoveButton2).toBeVisible(); + const {selectedUserRemoveButton: selectedUserRemoveButton3} = getSelectedUser(testOtherUser3.id); + await expect(selectedUserRemoveButton3).toBeVisible(); + const {selectedUserRemoveButton: selectedUserRemoveButton4} = getSelectedUser(testOtherUser4.id); + await expect(selectedUserRemoveButton4).toBeVisible(); + + // # Remove 2 users + await selectedUserRemoveButton1.tap(); + await selectedUserRemoveButton3.tap(); + + // * Verify 2 users are removed + await expect(selectedUserRemoveButton1).not.toBeVisible(); + await expect(selectedUserRemoveButton3).not.toBeVisible(); + await expect(selectedUserRemoveButton2).toBeVisible(); + await expect(selectedUserRemoveButton4).toBeVisible(); + + // # Go back to channel + await closeMoreDirectMessagesButton.tap(); + }); + + it('MM-T464 should not be able to add users more than max', async () => { + // # Add 7 users + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await searchInput.typeText(testOtherUsers.get('user-0').username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-1').username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-2').username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-3').username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-4').username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-5').username); + await getUserAtIndex(0).tap(); + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-6').username); + await getUserAtIndex(0).tap(); + + // * Verify message that you cannot more users + await expect(element(by.text('You cannot add more users'))).toBeVisible(); + + // # Attempt to add one more user + await searchInput.clearText(); + await searchInput.typeText(testOtherUsers.get('user-7').username); + await getUserAtIndex(0).tap(); + + // * Verify last user is not added + const {selectedUser: lastUser} = getSelectedUser(testOtherUsers.get('user-7').id); + await expect(lastUser).not.toBeVisible(); + + // # Start a GM + await startButton.tap(); + + // * Verify on GM screen + await ChannelScreen.toBeVisible(); + const expectedUsernames = Array.from(testOtherUsers, ([, v]) => (v.username)).sort(); + expectedUsernames.pop(); + expectedUsernames.forEach(async (username) => { + await expect(element(by.text(username))).toBeVisible(); + }); + }); +}); diff --git a/detox/e2e/test/main_sidebar/main_sidebar.e2e.js b/detox/e2e/test/main_sidebar/main_sidebar.e2e.js new file mode 100644 index 000000000..10442cf77 --- /dev/null +++ b/detox/e2e/test/main_sidebar/main_sidebar.e2e.js @@ -0,0 +1,73 @@ +// 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 { + Setup, + Team, +} from '@support/server_api'; + +describe('Main Sidebar', () => { + const { + channelNavBarTitle, + closeMainSidebar, + goToChannel, + openMainSidebar, + } = ChannelScreen; + const { + searchInput, + switchTeamsButton, + } = MainSidebar; + let testChannel; + + beforeAll(async () => { + const {user, channel} = await Setup.apiInit(); + testChannel = channel; + + const {team: testOtherTeam} = await Team.apiCreateTeam(); + await Team.apiAddUserToTeam(user.id, testOtherTeam.id); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T3412 should close the sidebar menu when selecting the same channel', async () => { + // # Go to unread channel + await goToChannel(testChannel.display_name); + + // # Go to the same channel again + await goToChannel(testChannel.display_name); + + // * Verify sidebar menu is not open + await expect(MainSidebar.mainSidebar).not.toBeVisible(); + + // * Selected channel should remain the same + await expect(channelNavBarTitle).toHaveText(testChannel.display_name); + }); + + it('MM-T435 should not show switch teams button when jump to search is focused', async () => { + // # Open main sidebar + await openMainSidebar(); + + // # Tap on search input + await expect(switchTeamsButton).toBeVisible(); + await searchInput.tap(); + + // * Verify switch teams button is not visible + await expect(switchTeamsButton).not.toBeVisible(); + + // # Go back to channel + await closeMainSidebar(); + }); +}); diff --git a/detox/e2e/test/main_sidebar/select_channel.e2e.js b/detox/e2e/test/main_sidebar/select_channel.e2e.js deleted file mode 100644 index aa534b4bd..000000000 --- a/detox/e2e/test/main_sidebar/select_channel.e2e.js +++ /dev/null @@ -1,47 +0,0 @@ -// 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 {Setup} from '@support/server_api'; - -describe('Select channel', () => { - let newChannel; - - beforeAll(async () => { - const {user, channel} = await Setup.apiInit(); - newChannel = channel; - - // # Open channel screen - await ChannelScreen.open(user); - }); - - afterAll(async () => { - await ChannelScreen.logout(); - }); - - it('MM-T3412 should close the sidebar menu when selecting the same channel', async () => { - const { - channelNavBarTitle, - goToChannel, - } = ChannelScreen; - - // # Go to unread channel - await goToChannel(newChannel.display_name); - - // # Go to the same channel again - await goToChannel(newChannel.display_name); - - // * Verify sidebar menu is not open - await expect(MainSidebar.mainSidebar).not.toBeVisible(); - - // * Selected channel should remain the same - await expect(channelNavBarTitle).toHaveText(newChannel.display_name); - }); -});