diff --git a/detox/e2e/test/channels/channels_list.e2e.js b/detox/e2e/test/channels/channels_list.e2e.js new file mode 100644 index 000000000..eeb48426c --- /dev/null +++ b/detox/e2e/test/channels/channels_list.e2e.js @@ -0,0 +1,254 @@ +// 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, + MoreChannelsScreen, + MoreDirectMessagesScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Preference, + Setup, + Team, + User, +} from '@support/server_api'; +import {getRandomId} from '@support/utils'; + +describe('Channels List', () => { + const searchTerm = getRandomId(); + const { + channelNavBarTitle, + closeMainSidebar, + goToChannel, + openMainSidebar, + } = ChannelScreen; + const { + getUserAtIndex, + startButton, + } = MoreDirectMessagesScreen; + const { + channelsList, + channelsListUnreadIndicator, + filteredChannelsList, + getChannelDisplayNameAtIndex, + getFilteredChannelByDisplayName, + hasChannelDisplayNameAtIndex, + hasFilteredChannelDisplayNameAtIndex, + searchInput, + } = MainSidebar; + let testMessage; + let unreadChannel; + let favoriteChannel; + let publicChannel; + let privateChannel; + let nonJoinedChannel; + let directMessageChannel; + let dmOtherUser; + let nonDmOtherUser; + let testTeam; + let testUser; + + beforeAll(async () => { + const {user, channel, team} = await Setup.apiInit({channelOptions: {prefix: `channel-${searchTerm}`}}); + testUser = user; + unreadChannel = channel; + testTeam = team; + + testMessage = `Mention @${user.username}`; + await Post.apiCreatePost({ + channelId: unreadChannel.id, + message: testMessage, + }); + + ({channel: favoriteChannel} = await Channel.apiCreateChannel({type: 'O', prefix: `4-favorite-${searchTerm}`, teamId: testTeam.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-${searchTerm}`, teamId: testTeam.id})); + await Channel.apiAddUserToChannel(user.id, publicChannel.id); + + ({channel: privateChannel} = await Channel.apiCreateChannel({type: 'P', prefix: `2-private-${searchTerm}`, teamId: testTeam.id})); + await Channel.apiAddUserToChannel(user.id, privateChannel.id); + + ({channel: nonJoinedChannel} = await Channel.apiCreateChannel({type: 'O', prefix: `1-non-joined-${searchTerm}`, teamId: testTeam.id})); + + ({user: dmOtherUser} = await User.apiCreateUser({prefix: `user-${searchTerm}-1`})); + await Team.apiAddUserToTeam(dmOtherUser.id, testTeam.id); + ({channel: directMessageChannel} = await Channel.apiCreateDirectChannel([user.id, dmOtherUser.id])); + await Post.apiCreatePost({ + channelId: directMessageChannel.id, + message: testMessage, + }); + + ({user: nonDmOtherUser} = await User.apiCreateUser({prefix: `user-${searchTerm}-2`})); + await Team.apiAddUserToTeam(nonDmOtherUser.id, testTeam.id); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T844 should display unfiltered channels list', async () => { + // # Open main sidebar + await openMainSidebar(); + + // * Verify order when all channels are unread + await hasChannelDisplayNameAtIndex(0, privateChannel.display_name); + await hasChannelDisplayNameAtIndex(1, publicChannel.display_name); + await hasChannelDisplayNameAtIndex(2, favoriteChannel.display_name); + await hasChannelDisplayNameAtIndex(3, unreadChannel.display_name); + await hasChannelDisplayNameAtIndex(4, dmOtherUser.username); + await hasChannelDisplayNameAtIndex(5, 'Off-Topic'); + await hasChannelDisplayNameAtIndex(6, 'Town Square'); + await expect(element(by.id(nonJoinedChannel.display_name))).not.toBeVisible(); + await expect(element(by.id(nonDmOtherUser.username))).not.toBeVisible(); + await closeMainSidebar(); + + // # 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(); + + // * Verify order when all channels are read except for unread channel + await openMainSidebar(); + await hasChannelDisplayNameAtIndex(0, unreadChannel.display_name); + await hasChannelDisplayNameAtIndex(1, favoriteChannel.display_name); + await hasChannelDisplayNameAtIndex(2, publicChannel.display_name); + await hasChannelDisplayNameAtIndex(3, 'Off-Topic'); + await hasChannelDisplayNameAtIndex(4, 'Town Square'); + await hasChannelDisplayNameAtIndex(5, privateChannel.display_name); + await hasChannelDisplayNameAtIndex(6, dmOtherUser.username); + await expect(element(by.id(nonJoinedChannel.display_name))).not.toBeVisible(); + await expect(element(by.id(nonDmOtherUser.username))).not.toBeVisible(); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T847 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(searchTerm); + await searchInput.tapBackspaceKey(); + + // * Verify order when channels list is filtered + await hasFilteredChannelDisplayNameAtIndex(0, unreadChannel.display_name); + await hasFilteredChannelDisplayNameAtIndex(1, dmOtherUser.username); + await hasFilteredChannelDisplayNameAtIndex(2, privateChannel.display_name); + 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(); + + // # Tap a channel from filtered list + await getFilteredChannelByDisplayName(unreadChannel.display_name).tap(); + + // * Verify filtered channel opens + await expect(channelNavBarTitle).toHaveText(unreadChannel.display_name); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T848 should display deactivated user in filtered channels list', async () => { + const {user: deactivatedUser} = await User.apiCreateUser({prefix: 'deactivated'}); + await Team.apiAddUserToTeam(deactivatedUser.id, testTeam.id); + await User.apiDeactivateUser(deactivatedUser.id); + + // # Open main sidebar + await openMainSidebar(); + + // # Enter search term + await searchInput.typeText(deactivatedUser.username); + await searchInput.tapBackspaceKey(); + + // * Verify deactivated user is displayed + await hasFilteredChannelDisplayNameAtIndex(0, deactivatedUser.username); + + // # Tap on deactivated user + await getFilteredChannelByDisplayName(deactivatedUser.username).tap(); + + // * Verify deactivated user dm opens + await expect(channelNavBarTitle).toHaveText(deactivatedUser.username); + await expect(element(by.text('You are viewing an archived channel. New messages cannot be posted.'))).toBeVisible(); + }); + + it('MM-T864 should be able to search for public channel to join', async () => { + // # Open main sidebar + await openMainSidebar(); + + // * Verify public channel is searchable + await MoreChannelsScreen.open(); + await MoreChannelsScreen.searchInput.typeText('a'); + await expect(MoreChannelsScreen.cancelButton).toBeVisible(); + await MoreChannelsScreen.searchInput.clearText(); + await MoreChannelsScreen.searchInput.typeText(nonJoinedChannel.display_name); + await MoreChannelsScreen.hasChannelDisplayNameAtIndex(0, nonJoinedChannel.display_name); + + // # Join public channel + await MoreChannelsScreen.getChannelByDisplayName(nonJoinedChannel.display_name).tap(); + + // * Verify redirected to public channel + await expect(channelNavBarTitle).toHaveText(nonJoinedChannel.display_name); + }); + + it('MM-T889 should display more unreads indicator when unreads are off-screen in channels list', async () => { + // # Create 10 unread channels + [...Array(10).keys()].forEach(async (key) => { + const {channel} = await Channel.apiCreateChannel({type: 'O', prefix: `a-unread-channel-${key}`, teamId: testTeam.id}); + await Channel.apiAddUserToChannel(testUser.id, channel.id); + }); + + // # Open main sidebar (with at least one unread channel) + await openMainSidebar(); + + // # Scroll to bottom + await channelsList.scroll(500, 'down'); + + // * Verify more unreads indicator is displayed + await expect(channelsListUnreadIndicator).toBeVisible(); + + // # Tap on unread channels indicator + await channelsListUnreadIndicator.tap(); + + // * Verify tapping on top unread channel redirects to channels screen + await getChannelDisplayNameAtIndex(0).tap(); + await ChannelScreen.toBeVisible(); + }); +}); diff --git a/detox/e2e/test/messaging/message_posting.e2e.js b/detox/e2e/test/messaging/message_posting.e2e.js index 8c77ce014..6502aeeb9 100644 --- a/detox/e2e/test/messaging/message_posting.e2e.js +++ b/detox/e2e/test/messaging/message_posting.e2e.js @@ -7,12 +7,32 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* +import moment from 'moment-timezone'; + import {ChannelScreen} from '@support/ui/screen'; -import {Setup} from '@support/server_api'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; +import {isAndroid} from '@support/utils'; describe('Messaging', () => { + const { + getPostListPostItem, + goToChannel, + postInput, + sendButton, + sendButtonDisabled, + } = ChannelScreen; + let testChannel; + let townSquareChannel; + beforeAll(async () => { - const {user} = await Setup.apiInit(); + const {channel, team, user} = await Setup.apiInit(); + testChannel = channel; + + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.id, 'town-square')); // # Open channel screen await ChannelScreen.open(user); @@ -23,15 +43,6 @@ describe('Messaging', () => { }); it('MM-T3486 should post a message when send button is tapped', async () => { - // * Verify channel screen is visible - await ChannelScreen.toBeVisible(); - - const { - postInput, - sendButton, - sendButtonDisabled, - } = ChannelScreen; - // * Verify post input is visible and send button is disabled await expect(postInput).toBeVisible(); await expect(sendButtonDisabled).toBeVisible(); @@ -52,4 +63,50 @@ describe('Messaging', () => { // * Verify message is posted await expect(element(by.text(message))).toBeVisible(); }); + + it('MM-T891 should be able to scroll up in channel with long history', async () => { + // # Post messages + await goToChannel(testChannel.display_name); + const size = 50; + const firstMessageDate = moment().subtract(size + 1, 'days').toDate(); + const firstMessage = `${firstMessageDate} first`; + const firstPost = await Post.apiCreatePost({ + channelId: testChannel.id, + message: firstMessage, + createAt: firstMessageDate.getTime(), + }); + [...Array(size).keys()].forEach(async (key) => { + const messageDate = moment().subtract(key + 1, 'days').toDate(); + const message = `${messageDate}-${key}.`.repeat(10); + await Post.apiCreatePost({ + channelId: testChannel.id, + message, + createAt: messageDate.getTime(), + }); + }); + const lastMessageDate = moment().toDate(); + const lastMessage = `${lastMessageDate} last`; + const lastPost = await Post.apiCreatePost({ + channelId: testChannel.id, + message: lastMessage, + createAt: lastMessageDate.getTime(), + }); + + // # Switch channels + await goToChannel(townSquareChannel.display_name); + await goToChannel(testChannel.display_name); + + // Detox is having trouble scrolling + if (isAndroid()) { + return; + } + + // * Verify last message is posted + const {postListPostItem: lastPostItem} = await getPostListPostItem(lastPost.post.id, lastMessage); + await waitFor(lastPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(1000, 'down'); + + // * Verify user can scroll up multiple times until first matching post is seen + const {postListPostItem: firstPostItem} = await getPostListPostItem(firstPost.post.id, firstMessage); + await waitFor(firstPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(2000, 'up'); + }); });