diff --git a/detox/e2e/support/ui/screen/channel.ts b/detox/e2e/support/ui/screen/channel.ts index 6bfa21bcf..7e7194543 100644 --- a/detox/e2e/support/ui/screen/channel.ts +++ b/detox/e2e/support/ui/screen/channel.ts @@ -145,15 +145,16 @@ class ChannelScreen { return this.postList.getPostMessageAtIndex(index); }; - toBeVisible = async () => { + toBeVisible = async (timeout = timeouts.TEN_SEC) => { await wait(timeouts.ONE_SEC); - await waitFor(this.channelScreen).toExist().withTimeout(timeouts.TEN_SEC); + await waitFor(this.channelScreen).toExist().withTimeout(timeout); return this.channelScreen; }; open = async (categoryKey: string, channelName: string) => { // # Open channel screen + await waitFor(ChannelListScreen.getChannelItemDisplayName(categoryKey, channelName)).toBeVisible().withTimeout(timeouts.TEN_SEC); await ChannelListScreen.getChannelItemDisplayName(categoryKey, channelName).tap(); try { await this.scheduledPostTooltipCloseButton.tap(); @@ -311,6 +312,35 @@ class ChannelScreen { await this.scheduleDraftInforMessage.tap(); } }; + + assertPostMessageEdited = async ( + postId: string, + updatedMessage: string, + locator: 'channel_page' | 'pinned_page' | 'thread_page' | 'search_page' | 'saved_messages_page' | 'recent_mentions_page' = 'channel_page', + ) => { + const locatorTestIDs = { + channel_page: 'channel.post_list.post', + pinned_page: 'pinned_messages.post_list.post', + search_page: 'search_results.post_list.post', + thread_page: 'thread.post_list.post', + saved_messages_page: 'saved_messages.post_list.post', + recent_mentions_page: 'recent_mentions.post_list.post', + }; + + const postItemTestID = locatorTestIDs[locator]; + const postItemElement = `${postItemTestID}.${postId}`; + const postItemMatcher = by.id(postItemElement); + + // Escape special characters in the message for regex + const escapedMessage = updatedMessage.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + + // Match text that contains the updated message followed by "Edited" (with possible spacing/icon) + const completeTextPattern = new RegExp(`${escapedMessage}.*Edited`, 'i'); + const completeTextMatcher = by.text(completeTextPattern).withAncestor(postItemMatcher); + + // Wait for the text containing both message and "Edited" to be visible + await waitFor(element(completeTextMatcher)).toBeVisible().withTimeout(timeouts.TEN_SEC); + }; } const channelScreen = new ChannelScreen(); diff --git a/detox/e2e/support/ui/screen/create_or_edit_channel.ts b/detox/e2e/support/ui/screen/create_or_edit_channel.ts index e3110a549..277b8c102 100644 --- a/detox/e2e/support/ui/screen/create_or_edit_channel.ts +++ b/detox/e2e/support/ui/screen/create_or_edit_channel.ts @@ -6,7 +6,7 @@ import { ChannelScreen, ChannelListScreen, } from '@support/ui/screen'; -import {timeouts, wait} from '@support/utils'; +import {timeouts} from '@support/utils'; import {expect} from 'detox'; class CreateOrEditChannelScreen { @@ -51,7 +51,6 @@ class CreateOrEditChannelScreen { openCreateChannel = async () => { // # Open create channel screen await ChannelListScreen.headerPlusButton.tap(); - await wait(timeouts.ONE_SEC); await ChannelListScreen.createNewChannelItem.tap(); return this.toBeVisible(); diff --git a/detox/e2e/support/ui/screen/edit_profile.ts b/detox/e2e/support/ui/screen/edit_profile.ts index b99d8fa20..545421538 100644 --- a/detox/e2e/support/ui/screen/edit_profile.ts +++ b/detox/e2e/support/ui/screen/edit_profile.ts @@ -19,6 +19,7 @@ class EditProfileScreen { lastNameInputDisabled: 'edit_profile_form.lastName.input.disabled', usernameInput: 'edit_profile_form.username.input', usernameInputDisabled: 'edit_profile_form.username.input.disabled', + usernameInputError: 'edit_profile_form.username.input.error', emailInput: 'edit_profile_form.email.input', emailInputDisabled: 'edit_profile_form.email.input.disabled', emailInputDescription: 'edit_profile_form.email.input.description', @@ -38,6 +39,7 @@ class EditProfileScreen { lastNameInputDisabled = element(by.id(this.testID.lastNameInputDisabled)); usernameInput = element(by.id(this.testID.usernameInput)); usernameInputDisabled = element(by.id(this.testID.usernameInputDisabled)); + usernameInputError = element(by.id(this.testID.usernameInputError)); emailInput = element(by.id(this.testID.emailInput)); emailInputDisabled = element(by.id(this.testID.emailInputDisabled)); emailInputDescription = element(by.id(this.testID.emailInputDescription)); diff --git a/detox/e2e/support/ui/screen/find_channels.ts b/detox/e2e/support/ui/screen/find_channels.ts index 47bea21f8..59ad3da53 100644 --- a/detox/e2e/support/ui/screen/find_channels.ts +++ b/detox/e2e/support/ui/screen/find_channels.ts @@ -9,6 +9,7 @@ import {expect} from 'detox'; class FindChannelsScreen { testID = { findChannelsScreenPrefix: 'find_channels.', + filteredArchivedChannelItemPrefix: 'find_channels.filtered_list.remote_channel_item.', filteredChannelItemPrefix: 'find_channels.filtered_list.channel_item.', unfilteredChannelItemPrefix: 'find_channels.unfiltered_list.channel_item.', findChannelsScreen: 'find_channels.screen', @@ -42,6 +43,10 @@ class FindChannelsScreen { return element(by.id(`${this.testID.unfilteredChannelItemPrefix}${channelName}.display_name`)); }; + getFilteredArchivedChannelItem = (channelName: string) => { + return element(by.id(`${this.testID.filteredArchivedChannelItemPrefix}${channelName}`)); + }; + getFilteredChannelItem = (channelName: string) => { return element(by.id(`${this.testID.filteredChannelItemPrefix}${channelName}`)); }; diff --git a/detox/e2e/support/ui/screen/login.ts b/detox/e2e/support/ui/screen/login.ts index 086acbdc1..42998cad6 100644 --- a/detox/e2e/support/ui/screen/login.ts +++ b/detox/e2e/support/ui/screen/login.ts @@ -73,7 +73,26 @@ class LoginScreen { }; login = async (user: any = {}) => { - await retryWithReload(() => this.loginWithRetryIfStuck(user)); + const maxAttempts = 3; + let attempt = 0; + let lastError; + + while (attempt < maxAttempts) { + try { + // eslint-disable-next-line no-await-in-loop + await retryWithReload(() => this.loginWithRetryIfStuck(user)); + return; + } catch (error) { + lastError = error; + attempt += 1; + if (attempt < maxAttempts) { + // eslint-disable-next-line no-await-in-loop + await wait(timeouts.ONE_SEC); + } + } + } + + throw lastError; }; loginAsAdmin = async (user: any = {}) => { diff --git a/detox/e2e/support/ui/screen/pinned_messages.ts b/detox/e2e/support/ui/screen/pinned_messages.ts index f9ccd3008..b2224d3f8 100644 --- a/detox/e2e/support/ui/screen/pinned_messages.ts +++ b/detox/e2e/support/ui/screen/pinned_messages.ts @@ -75,6 +75,22 @@ class PinnedMessagesScreen { this.getPostMessageAtIndex(index), ).toHaveText(postMessage); }; + + verifyReplyCount = async (postId: string, replyCount: number) => { + const replyCountElement = element( + by.id(`pinned_messages.post_list.post.${postId}`). + withDescendant(by.text(`${replyCount} repl${replyCount === 1 ? 'y' : 'ies'}`)), + ); + await expect(replyCountElement).toBeVisible(); + }; + + verifyFollowingLabel = async (postId: string, following: boolean = false) => { + const followingLabelElement = element( + by.id(`pinned_messages.post_list.post.${postId}`). + withDescendant(by.text(following? 'Following' : 'Follow')), + ); + await expect(followingLabelElement).toBeVisible(); + }; } const pinnedMessagesScreen = new PinnedMessagesScreen(); diff --git a/detox/e2e/support/ui/screen/post_options.ts b/detox/e2e/support/ui/screen/post_options.ts index 6f6f04db8..d619ba77b 100644 --- a/detox/e2e/support/ui/screen/post_options.ts +++ b/detox/e2e/support/ui/screen/post_options.ts @@ -22,8 +22,11 @@ class PostOptionsScreen { unpinPostOption: 'post_options.unpin_post.option', editPostOption: 'post_options.edit_post.option', deletePostOption: 'post_options.delete_post.option', + pinnedPostListItemPrefix: 'pinned_messages.post_list.post', }; + searchedPostListItem = (postId: string) => element(by.id(`search_results.post_list.post.${postId}`)); + pinnedPostListItem = (postId: string) => element(by.id(`pinned_messages.post_list.post.${postId}`)); postOptionsScreen = element(by.id(this.testID.postOptionsScreen)); pickReactionButton = element(by.id(this.testID.pickReactionButton)); replyPostOption = element(by.id(this.testID.replyPostOption)); @@ -82,6 +85,16 @@ class PostOptionsScreen { await this.close(); } }; + + openPostOptionsForPinedPosts = async (postId: string) => { + await waitFor(this.pinnedPostListItem(postId)).toExist().withTimeout(timeouts.TWO_SEC); + await this.pinnedPostListItem(postId).longPress(); + }; + + openPostOptionsForSearchedPosts = async (postId: string) => { + await waitFor(this.searchedPostListItem(postId)).toExist().withTimeout(timeouts.TWO_SEC); + await this.searchedPostListItem(postId).longPress(); + }; } const postOptionsScreen = new PostOptionsScreen(); diff --git a/detox/e2e/support/ui/screen/recent_mentions.ts b/detox/e2e/support/ui/screen/recent_mentions.ts index 5989de2ae..931280d8c 100644 --- a/detox/e2e/support/ui/screen/recent_mentions.ts +++ b/detox/e2e/support/ui/screen/recent_mentions.ts @@ -14,12 +14,14 @@ import {expect} from 'detox'; class RecentMentionsScreen { testID = { + recentMentionPostList: 'recent_mentions.post_list.post', recentMentionsScreenPrefix: 'recent_mentions.', recentMentionsScreen: 'recent_mentions.screen', emptyTitle: 'recent_mentions.empty.title', emptyParagraph: 'recent_mentions.empty.paragraph', }; + recentMentionPostList = element(by.id(this.testID.recentMentionPostList)); recentMentionsScreen = element(by.id(this.testID.recentMentionsScreen)); emptyTitle = element(by.id(this.testID.emptyTitle)); emptyParagraph = element(by.id(this.testID.emptyParagraph)); @@ -48,6 +50,10 @@ class RecentMentionsScreen { return this.recentMentionsScreen; }; + recentMentionPostListToBeVisible = async () => { + await waitFor(this.recentMentionPostList).toBeVisible().withTimeout(timeouts.TEN_SEC); + }; + open = async () => { // # Open recent mentions screen await HomeScreen.mentionsTab.tap(); diff --git a/detox/e2e/support/ui/screen/server.ts b/detox/e2e/support/ui/screen/server.ts index 3ce8a7108..24ecdf1b8 100644 --- a/detox/e2e/support/ui/screen/server.ts +++ b/detox/e2e/support/ui/screen/server.ts @@ -91,8 +91,8 @@ class ServerScreen { }; enterPreauthSecret = async (secret: string) => { - await waitFor(this.preauthSecretInput).toBeVisible().withTimeout(timeouts.TEN_SEC); - await this.preauthSecretInput.replaceText(secret); + await waitFor(this.preauthSecretInput.atIndex(0)).toBeVisible().withTimeout(timeouts.TEN_SEC); + await this.preauthSecretInput.atIndex(0).replaceText(secret); }; connectToServerWithPreauthSecret = async (serverUrl: string, serverDisplayName: string, preauthSecret: string) => { diff --git a/detox/e2e/test/products/channels/account/account_menu.e2e.ts b/detox/e2e/test/products/channels/account/account_menu.e2e.ts index c96437304..6de92a031 100644 --- a/detox/e2e/test/products/channels/account/account_menu.e2e.ts +++ b/detox/e2e/test/products/channels/account/account_menu.e2e.ts @@ -7,13 +7,14 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {Setup} from '@support/server_api'; +import {Channel, Post, Setup, User} from '@support/server_api'; import { serverOneUrl, siteOneUrl, } from '@support/test_config'; import { AccountScreen, + ChannelScreen, CustomStatusScreen, EditProfileScreen, HomeScreen, @@ -21,16 +22,23 @@ import { ServerScreen, SettingsScreen, } from '@support/ui/screen'; -import {timeouts, wait} from '@support/utils'; +import {getRandomId, timeouts, wait} from '@support/utils'; import {expect} from 'detox'; describe('Account - Account Menu', () => { const serverOneDisplayName = 'Server 1'; + const channelsCategory = 'channels'; let testUser: any; + let otherUser: any; + let testChannel: any; beforeAll(async () => { - const {user} = await Setup.apiInit(siteOneUrl); + const {channel, user} = await Setup.apiInit(siteOneUrl); testUser = user; + testChannel = channel; + + ({user: otherUser} = await User.apiCreateUser(siteOneUrl)); + await Channel.apiAddUserToChannel(siteOneUrl, otherUser.id, testChannel.id); // # Log in to server and go to account screen await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName); @@ -135,4 +143,74 @@ describe('Account - Account Menu', () => { // # Go back to account screen await SettingsScreen.close(); }); + + it('MM-T3472 - should be able to add Nickname', async () => { + const nickname = 'nickname'; + const existingNickname = testUser.nickname; + + await AccountScreen.yourProfileOption.tap(); + await EditProfileScreen.toBeVisible(); + + await EditProfileScreen.nicknameInput.replaceText(nickname); + await EditProfileScreen.saveButton.tap(); + await AccountScreen.toBeVisible(); + + // Verify nickname is shown in the profile screen + await AccountScreen.yourProfileOption.tap(); + await EditProfileScreen.toBeVisible(); + await waitFor(EditProfileScreen.nicknameInput).toHaveText(nickname).withTimeout(timeouts.TEN_SEC); + + // Verify nickname is different than previous nickname + const {user} = await User.apiGetUserById(siteOneUrl, testUser.id); + if (existingNickname === user.nickname) { + throw new Error('Nickname was not updated'); + } + + // # Go back to account screen + await EditProfileScreen.close(); + + }); + + it('MM-T3472 - should show error when Username is updated with invalid characters', async () => { + await AccountScreen.yourProfileOption.tap(); + await EditProfileScreen.toBeVisible(); + + await EditProfileScreen.usernameInput.typeText('+new'); + await EditProfileScreen.saveButton.tap(); + + await waitFor(AccountScreen.accountScreen).not.toBeVisible().withTimeout(timeouts.TWO_SEC); + await EditProfileScreen.toBeVisible(); + await expect(EditProfileScreen.usernameInputError).toHaveText('Username must begin with a letter, and contain between 3 to 22 lowercase characters made up of numbers, letters, and the symbols \".\", \"-\", and \"_\".'); + await EditProfileScreen.close(); + }); + + it('MM-T2056 - Username changes when viewed by other user', async () => { + const message = `Test message ${getRandomId()}`; + const newUsername = `newusername${getRandomId()}`; + await HomeScreen.channelListTab.tap(); + await ChannelScreen.open(channelsCategory, testChannel.name); + await ChannelScreen.postMessage(message); + + const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id); + const {postListPostItem, postListPostItemHeaderDisplayName} = ChannelScreen.getPostListPostItem(post.id, message); + await expect(postListPostItem).toBeVisible(); + await expect(postListPostItemHeaderDisplayName).toHaveText(testUser.username); + + // Also check profile screen + await ChannelScreen.back(); + await AccountScreen.open(); + + await AccountScreen.yourProfileOption.tap(); + await EditProfileScreen.toBeVisible(); + + await EditProfileScreen.usernameInput.replaceText(newUsername); + await EditProfileScreen.saveButton.tap(); + await AccountScreen.toBeVisible(); + + await HomeScreen.channelListTab.tap(); + await ChannelScreen.open(channelsCategory, testChannel.name); + + const {postListPostItemHeaderDisplayName: updatedUsername} = ChannelScreen.getPostListPostItem(post.id, message); + await expect(updatedUsername).toHaveText(newUsername); + }); }); diff --git a/detox/e2e/test/products/channels/account/theme_display_settings.e2e.ts b/detox/e2e/test/products/channels/account/theme_display_settings.e2e.ts index 466eeb5b1..ad3ce4b88 100644 --- a/detox/e2e/test/products/channels/account/theme_display_settings.e2e.ts +++ b/detox/e2e/test/products/channels/account/theme_display_settings.e2e.ts @@ -66,6 +66,7 @@ describe('Account - Settings - Theme Display Settings', () => { it('MM-T5111_2 - should be able to select a theme and save by tapping navigation back button', async () => { // # Tap on a sapphire option and tap on back button await ThemeDisplaySettingsScreen.sapphireOption.tap(); + await ThemeDisplaySettingsScreen.back(); // * Verify on display settings screen and sapphire is set await DisplaySettingsScreen.toBeVisible(); @@ -79,6 +80,7 @@ describe('Account - Settings - Theme Display Settings', () => { // # Tap on denim option and tap on back button await ThemeDisplaySettingsScreen.denimOption.tap(); + await ThemeDisplaySettingsScreen.back(); // * Verify on display settings screen and denim is set await DisplaySettingsScreen.toBeVisible(); diff --git a/detox/e2e/test/products/channels/channels/archive_channel.e2e.ts b/detox/e2e/test/products/channels/channels/archive_channel.e2e.ts index 4f329d32f..9bfda14af 100644 --- a/detox/e2e/test/products/channels/channels/archive_channel.e2e.ts +++ b/detox/e2e/test/products/channels/channels/archive_channel.e2e.ts @@ -17,7 +17,6 @@ import { } from '@support/test_config'; import { BrowseChannelsScreen, - ChannelDropdownMenuScreen, ChannelScreen, ChannelListScreen, HomeScreen, @@ -64,22 +63,13 @@ describe('Channels - Archive Channel', () => { await ChannelInfoScreen.open(); await ChannelInfoScreen.archivePublicChannel({confirm: true}); - // * Verify on channel screen and post draft archived message is displayed - await ChannelScreen.toBeVisible(); - await expect(ChannelScreen.postDraftArchived).toBeVisible(); - await expect(element(by.text('You are viewing an archived channel. New messages cannot be posted.'))).toBeVisible(); - - // # Tap on close channel button, open browse channels screen, tap on channel dropdown, tap on archived channels menu item, and search for the archived public channel - await ChannelScreen.postDraftArchivedCloseChannelButton.tap(); + // # Tap on close channel button, open browse channels screen, search for the archived public channel await BrowseChannelsScreen.open(); - await BrowseChannelsScreen.channelDropdownTextPublic.tap(); - await wait(timeouts.ONE_SEC); - await ChannelDropdownMenuScreen.archivedChannelsItem.tap(); await BrowseChannelsScreen.searchInput.replaceText(publicChannel.name); // * Verify search returns the archived public channel item await wait(timeouts.ONE_SEC); - await expect(BrowseChannelsScreen.getChannelItemDisplayName(publicChannel.name)).toHaveText(publicChannel.display_name); + await expect(element(by.text(`No matches found for “${publicChannel.name}”`))).toBeVisible(); // # Go back to channel list screen await BrowseChannelsScreen.close(); @@ -113,22 +103,13 @@ describe('Channels - Archive Channel', () => { await ChannelInfoScreen.open(); await ChannelInfoScreen.archivePrivateChannel({confirm: true}); - // * Verify on channel screen and post draft archived message is displayed - await ChannelScreen.toBeVisible(); - await expect(ChannelScreen.postDraftArchived).toBeVisible(); - await expect(element(by.text('You are viewing an archived channel. New messages cannot be posted.'))).toBeVisible(); - // # Tap on close channel button, open browse channels screen, tap on channel dropdown, tap on archived channels menu item, and search for the archived private channel - await ChannelScreen.postDraftArchivedCloseChannelButton.tap(); await BrowseChannelsScreen.open(); - await BrowseChannelsScreen.channelDropdownTextPublic.tap(); - await wait(timeouts.ONE_SEC); - await ChannelDropdownMenuScreen.archivedChannelsItem.tap(); await BrowseChannelsScreen.searchInput.replaceText(privateChannel.name); // * Verify search returns the archived private channel item await wait(timeouts.ONE_SEC); - await expect(BrowseChannelsScreen.getChannelItemDisplayName(privateChannel.name)).toHaveText(privateChannel.display_name); + await expect(element(by.text(`No matches found for “${privateChannel.name}”`))).toBeVisible(); // # Go back to channel list screen await BrowseChannelsScreen.close(); diff --git a/detox/e2e/test/products/channels/channels/browse_channels.e2e.ts b/detox/e2e/test/products/channels/channels/browse_channels.e2e.ts index 6af5c8244..53eae3d75 100644 --- a/detox/e2e/test/products/channels/channels/browse_channels.e2e.ts +++ b/detox/e2e/test/products/channels/channels/browse_channels.e2e.ts @@ -19,7 +19,6 @@ import { } from '@support/test_config'; import { BrowseChannelsScreen, - ChannelDropdownMenuScreen, ChannelScreen, ChannelListScreen, HomeScreen, @@ -85,6 +84,7 @@ describe('Channels - Browse Channels', () => { // # Tap on the new public channel item await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2); + await wait(timeouts.ONE_SEC); await BrowseChannelsScreen.dismissScheduledPostTooltip(); // * Verify on newly joined public channel screen @@ -146,14 +146,15 @@ describe('Channels - Browse Channels', () => { await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, archivedChannel.id); await Channel.apiDeleteChannel(siteOneUrl, archivedChannel.id); await BrowseChannelsScreen.open(); - await BrowseChannelsScreen.channelDropdownTextPublic.tap(); await wait(timeouts.ONE_SEC); - await ChannelDropdownMenuScreen.archivedChannelsItem.tap(); await BrowseChannelsScreen.searchInput.replaceText(archivedChannel.name); // * Verify search returns the archived channel item await wait(timeouts.ONE_SEC); - await expect(BrowseChannelsScreen.getChannelItemDisplayName(archivedChannel.name)).toHaveText(archivedChannel.display_name); + + // * Verify empty search state for browse channels + await wait(timeouts.ONE_SEC); + await expect(element(by.text(`No matches found for “${archivedChannel.name}”`))).toBeVisible(); // # Go back to channel list screen await BrowseChannelsScreen.close(); diff --git a/detox/e2e/test/products/channels/channels/convert_to_private_channel.e2e.ts b/detox/e2e/test/products/channels/channels/convert_to_private_channel.e2e.ts index 10899e09b..dc62a0530 100644 --- a/detox/e2e/test/products/channels/channels/convert_to_private_channel.e2e.ts +++ b/detox/e2e/test/products/channels/channels/convert_to_private_channel.e2e.ts @@ -7,7 +7,7 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {siteOneUrl} from '@support/test_config'; +import {siteTwoUrl} from '@support/test_config'; import { ChannelScreen, ChannelListScreen, @@ -26,7 +26,7 @@ describe('Channels - Convert to Private Channel', () => { beforeAll(async () => { // # Log in to server as admin - await ServerScreen.connectToServer(siteOneUrl, siteOneDisplayName); + await ServerScreen.connectToServer(siteTwoUrl, siteOneDisplayName); await LoginScreen.loginAsAdmin(getAdminAccount()); await wait(timeouts.TWO_SEC); }); diff --git a/detox/e2e/test/products/channels/channels/find_channels.e2e.ts b/detox/e2e/test/products/channels/channels/find_channels.e2e.ts index 4d872f4b5..3b93bd893 100644 --- a/detox/e2e/test/products/channels/channels/find_channels.e2e.ts +++ b/detox/e2e/test/products/channels/channels/find_channels.e2e.ts @@ -71,10 +71,11 @@ describe('Channels - Find Channels', () => { it('MM-T4907_2 - should be able to find and navigate to a public channel', async () => { // # Open find channels screen and search for a public channel to navigate to await FindChannelsScreen.open(); - await FindChannelsScreen.searchInput.replaceText(testChannel.display_name); + await FindChannelsScreen.searchInput.typeText(testChannel.display_name); // # Tap on the target public channel item await FindChannelsScreen.getFilteredChannelItem(testChannel.name).tap(); + await wait(timeouts.FOUR_SEC); // * Verify on target public channel screen await verifyDetailsOnChannelScreen(testChannel.display_name); @@ -87,12 +88,13 @@ describe('Channels - Find Channels', () => { // # Open find channels screen and search for a non-existent channel const searchTerm = 'blahblahblahblah'; await FindChannelsScreen.open(); - await FindChannelsScreen.searchInput.replaceText(searchTerm); + await FindChannelsScreen.searchInput.typeText(searchTerm); // * Verify empty search state for find channels - await wait(timeouts.ONE_SEC); + await wait(timeouts.TWO_SEC); await expect(element(by.text(`No matches found for “${searchTerm}”`))).toBeVisible(); await expect(element(by.text('Check the spelling or try another search.'))).toBeVisible(); + await FindChannelsScreen.searchInput.clearText(); // # Go back to channel list screen await FindChannelsScreen.close(); @@ -110,18 +112,24 @@ describe('Channels - Find Channels', () => { await FindChannelsScreen.searchInput.replaceText(testOtherUser1.username); // * Verify search returns the target direct message channel item - await wait(timeouts.ONE_SEC); + await wait(timeouts.TWO_SEC); await expect(FindChannelsScreen.getFilteredChannelItemDisplayName(directMessageChannel.name)).toHaveText(testOtherUser1.username); // # Search for the group message channel await FindChannelsScreen.searchInput.replaceText(testOtherUser2.username); // * Verify search returns the target group message channel item - await wait(timeouts.ONE_SEC); + await wait(timeouts.TWO_SEC); await FindChannelsScreen.getFilteredChannelItem(groupMessageChannel.name).tap(); + await wait(timeouts.FOUR_SEC); // * Verify on target GM screen - await verifyDetailsOnChannelScreen(`${testOtherUser1.username}, admin, ${testOtherUser2.username}`); + const attributes = await element(by.id('channel_post_list.intro.display_name')).getAttributes(); + + if ('label' in attributes && typeof attributes.label === 'string') { + const displayName = attributes.label; + await expect(ChannelScreen.headerTitle).toHaveText(displayName); + } // # Go back to channel list screen await ChannelScreen.back(); @@ -132,12 +140,14 @@ describe('Channels - Find Channels', () => { const {channel: archivedChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id}); await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, archivedChannel.id); await Channel.apiDeleteChannel(siteOneUrl, archivedChannel.id); + await wait(timeouts.TEN_SEC); // Adding a short wait to ensure channel is archived before searching await FindChannelsScreen.open(); - await FindChannelsScreen.searchInput.replaceText(archivedChannel.name); + await FindChannelsScreen.searchInput.typeText(archivedChannel.display_name); // * Verify search returns the target archived channel item - await wait(timeouts.ONE_SEC); + await wait(timeouts.FOUR_SEC); await FindChannelsScreen.getFilteredChannelItem(archivedChannel.name).tap(); + await wait(timeouts.FOUR_SEC); // * Verify on archievd channel name await verifyDetailsOnChannelScreen(archivedChannel.display_name); @@ -156,13 +166,13 @@ describe('Channels - Find Channels', () => { await FindChannelsScreen.searchInput.replaceText(joinedPrivateChannel.name); // * Verify search returns the target joined private channel item - await wait(timeouts.ONE_SEC); + await wait(timeouts.TWO_SEC); // # Search for an unjoined private channel await FindChannelsScreen.searchInput.replaceText(unjoinedPrivateChannel.name); // * Verify empty search state for find channels - await wait(timeouts.ONE_SEC); + await wait(timeouts.TWO_SEC); await expect(element(by.text(`No matches found for “${unjoinedPrivateChannel.name}”`))).toBeVisible(); // # Go back to channel list screen @@ -171,6 +181,7 @@ describe('Channels - Find Channels', () => { }); async function verifyDetailsOnChannelScreen(display_name: string) { + await wait(timeouts.TWO_SEC); try { await ChannelScreen.scheduledPostTooltipCloseButton.tap(); } catch (error) { diff --git a/detox/e2e/test/products/channels/channels/unarchive_channel.e2e.ts b/detox/e2e/test/products/channels/channels/unarchive_channel.e2e.ts index bbfc48292..ff2301840 100644 --- a/detox/e2e/test/products/channels/channels/unarchive_channel.e2e.ts +++ b/detox/e2e/test/products/channels/channels/unarchive_channel.e2e.ts @@ -7,11 +7,12 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {serverOneUrl} from '@support/test_config'; +import {serverTwoUrl} from '@support/test_config'; import { ChannelScreen, ChannelListScreen, CreateOrEditChannelScreen, + FindChannelsScreen, HomeScreen, LoginScreen, ServerScreen, @@ -26,7 +27,7 @@ describe('Channels - Unarchive Channel', () => { beforeAll(async () => { // # Log in to server as admin - await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName); + await ServerScreen.connectToServer(serverTwoUrl, serverOneDisplayName); await LoginScreen.loginAsAdmin(getAdminAccount()); await wait(timeouts.TWO_SEC); }); @@ -44,25 +45,45 @@ describe('Channels - Unarchive Channel', () => { it('MM-T4944_1 - should be able to unarchive a public channel and confirm', async () => { // # Create a public channel screen, open channel info screen, and tap on archive channel option and confirm const channelDisplayName = `Channel ${getRandomId()}`; + const channelName = channelDisplayName.toLowerCase().replace(/ /g, '-'); + await CreateOrEditChannelScreen.openCreateChannel(); await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName); await CreateOrEditChannelScreen.createButton.tap(); - await wait(timeouts.TWO_SEC); + await wait(timeouts.FOUR_SEC); await expect(ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount).toBeVisible(); await ChannelScreen.scheduledPostTooltipCloseButtonAdminAccount.tap(); await ChannelInfoScreen.open(); await ChannelInfoScreen.archivePublicChannel({confirm: true}); // * Verify on public channel screen and archived post draft is displayed + await ChannelListScreen.toBeVisible(); + await FindChannelsScreen.open(); + await FindChannelsScreen.searchInput.typeText(channelDisplayName); + + // * Verify search returns the target archived channel item + await wait(timeouts.TWO_SEC); + try { + await FindChannelsScreen.getFilteredArchivedChannelItem(channelName).tap(); + } catch { + // Retry tapping the archived channel item if the first attempt fails + await FindChannelsScreen.getFilteredChannelItem(channelName).tap(); + } + + // * Verify on archievd channel name await ChannelScreen.toBeVisible(); - await expect(ChannelScreen.postDraftArchived).toBeVisible(); + await expect(ChannelScreen.headerTitle).toHaveText(channelDisplayName); + await expect(ChannelScreen.introDisplayName).toHaveText(channelDisplayName); + + // # Go back to channel list screen by closing archived channel + await expect(ChannelScreen.archievedCloseChannelButton).toBeVisible(); // # Open channel info screen, tap on unarchive channel and confirm, close and re-open app to reload, and re-open unarchived public channel await ChannelInfoScreen.open(); await ChannelInfoScreen.unarchivePublicChannel({confirm: true}); + await wait(timeouts.FOUR_SEC); // * Verify on unarchived public channel screen and active post draft is displayed - await ChannelScreen.toBeVisible(); await expect(ChannelScreen.postDraft).toBeVisible(); // # Go back to channel list screen @@ -72,20 +93,42 @@ describe('Channels - Unarchive Channel', () => { it('MM-T4944_2 - should be able to unarchive a private channel and confirm', async () => { // # Create a private channel screen, open channel info screen, and tap on archive channel option and confirm const channelDisplayName = `Channel ${getRandomId()}`; + const channelName = channelDisplayName.toLowerCase().replace(/ /g, '-'); await CreateOrEditChannelScreen.openCreateChannel(); + await CreateOrEditChannelScreen.toggleMakePrivateOn(); await CreateOrEditChannelScreen.displayNameInput.replaceText(channelDisplayName); await CreateOrEditChannelScreen.createButton.tap(); + await wait(timeouts.FOUR_SEC); await ChannelInfoScreen.open(); await ChannelInfoScreen.archivePrivateChannel({confirm: true}); - // * Verify on private channel screen and archived post draft is displayed + await ChannelListScreen.toBeVisible(); + await FindChannelsScreen.open(); + await FindChannelsScreen.searchInput.typeText(channelDisplayName); + + // * Verify search returns the target archived channel item + await wait(timeouts.TWO_SEC); + try { + await FindChannelsScreen.getFilteredArchivedChannelItem(channelName).tap(); + } catch { + // Retry tapping the archived channel item if the first attempt fails + await FindChannelsScreen.getFilteredChannelItem(channelName).tap(); + } + await wait(timeouts.TWO_SEC); + + // * Verify on archievd channel name await ChannelScreen.toBeVisible(); + await expect(ChannelScreen.headerTitle).toHaveText(channelDisplayName); + await expect(ChannelScreen.introDisplayName).toHaveText(channelDisplayName); + + // * Verify on private channel screen and archived post draft is displayed await expect(ChannelScreen.postDraftArchived).toBeVisible(); // # Open channel info screen, tap on unarchive channel and confirm, close and re-open app to reload, and re-open unarchived private channel await ChannelInfoScreen.open(); await ChannelInfoScreen.unarchivePrivateChannel({confirm: true}); + await wait(timeouts.FOUR_SEC); // * Verify on unarchived private channel screen and active post draft is displayed await ChannelScreen.toBeVisible(); diff --git a/detox/e2e/test/products/channels/messaging/markdown_latex.e2e.ts b/detox/e2e/test/products/channels/messaging/markdown_latex.e2e.ts index cec26a5c9..a5071981a 100644 --- a/detox/e2e/test/products/channels/messaging/markdown_latex.e2e.ts +++ b/detox/e2e/test/products/channels/messaging/markdown_latex.e2e.ts @@ -48,19 +48,22 @@ describe('Messaging - Markdown Latex', () => { await HomeScreen.logout(); }); - // TODO: Uncomment the test when the issue is fixed https://mattermost.atlassian.net/browse/MM-63817 - it.skip('MM-T4900_1 - should be able to display markdown latex code block', async () => { + it('MM-T4900_1 - should be able to display markdown latex code block', async () => { // # Open a channel screen and post a markdown latex code block - // eslint-disable-next-line no-useless-escape - const message = 'X_k = \sum_{n=0}^{2N-1} x_n \cos \left[\frac{\pi}{N} \left(n+\frac{1}{2}+\frac{N}{2}\right) \left(k+\frac{1}{2}\right) \right]'; + const message = String.raw`X_k = \sum_{n=0}^{2N-1} x_n \cos\left[\frac{\pi}{N}\left(n+\frac{1}{2}\right)\left(k+\frac{N}{2}\right)\right]`; const markdownLatexCodeBlock = `\`\`\`latex\n${message}\n\`\`\``; + await ChannelScreen.open(channelsCategory, testChannel.name); await ChannelScreen.postMessage(markdownLatexCodeBlock); // * Verify markdown latex code block is displayed const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id); const {postListPostItemLatexCodeBlock} = ChannelScreen.getPostListPostItem(post.id); - await waitFor(postListPostItemLatexCodeBlock).toBeVisible().whileElement(by.id(ChannelScreen.postList.testID.flatList)).scroll(50, 'down'); + + await waitFor(postListPostItemLatexCodeBlock). + toBeVisible(). + whileElement(by.id(ChannelScreen.postList.testID.flatList)). + scroll(50, 'down'); await expect(postListPostItemLatexCodeBlock).toBeVisible(); // # Go back to channel list screen diff --git a/detox/e2e/test/products/channels/messaging/message_edit.e2e.ts b/detox/e2e/test/products/channels/messaging/message_edit.e2e.ts index e509c3974..5703e682d 100644 --- a/detox/e2e/test/products/channels/messaging/message_edit.e2e.ts +++ b/detox/e2e/test/products/channels/messaging/message_edit.e2e.ts @@ -75,10 +75,12 @@ describe('Messaging - Message Edit', () => { await EditPostScreen.messageInput.replaceText(updatedMessage); await EditPostScreen.saveButton.tap(); - // * Verify post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedPostListPostItem, postListPostItemEditedIndicator} = ChannelScreen.getPostListPostItem(post.id, updatedMessage); + await expect(EditPostScreen.editPostScreen).not.toBeVisible(); + + const {postListPostItem: updatedPostListPostItem} = ChannelScreen.getPostListPostItem(post.id); await expect(updatedPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + + await ChannelScreen.assertPostMessageEdited(post.id, updatedMessage); // # Go back to channel list screen await ChannelScreen.back(); @@ -141,10 +143,13 @@ describe('Messaging - Message Edit', () => { await EditPostScreen.messageInput.replaceText(updatedReplyMessage); await EditPostScreen.saveButton.tap(); + await expect(EditPostScreen.editPostScreen).not.toBeVisible(); + // * Verify reply post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedReplyPostListPostItem, postListPostItemEditedIndicator} = ThreadScreen.getPostListPostItem(replyPost.id, updatedReplyMessage); + const {postListPostItem: updatedReplyPostListPostItem} = ThreadScreen.getPostListPostItem(replyPost.id); await expect(updatedReplyPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + + await ChannelScreen.assertPostMessageEdited(replyPost.id, updatedReplyMessage, 'thread_page'); // # Go back to channel list screen await ThreadScreen.back(); diff --git a/detox/e2e/test/messaging/message_permalink.e2e.ts b/detox/e2e/test/products/channels/messaging/message_permalink.e2e.ts similarity index 65% rename from detox/e2e/test/messaging/message_permalink.e2e.ts rename to detox/e2e/test/products/channels/messaging/message_permalink.e2e.ts index 78fc6c563..215d671ee 100644 --- a/detox/e2e/test/messaging/message_permalink.e2e.ts +++ b/detox/e2e/test/products/channels/messaging/message_permalink.e2e.ts @@ -35,6 +35,28 @@ describe('Messaging - Message Permalink Preview', () => { let testUser: any; let testOtherUser: any; + const copyLinkFromPost = async (postItem: any) => { + await postItem.longPress(); + await PostOptionsScreen.toBeVisible(); + await PostOptionsScreen.copyLinkOption.tap(); + await wait(timeouts.ONE_SEC); + await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); + }; + + const sendMessage = async (text: string) => { + await ChannelScreen.postInput.tap(); + await ChannelScreen.postInput.replaceText(text); + await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); + await ChannelScreen.sendButton.tap(); + }; + + const expectPermalinkPreviewVisible = async (message: string, channelName: string) => { + const container = element(by.id('permalink-preview-container')); + await waitFor(container).toBeVisible().withTimeout(timeouts.FOUR_SEC); + await expect(element(by.text(message).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expect(element(by.text(`Originally posted in ~${channelName}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + }; + beforeAll(async () => { const {channel, team, user} = await Setup.apiInit(siteOneUrl); testChannel = channel; @@ -67,29 +89,15 @@ describe('Messaging - Message Permalink Preview', () => { await ChannelScreen.open(channelsCategory, testChannel.name); const {postListPostItem} = ChannelScreen.getPostListPostItem(targetPost.post.id, targetMessage); - await postListPostItem.longPress(); - - await PostOptionsScreen.toBeVisible(); - await PostOptionsScreen.copyLinkOption.tap(); - await wait(timeouts.ONE_SEC); - - await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); + await copyLinkFromPost(postListPostItem); const copiedPermalink = `${serverOneUrl}/${testTeam.name}/pl/${targetPost.post.id}`; const messageWithPastedLink = `Check this out ${copiedPermalink}`; - await ChannelScreen.postInput.tap(); - await ChannelScreen.postInput.replaceText(messageWithPastedLink); + await sendMessage(messageWithPastedLink); - await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await ChannelScreen.sendButton.tap(); + await wait(timeouts.FOUR_SEC); // Wait for preview to generate - await wait(timeouts.FOUR_SEC); - - const permalinkPreview = element(by.id('permalink-preview-container')); - await waitFor(permalinkPreview).toBeVisible().withTimeout(timeouts.FOUR_SEC); - - await expect(element(by.text(targetMessage).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); - await expect(element(by.text(`Originally posted in ~${testChannel.display_name}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expectPermalinkPreviewVisible(targetMessage, testChannel.display_name); await ChannelScreen.back(); }); @@ -107,39 +115,24 @@ describe('Messaging - Message Permalink Preview', () => { await ChannelScreen.open(channelsCategory, testChannel.name); const {postListPostItem} = ChannelScreen.getPostListPostItem(targetPost.post.id, targetMessage); - await postListPostItem.longPress(); + await copyLinkFromPost(postListPostItem); - await PostOptionsScreen.toBeVisible(); - await PostOptionsScreen.copyLinkOption.tap(); - await wait(timeouts.ONE_SEC); - - await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); - - await wait(timeouts.FOUR_SEC); + await wait(timeouts.FOUR_SEC); // Wait for toast/animation if any await ChannelScreen.back(); await ChannelScreen.open(channelsCategory, otherChannel.name); const copiedPermalink = `${serverOneUrl}/${testTeam.name}/pl/${targetPost.post.id}`; const messageWithPastedLink = `Check this out from the other channel ${copiedPermalink}`; - await ChannelScreen.postInput.tap(); - await ChannelScreen.postInput.replaceText(messageWithPastedLink); - - await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await ChannelScreen.sendButton.tap(); + await sendMessage(messageWithPastedLink); await wait(timeouts.FOUR_SEC); - const permalinkPreview = element(by.id('permalink-preview-container')); - await waitFor(permalinkPreview).toBeVisible().withTimeout(timeouts.FOUR_SEC); - - await expect(element(by.text(targetMessage).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); - await expect(element(by.text(`Originally posted in ~${testChannel.display_name}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expectPermalinkPreviewVisible(targetMessage, testChannel.display_name); await ChannelScreen.back(); }); it('MM-T4877_3 - should navigate to original post when tapping on permalink preview', async () => { - // Create both channels at the beginning const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id}); await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id); await Channel.apiAddUserToChannel(siteOneUrl, testOtherUser.id, targetChannel.id); @@ -156,13 +149,7 @@ describe('Messaging - Message Permalink Preview', () => { await ChannelScreen.open(channelsCategory, targetChannel.name); const {postListPostItem} = ChannelScreen.getPostListPostItem(targetPost.post.id, targetMessage); - await postListPostItem.longPress(); - - await PostOptionsScreen.toBeVisible(); - await PostOptionsScreen.copyLinkOption.tap(); - await wait(timeouts.ONE_SEC); - - await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); + await copyLinkFromPost(postListPostItem); await wait(timeouts.FOUR_SEC); await ChannelScreen.back(); @@ -170,21 +157,13 @@ describe('Messaging - Message Permalink Preview', () => { const copiedPermalink = `${serverOneUrl}/${testTeam.name}/pl/${targetPost.post.id}`; const messageWithPastedLink = `Check this out ${copiedPermalink}`; - await ChannelScreen.postInput.tap(); - await ChannelScreen.postInput.replaceText(messageWithPastedLink); - - await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await ChannelScreen.sendButton.tap(); + await sendMessage(messageWithPastedLink); await wait(timeouts.FOUR_SEC); + await expectPermalinkPreviewVisible(targetMessage, targetChannel.display_name); + const permalinkPreview = element(by.id('permalink-preview-container')); - await waitFor(permalinkPreview).toBeVisible().withTimeout(timeouts.FOUR_SEC); - - await expect(element(by.text(targetMessage).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); - - await expect(element(by.text(`Originally posted in ~${targetChannel.display_name}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); - await permalinkPreview.tap(); await wait(timeouts.ONE_SEC); @@ -201,7 +180,6 @@ describe('Messaging - Message Permalink Preview', () => { }); it('MM-T4877_4 - should update permalink preview when original post is edited', async () => { - // Create the other channel at the beginning const {channel: otherChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id}); await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, otherChannel.id); @@ -214,13 +192,7 @@ describe('Messaging - Message Permalink Preview', () => { await ChannelScreen.open(channelsCategory, testChannel.name); const {postListPostItem} = ChannelScreen.getPostListPostItem(targetPost.post.id, originalMessage); - await postListPostItem.longPress(); - - await PostOptionsScreen.toBeVisible(); - await PostOptionsScreen.copyLinkOption.tap(); - await wait(timeouts.ONE_SEC); - - await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); + await copyLinkFromPost(postListPostItem); await wait(timeouts.FOUR_SEC); @@ -229,18 +201,10 @@ describe('Messaging - Message Permalink Preview', () => { const copiedPermalink = `${serverOneUrl}/${testTeam.name}/pl/${targetPost.post.id}`; const messageWithPastedLink = `Check this out ${copiedPermalink}`; - await ChannelScreen.postInput.tap(); - await ChannelScreen.postInput.replaceText(messageWithPastedLink); - - await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await ChannelScreen.sendButton.tap(); + await sendMessage(messageWithPastedLink); await wait(timeouts.FOUR_SEC); - const permalinkPreview = element(by.id('permalink-preview-container')); - await waitFor(permalinkPreview).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await expect(element(by.text(originalMessage).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); - - await expect(element(by.text(`Originally posted in ~${testChannel.display_name}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expectPermalinkPreviewVisible(originalMessage, testChannel.display_name); await wait(timeouts.FOUR_SEC); @@ -255,15 +219,13 @@ describe('Messaging - Message Permalink Preview', () => { await wait(timeouts.FOUR_SEC); - await expect(element(by.text(editedMessage).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expectPermalinkPreviewVisible(editedMessage, testChannel.display_name); await expect(element(by.text(originalMessage).withAncestor(by.id('permalink-preview-container')))).not.toBeVisible(); - await expect(element(by.text(`Originally posted in ~${testChannel.display_name}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); await ChannelScreen.back(); }); it('MM-T4877_6 - should handle permalink preview with long message content', async () => { - // Create the other channel at the beginning const {channel: otherChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id}); await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, otherChannel.id); @@ -276,13 +238,7 @@ describe('Messaging - Message Permalink Preview', () => { await ChannelScreen.open(channelsCategory, testChannel.name); const {postListPostItem} = ChannelScreen.getPostListPostItem(targetPost.post.id, longMessage); - await postListPostItem.longPress(); - - await PostOptionsScreen.toBeVisible(); - await PostOptionsScreen.copyLinkOption.tap(); - await wait(timeouts.ONE_SEC); - - await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); + await copyLinkFromPost(postListPostItem); await wait(timeouts.FOUR_SEC); await ChannelScreen.back(); @@ -290,27 +246,18 @@ describe('Messaging - Message Permalink Preview', () => { const copiedPermalink = `${serverOneUrl}/${testTeam.name}/pl/${targetPost.post.id}`; const messageWithPastedLink = `Long message link ${copiedPermalink}`; - await ChannelScreen.postInput.tap(); - await ChannelScreen.postInput.replaceText(messageWithPastedLink); - - await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await ChannelScreen.sendButton.tap(); + await sendMessage(messageWithPastedLink); await wait(timeouts.FOUR_SEC); - const permalinkPreview = element(by.id('permalink-preview-container')); - await waitFor(permalinkPreview).toBeVisible().withTimeout(timeouts.FOUR_SEC); - const truncatedText = longMessage.substring(0, 150) + '...'; - await expect(element(by.text(truncatedText).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expectPermalinkPreviewVisible(truncatedText, testChannel.display_name); await expect(element(by.text(longMessage).withAncestor(by.id('permalink-preview-container')))).not.toBeVisible(); - await expect(element(by.text(`Originally posted in ~${testChannel.display_name}`).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); await ChannelScreen.back(); }); it('MM-T4877_7 - should handle permalink preview when original post is deleted', async () => { - // Create the other channel at the beginning const {channel: otherChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id}); await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, otherChannel.id); @@ -325,13 +272,7 @@ describe('Messaging - Message Permalink Preview', () => { const {postListPostItem} = ChannelScreen.getPostListPostItem(targetPost.id, targetMessage); await expect(postListPostItem).toBeVisible(); await wait(timeouts.ONE_SEC); - await postListPostItem.longPress(); - - await PostOptionsScreen.toBeVisible(); - await PostOptionsScreen.copyLinkOption.tap(); - await wait(timeouts.ONE_SEC); - - await expect(PostOptionsScreen.postOptionsScreen).not.toBeVisible(); + await copyLinkFromPost(postListPostItem); await wait(timeouts.FOUR_SEC); @@ -340,16 +281,10 @@ describe('Messaging - Message Permalink Preview', () => { const copiedPermalink = `${serverOneUrl}/${testTeam.name}/pl/${targetPost.id}`; const messageWithPastedLink = `Check this post ${copiedPermalink}`; - await ChannelScreen.postInput.tap(); - await ChannelScreen.postInput.replaceText(messageWithPastedLink); - - await waitFor(ChannelScreen.sendButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await ChannelScreen.sendButton.tap(); + await sendMessage(messageWithPastedLink); await wait(timeouts.FOUR_SEC); - const permalinkPreview = element(by.id('permalink-preview-container')); - await waitFor(permalinkPreview).toBeVisible().withTimeout(timeouts.FOUR_SEC); - await expect(element(by.text(targetMessage).withAncestor(by.id('permalink-preview-container')))).toBeVisible(); + await expectPermalinkPreviewVisible(targetMessage, testChannel.display_name); await wait(timeouts.FOUR_SEC); @@ -370,7 +305,7 @@ describe('Messaging - Message Permalink Preview', () => { await wait(timeouts.FOUR_SEC); await expect(element(by.text(targetMessage).withAncestor(by.id('permalink-preview-container')))).not.toBeVisible(); - await expect(permalinkPreview).not.toBeVisible(); + await expect(element(by.id('permalink-preview-container'))).not.toBeVisible(); await ChannelScreen.back(); }); diff --git a/detox/e2e/test/products/channels/search/cross_team_search.e2e.ts b/detox/e2e/test/products/channels/search/cross_team_search.e2e.ts new file mode 100644 index 000000000..4c4805c66 --- /dev/null +++ b/detox/e2e/test/products/channels/search/cross_team_search.e2e.ts @@ -0,0 +1,238 @@ +// 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 { + Channel, + Post, + Setup, + Team, +} from '@support/server_api'; +import { + serverOneUrl, + siteOneUrl, +} from '@support/test_config'; +import { + ChannelListScreen, + ChannelScreen, + HomeScreen, + LoginScreen, + PermalinkScreen, + SearchMessagesScreen, + ServerScreen, + TeamDropdownMenuScreen, +} from '@support/ui/screen'; +import {timeouts, wait} from '@support/utils'; +import {expect} from 'detox'; + +describe('Search - Cross Team Search', () => { + const serverOneDisplayName = 'Server 1'; + const channelsCategory = 'channels'; + const searchTerm = 'Horses are fun'; + let testUser: any; + let teamOpen: any; + let teamRainforest: any; + let offTopicChannel: any; + let townSquareChannel: any; + let offTopicPost: any; + let townSquarePost: any; + + beforeAll(async () => { + // # Create first team (Rainforest) with user - this will be the team with Off-Topic + const {team, user} = await Setup.apiInit(siteOneUrl, {teamOptions: {prefix: 'rainforest'}}); + teamRainforest = team; + testUser = user; + + // # Get Off-Topic channel for Rainforest team + const {channel: offTopicChannelResult} = await Channel.apiGetChannelByName(siteOneUrl, teamRainforest.id, 'off-topic'); + offTopicChannel = offTopicChannelResult; + + // # Create second team (Team Open) and add user to it + const {team: testTeamOpen} = await Team.apiCreateTeam(siteOneUrl, {prefix: 'team-open'}); + teamOpen = testTeamOpen; + await Team.apiAddUserToTeam(siteOneUrl, testUser.id, teamOpen.id); + + // # Get Town Square channel for Team Open + const {channel: townSquareChannelResult} = await Channel.apiGetChannelByName(siteOneUrl, teamOpen.id, 'town-square'); + townSquareChannel = townSquareChannelResult; + + // # Log in to server + await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName); + await LoginScreen.login(testUser); + }); + + beforeEach(async () => { + // * Verify on channel list screen + await ChannelListScreen.toBeVisible(); + }); + + afterAll(async () => { + // # Log out + await HomeScreen.logout(); + }); + + it('MM-T5827 - should be able to search messages across multiple teams and navigate to results', async () => { + // # a) Click on Off-Topic channel and dismiss tutorial if present + await ChannelScreen.open(channelsCategory, offTopicChannel.name); + + // * a) Verify in Off-Topic channel + await ChannelScreen.toBeVisible(); + await expect(ChannelScreen.introDisplayName).toHaveText(offTopicChannel.display_name); + + // # Dismiss "Type a message" pop-up if present + try { + const tutorialX = element(by.text('X')); + await tutorialX.tap(); + await wait(timeouts.ONE_SEC); + } catch (e) { + // Tutorial not present, continue + } + + // # b) In the "Write to..." field, type "Horses are fun" then tap on the airplane icon + await ChannelScreen.postMessage(searchTerm); + + // * b) Verify message posted in the channel + const {post: offTopicPostResult} = await Post.apiGetLastPostInChannel(siteOneUrl, offTopicChannel.id); + offTopicPost = offTopicPostResult; + const {postListPostItem: offTopicPostItem} = ChannelScreen.getPostListPostItem(offTopicPost.id, searchTerm); + await expect(offTopicPostItem).toBeVisible(); + + // # c) Tap on the back arrow, top left of the screen + await ChannelScreen.back(); + + // * c) Verify back at the main screen showing the channels (in Rainforest team) + await ChannelListScreen.toBeVisible(); + await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(teamRainforest.display_name); + + // # d) Tap on Team Open from team sidebar (TE button on the left) + await ChannelListScreen.getTeamItemNotSelected(teamOpen.id).tap(); + await wait(timeouts.ONE_SEC); + + // * d) Verify in Team Open + await expect(ChannelListScreen.headerTeamDisplayName).toHaveText(teamOpen.display_name); + + // # e) Tap on Town Square channel + await ChannelScreen.open(channelsCategory, townSquareChannel.name); + + // * e) Verify in Town Square channel + await ChannelScreen.toBeVisible(); + await expect(ChannelScreen.introDisplayName).toHaveText(townSquareChannel.display_name); + + // # f) In the "Write to..." field, type "Horses are fun" then tap on the airplane icon + await ChannelScreen.postMessage(searchTerm); + + // * f) Verify message posted in the channel + const {post: townSquarePostResult} = await Post.apiGetLastPostInChannel(siteOneUrl, townSquareChannel.id); + townSquarePost = townSquarePostResult; + const {postListPostItem: townSquarePostItem} = ChannelScreen.getPostListPostItem(townSquarePost.id, searchTerm); + await expect(townSquarePostItem).toBeVisible(); + + // # g) Tap on the back arrow, top left + await ChannelScreen.back(); + + // * g) Verify back at the main screen showing the channels + await ChannelListScreen.toBeVisible(); + + // # h) Tap on the search icon, 2nd from the left in the white bar along the bottom of the screen + await SearchMessagesScreen.open(); + + // * h) Verify on the Search screen + await SearchMessagesScreen.toBeVisible(); + await expect(SearchMessagesScreen.largeHeaderTitle).toHaveText('Search'); + + // * i) Verify to the right of "Search Options" is "Team Open" with a drop-down arrow + await expect(SearchMessagesScreen.searchModifierHeader).toHaveText('Search options'); + await expect(element(by.text(teamOpen.display_name))).toBeVisible(); + + // # j) Tap on Team Open with the drop-down arrow, then select All teams + await SearchMessagesScreen.teamPickerButton.tap(); + await wait(timeouts.ONE_SEC); + await element(by.text('All teams')).tap(); + + // * j) Verify the selector changed to All teams + await expect(element(by.text('All teams'))).toBeVisible(); + + // # k) In the "Search messages and files" field, type "horses" and press Enter + await SearchMessagesScreen.searchInput.typeText('horses'); + await SearchMessagesScreen.searchInput.tapReturnKey(); + await wait(timeouts.TWO_SEC); + + // * k) Verify search results contain messages from both teams + const {postListPostItem: offTopicSearchResult} = SearchMessagesScreen.getPostListPostItem(offTopicPost.id, searchTerm); + const {postListPostItem: townSquareSearchResult} = SearchMessagesScreen.getPostListPostItem(townSquarePost.id, searchTerm); + await expect(offTopicSearchResult).toBeVisible(); + await expect(townSquareSearchResult).toBeVisible(); + + // # l) Tap on the "All teams" with the drop-down arrow selector and select "Team Open" + await SearchMessagesScreen.teamPickerButton.tap(); + await wait(timeouts.ONE_SEC); + await TeamDropdownMenuScreen.getTeamIcon(teamOpen.id).tap(); + + // * l) Verify search results update to show only the message from Team Open + await expect(townSquareSearchResult).toBeVisible(); + await expect(offTopicSearchResult).not.toBeVisible(); + + // # m) Clear the search input field + await SearchMessagesScreen.searchClearButton.tap(); + + // # m) Then tap on "Team Open" selector to the right of Search options + await SearchMessagesScreen.teamPickerButton.tap(); + await wait(timeouts.ONE_SEC); + + // * m) Verify pop-up headed "Select a team to search" + await expect(element(by.text('Select a team to search'))).toBeVisible(); + + // # n) Tap on "All teams" option + await element(by.text('All teams')).tap(); + + // * n) Verify only two options visible - "exclude search terms" and "messages with phrases" + await expect(SearchMessagesScreen.searchModifierExclude).toBeVisible(); + await expect(SearchMessagesScreen.searchModifierPhrases).toBeVisible(); + await expect(SearchMessagesScreen.searchModifierFrom).not.toBeVisible(); + await expect(SearchMessagesScreen.searchModifierIn).not.toBeVisible(); + + // # o) Tap on "All teams" drop-down selector and select "Rainforest" + await SearchMessagesScreen.teamPickerButton.tap(); + await wait(timeouts.ONE_SEC); + await TeamDropdownMenuScreen.getTeamIcon(teamRainforest.id).tap(); + + // * o) Verify search options now include "From:" and "In:" (4 options total) + await expect(SearchMessagesScreen.searchModifierFrom).toBeVisible(); + await expect(SearchMessagesScreen.searchModifierIn).toBeVisible(); + await expect(SearchMessagesScreen.searchModifierExclude).toBeVisible(); + await expect(SearchMessagesScreen.searchModifierPhrases).toBeVisible(); + + // # p) Tap on "From:" then insert testUser's at_mention_name and press Enter + await SearchMessagesScreen.searchModifierFrom.tap(); + await SearchMessagesScreen.searchInput.typeText(`${testUser.username} horses`); + await SearchMessagesScreen.searchInput.tapReturnKey(); + await wait(timeouts.TWO_SEC); + + // * p) Verify the message posted in Off-Topic channel in Rainforest team appears in search results + await expect(offTopicSearchResult).toBeVisible(); + + // # q) Tap on the message in the search results + await offTopicSearchResult.tap(); + await wait(timeouts.TWO_SEC); + + // * q) Verify pop-up headed "Off-Topic" and a button saying "Jump to recent messages" + await PermalinkScreen.toBeVisible(); + await expect(PermalinkScreen.jumpToRecentMessagesButton).toBeVisible(); + + // # r) Tap on "Jump to recent messages" + await PermalinkScreen.jumpToRecentMessages(); + await wait(timeouts.TWO_SEC); + + // * r) Verify in the Off-Topic channel and see the message posted earlier + await ChannelScreen.toBeVisible(); + await expect(offTopicPostItem).toBeVisible(); + + // # Go back to channel list screen + await ChannelScreen.back(); + }); +}); diff --git a/detox/e2e/test/products/channels/search/pinned_messages.e2e.ts b/detox/e2e/test/products/channels/search/pinned_messages.e2e.ts index 6b50a240b..50cee8dfe 100644 --- a/detox/e2e/test/products/channels/search/pinned_messages.e2e.ts +++ b/detox/e2e/test/products/channels/search/pinned_messages.e2e.ts @@ -19,7 +19,6 @@ import { ChannelListScreen, ChannelScreen, EditPostScreen, - HomeScreen, LoginScreen, PermalinkScreen, PostOptionsScreen, @@ -52,11 +51,6 @@ describe('Search - Pinned Messages', () => { await ChannelListScreen.toBeVisible(); }); - afterAll(async () => { - // # Log out - await HomeScreen.logout(); - }); - it('MM-T4918_1 - should match elements on pinned messages screen', async () => { // # Open a channel screen, open channel info screen, and open pinned messages screen await ChannelScreen.open(channelsCategory, testChannel.name); @@ -64,6 +58,7 @@ describe('Search - Pinned Messages', () => { await PinnedMessagesScreen.open(); // * Verify basic elements on pinned messages screen + await waitFor(element(by.text('No pinned messages yet'))).toBeVisible().withTimeout(timeouts.FOUR_SEC); await expect(PinnedMessagesScreen.emptyTitle).toHaveText('No pinned messages yet'); await expect(PinnedMessagesScreen.emptyParagraph).toHaveText('To pin important messages, long-press on a message and choose Pin To Channel. Pinned messages will be visible to everyone in this channel.'); @@ -98,6 +93,7 @@ describe('Search - Pinned Messages', () => { // # Tap on post and jump to recent messages await pinnedMessagesPostListPostItem.tap(); + await waitFor(PermalinkScreen.jumpToRecentMessagesButton).toBeVisible().withTimeout(timeouts.FOUR_SEC); await PermalinkScreen.jumpToRecentMessages(); // * Verify on channel screen and pinned message is displayed @@ -136,12 +132,12 @@ describe('Search - Pinned Messages', () => { await EditPostScreen.saveButton.tap(); // * Verify post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedPostListPostItem, postListPostItemEditedIndicator} = PinnedMessagesScreen.getPostListPostItem(pinnedPost.id, updatedMessage); - await expect(updatedPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + const {postListPostItem: updatedPostListPostItem} = PinnedMessagesScreen.getPostListPostItem(pinnedPost.id); + await waitFor(updatedPostListPostItem).toBeVisible().withTimeout(timeouts.FOUR_SEC); + await ChannelScreen.assertPostMessageEdited(pinnedPost.id, updatedMessage, 'pinned_page'); // # Open post options for updated pinned message and tap on reply option - await PinnedMessagesScreen.openPostOptionsFor(pinnedPost.id, updatedMessage); + await PostOptionsScreen.openPostOptionsForPinedPosts(pinnedPost.id); await PostOptionsScreen.replyPostOption.tap(); // * Verify on thread screen @@ -160,12 +156,12 @@ describe('Search - Pinned Messages', () => { await ThreadScreen.back(); // * Verify reply count and following button - const {postListPostItem, postListPostItemFooterReplyCount, postListPostItemFooterFollowingButton} = PinnedMessagesScreen.getPostListPostItem(pinnedPost.id, updatedMessage); - await expect(postListPostItemFooterReplyCount).toHaveText('1 reply'); - await expect(postListPostItemFooterFollowingButton).toBeVisible(); + const {postListPostItem} = PinnedMessagesScreen.getPostListPostItem(pinnedPost.id, updatedMessage); + await PinnedMessagesScreen.verifyReplyCount(pinnedPost.id, 1); + await PinnedMessagesScreen.verifyFollowingLabel(pinnedPost.id, true); // # Open post options for updated pinned message and delete post - await PinnedMessagesScreen.openPostOptionsFor(pinnedPost.id, updatedMessage); + await PostOptionsScreen.openPostOptionsForPinedPosts(pinnedPost.id); await PostOptionsScreen.deletePost({confirm: true}); // * Verify updated pinned message is deleted diff --git a/detox/e2e/test/products/channels/search/recent_mentions.e2e.ts b/detox/e2e/test/products/channels/search/recent_mentions.e2e.ts index fb5e95ecb..d6039a8fd 100644 --- a/detox/e2e/test/products/channels/search/recent_mentions.e2e.ts +++ b/detox/e2e/test/products/channels/search/recent_mentions.e2e.ts @@ -30,6 +30,7 @@ import { ServerScreen, ThreadScreen, } from '@support/ui/screen'; +import {timeouts} from '@support/utils'; import {expect} from 'detox'; describe('Search - Recent Mentions', () => { @@ -91,6 +92,7 @@ describe('Search - Recent Mentions', () => { // * Verify on recent mentions screen and recent mention is displayed with channel info await RecentMentionsScreen.toBeVisible(); + await RecentMentionsScreen.recentMentionPostListToBeVisible(); const {postListPostItem: recentMentionsPostListPostItem, postListPostItemChannelInfoChannelDisplayName, postListPostItemChannelInfoTeamDisplayName} = RecentMentionsScreen.getPostListPostItem(post.id, message); await expect(recentMentionsPostListPostItem).toBeVisible(); await expect(postListPostItemChannelInfoChannelDisplayName).toHaveText(testChannel.display_name); @@ -135,12 +137,10 @@ describe('Search - Recent Mentions', () => { await EditPostScreen.saveButton.tap(); // * Verify post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedPostListPostItem, postListPostItemEditedIndicator} = RecentMentionsScreen.getPostListPostItem(mentionPost.id, updatedMessage); - await expect(updatedPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + await ChannelScreen.assertPostMessageEdited(mentionPost.id, updatedMessage, 'recent_mentions_page'); // # Open post options for recent mention and tap on reply option - await RecentMentionsScreen.openPostOptionsFor(mentionPost.id, updatedMessage); + await element(by.id(`recent_mentions.post_list.post.${mentionPost.id}`)).longPress(); await PostOptionsScreen.replyPostOption.tap(); // * Verify on thread screen @@ -159,12 +159,11 @@ describe('Search - Recent Mentions', () => { await ThreadScreen.back(); // * Verify reply count and following button - const {postListPostItemFooterReplyCount, postListPostItemFooterFollowingButton} = RecentMentionsScreen.getPostListPostItem(mentionPost.id, updatedMessage); - await expect(postListPostItemFooterReplyCount).toHaveText('1 reply'); - await expect(postListPostItemFooterFollowingButton).toBeVisible(); + await waitFor(element(by.text('1 reply'))).toBeVisible().withTimeout(timeouts.TWO_SEC); + await waitFor(element(by.text('Following'))).toBeVisible().withTimeout(timeouts.TWO_SEC); // # Open post options for updated recent mention and delete post - await RecentMentionsScreen.openPostOptionsFor(mentionPost.id, updatedMessage); + await element(by.id(`recent_mentions.post_list.post.${mentionPost.id}`)).longPress(); await PostOptionsScreen.deletePost({confirm: true}); // * Verify updated recent mention is deleted diff --git a/detox/e2e/test/products/channels/search/saved_messages.e2e.ts b/detox/e2e/test/products/channels/search/saved_messages.e2e.ts index a11f1cee1..91ae94ddf 100644 --- a/detox/e2e/test/products/channels/search/saved_messages.e2e.ts +++ b/detox/e2e/test/products/channels/search/saved_messages.e2e.ts @@ -29,7 +29,7 @@ import { ServerScreen, ThreadScreen, } from '@support/ui/screen'; -import {getRandomId} from '@support/utils'; +import {getRandomId, timeouts} from '@support/utils'; import {expect} from 'detox'; describe('Search - Saved Messages', () => { @@ -138,12 +138,10 @@ describe('Search - Saved Messages', () => { await EditPostScreen.saveButton.tap(); // * Verify post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedPostListPostItem, postListPostItemEditedIndicator} = SavedMessagesScreen.getPostListPostItem(savedPost.id, updatedMessage); - await expect(updatedPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + await ChannelScreen.assertPostMessageEdited(savedPost.id, updatedMessage, 'saved_messages_page'); // # Open post options for updated saved message and tap on reply option - await SavedMessagesScreen.openPostOptionsFor(savedPost.id, updatedMessage); + await element(by.id(`saved_messages.post_list.post.${savedPost.id}`)).longPress(); await PostOptionsScreen.replyPostOption.tap(); // * Verify on thread screen @@ -162,12 +160,12 @@ describe('Search - Saved Messages', () => { await ThreadScreen.back(); // * Verify reply count and following button - const {postListPostItem, postListPostItemFooterReplyCount, postListPostItemFooterFollowingButton} = SavedMessagesScreen.getPostListPostItem(savedPost.id, updatedMessage); - await expect(postListPostItemFooterReplyCount).toHaveText('1 reply'); - await expect(postListPostItemFooterFollowingButton).toBeVisible(); + const {postListPostItem} = SavedMessagesScreen.getPostListPostItem(savedPost.id, updatedMessage); + await waitFor(element(by.text('1 reply'))).toBeVisible().withTimeout(timeouts.TWO_SEC); + await waitFor(element(by.text('Following'))).toBeVisible().withTimeout(timeouts.TWO_SEC); // # Open post options for updated saved message and delete post - await SavedMessagesScreen.openPostOptionsFor(savedPost.id, updatedMessage); + await element(by.id(`saved_messages.post_list.post.${savedPost.id}`)).longPress(); await PostOptionsScreen.deletePost({confirm: true}); // * Verify updated saved message is deleted diff --git a/detox/e2e/test/products/channels/search/search_messages.e2e.ts b/detox/e2e/test/products/channels/search/search_messages.e2e.ts index 9e6179cb0..ab09d6789 100644 --- a/detox/e2e/test/products/channels/search/search_messages.e2e.ts +++ b/detox/e2e/test/products/channels/search/search_messages.e2e.ts @@ -391,12 +391,10 @@ describe('Search - Search Messages', () => { await EditPostScreen.saveButton.tap(); // * Verify post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedPostListPostItem, postListPostItemEditedIndicator} = SearchMessagesScreen.getPostListPostItem(searchedPost.id, updatedMessage); - await expect(updatedPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + await ChannelScreen.assertPostMessageEdited(searchedPost.id, updatedMessage, 'search_page'); // # Open post options for searched message and tap on reply option - await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, updatedMessage); + await PostOptionsScreen.openPostOptionsForSearchedPosts(searchedPost.id); await PostOptionsScreen.replyPostOption.tap(); // * Verify on thread screen @@ -413,14 +411,15 @@ describe('Search - Search Messages', () => { // # Go back to search results screen await ThreadScreen.back(); + await SearchMessagesScreen.toBeVisible(); // * Verify reply count and following button - const {postListPostItemFooterReplyCount, postListPostItemFooterFollowingButton} = SearchMessagesScreen.getPostListPostItem(searchedPost.id, updatedMessage); - await expect(postListPostItemFooterReplyCount).toHaveText('1 reply'); - await expect(postListPostItemFooterFollowingButton).toBeVisible(); + await wait(timeouts.FOUR_SEC); + await waitFor(element(by.text('1 reply'))).toBeVisible().withTimeout(timeouts.TWO_SEC); + await waitFor(element(by.text('Following'))).toBeVisible().withTimeout(timeouts.TWO_SEC); // # Open post options for updated searched message and delete post - await SearchMessagesScreen.openPostOptionsFor(searchedPost.id, updatedMessage); + await element(by.id(`search_results.post_list.post.${searchedPost.id}`)).longPress(); await PostOptionsScreen.deletePost({confirm: true}); // * Verify updated searched message is deleted diff --git a/detox/e2e/test/products/channels/smoke_test/account.e2e.ts b/detox/e2e/test/products/channels/smoke_test/account.e2e.ts index f50cec25d..1f27a3e7b 100644 --- a/detox/e2e/test/products/channels/smoke_test/account.e2e.ts +++ b/detox/e2e/test/products/channels/smoke_test/account.e2e.ts @@ -174,6 +174,7 @@ describe('Smoke Test - Account', () => { await DisplaySettingsScreen.open(); await ThemeDisplaySettingsScreen.open(); await ThemeDisplaySettingsScreen.denimOption.tap(); + await ThemeDisplaySettingsScreen.back(); // * Verify on display settings screen and denim is set await DisplaySettingsScreen.toBeVisible(); diff --git a/detox/e2e/test/products/channels/smoke_test/channels.e2e.ts b/detox/e2e/test/products/channels/smoke_test/channels.e2e.ts index 08551b650..8f7d6223d 100644 --- a/detox/e2e/test/products/channels/smoke_test/channels.e2e.ts +++ b/detox/e2e/test/products/channels/smoke_test/channels.e2e.ts @@ -201,16 +201,7 @@ describe('Smoke Test - Channels', () => { await ChannelInfoScreen.archivePublicChannel({confirm: true}); // * Verify on channel screen and post draft archived message is displayed - await ChannelScreen.toBeVisible(); - await expect(ChannelScreen.postDraftArchived).toBeVisible(); - await expect(element(by.text('You are viewing an archived channel. New messages cannot be posted.'))).toBeVisible(); - - // # Open channel info screen, and tap on leave channel option and confirm - await ChannelInfoScreen.open(); - await ChannelInfoScreen.leaveChannel({confirm: true}); - - // * Verify on channel list screen and the channel left by the user does not appear on the list - await ChannelListScreen.toBeVisible(); + await waitFor(ChannelListScreen.channelListScreen).toBeVisible().withTimeout(timeouts.TEN_SEC); await expect(ChannelListScreen.getChannelItem(channelsCategory, channel.name)).not.toExist(); }); }); diff --git a/detox/e2e/test/products/channels/smoke_test/messaging.e2e.ts b/detox/e2e/test/products/channels/smoke_test/messaging.e2e.ts index b04366794..391805426 100644 --- a/detox/e2e/test/products/channels/smoke_test/messaging.e2e.ts +++ b/detox/e2e/test/products/channels/smoke_test/messaging.e2e.ts @@ -84,12 +84,11 @@ describe('Smoke Test - Messaging', () => { await EditPostScreen.saveButton.tap(); // * Verify post message is updated and displays edited indicator '(edited)' - const {postListPostItem: updatedPostListPostItem, postListPostItemEditedIndicator} = ChannelScreen.getPostListPostItem(post.id, updatedMessage); - await expect(updatedPostListPostItem).toBeVisible(); - await expect(postListPostItemEditedIndicator).toHaveText('Edited'); + const {postListPostItem: updatedPostListPostItem} = ChannelScreen.getPostListPostItem(post.id, updatedMessage); + await ChannelScreen.assertPostMessageEdited(post.id, updatedMessage); // # Open post options for the updated message, tap delete option and confirm - await ChannelScreen.openPostOptionsFor(post.id, updatedMessage); + await element(by.id(`channel.post_list.post.${post.id}`)).longPress(); await PostOptionsScreen.deletePost({confirm: true}); // * Verify post message is deleted @@ -144,7 +143,7 @@ describe('Smoke Test - Messaging', () => { await expect(postListPostItem).toBeVisible(); // # Open post options for message, open emoji picker screen, and add a reaction - await ChannelScreen.openPostOptionsFor(post.id, resolvedMessage); + await element(by.id(`channel.post_list.post.${post.id}`)).longPress(); await EmojiPickerScreen.open(true); await EmojiPickerScreen.searchInput.replaceText('clown_face'); await element(by.text('🤡')).tap(); @@ -162,7 +161,7 @@ describe('Smoke Test - Messaging', () => { await ChannelScreen.open(channelsCategory, testChannel.name); await ChannelScreen.postMessage(message); const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id); - await ChannelScreen.openPostOptionsFor(post.id, message); + await element(by.id(`channel.post_list.post.${post.id}`)).longPress(); await PostOptionsScreen.followThreadOption.tap(); // * Verify message is followed by user via post footer @@ -171,12 +170,13 @@ describe('Smoke Test - Messaging', () => { // # Tap on following button via post footer await postListPostItemFooterFollowingButton.tap(); + await wait(timeouts.FOUR_SEC); // * Verify message is not followed by user via post footer await expect(postListPostItemFooterFollowingButton).not.toBeVisible(); // # Open post options for message and tap on save option - await ChannelScreen.openPostOptionsFor(post.id, message); + await element(by.id(`channel.post_list.post.${post.id}`)).longPress(); await PostOptionsScreen.savePostOption.tap(); // * Verify saved text is displayed on the post pre-header @@ -203,7 +203,7 @@ describe('Smoke Test - Messaging', () => { // # Go back to channel, open post options for message, and tap on unpin from channel option await ThreadScreen.back(); - await ChannelScreen.openPostOptionsFor(post.id, message); + await element(by.id(`channel.post_list.post.${post.id}`)).longPress(); await PostOptionsScreen.unpinPostOption.tap(); // * Verify pinned text is not displayed on the post pre-header diff --git a/detox/e2e/test/products/channels/smoke_test/threads.e2e.ts b/detox/e2e/test/products/channels/smoke_test/threads.e2e.ts index c147a1b75..dae379615 100644 --- a/detox/e2e/test/products/channels/smoke_test/threads.e2e.ts +++ b/detox/e2e/test/products/channels/smoke_test/threads.e2e.ts @@ -64,13 +64,16 @@ describe('Smoke Test - Threads', () => { await ChannelScreen.openReplyThreadFor(parentPost.id, parentMessage); await waitFor(ThreadScreen.postInput).toBeVisible().withTimeout(timeouts.FOUR_SEC); await ThreadScreen.postMessage(`${parentMessage} reply`); + await wait(timeouts.ONE_SEC); await ThreadScreen.followingButton.tap(); + await wait(timeouts.FOUR_SEC); // * Verify thread is not followed by user via thread navigation await expect(ThreadScreen.followButton).toBeVisible(); // # Follow thread via thread navigation await ThreadScreen.followButton.tap(); + await wait(timeouts.FOUR_SEC); // * Verify thread is followed by user via thread navigation await expect(ThreadScreen.followingButton).toBeVisible(); @@ -86,15 +89,14 @@ describe('Smoke Test - Threads', () => { await GlobalThreadsScreen.headerUnreadThreadsButton.tap(); // * Verify thread is displayed in unread threads section - await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible(); + await waitFor(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible().withTimeout(timeouts.FOUR_SEC); // # Open thread options for thread and tap on mark as read option await GlobalThreadsScreen.openThreadOptionsFor(parentPost.id); await ThreadOptionsScreen.markAsReadOption.tap(); // * Verify thread is not displayed anymore in unread threads section - await wait(timeouts.ONE_SEC); - await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible(); + await waitFor(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible().withTimeout(timeouts.FOUR_SEC); // # Tap on all your threads button, tap on the thread, and add new reply to thread await GlobalThreadsScreen.headerAllThreadsButton.tap(); diff --git a/detox/e2e/test/products/channels/threads/follow_and_unfollow_thread.e2e.ts b/detox/e2e/test/products/channels/threads/follow_and_unfollow_thread.e2e.ts index b562da73e..100c69cb9 100644 --- a/detox/e2e/test/products/channels/threads/follow_and_unfollow_thread.e2e.ts +++ b/detox/e2e/test/products/channels/threads/follow_and_unfollow_thread.e2e.ts @@ -61,6 +61,7 @@ describe('Threads - Follow and Unfollow Thread', () => { const {post: parentPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id); await ChannelScreen.openReplyThreadFor(parentPost.id, parentMessage); await ThreadScreen.postMessage(`${parentMessage} reply`); + await wait(timeouts.ONE_SEC); // * Verify thread is followed by user by default via thread navigation await expect(ThreadScreen.followingButton).toBeVisible(); @@ -72,6 +73,7 @@ describe('Threads - Follow and Unfollow Thread', () => { await expect(ThreadScreen.followButton).toBeVisible(); // # Follow thread via thread navigation + await wait(timeouts.TWO_SEC); await ThreadScreen.followButton.tap(); // * Verify thread is followed by user via thread navigation @@ -90,6 +92,7 @@ describe('Threads - Follow and Unfollow Thread', () => { const {post: parentPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id); await ChannelScreen.openReplyThreadFor(parentPost.id, parentMessage); await ThreadScreen.postMessage(`${parentMessage} reply`); + await wait(timeouts.ONE_SEC); await ThreadScreen.back(); // * Verify thread is followed by user by default via post footer @@ -103,6 +106,7 @@ describe('Threads - Follow and Unfollow Thread', () => { await expect(postListPostItemFooterFollowButton).toBeVisible(); // # Follow thread via post footer + await wait(timeouts.TWO_SEC); await postListPostItemFooterFollowButton.tap(); // * Verify thread is followed by user via post footer @@ -124,9 +128,11 @@ describe('Threads - Follow and Unfollow Thread', () => { await ChannelScreen.openPostOptionsFor(parentPost.id, parentMessage); // * Verify thread is followed by user by default via post options + await waitFor(PostOptionsScreen.followingThreadOption).toBeVisible().withTimeout(timeouts.TWO_SEC); await expect(PostOptionsScreen.followingThreadOption).toBeVisible(); // # Unfollow thread via post options + await waitFor(PostOptionsScreen.followingThreadOption).toBeVisible().withTimeout(timeouts.TWO_SEC); await PostOptionsScreen.followingThreadOption.tap(); // * Verify thread is not followed by user via post footer @@ -140,6 +146,7 @@ describe('Threads - Follow and Unfollow Thread', () => { await expect(PostOptionsScreen.followThreadOption).toBeVisible(); // # Tap on follow thread option + await wait(timeouts.TWO_SEC); await PostOptionsScreen.followThreadOption.tap(); // * Verify thread is followed by user via post footer diff --git a/detox/e2e/test/products/channels/threads/mark_thread_as_read_and_unread.e2e.ts b/detox/e2e/test/products/channels/threads/mark_thread_as_read_and_unread.e2e.ts index 8f8339c21..42c492d2d 100644 --- a/detox/e2e/test/products/channels/threads/mark_thread_as_read_and_unread.e2e.ts +++ b/detox/e2e/test/products/channels/threads/mark_thread_as_read_and_unread.e2e.ts @@ -65,7 +65,6 @@ describe('Threads - Mark Thread as Read and Unread', () => { rootId: parentPost.id, }); await ChannelScreen.back(); - await device.reloadReactNative(); await GlobalThreadsScreen.open(); await GlobalThreadsScreen.headerUnreadThreadsButton.tap(); @@ -105,7 +104,6 @@ describe('Threads - Mark Thread as Read and Unread', () => { rootId: parentPost.id, }); await ChannelScreen.back(); - await device.reloadReactNative(); await GlobalThreadsScreen.open(); await GlobalThreadsScreen.headerUnreadThreadsButton.tap(); @@ -156,7 +154,6 @@ describe('Threads - Mark Thread as Read and Unread', () => { rootId: parentPost.id, }); await ChannelScreen.back(); - await device.reloadReactNative(); await GlobalThreadsScreen.open(); await GlobalThreadsScreen.headerUnreadThreadsButton.tap(); @@ -175,6 +172,7 @@ describe('Threads - Mark Thread as Read and Unread', () => { await Alert.markReadButton.tap(); // * Verify thread is not displayed anymore and unread threads section is empty + await waitFor(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible().withTimeout(timeouts.TEN_SEC); await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).not.toBeVisible(); await expect(GlobalThreadsScreen.emptyThreadsList).toBeVisible(); diff --git a/detox/e2e/test/products/playbooks/playbooks.e2e.ts b/detox/e2e/test/products/playbooks/playbooks-e2e.ts similarity index 100% rename from detox/e2e/test/products/playbooks/playbooks.e2e.ts rename to detox/e2e/test/products/playbooks/playbooks-e2e.ts