diff --git a/detox/e2e/support/ui/component/channels_list.js b/detox/e2e/support/ui/component/channels_list.js index 385c8ec78..166b41864 100644 --- a/detox/e2e/support/ui/component/channels_list.js +++ b/detox/e2e/support/ui/component/channels_list.js @@ -21,7 +21,7 @@ class ChannelsList { channelsList = element(by.id(this.testID.channelsList)); channelsListUnreadIndicator = element(by.id(this.testID.channelsListUnreadIndicator)); - filteredChannelsList = element(by.id(this.testID.channelsList)); + filteredChannelsList = element(by.id(this.testID.filteredChannelsList)); switchTeamsButton = element(by.id(this.testID.switchTeamsButton)); switchTeamsButtonBadge = element(by.id(this.testID.switchTeamsButtonBadge)); switchTeamsButtonBadgeUnreadCount = element(by.id(this.testID.switchTeamsButtonBadgeUnreadCount)); diff --git a/detox/e2e/support/ui/screen/more_channels.js b/detox/e2e/support/ui/screen/more_channels.js index 64e77feb3..a05269252 100644 --- a/detox/e2e/support/ui/screen/more_channels.js +++ b/detox/e2e/support/ui/screen/more_channels.js @@ -34,6 +34,9 @@ class MoreChannelsScreen { searchInput = SearchBar.getSearchInput(this.testID.moreChannelsScreenPrefix); cancelButton = SearchBar.getCancelButton(this.testID.moreChannelsScreenPrefix); clearButton = SearchBar.getClearButton(this.testID.moreChannelsScreenPrefix); + archivedChannelsOption = BottomSheet.archivedChannelsOption; + cancelOption = BottomSheet.cancelOption; + publicChannelsOption = BottomSheet.publicChannelsOption; getChannel = (channelId, displayName) => { const channelItemTestID = `${this.testID.channelItem}.${channelId}`; @@ -75,13 +78,13 @@ class MoreChannelsScreen { showArchivedChannels = async () => { await this.channelDropdown.tap(); - await BottomSheet.archivedChannelsOption.tap(); + await this.archivedChannelsOption.tap(); await expect(this.channelDropdownArchived).toBeVisible(); } showPublicChannels = async () => { await this.channelDropdown.tap(); - await BottomSheet.publicChannelsOption.tap(); + await this.publicChannelsOption.tap(); await expect(this.channelDropdownPublic).toBeVisible(); } diff --git a/detox/e2e/test/account_settings/clock_display.e2e.js b/detox/e2e/test/account_settings/clock_display.e2e.js new file mode 100644 index 000000000..b4c375d94 --- /dev/null +++ b/detox/e2e/test/account_settings/clock_display.e2e.js @@ -0,0 +1,112 @@ +// 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 moment from 'moment-timezone'; + +import { + ChannelScreen, + ClockDisplaySettingsScreen, + DisplaySettingsScreen, + GeneralSettingsScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, + System, + User, +} from '@support/server_api'; +import {isAndroid, isIos} from '@support/utils'; + +describe('Clock Display', () => { + const testDate = Date.UTC(new Date().getFullYear() + 1, 0, 5, 14, 37); // Jan 5, 2:37pm + const testTimezone = 'UTC'; + const testFullDate = moment(testDate).tz(testTimezone).format(); + const testMilitaryTime = moment(testDate).tz(testTimezone).format('HH:mm'); + const testNormalTime = moment(testDate).tz(testTimezone).format('h:mm A'); + const testMessage = `Hello from ${testFullDate}, ${testTimezone}`; + let testChannel; + + beforeAll(async () => { + // # Enable timezone + await System.apiUpdateConfig({DisplaySettings: {ExperimentalTimezone: true}}); + + const {team, user} = await Setup.apiInit(); + ({channel: testChannel} = await Channel.apiGetChannelByName(team.name, 'town-square')); + + // # Set user's timezone + await User.apiPatchUser(user.id, {timezone: {automaticTimezone: '', manualTimezone: testTimezone, useAutomaticTimezone: 'false'}}); + + // # Post message with a future date as sysadmin + await User.apiAdminLogin(); + await Post.apiCreatePost({ + channelId: testChannel.id, + message: testMessage, + createAt: testDate, + }); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T291 should be able to set clock display to 12-hour', async () => { + // # Set clock display to normal (12-hour) + await setClockDisplayTo('normal'); + + // * Verify clock display is normal + await verifyClockDisplay(testChannel, testMessage, testNormalTime); + }); + + it('MM-T292 should be able to set clock display to 24-hour', async () => { + // # Set clock display to military (24-hour) + await setClockDisplayTo('military'); + + // * Verify clock display is military + await verifyClockDisplay(testChannel, testMessage, testMilitaryTime); + }); +}); + +async function setClockDisplayTo(clockKey) { + const { + clockModalSaveButton, + getClockActionFor, + } = ClockDisplaySettingsScreen; + + // # Open clock display settings screen + await ChannelScreen.openSettingsSidebar(); + await GeneralSettingsScreen.open(); + await DisplaySettingsScreen.open(); + await ClockDisplaySettingsScreen.open(); + + // # Tap on clock option + await getClockActionFor(clockKey).tap(); + + // # Tap on Save button if Android + if (isAndroid()) { + await clockModalSaveButton.tap(); + } + + // # Go back to channel + await ClockDisplaySettingsScreen.back(); + if (isIos()) { + await DisplaySettingsScreen.back(); + } + await GeneralSettingsScreen.close(); +} + +async function verifyClockDisplay(testChannel, testMessage, clockDisplay) { + // * Verify clock display + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + const {postListPostItemHeaderDateTime} = await ChannelScreen.getPostListPostItem(post.id, testMessage); + await expect(postListPostItemHeaderDateTime).toHaveText(clockDisplay); +} diff --git a/detox/e2e/test/account_settings/language_settings.e2e.js b/detox/e2e/test/account_settings/language_settings.e2e.js new file mode 100644 index 000000000..89545dfd8 --- /dev/null +++ b/detox/e2e/test/account_settings/language_settings.e2e.js @@ -0,0 +1,43 @@ +// 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} from '@support/ui/screen'; +import { + Channel, + Setup, + User, +} from '@support/server_api'; + +describe('Language Settings', () => { + let testUser; + let townSquareChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + testUser = user; + + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.name, 'town-square')); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T304 no crash when setting language to zh-TW (Chinese Traditional)', async () => { // related issue https://mattermost.atlassian.net/browse/MM-35329 + // # Set user's locale + await User.apiPatchUser(testUser.id, {locale: 'zh-TW'}); + + // # Open channel screen + await ChannelScreen.open(testUser); + + // * Verify town square channel loads + await ChannelScreen.toBeVisible(); + await expect(ChannelScreen.channelNavBarTitle).toHaveText(townSquareChannel.display_name); + }); +}); diff --git a/detox/e2e/test/channels/archived_channels.e2e.js b/detox/e2e/test/channels/archived_channels.e2e.js index e21b274ec..ad2bc0bc2 100644 --- a/detox/e2e/test/channels/archived_channels.e2e.js +++ b/detox/e2e/test/channels/archived_channels.e2e.js @@ -14,36 +14,161 @@ import { } from '@support/ui/screen'; import { Channel, + Post, Setup, System, + Team, + User, } from '@support/server_api'; describe('Archived Channels', () => { - let archivedChannel; - let nonArchivedChannel; + const { + closeMainSidebar, + goToChannel, + mainSidebarDrawerButtonBadgeUnreadCount, + openMainSidebar, + postMessage, + } = ChannelScreen; + let testUser; + let testTeam; beforeAll(async () => { - // # Enable experimental view archived channels - await System.apiUpdateConfig({TeamSettings: {ExperimentalViewArchivedChannels: true}}); - - const {channel, team, user} = await Setup.apiInit(); - archivedChannel = channel; - - ({channel: nonArchivedChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'non-archived-channel', teamId: team.id})); + const {team, user} = await Setup.apiInit(); + testUser = user; + testTeam = team; // # Open channel screen - await ChannelScreen.open(user); + await ChannelScreen.open(testUser); + }); + + beforeEach(async () => { + // # Disable experimental view archived channels + await User.apiAdminLogin(); + await System.apiUpdateConfig({TeamSettings: {ExperimentalViewArchivedChannels: false}}); }); afterAll(async () => { await ChannelScreen.logout(); }); + it('MM-T1689 should remove favorited archived channel from main sidebar channels list', async () => { + const {channel: favoritedChannel} = await Channel.apiCreateChannel({type: 'P', prefix: 'favorited-channel', teamId: testTeam.id}); + await Channel.apiAddUserToChannel(testUser.id, favoritedChannel.id); + + // # Archive channel + await goToChannel(favoritedChannel.display_name); + await ChannelInfoScreen.open(); + await ChannelInfoScreen.archiveChannel({publicChannel: false}); + + // * Verify favorites section and favorited archived channel are not displayed + await openMainSidebar(); + await expect(element(by.text('FAVORITE CHANNELS'))).not.toBeVisible(); + await expect(element(by.text(favoritedChannel.display_name))).not.toBeVisible(); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T1690 should remove unread channel from main sidebar channels list when archived by another user', async () => { + const {user: otherUser} = await User.apiCreateUser(); + const {channel: unreadChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'unread-channel', teamId: testTeam.id}); + await Team.apiAddUserToTeam(otherUser.id, testTeam.id); + await Channel.apiAddUserToChannel(testUser.id, unreadChannel.id); + await Channel.apiAddUserToChannel(otherUser.id, unreadChannel.id); + + // # Archive unread channel using other user + await ChannelScreen.logout(); + await ChannelScreen.open(otherUser); + await goToChannel(unreadChannel.display_name); + await postMessage(Date.now().toString()); + await ChannelInfoScreen.open(); + await ChannelInfoScreen.archiveChannel(); + + // * Verify unread archived channel is not displayed in channels list + await ChannelScreen.logout(); + await ChannelScreen.open(testUser); + await openMainSidebar(); + await expect(element(by.text(unreadChannel.display_name))).not.toBeVisible(); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T1691 should remove unread channel with mention from main sidebar channels list when archived by another user', async () => { + const {user: otherUser} = await User.apiCreateUser(); + const {channel: unreadChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'unread-channel', teamId: testTeam.id}); + await Team.apiAddUserToTeam(otherUser.id, testTeam.id); + await Channel.apiAddUserToChannel(testUser.id, unreadChannel.id); + await Channel.apiAddUserToChannel(otherUser.id, unreadChannel.id); + + // # Archive unread channel with mention using other user + await ChannelScreen.logout(); + await ChannelScreen.open(otherUser); + await goToChannel(unreadChannel.display_name); + await postMessage(`${Date.now().toString()} @${testUser.username}`); + await ChannelInfoScreen.open(); + await ChannelInfoScreen.archiveChannel(); + + // * Verify unread badge is not displayed + await expect(mainSidebarDrawerButtonBadgeUnreadCount).not.toExist(); + + // * Verify unread archived channel with mention is not displayed in channels list + await ChannelScreen.logout(); + await ChannelScreen.open(testUser); + await openMainSidebar(); + await expect(element(by.text(unreadChannel.display_name))).not.toBeVisible(); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T1692 should remove read channel from main sidebar channels list when archived by another user', async () => { + const {user: otherUser} = await User.apiCreateUser(); + const {channel: readChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'read-channel', teamId: testTeam.id}); + await Team.apiAddUserToTeam(otherUser.id, testTeam.id); + await Channel.apiAddUserToChannel(testUser.id, readChannel.id); + await Channel.apiAddUserToChannel(otherUser.id, readChannel.id); + + // # Post message to channel using other user + await User.apiLogin(otherUser); + await Post.apiCreatePost({ + channelId: readChannel.id, + message: Date.now().toString(), + }); + + // # Read channel by user + await goToChannel(readChannel.display_name); + + // # Archive read channel using other user + await ChannelScreen.logout(); + await ChannelScreen.open(otherUser); + await goToChannel(readChannel.display_name); + await ChannelInfoScreen.open(); + await ChannelInfoScreen.archiveChannel(); + + // * Verify read archived channel is not displayed in channels list + await ChannelScreen.logout(); + await ChannelScreen.open(testUser); + await openMainSidebar(); + await expect(element(by.text(readChannel.display_name))).not.toBeVisible(); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T1712 should not display channel dropdown when disabled', async () => { + // # Open more channels screen + await openMainSidebar(); + await MoreChannelsScreen.open(); + + // * Verify channel dropdown is not available + await expect(MoreChannelsScreen.channelDropdown).not.toBeVisible(); + + // # Return to channel + await MoreChannelsScreen.close(); + }); + it('MM-T3618 should display archived channels list', async () => { - const { - goToChannel, - openMainSidebar, - } = ChannelScreen; const { getChannelByDisplayName, hasChannelDisplayNameAtIndex, @@ -51,6 +176,12 @@ describe('Archived Channels', () => { showArchivedChannels, showPublicChannels, } = MoreChannelsScreen; + const {channel: archivedChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'archived-channel', teamId: testTeam.id}); + const {channel: nonArchivedChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'non-archived-channel', teamId: testTeam.id}); + await Channel.apiAddUserToChannel(testUser.id, archivedChannel.id); + + // # Enable experimental view archived channels + await System.apiUpdateConfig({TeamSettings: {ExperimentalViewArchivedChannels: true}}); // # Archive channel await goToChannel(archivedChannel.display_name); diff --git a/detox/e2e/test/smoke_test/channels.e2e.js b/detox/e2e/test/smoke_test/channels.e2e.js index b5ccf853c..4cc881703 100644 --- a/detox/e2e/test/smoke_test/channels.e2e.js +++ b/detox/e2e/test/smoke_test/channels.e2e.js @@ -8,7 +8,10 @@ // ******************************************************************* import {MainSidebar} from '@support/ui/component'; -import {ChannelScreen} from '@support/ui/screen'; +import { + ChannelScreen, + MoreDirectMessagesScreen, +} from '@support/ui/screen'; import { Channel, Post, @@ -17,8 +20,27 @@ import { Team, User, } from '@support/server_api'; +import {getRandomId} from '@support/utils'; describe('Channels', () => { + const searchTerm = getRandomId(); + const { + channelNavBarTitle, + closeMainSidebar, + goToChannel, + openMainSidebar, + } = ChannelScreen; + const { + getUserAtIndex, + startButton, + } = MoreDirectMessagesScreen; + const { + filteredChannelsList, + getFilteredChannelByDisplayName, + hasChannelDisplayNameAtIndex, + hasFilteredChannelDisplayNameAtIndex, + searchInput, + } = MainSidebar; let testMessage; let unreadChannel; let favoriteChannel; @@ -28,21 +50,9 @@ describe('Channels', () => { let directMessageChannel; let dmOtherUser; let nonDmOtherUser; - const { - channelNavBarTitle, - closeMainSidebar, - goToChannel, - openMainSidebar, - } = ChannelScreen; - const { - getFilteredChannelByDisplayName, - hasChannelDisplayNameAtIndex, - hasFilteredChannelDisplayNameAtIndex, - searchInput, - } = MainSidebar; beforeAll(async () => { - const {user, channel, team} = await Setup.apiInit(); + const {user, channel, team} = await Setup.apiInit({channelOptions: {prefix: `channel-${searchTerm}`}}); unreadChannel = channel; testMessage = `Mention @${user.username}`; await Post.apiCreatePost({ @@ -50,19 +60,19 @@ describe('Channels', () => { message: testMessage, }); - ({channel: favoriteChannel} = await Channel.apiCreateChannel({type: 'O', prefix: '4-favorite-channel', teamId: team.id})); + ({channel: favoriteChannel} = await Channel.apiCreateChannel({type: 'O', prefix: `4-favorite-${searchTerm}`, teamId: team.id})); await Channel.apiAddUserToChannel(user.id, favoriteChannel.id); await Preference.apiSaveFavoriteChannelPreference(user.id, favoriteChannel.id); - ({channel: publicChannel} = await Channel.apiCreateChannel({type: 'O', prefix: '3-public-channel', teamId: team.id})); + ({channel: publicChannel} = await Channel.apiCreateChannel({type: 'O', prefix: `3-public-${searchTerm}`, teamId: team.id})); await Channel.apiAddUserToChannel(user.id, publicChannel.id); - ({channel: privateChannel} = await Channel.apiCreateChannel({type: 'P', prefix: '2-private-channel', teamId: team.id})); + ({channel: privateChannel} = await Channel.apiCreateChannel({type: 'P', prefix: `2-private-${searchTerm}`, teamId: team.id})); await Channel.apiAddUserToChannel(user.id, privateChannel.id); - ({channel: nonJoinedChannel} = await Channel.apiCreateChannel({type: 'O', prefix: '1-non-joined-channel', teamId: team.id})); + ({channel: nonJoinedChannel} = await Channel.apiCreateChannel({type: 'O', prefix: `1-non-joined-${searchTerm}`, teamId: team.id})); - ({user: dmOtherUser} = await User.apiCreateUser({prefix: 'testchannel-1'})); + ({user: dmOtherUser} = await User.apiCreateUser({prefix: `user-${searchTerm}-1`})); await Team.apiAddUserToTeam(dmOtherUser.id, team.id); ({channel: directMessageChannel} = await Channel.apiCreateDirectChannel([user.id, dmOtherUser.id])); await Post.apiCreatePost({ @@ -70,7 +80,7 @@ describe('Channels', () => { message: testMessage, }); - ({user: nonDmOtherUser} = await User.apiCreateUser({prefix: 'testchannel-2'})); + ({user: nonDmOtherUser} = await User.apiCreateUser({prefix: `user-${searchTerm}-2`})); await Team.apiAddUserToTeam(nonDmOtherUser.id, team.id); // # Open channel screen @@ -97,11 +107,17 @@ describe('Channels', () => { await expect(element(by.id(nonDmOtherUser.username))).not.toBeVisible(); await closeMainSidebar(); - // # Visit private, public, favorite, and direct message channels + // # Visit private, public, and favorite channels await goToChannel(privateChannel.display_name); await goToChannel(publicChannel.display_name); await goToChannel(favoriteChannel.display_name); - await goToChannel(dmOtherUser.username); + + // # Open DM with the other user + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await MoreDirectMessagesScreen.searchInput.typeText(dmOtherUser.username); + await getUserAtIndex(0).tap(); + await startButton.tap(); // * Verify order when all channels are read except for unread channel await openMainSidebar(); @@ -120,11 +136,23 @@ describe('Channels', () => { }); it('MM-T3186 should display filtered channels list and be able to change channels', async () => { + // # Visit private, public, and favorite channels + await goToChannel(privateChannel.display_name); + await goToChannel(publicChannel.display_name); + await goToChannel(favoriteChannel.display_name); + + // # Open DM with the other user + await openMainSidebar(); + await MoreDirectMessagesScreen.open(); + await MoreDirectMessagesScreen.searchInput.typeText(dmOtherUser.username); + await getUserAtIndex(0).tap(); + await startButton.tap(); + // # Open main sidebar await openMainSidebar(); // # Enter search term - await searchInput.typeText('channel'); + await searchInput.typeText(searchTerm); await searchInput.tapBackspaceKey(); // * Verify order when channels list is filtered @@ -134,6 +162,7 @@ describe('Channels', () => { await hasFilteredChannelDisplayNameAtIndex(3, publicChannel.display_name); await hasFilteredChannelDisplayNameAtIndex(4, favoriteChannel.display_name); await hasFilteredChannelDisplayNameAtIndex(5, nonDmOtherUser.username); + await filteredChannelsList.scrollTo('bottom'); await hasFilteredChannelDisplayNameAtIndex(6, nonJoinedChannel.display_name); await expect(element(by.text('Off-Topic'))).not.toBeVisible(); await expect(element(by.text('Town Square'))).not.toBeVisible(); diff --git a/detox/e2e/test/teams/teams_order.e2e.js b/detox/e2e/test/teams/teams_order.e2e.js index ae47cc69c..d5a3b87f8 100644 --- a/detox/e2e/test/teams/teams_order.e2e.js +++ b/detox/e2e/test/teams/teams_order.e2e.js @@ -58,8 +58,10 @@ describe('Teams Order', () => { // # Re-arrange teams order via API await Preference.apiSaveTeamsOrderPreference(testUser.id, [testTeam3.id, testTeam1.id, testTeam2.id]); + await closeTeamSidebar(); // * Verify updated teams order + await openTeamSidebar(); await hasTeamDisplayNameAtIndex(0, testTeam3.display_name); await hasTeamDisplayNameAtIndex(1, testTeam1.display_name); await hasTeamDisplayNameAtIndex(2, testTeam2.display_name); @@ -71,9 +73,9 @@ describe('Teams Order', () => { await Team.apiAddUserToTeam(testUser.id, testTeam5.id); ({team: testTeam6} = await Team.apiCreateTeam({prefix: 'team-d'})); await Team.apiAddUserToTeam(testUser.id, testTeam6.id); + await device.reloadReactNative(); // * Verify additional teams order is ascending - await device.reloadReactNative(); await openTeamSidebar(); await hasTeamDisplayNameAtIndex(3, testTeam6.display_name); await hasTeamDisplayNameAtIndex(4, testTeam5.display_name);