diff --git a/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js b/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js index 1cb49a896..d5e18b4fd 100644 --- a/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js +++ b/app/components/sidebars/main/channels_list/filtered_list/filtered_list.js @@ -380,10 +380,7 @@ class FilteredList extends Component { const {styles, testID} = this.props; const dataSource = this.buildData(this.props); return ( - + diff --git a/app/components/sidebars/main/channels_list/list/__snapshots__/list.test.js.snap b/app/components/sidebars/main/channels_list/list/__snapshots__/list.test.js.snap index d0f3f5a3c..59945edea 100644 --- a/app/components/sidebars/main/channels_list/list/__snapshots__/list.test.js.snap +++ b/app/components/sidebars/main/channels_list/list/__snapshots__/list.test.js.snap @@ -3,7 +3,6 @@ exports[`ChannelsList List should match snapshot 1`] = ` @@ -419,6 +418,7 @@ export default class List extends PureComponent { onViewableItemsChanged={this.updateUnreadIndicators} maxToRenderPerBatch={10} stickySectionHeadersEnabled={true} + testID={testID} viewabilityConfig={VIEWABILITY_CONFIG} {...this.keyboardDismissProp} /> diff --git a/app/components/sidebars/main/channels_list/unread_indicator/unread_indicator.base.js b/app/components/sidebars/main/channels_list/unread_indicator/unread_indicator.base.js index 35a86ce1c..c37f5a2eb 100644 --- a/app/components/sidebars/main/channels_list/unread_indicator/unread_indicator.base.js +++ b/app/components/sidebars/main/channels_list/unread_indicator/unread_indicator.base.js @@ -35,6 +35,7 @@ export default class UnreadIndicatorBase extends PureComponent { diff --git a/app/screens/long_post/__snapshots__/long_post.test.js.snap b/app/screens/long_post/__snapshots__/long_post.test.js.snap index c23320656..7b7608379 100644 --- a/app/screens/long_post/__snapshots__/long_post.test.js.snap +++ b/app/screens/long_post/__snapshots__/long_post.test.js.snap @@ -277,6 +277,7 @@ LongPost { "width": 40, } } + testID="close.long_post.button" > { + await this.openMainSidebar(); + const channelItem = MainSidebar.getChannelByDisplayName(displayName); + await channelItem.tap(); + await expect(this.channelNavBarTitle).toHaveText(displayName); + } + openMainSidebar = async () => { // # Open main sidebar await this.mainSidebarDrawerButton.tap(); diff --git a/detox/e2e/support/ui/screen/channel_members.js b/detox/e2e/support/ui/screen/channel_members.js new file mode 100644 index 000000000..ab8498796 --- /dev/null +++ b/detox/e2e/support/ui/screen/channel_members.js @@ -0,0 +1,104 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import { + Alert, + MainSidebar, + SearchBar, +} from '@support/ui/component'; +import {timeouts, wait} from '@support/utils'; + +class ChannelMembersScreen { + testID = { + channelMembersScreenPrefix: 'channel_members.', + channelMembersScreen: 'channel_members.screen', + removeButton: 'channel_members.remove.button', + backButton: 'screen.back.button', + usersList: 'channel_members.custom_list', + userItem: 'channel_members.custom_list.user_item', + userItemDisplayUsername: 'channel_members.custom_list.user_item.display_username', + } + + channelMembersScreen = element(by.id(this.testID.channelMembersScreen)); + removeButton = element(by.id(this.testID.removeButton)); + backButton = element(by.id(this.testID.backButton)); + usersList = element(by.id(this.testID.usersList)); + + // convenience props + searchBar = SearchBar.getSearchBar(this.testID.channelMembersScreenPrefix); + searchInput = SearchBar.getSearchInput(this.testID.channelMembersScreenPrefix); + cancelButton = SearchBar.getCancelButton(this.testID.channelMembersScreenPrefix); + clearButton = SearchBar.getClearButton(this.testID.channelMembersScreenPrefix); + + 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); + + return { + userItem: element(userItemMatcher), + userItemUsernameDisplay: element(userItemUsernameDisplayMatcher), + }; + } + + getUserAtIndex = (index) => { + return element(by.id(this.testID.userItem).withAncestor(by.id(this.testID.usersList))).atIndex(index); + } + + getUserByDisplayUsername = (displayUsername) => { + return element(by.text(displayUsername).withAncestor(by.id(this.testID.usersList))); + } + + getDisplayUsernameAtIndex = (index) => { + return element(by.id(this.testID.userItemDisplayUsername)).atIndex(index); + } + + toBeVisible = async () => { + await expect(this.channelMembersScreen).toBeVisible(); + + return this.channelMembersScreen; + } + + open = async () => { + // # Open more direct messages screen + await MainSidebar.openChannelMembersButton.tap(); + + return this.toBeVisible(); + } + + back = async () => { + await this.backButton.tap(); + await expect(this.channelMembersScreen).not.toBeVisible(); + } + + removeMembers = async (displayUsernameList, confirm = true) => { + displayUsernameList.forEach(async (displayUsername) => { + await this.getUserByDisplayUsername(`@${displayUsername}`).tap(); + }); + await wait(timeouts.ONE_SEC); + await this.removeButton.tap(); + const { + removeMembersTitle, + noButton, + yesButton, + } = Alert; + await expect(removeMembersTitle).toBeVisible(); + if (confirm) { + yesButton.tap(); + } else { + noButton.tap(); + } + await wait(timeouts.ONE_SEC); + await expect(this.channelMembersScreen).not.toBeVisible(); + } + + hasUserDisplayUsernameAtIndex = async (index, displayUsername) => { + await expect( + this.getDisplayUsernameAtIndex(index), + ).toHaveText(displayUsername); + } +} + +const channelMembersScreen = new ChannelMembersScreen(); +export default channelMembersScreen; diff --git a/detox/e2e/support/ui/screen/index.js b/detox/e2e/support/ui/screen/index.js index c5b9a4fb3..ad6c3be1d 100644 --- a/detox/e2e/support/ui/screen/index.js +++ b/detox/e2e/support/ui/screen/index.js @@ -3,6 +3,7 @@ import AddReactionScreen from './add_reaction'; import ChannelInfoScreen from './channel_info'; +import ChannelMembersScreen from './channel_members'; import ChannelNotificationPreferenceScreen from './channel_notification_preference'; import ChannelScreen from './channel'; import ClockDisplaySettingsScreen from './clock_display_settings'; @@ -32,6 +33,7 @@ import ThreadScreen from './thread'; export { AddReactionScreen, ChannelInfoScreen, + ChannelMembersScreen, ChannelNotificationPreferenceScreen, ChannelScreen, ClockDisplaySettingsScreen, diff --git a/detox/e2e/support/ui/screen/long_post.js b/detox/e2e/support/ui/screen/long_post.js index 2745db3b0..f27ba9fa3 100644 --- a/detox/e2e/support/ui/screen/long_post.js +++ b/detox/e2e/support/ui/screen/long_post.js @@ -6,8 +6,11 @@ import {Post} from '@support/ui/component'; class LongPostScreen { testID = { longPostItem: 'long_post.post', + closeLongPostButton: 'close.long_post.button', } + closeLongPostButton = element(by.id(this.testID.closeLongPostButton)); + getPost = (postId, postMessage, postProfileOptions = {}) => { const { postItem, diff --git a/detox/e2e/test/channels/archived_channels.e2e.js b/detox/e2e/test/channels/archived_channels.e2e.js index d7379da3e..e21b274ec 100644 --- a/detox/e2e/test/channels/archived_channels.e2e.js +++ b/detox/e2e/test/channels/archived_channels.e2e.js @@ -7,7 +7,6 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {MainSidebar} from '@support/ui/component'; import { ChannelScreen, ChannelInfoScreen, @@ -41,7 +40,10 @@ describe('Archived Channels', () => { }); it('MM-T3618 should display archived channels list', async () => { - const {openMainSidebar} = ChannelScreen; + const { + goToChannel, + openMainSidebar, + } = ChannelScreen; const { getChannelByDisplayName, hasChannelDisplayNameAtIndex, @@ -51,8 +53,7 @@ describe('Archived Channels', () => { } = MoreChannelsScreen; // # Archive channel - await openMainSidebar(); - await MainSidebar.getChannelByDisplayName(archivedChannel.display_name).tap(); + await goToChannel(archivedChannel.display_name); await ChannelInfoScreen.open(); await ChannelInfoScreen.archiveChannel(); diff --git a/detox/e2e/test/main_sidebar/select_channel.e2e.js b/detox/e2e/test/main_sidebar/select_channel.e2e.js index 3033c38c6..aa534b4bd 100644 --- a/detox/e2e/test/main_sidebar/select_channel.e2e.js +++ b/detox/e2e/test/main_sidebar/select_channel.e2e.js @@ -9,7 +9,6 @@ import {MainSidebar} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; -import {isAndroid} from '@support/utils'; import {Setup} from '@support/server_api'; describe('Select channel', () => { @@ -28,26 +27,19 @@ describe('Select channel', () => { }); it('MM-T3412 should close the sidebar menu when selecting the same channel', async () => { - const {channelNavBarTitle} = ChannelScreen; + const { + channelNavBarTitle, + goToChannel, + } = ChannelScreen; - // # Open main sidebar (with at least one unread channel) - await ChannelScreen.openMainSidebar(); + // # Go to unread channel + await goToChannel(newChannel.display_name); - // # Tap a channel to view it - const channelItem = MainSidebar.getChannelByDisplayName(newChannel.display_name); - await channelItem.tap(); + // # Go to the same channel again + await goToChannel(newChannel.display_name); - // * Selected channel should be visible - await expect(channelNavBarTitle).toHaveText(newChannel.display_name); - - // # Open main sidebar again and select the same channel - await ChannelScreen.openMainSidebar(); - await channelItem.tap(); - - // * Drawer should not be visible on Android - if (isAndroid()) { - await expect(MainSidebar.mainSidebar).not.toBeVisible(); - } + // * 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); diff --git a/detox/e2e/test/messaging/permalink.e2e.js b/detox/e2e/test/messaging/permalink.e2e.js index f30dd2e1a..f611f33bb 100644 --- a/detox/e2e/test/messaging/permalink.e2e.js +++ b/detox/e2e/test/messaging/permalink.e2e.js @@ -7,7 +7,6 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {MainSidebar} from '@support/ui/component'; import {ChannelScreen, PermalinkScreen} from '@support/ui/screen'; import {User, Setup, Channel, Post} from '@support/server_api'; @@ -40,9 +39,7 @@ describe('Permalink', () => { }); // # Go to test channel - await ChannelScreen.openMainSidebar(); - await MainSidebar.getChannelByDisplayName(testChannel.display_name).tap(); - await ChannelScreen.toBeVisible(); + await ChannelScreen.goToChannel(testChannel.display_name); // # Tap the channel permalink await element(by.text(permalinkLabel)).tap({x: 5, y: 10}); diff --git a/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js b/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js index 6418b21a1..0872a7389 100644 --- a/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js +++ b/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js @@ -7,18 +7,43 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {Alert, MainSidebar} from '@support/ui/component'; -import {ChannelScreen, CreateChannelScreen, PermalinkScreen} from '@support/ui/screen'; - -import {Channel, Post, Setup, Team, User} from '@support/server_api'; -import {adminUsername, adminPassword, serverUrl} from '@support/test_config'; -import {getRandomId} from '@support/utils'; +import { + Alert, + MainSidebar, + TeamsList, +} from '@support/ui/component'; +import { + ChannelScreen, + CreateChannelScreen, + PermalinkScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, + Team, + User, +} from '@support/server_api'; +import { + serverUrl, +} from '@support/test_config'; +import { + getAdminAccount, + getRandomId, +} from '@support/utils'; describe('Messaging', () => { + const { + channelNavBarTitle, + goToChannel, + logout, + openTeamSidebar, + postMessage, + } = ChannelScreen; let testChannel; let testTeam; - beforeEach(async () => { + beforeAll(async () => { const {channel, team, user} = await Setup.apiInit(); testChannel = channel; testTeam = team; @@ -26,25 +51,19 @@ describe('Messaging', () => { await ChannelScreen.open(user); }); - afterEach(async () => { + afterAll(async () => { await ChannelScreen.logout(); }); it('MM-T3471 Tapping channel URL link joins public channel', async () => { - const { - channelNavBarTitle, - logout, - postMessage, - } = ChannelScreen; - // # Go to the Town Square channel - await gotoChannel('Town Square'); + await goToChannel('Town Square'); // # There's no way to get a channel permalink on mobile so we make one manually const channelPermalink = `${serverUrl}/${testTeam.name}/channels/${testChannel.name}`; // # Post the channel permalink to the test channel in Town Square - await postMessage(channelPermalink); + await postMessage(channelPermalink, {quickReplace: true}); await expect(element(by.text(channelPermalink))).toBeVisible(); // # Create another user in the same team, log in and go to town square @@ -52,7 +71,7 @@ describe('Messaging', () => { await Team.apiAddUserToTeam(otherUser.id, testTeam.id); await logout(); await ChannelScreen.open(otherUser); - await gotoChannel('Town Square'); + await goToChannel('Town Square'); // # As this new user, tap the channel permalink we posted earlier await tapLink(channelPermalink); @@ -66,11 +85,6 @@ describe('Messaging', () => { // - Post a message in second channel and post the permalink of it in the public channel // - Confirm the prompt and join the channel it('MM-30237 System admins prompted before joining private channel via permalink', async () => { - const { - logout, - openTeamSidebar, - postMessage, - } = ChannelScreen; const {getTeamByDisplayName} = MainSidebar; // # Create Private Channel 1 @@ -89,34 +103,32 @@ describe('Messaging', () => { const {post} = await Post.apiGetLastPostInChannel(privateChannel2.id); // # Go to the Town Square channel - await gotoChannel('Town Square'); + await goToChannel('Town Square'); // # Post Private Channel 1 Permalink const message1 = `${serverUrl}/${testTeam.name}/channels/${privateChannel1Name}`; - await postMessage(message1); + await postMessage(message1, {quickReplace: true}); // * Check that message is successfully posted await expect(element(by.text(message1))).toExist(); // # Post Private Channel 2's POST Permalink const message2 = `${serverUrl}/${testTeam.name}/pl/${post.id}`; - await postMessage(message2); + await postMessage(message2, {quickReplace: true}); // * Check that message is successfully posted await expect(element(by.text(message2))).toExist(); // # Logout and login as sysadmin await logout(); - await ChannelScreen.open({ - username: adminUsername, - password: adminPassword, - }); + await ChannelScreen.open(getAdminAccount()); // * Verify channel screen is visible await ChannelScreen.toBeVisible(); // # Go to the team await openTeamSidebar(); + await waitFor(getTeamByDisplayName(testTeam.display_name)).toBeVisible().whileElement(by.id(TeamsList.testID.teamsList)).scroll(500, 'down'); await getTeamByDisplayName(testTeam.display_name).tap(); // # Press on message 1 @@ -129,7 +141,7 @@ describe('Messaging', () => { await expect(ChannelScreen.channelIntro).toHaveText('Beginning of ' + privateChannel1Name); // # Go to Townsquare - await gotoChannel('Town Square'); + await goToChannel('Town Square'); // # Press on message 2 await tapLink(message2); @@ -166,13 +178,6 @@ async function createPrivateChannel(channelName) { await expect(ChannelScreen.channelIntro).toHaveText('Beginning of ' + channelName); } -async function gotoChannel(name) { - await ChannelScreen.openMainSidebar(); - const channelItem = MainSidebar.getChannelByDisplayName(name); - await channelItem.tap(); - await expect(ChannelScreen.channelNavBarTitle).toHaveText(name); -} - async function joinPrivateChannel() { await expect(Alert.joinPrivateChannelTitle).toBeVisible(); await Alert.joinButton.tap(); diff --git a/detox/e2e/test/notifications/channel_notification_default.e2e.js b/detox/e2e/test/notifications/channel_notification_default.e2e.js index 185c326b9..571576af8 100644 --- a/detox/e2e/test/notifications/channel_notification_default.e2e.js +++ b/detox/e2e/test/notifications/channel_notification_default.e2e.js @@ -7,7 +7,6 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {MainSidebar} from '@support/ui/component'; import { ChannelInfoScreen, ChannelNotificationPreferenceScreen, @@ -32,8 +31,7 @@ describe('Channel Notification Preference - Default', () => { beforeEach(async () => { // # Go to channel - await ChannelScreen.openMainSidebar(); - await MainSidebar.getChannelByDisplayName(testChannel.display_name).tap(); + await ChannelScreen.goToChannel(testChannel.display_name); }); afterAll(async () => { diff --git a/detox/e2e/test/notifications/channel_notification_preference.e2e.js b/detox/e2e/test/notifications/channel_notification_preference.e2e.js index 500c5cad1..cd0c25376 100644 --- a/detox/e2e/test/notifications/channel_notification_preference.e2e.js +++ b/detox/e2e/test/notifications/channel_notification_preference.e2e.js @@ -7,7 +7,6 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {MainSidebar} from '@support/ui/component'; import { ChannelInfoScreen, ChannelNotificationPreferenceScreen, @@ -30,8 +29,7 @@ describe('Channel Notification Preference', () => { beforeEach(async () => { // # Go to channel - await ChannelScreen.openMainSidebar(); - await MainSidebar.getChannelByDisplayName(testChannel.display_name).tap(); + await ChannelScreen.goToChannel(testChannel.display_name); }); afterAll(async () => { diff --git a/detox/e2e/test/smoke_test/channels.e2e.js b/detox/e2e/test/smoke_test/channels.e2e.js index 84a32c327..181d41a3e 100644 --- a/detox/e2e/test/smoke_test/channels.e2e.js +++ b/detox/e2e/test/smoke_test/channels.e2e.js @@ -31,10 +31,10 @@ describe('Channels', () => { const { channelNavBarTitle, closeMainSidebar, + goToChannel, openMainSidebar, } = ChannelScreen; const { - getChannelByDisplayName, getFilteredChannelByDisplayName, hasChannelDisplayNameAtIndex, hasFilteredChannelDisplayNameAtIndex, @@ -94,18 +94,16 @@ describe('Channels', () => { 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, favorite, and direct message channels - await getChannelByDisplayName(privateChannel.display_name).tap(); - await openMainSidebar(); - await getChannelByDisplayName(publicChannel.display_name).tap(); - await openMainSidebar(); - await getChannelByDisplayName(favoriteChannel.display_name).tap(); - await openMainSidebar(); - await getChannelByDisplayName(dmOtherUser.username).tap(); - await openMainSidebar(); + await goToChannel(privateChannel.display_name); + await goToChannel(publicChannel.display_name); + await goToChannel(favoriteChannel.display_name); + await goToChannel(dmOtherUser.username); // * 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); diff --git a/detox/e2e/test/smoke_test/direct_messages.e2e.js b/detox/e2e/test/smoke_test/direct_messages.e2e.js index 1677abd87..84806f8cb 100644 --- a/detox/e2e/test/smoke_test/direct_messages.e2e.js +++ b/detox/e2e/test/smoke_test/direct_messages.e2e.js @@ -26,6 +26,7 @@ describe('Direct Messages', () => { const { channelNavBarTitle, closeMainSidebar, + goToChannel, openMainSidebar, } = ChannelScreen; const { @@ -67,8 +68,7 @@ describe('Direct Messages', () => { await ChannelInfoScreen.open(); await expect(ChannelInfoScreen.headerDisplayName).toHaveText(testOtherUser.username); await ChannelInfoScreen.close(); - await openMainSidebar(); - await getChannelByDisplayName(testOtherUser.username).tap(); + await goToChannel(testOtherUser.username); // # Close DM channel await ChannelInfoScreen.open(); @@ -78,6 +78,8 @@ describe('Direct Messages', () => { await expect(channelNavBarTitle).toHaveText(townSquareChannel.display_name); await openMainSidebar(); await expect(getChannelByDisplayName(testOtherUser.username)).not.toBeVisible(); + + // # Close main sidebar await closeMainSidebar(); }); diff --git a/detox/e2e/test/smoke_test/favorite_channels.e2e.js b/detox/e2e/test/smoke_test/favorite_channels.e2e.js index c9aa1b9ff..dd9f54369 100644 --- a/detox/e2e/test/smoke_test/favorite_channels.e2e.js +++ b/detox/e2e/test/smoke_test/favorite_channels.e2e.js @@ -17,16 +17,14 @@ import {Setup} from '@support/server_api'; describe('Favorite Channels', () => { const { closeMainSidebar, + goToChannel, openMainSidebar, } = ChannelScreen; const { favoriteSwitchFalse, favoriteSwitchTrue, } = ChannelInfoScreen; - const { - getChannelByDisplayName, - hasChannelDisplayNameAtIndex, - } = MainSidebar; + const {hasChannelDisplayNameAtIndex} = MainSidebar; let testChannel; beforeAll(async () => { @@ -43,8 +41,7 @@ describe('Favorite Channels', () => { it('MM-T3191 should be able to favorite a channel', async () => { // # Open channel info screen - await openMainSidebar(); - await getChannelByDisplayName(testChannel.display_name).tap(); + await goToChannel(testChannel.display_name); await ChannelInfoScreen.open(); // * Verify favorite switch is toggled off @@ -70,8 +67,7 @@ describe('Favorite Channels', () => { it('MM-T3192 should be able to un-favorite a channel', async () => { // # Open channel info screen - await openMainSidebar(); - await getChannelByDisplayName(testChannel.display_name).tap(); + await goToChannel(testChannel.display_name); await ChannelInfoScreen.open(); // * Verify favorite switch is toggled on diff --git a/detox/e2e/test/smoke_test/manage_members.e2e.js b/detox/e2e/test/smoke_test/manage_members.e2e.js new file mode 100644 index 000000000..ad90204c7 --- /dev/null +++ b/detox/e2e/test/smoke_test/manage_members.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 { + ChannelInfoScreen, + ChannelMembersScreen, + ChannelScreen, +} from '@support/ui/screen'; +import { + Channel, + Setup, + Team, + User, +} from '@support/server_api'; +import {getRandomId} from '@support/utils'; + +describe('Manage Members', () => { + const searchTerm = getRandomId(); + const { + removeMembers, + searchInput, + } = ChannelMembersScreen; + let testChannel; + let testUser; + let testOtherUser; + + beforeAll(async () => { + const {channel, team, user} = await Setup.apiInit(); + testChannel = channel; + testUser = user; + + ({user: testOtherUser} = await User.apiCreateUser({prefix: searchTerm})); + await Team.apiAddUserToTeam(testOtherUser.id, team.id); + await Channel.apiAddUserToChannel(testOtherUser.id, testChannel.id); + + // # Open channel screen + await ChannelScreen.open(testUser); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T3198 should be able to remove a member', async () => { + // # Go to channel + await ChannelScreen.goToChannel(testChannel.display_name); + + // * Verify initial member count is 3 + await ChannelInfoScreen.open(); + await expect(element(by.id(ChannelInfoScreen.testID.manageMembersAction).withDescendant(by.text('3')))).toBeVisible(); + + // # Remove 1 member + await ChannelInfoScreen.manageMembersAction.tap(); + await searchInput.typeText(searchTerm); + await removeMembers([testOtherUser.username]); + + // * Verify member count is 2 and member is removed + await expect(element(by.id(ChannelInfoScreen.testID.manageMembersAction).withDescendant(by.text('2')))).toBeVisible(); + await ChannelInfoScreen.manageMembersAction.tap(); + await searchInput.typeText(searchTerm); + await expect(element(by.text('No Results'))).toBeVisible(); + await ChannelMembersScreen.back(); + + // # Close channel info screen + await ChannelInfoScreen.close(); + }); +}); diff --git a/detox/e2e/test/smoke_test/message_posting.e2e.js b/detox/e2e/test/smoke_test/message_posting.e2e.js index e1f6c9716..c7b4016f7 100644 --- a/detox/e2e/test/smoke_test/message_posting.e2e.js +++ b/detox/e2e/test/smoke_test/message_posting.e2e.js @@ -7,7 +7,10 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {ChannelScreen} from '@support/ui/screen'; +import { + ChannelScreen, + LongPostScreen, +} from '@support/ui/screen'; import { Channel, Post, @@ -15,7 +18,9 @@ import { } from '@support/server_api'; describe('Message Posting', () => { + const longMessage = 'The quick brown fox jumps over the lazy dog.'.repeat(30); const { + getLongPostItem, getPostListPostItem, postMessage, } = ChannelScreen; @@ -37,19 +42,32 @@ describe('Message Posting', () => { it('MM-T3217 should be able to post a long message', async () => { // # Post a long message - const message = 'The quick brown fox jumps over the lazy dog.'.repeat(30); - await postMessage(message, {quickReplace: true}); + await postMessage(longMessage, {quickReplace: true}); // * Verify message is posted const {post} = await Post.apiGetLastPostInChannel(testChannel.id); const { postListPostItem, postListPostItemShowMoreButton, - } = await getPostListPostItem(post.id, message); + } = await getPostListPostItem(post.id, longMessage); await expect(postListPostItem).toExist(); await expect(postListPostItemShowMoreButton).toExist(); }); + it('MM-T3229 should be able to open long post via show more', async () => { + // # Open long post via show more + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + const {postListPostItemShowMoreButton} = await getPostListPostItem(post.id, longMessage); + await postListPostItemShowMoreButton.tap(); + + // * Verify long post is displayed + const {longPostItem} = getLongPostItem(post.id, longMessage); + await expect(longPostItem).toExist(); + + // # Close long post screen + await LongPostScreen.closeLongPostButton.tap(); + }); + it('MM-T3269 should be able to post a markdown image', async () => { // # Post a markdown image const message = '![alt text that displays if loading fails](https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png)'; diff --git a/detox/e2e/test/smoke_test/mute_channels.e2e.js b/detox/e2e/test/smoke_test/mute_channels.e2e.js new file mode 100644 index 000000000..322c2fb44 --- /dev/null +++ b/detox/e2e/test/smoke_test/mute_channels.e2e.js @@ -0,0 +1,66 @@ +// 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, +} from '@support/ui/screen'; +import {Setup} from '@support/server_api'; + +describe('Mute Channels', () => { + const { + closeMainSidebar, + goToChannel, + openMainSidebar, + } = ChannelScreen; + const { + muteSwitchFalse, + muteSwitchTrue, + } = ChannelInfoScreen; + const {hasChannelDisplayNameAtIndex} = MainSidebar; + let testChannel; + + beforeAll(async () => { + const {channel, user} = await Setup.apiInit(); + testChannel = channel; + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T3193 should be able to mute a channel', async () => { + // # Open channel info screen + await goToChannel(testChannel.display_name); + await ChannelInfoScreen.open(); + + // * Verify favorite switch is toggled off + await expect(muteSwitchFalse).toBeVisible(); + await expect(muteSwitchTrue).not.toBeVisible(); + + // # Toggle on mute switch + await muteSwitchFalse.tap(); + + // * Verify mute switch is toggled on + await expect(muteSwitchTrue).toBeVisible(); + await expect(muteSwitchFalse).not.toBeVisible(); + + // * Verify channel appears last in the channels list + await ChannelInfoScreen.close(); + await openMainSidebar(); + await hasChannelDisplayNameAtIndex(2, testChannel.display_name); + + // # Close main sidebar + await closeMainSidebar(); + }); +}); diff --git a/detox/e2e/test/smoke_test/unread_channels.e2e.js b/detox/e2e/test/smoke_test/unread_channels.e2e.js index 2dc7c2f74..134a4f163 100644 --- a/detox/e2e/test/smoke_test/unread_channels.e2e.js +++ b/detox/e2e/test/smoke_test/unread_channels.e2e.js @@ -15,22 +15,36 @@ import { } from '@support/server_api'; describe('Unread channels', () => { + const { + goToChannel, + closeMainSidebar, + openMainSidebar, + } = ChannelScreen; + const { + channelsList, + channelsListUnreadIndicator, + hasChannelDisplayNameAtIndex, + } = MainSidebar; + let testUser; + let testTeam; let newChannel; let aChannel; let zChannel; beforeAll(async () => { - const {user, channel, team} = await Setup.apiInit(); + const {channel, team, user} = await Setup.apiInit(); + testUser = user; + testTeam = team; newChannel = channel; - ({channel: aChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'a-channel', teamId: team.id})); - await Channel.apiAddUserToChannel(user.id, aChannel.id); + ({channel: aChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'a-channel', teamId: testTeam.id})); + await Channel.apiAddUserToChannel(testUser.id, aChannel.id); - ({channel: zChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'z-channel', teamId: team.id})); - await Channel.apiAddUserToChannel(user.id, zChannel.id); + ({channel: zChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'z-channel', teamId: testTeam.id})); + await Channel.apiAddUserToChannel(testUser.id, zChannel.id); // # Open channel screen - await ChannelScreen.open(user); + await ChannelScreen.open(testUser); }); afterAll(async () => { @@ -38,12 +52,6 @@ describe('Unread channels', () => { }); it('MM-T3187 Unread channels sort at top', async () => { - const {openMainSidebar} = ChannelScreen; - const { - getChannelByDisplayName, - hasChannelDisplayNameAtIndex, - } = MainSidebar; - // # Open main sidebar (with at least one unread channel) await openMainSidebar(); @@ -52,13 +60,12 @@ describe('Unread channels', () => { await hasChannelDisplayNameAtIndex(0, aChannel.display_name); await hasChannelDisplayNameAtIndex(1, newChannel.display_name); await hasChannelDisplayNameAtIndex(2, zChannel.display_name); + await closeMainSidebar(); // # Tap an unread channel to view it - await getChannelByDisplayName(aChannel.display_name).tap(); - await openMainSidebar(); - await getChannelByDisplayName(newChannel.display_name).tap(); - await openMainSidebar(); - await getChannelByDisplayName(zChannel.display_name).tap(); + await goToChannel(aChannel.display_name); + await goToChannel(newChannel.display_name); + await goToChannel(zChannel.display_name); // * Channel you just read is no longer listed in Unreads await openMainSidebar(); @@ -69,6 +76,28 @@ describe('Unread channels', () => { await hasChannelDisplayNameAtIndex(2, 'Off-Topic'); await hasChannelDisplayNameAtIndex(3, 'Town Square'); await hasChannelDisplayNameAtIndex(4, zChannel.display_name); - await getChannelByDisplayName(aChannel.display_name).tap(); + + // # Close main sidebar + await closeMainSidebar(); + }); + + it('MM-T3188 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: `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(); + + // # Close main sidebar + await closeMainSidebar(); }); }); diff --git a/detox/e2e/test/smoke_test/user_status.e2e.js b/detox/e2e/test/smoke_test/user_status.e2e.js index c4d22c621..590dbbce6 100644 --- a/detox/e2e/test/smoke_test/user_status.e2e.js +++ b/detox/e2e/test/smoke_test/user_status.e2e.js @@ -7,10 +7,7 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import { - MainSidebar, - SettingsSidebar, -} from '@support/ui/component'; +import {SettingsSidebar} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; import { Channel, @@ -52,7 +49,7 @@ describe('User Status', () => { const { closeSettingsSidebar, getPostListPostItem, - openMainSidebar, + goToChannel, openSettingsSidebar, } = ChannelScreen; @@ -66,8 +63,7 @@ describe('User Status', () => { await ChannelScreen.open(testOtherUser); // * Verify user's profile has status away as seen by other user - await openMainSidebar(); - await MainSidebar.getChannelByDisplayName(testUser.username).tap(); + await goToChannel(testUser.username); const {post} = await Post.apiGetLastPostInChannel(testChannel.id); const {postListPostItemProfilePictureUserStatus} = await getPostListPostItem(post.id, testMessage, {userId: testUser.id, userStatus: 'away'}); await expect(postListPostItemProfilePictureUserStatus).toBeVisible();