diff --git a/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap b/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap index 01071106c..6cf2807ad 100644 --- a/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap +++ b/app/screens/reaction_list/__snapshots__/reaction_list.test.js.snap @@ -7,6 +7,7 @@ exports[`ReactionList should match snapshot 1`] = ` "flex": 1, } } + testID="reaction_list.screen" > + @@ -48,6 +49,7 @@ exports[`ReactionRow should match snapshot, renderContent 1`] = ` "width": "74%", } } + testID="reaction_row.user.user_id" > @@ -99,6 +100,7 @@ export default class ReactionRow extends React.PureComponent { style={style.textContainer} ellipsizeMode='tail' numberOfLines={1} + testID={`reaction_row.user.${id}`} > {usernameDisplay} @@ -113,6 +115,7 @@ export default class ReactionRow extends React.PureComponent { emojiName={emojiName} textStyle={style.emojiText} size={24} + testID={`reaction_row.emoji.${emojiName}.${id}`} /> diff --git a/detox/e2e/support/ui/component/post_options.js b/detox/e2e/support/ui/component/post_options.js index cff563b39..56c4d6b44 100644 --- a/detox/e2e/support/ui/component/post_options.js +++ b/detox/e2e/support/ui/component/post_options.js @@ -6,6 +6,7 @@ import {isAndroid, timeouts, wait} from '@support/utils'; class PostOptions { testID = { + reactionButtonPrefix: 'reaction_picker.reaction_button.', postOptions: 'post.options', reactionPickerAction: 'post.options.reaction_picker.action', replyAction: 'post.options.reply.action', @@ -37,6 +38,10 @@ class PostOptions { openAddReactionButton = element(by.id(this.testID.openAddReactionButton)); slideUpPanel = element(by.id(this.testID.slideUpPanel)); + getReactionButton = (emojiName) => { + return element(by.id(`${this.testID.reactionButtonPrefix}${emojiName}`)); + } + toBeVisible = async () => { await expect(this.postOptions).toExist(); diff --git a/detox/e2e/support/ui/screen/index.js b/detox/e2e/support/ui/screen/index.js index 4301127dc..ec5dbcc14 100644 --- a/detox/e2e/support/ui/screen/index.js +++ b/detox/e2e/support/ui/screen/index.js @@ -26,6 +26,7 @@ import NotificationSettingsMobileScreen from './notification_settings_mobile'; import NotificationSettingsScreen from './notification_settings'; import PermalinkScreen from './permalink'; import PinnedMessagesScreen from './pinned_messages'; +import ReactionListScreen from './reaction_list'; import RecentMentionsScreen from './recent_mentions'; import SavedMessagesScreen from './saved_messages'; import SearchResultPostScreen from './search_result_post'; @@ -62,6 +63,7 @@ export { NotificationSettingsScreen, PermalinkScreen, PinnedMessagesScreen, + ReactionListScreen, RecentMentionsScreen, SavedMessagesScreen, SearchResultPostScreen, diff --git a/detox/e2e/support/ui/screen/reaction_list.js b/detox/e2e/support/ui/screen/reaction_list.js new file mode 100644 index 000000000..865b3a6d3 --- /dev/null +++ b/detox/e2e/support/ui/screen/reaction_list.js @@ -0,0 +1,37 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +class ReactionListScreen { + testID = { + reactionRowPrefix: 'reaction_row.', + reactionListScreen: 'reaction_list.screen', + } + + reactionListScreen = element(by.id(this.testID.reactionListScreen)); + + toBeVisible = async () => { + await expect(this.reactionListScreen).toExist(); + + return reactionListScreen; + } + + close = async () => { + await this.reactionListScreen.tap({x: 5, y: 10}); + await expect(this.reactionListScreen).not.toBeVisible(); + } + + getReactionRow = (userId, emojiName) => { + const reactionRowEmojiMatcher = by.id(`${this.testID.reactionRowPrefix}emoji.${emojiName}.${userId}`); + const reactionRowProfilePictureMatcher = by.id(`${this.testID.reactionRowPrefix}profile_picture.${userId}`); + const reactionRowUserMatcher = by.id(`${this.testID.reactionRowPrefix}user.${userId}`); + + return { + reactionRowEmoji: element(reactionRowEmojiMatcher), + reactionRowProfilePicture: element(reactionRowProfilePictureMatcher), + reactionRowUser: element(reactionRowUserMatcher), + }; + } +} + +const reactionListScreen = new ReactionListScreen(); +export default reactionListScreen; diff --git a/detox/e2e/test/channels/pinned_messages.e2e.js b/detox/e2e/test/channels/pinned_messages.e2e.js deleted file mode 100644 index f2b286e25..000000000 --- a/detox/e2e/test/channels/pinned_messages.e2e.js +++ /dev/null @@ -1,144 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -// ******************************************************************* -// - [#] indicates a test step (e.g. # Go to a screen) -// - [*] indicates an assertion (e.g. * Check the title) -// - Use element testID when selecting an element. Create one if none. -// ******************************************************************* - -import {PostOptions} from '@support/ui/component'; -import { - ChannelInfoScreen, - ChannelScreen, - EditPostScreen, - PermalinkScreen, - PinnedMessagesScreen, - ThreadScreen, -} from '@support/ui/screen'; -import { - Channel, - Post, - Setup, -} from '@support/server_api'; - -describe('Pinned Messages', () => { - const { - getPostListPostItem, - openPostOptionsFor, - openReplyThreadFor, - postMessage, - } = ChannelScreen; - const {pinAction} = PostOptions; - let townSquareChannel; - - beforeAll(async () => { - const {team, user} = await Setup.apiInit(); - - const {channel} = await Channel.apiGetChannelByName(team.id, 'town-square'); - townSquareChannel = channel; - - // # Open channel screen - await ChannelScreen.open(user); - }); - - afterAll(async () => { - await ChannelScreen.logout(); - }); - - it('MM-T851_1 should be able pin a message from channel post list', async () => { - // # Post a message - const testMessage = Date.now().toString(); - await postMessage(testMessage); - - // # Pin message from channel post list - const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); - await openPostOptionsFor(post.id, testMessage); - await pinAction.tap(); - - // * Verify message appears in pinned messages - const {postListPostItemPreHeaderText} = await getPostListPostItem(post.id, testMessage); - await expect(postListPostItemPreHeaderText).toHaveText('Pinned'); - await ChannelInfoScreen.open(); - await PinnedMessagesScreen.open(); - const {searchResultPostItem} = await PinnedMessagesScreen.getSearchResultPostItem(post.id, testMessage); - await expect(searchResultPostItem).toBeVisible(); - - // # Go back to channel - await PinnedMessagesScreen.back(); - await ChannelInfoScreen.close(); - }); - - it('MM-T851_2 should be able to jump to recent messages from pinned post', async () => { - // # Post a message - const testMessage = Date.now().toString(); - await postMessage(testMessage); - - // # Pin message from channel post list - const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); - await openPostOptionsFor(post.id, testMessage); - await pinAction.tap(); - - // # Jump to recent messages from pinned post - await ChannelInfoScreen.open(); - await PinnedMessagesScreen.open(); - const {searchResultPostItem} = await PinnedMessagesScreen.getSearchResultPostItem(post.id, testMessage); - await searchResultPostItem.tap(); - await PermalinkScreen.jumpToRecentMessages(); - - // * Verify user is on channel where message is posted - await expect(ChannelScreen.channelNavBarTitle).toHaveText(townSquareChannel.display_name); - const {postListPostItem: channelPostItem} = await ChannelScreen.getPostListPostItem(post.id, testMessage); - await expect(channelPostItem).toBeVisible(); - }); - - it('MM-T851_3 should be able to open reply thread for pinned message', async () => { - // # Post a message - const testMessage = Date.now().toString(); - await postMessage(testMessage); - - // # Pin message from channel post list - const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); - await openPostOptionsFor(post.id, testMessage); - await pinAction.tap(); - - // # Open reply thread for pinned message - await openReplyThreadFor(post.id, testMessage); - - // * Verify reply thread for pinned message is displayed - const {postListPostItem} = await ThreadScreen.getPostListPostItem(post.id, testMessage); - await expect(postListPostItem).toBeVisible(); - - // # Go back to channel - await ThreadScreen.back(); - }); - - it('MM-T851_4 should be able to edit pinned message', async () => { - const { - messageInput, - saveButton, - } = EditPostScreen; - - // # Post a message - const testMessage = Date.now().toString(); - await postMessage(testMessage); - - // # Pin message from channel post list - const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); - await openPostOptionsFor(post.id, testMessage); - await pinAction.tap(); - - // # Edit pinned message - const additionalText = ' additional text'; - await openPostOptionsFor(post.id, testMessage); - await EditPostScreen.open(); - await messageInput.tap(); - await messageInput.typeText(additionalText); - await saveButton.tap(); - - // * Verify pinned message is edited - await ChannelScreen.toBeVisible(); - const {postListPostItem} = await getPostListPostItem(post.id, `${testMessage}${additionalText} (edited)`); - await expect(postListPostItem).toBeVisible(); - }); -}); diff --git a/detox/e2e/test/messaging/emojis_and_reactions.e2e.js b/detox/e2e/test/messaging/emojis_and_reactions.e2e.js new file mode 100644 index 000000000..4b99bba3a --- /dev/null +++ b/detox/e2e/test/messaging/emojis_and_reactions.e2e.js @@ -0,0 +1,134 @@ +// 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 {PostOptions} from '@support/ui/component'; +import { + ChannelScreen, + AddReactionScreen, + ReactionListScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; + +describe('Emojis and Reactions', () => { + const { + openPostOptionsFor, + postMessage, + } = ChannelScreen; + let testUser; + let townSquareChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + testUser = user; + + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.id, 'town-square')); + + // # Open channel screen + await ChannelScreen.open(testUser); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T2347 should be able to access recent reactions from the long press menu', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // * Verify default reactions are displayed + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await expect(PostOptions.getReactionButton('thumbsup')).toBeVisible(); + await expect(PostOptions.getReactionButton('smiley')).toBeVisible(); + await expect(PostOptions.getReactionButton('white_check_mark')).toBeVisible(); + await expect(PostOptions.getReactionButton('heart')).toBeVisible(); + await expect(PostOptions.getReactionButton('eyes')).toBeVisible(); + await expect(PostOptions.getReactionButton('raised_hands')).toBeVisible(); + + // # Add a different reaction + await AddReactionScreen.open(); + await AddReactionScreen.searchInput.typeText(':clown_face:'); + await element(by.text('🤡')).tap(); + + // * Verify recent reactions are displayed + await openPostOptionsFor(post.id, testMessage); + await expect(PostOptions.getReactionButton('clown_face')).toBeVisible(); + await expect(PostOptions.getReactionButton('thumbsup')).toBeVisible(); + await expect(PostOptions.getReactionButton('smiley')).toBeVisible(); + await expect(PostOptions.getReactionButton('white_check_mark')).toBeVisible(); + await expect(PostOptions.getReactionButton('heart')).toBeVisible(); + await expect(PostOptions.getReactionButton('eyes')).toBeVisible(); + await expect(PostOptions.getReactionButton('raised_hands')).not.toBeVisible(); + + // # Go back to channel + await PostOptions.close(); + }); + + it('MM-T150 should be able to long press on an emoji to view list of users who reacted', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Add a reaction + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await AddReactionScreen.open(); + await AddReactionScreen.searchInput.typeText(':fire:'); + await element(by.text('🔥')).tap(); + + // # Long press on the reaction + await element(by.text('🔥').withAncestor(by.id(`channel.post_list.post.${post.id}`))).longPress(); + + // * Verify user who reacted with the emoji + await ReactionListScreen.toBeVisible(); + const { + reactionRowEmoji, + reactionRowProfilePicture, + reactionRowUser, + } = await ReactionListScreen.getReactionRow(testUser.id, 'fire'); + await expect(reactionRowProfilePicture).toBeVisible(); + await expect(reactionRowUser).toHaveText(`@${testUser.username} ${testUser.username}`); + await expect(reactionRowEmoji).toBeVisible(); + + // # Go back to channel + await ReactionListScreen.close(); + }); + + it('MM-T3495 should include post message emojis in Recent Emojis section and Recently Used section', async () => { + // # Post a message + const testMessage = 'The quick brown fox :fox_face: jumps over the lazy dog :dog:'; + await postMessage(testMessage); + + // * Verify message is posted + await expect(element(by.text('The quick brown fox 🦊 jumps over the lazy dog 🐶'))).toBeVisible(); + + // # Open PostOptions + await element(by.text('The quick brown fox 🦊 jumps over the lazy dog 🐶')).longPress(); + await PostOptions.toBeVisible(); + + // * Verify emojis exist + await expect(element(by.text('🦊'))).toExist(); + await expect(element(by.text('🐶'))).toExist(); + + // # Open AddReaction screen + await AddReactionScreen.open(); + + // * Verify Emojis exist in recently used section + await expect(element(by.text('🦊').withAncestor(by.id('RECENTLY USED')))).toExist(); + await expect(element(by.text('🐶').withAncestor(by.id('RECENTLY USED')))).toExist(); + + // # Close AddReaction Screen + await AddReactionScreen.close(); + }); +}); diff --git a/detox/e2e/test/messaging/pinned_messages.e2e.js b/detox/e2e/test/messaging/pinned_messages.e2e.js new file mode 100644 index 000000000..2065ca5c4 --- /dev/null +++ b/detox/e2e/test/messaging/pinned_messages.e2e.js @@ -0,0 +1,232 @@ +// 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 {PostOptions} from '@support/ui/component'; +import { + ChannelInfoScreen, + ChannelScreen, + EditPostScreen, + PinnedMessagesScreen, + ThreadScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; + +describe('Pinned Messages', () => { + const { + getPostListPostItem, + openPostOptionsFor, + openReplyThreadFor, + postMessage, + } = ChannelScreen; + const { + pinAction, + saveAction, + unpinAction, + } = PostOptions; + let townSquareChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + + const {channel} = await Channel.apiGetChannelByName(team.id, 'town-square'); + townSquareChannel = channel; + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T139 should be able to open pinned messages', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Pin message from channel post list + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await pinAction.tap(); + + // * Verify message appears in pinned messages + const {postListPostItemPreHeaderText: pinnedPostItemPreHeaderText} = await getPostListPostItem(post.id, testMessage); + await expect(pinnedPostItemPreHeaderText).toHaveText('Pinned'); + await ChannelInfoScreen.open(); + await PinnedMessagesScreen.open(); + const {searchResultPostItem} = await PinnedMessagesScreen.getSearchResultPostItem(post.id, testMessage); + await expect(searchResultPostItem).toBeVisible(); + + // # Unpin message + await PinnedMessagesScreen.back(); + await ChannelInfoScreen.close(); + await openPostOptionsFor(post.id, testMessage); + await unpinAction.tap(); + + // * Verify message is removed from pinned messages + const {postListPostItemPreHeaderText: unpinnedPostItemPreHeaderText} = await getPostListPostItem(post.id, testMessage); + await expect(unpinnedPostItemPreHeaderText).not.toExist(); + await ChannelInfoScreen.open(); + await PinnedMessagesScreen.open(); + await expect(element(by.text('No Pinned messages yet'))).toBeVisible(); + + // # Go back to channel + await PinnedMessagesScreen.back(); + await ChannelInfoScreen.close(); + }); + + it('MM-T138 should be able to pin a reply post', async () => { + // # Post a parent message + const parentMessage = Date.now().toString(); + await postMessage(parentMessage); + + // # Post a reply message + const {post: parentPost} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openReplyThreadFor(parentPost.id, parentMessage); + const replyMessage = Date.now().toString(); + await ThreadScreen.postMessage(replyMessage); + + // # Go back to main thread and pin reply post + await ThreadScreen.back(); + const {post: replyPost} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(replyPost.id, replyMessage); + await pinAction.tap(); + + // * Verify reply post from main thread is pinned + const {postListPostItemPreHeaderText} = await getPostListPostItem(replyPost.id, replyMessage); + await expect(postListPostItemPreHeaderText).toHaveText('Pinned'); + }); + + it('MM-T140 should be able to pin and save the same post', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Pin and save message from channel post list + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await pinAction.tap(); + await openPostOptionsFor(post.id, testMessage); + await saveAction.tap(); + + // * Verify message is pinned and saved + const {postListPostItemPreHeaderText} = await getPostListPostItem(post.id, testMessage); + await expect(postListPostItemPreHeaderText).toHaveText('Pinned and Saved'); + }); + + it('MM-T851_1 should be able pin a message from channel post list', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Pin message from channel post list + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await pinAction.tap(); + + // * Verify message appears in pinned messages + const {postListPostItemPreHeaderText} = await getPostListPostItem(post.id, testMessage); + await expect(postListPostItemPreHeaderText).toHaveText('Pinned'); + await ChannelInfoScreen.open(); + await PinnedMessagesScreen.open(); + const {searchResultPostItem} = await PinnedMessagesScreen.getSearchResultPostItem(post.id, testMessage); + await expect(searchResultPostItem).toBeVisible(); + + // # Go back to channel + await PinnedMessagesScreen.back(); + await ChannelInfoScreen.close(); + }); + + it('MM-T851_2 should be able to open reply thread for pinned message by tapping on search result item', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Pin message from channel post list + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await pinAction.tap(); + + // # Tap on search result item to open reply thread for pinned message + await ChannelInfoScreen.open(); + await PinnedMessagesScreen.open(); + const {searchResultPostItem} = await PinnedMessagesScreen.getSearchResultPostItem(post.id, testMessage); + await searchResultPostItem.tap(); + + // * Verify message opens in a thread + await expect(element(by.text(`${townSquareChannel.display_name} Thread`))).toBeVisible(); + const {postListPostItem: threadPostItem} = await ThreadScreen.getPostListPostItem(post.id, testMessage); + await expect(threadPostItem).toBeVisible(); + + // # Go back to channel + await ThreadScreen.back(); + await PinnedMessagesScreen.back(); + await ChannelInfoScreen.close(); + }); + + it('MM-T851_3 should be able to open reply thread for pinned message by tapping on search result item reply arrow', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Pin message from channel post list + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await pinAction.tap(); + + // # Tap on search result item reply arrow to open reply thread for pinned message + await ChannelInfoScreen.open(); + await PinnedMessagesScreen.open(); + const {searchResultPostItemHeaderReply} = await PinnedMessagesScreen.getSearchResultPostItem(post.id, testMessage); + await searchResultPostItemHeaderReply.tap(); + + // * Verify message opens in a thread + await expect(element(by.text(`${townSquareChannel.display_name} Thread`))).toBeVisible(); + const {postListPostItem: threadPostItem} = await ThreadScreen.getPostListPostItem(post.id, testMessage); + await expect(threadPostItem).toBeVisible(); + + // # Go back to channel + await ThreadScreen.back(); + await PinnedMessagesScreen.back(); + await ChannelInfoScreen.close(); + }); + + it('MM-T851_4 should be able to edit pinned message', async () => { + const { + messageInput, + saveButton, + } = EditPostScreen; + + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Pin message from channel post list + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await pinAction.tap(); + + // # Edit pinned message + const additionalText = ' additional text'; + await openPostOptionsFor(post.id, testMessage); + await EditPostScreen.open(); + await messageInput.tap(); + await messageInput.typeText(additionalText); + await saveButton.tap(); + + // * Verify pinned message is edited + await ChannelScreen.toBeVisible(); + const {postListPostItem} = await getPostListPostItem(post.id, `${testMessage}${additionalText} (edited)`); + await expect(postListPostItem).toBeVisible(); + }); +}); diff --git a/detox/e2e/test/messaging/recent_emojis.e2e.js b/detox/e2e/test/messaging/recent_emojis.e2e.js deleted file mode 100644 index ff9a31ccc..000000000 --- a/detox/e2e/test/messaging/recent_emojis.e2e.js +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -// ******************************************************************* -// - [#] indicates a test step (e.g. # Go to a screen) -// - [*] indicates an assertion (e.g. * Check the title) -// - Use element testID when selecting an element. Create one if none. -// ******************************************************************* - -import {ChannelScreen, AddReactionScreen} from '@support/ui/screen'; -import {Setup} from '@support/server_api'; -import {PostOptions} from '@support/ui/component'; - -describe('Messaging', () => { - beforeAll(async () => { - const {user} = await Setup.apiInit(); - - // # Open channel screen - await ChannelScreen.open(user); - }); - - afterAll(async () => { - await ChannelScreen.logout(); - }); - - it('MM-T3495 should include post message emojis in Recent Emojis section and Recently Used section', async () => { - // # Post a message - const message = 'The quick brown fox :fox_face: jumps over the lazy dog :dog:'; - await ChannelScreen.postMessage(message); - - // * Verify message is posted - await expect(element(by.text('The quick brown fox 🦊 jumps over the lazy dog 🐶'))).toBeVisible(); - - // # Open PostOptions - await element(by.text('The quick brown fox 🦊 jumps over the lazy dog 🐶')).longPress(); - await PostOptions.toBeVisible(); - - // * Verify emojis exist - await expect(element(by.text('🦊'))).toExist(); - await expect(element(by.text('🐶'))).toExist(); - - // # Open AddReaction screen - await AddReactionScreen.open(); - - // * Verify Emojis exist in recently used section - await expect(element(by.text('🦊').withAncestor(by.id('RECENTLY USED')))).toExist(); - await expect(element(by.text('🐶').withAncestor(by.id('RECENTLY USED')))).toExist(); - - // # Close AddReaction Screen - await AddReactionScreen.close(); - }); -}); diff --git a/detox/e2e/test/messaging/saved_messages.e2e.js b/detox/e2e/test/messaging/saved_messages.e2e.js new file mode 100644 index 000000000..16b91934d --- /dev/null +++ b/detox/e2e/test/messaging/saved_messages.e2e.js @@ -0,0 +1,93 @@ +// 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 {PostOptions} from '@support/ui/component'; +import { + ChannelScreen, + SavedMessagesScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, + System, +} from '@support/server_api'; + +describe('Saved Messages', () => { + const { + getPostListPostItem, + goToChannel, + openPostOptionsFor, + openSettingsSidebar, + postMessage, + } = ChannelScreen; + const { + saveAction, + unsaveAction, + } = PostOptions; + let testChannel; + let townSquareChannel; + + beforeAll(async () => { + const {channel, team, user} = await Setup.apiInit(); + testChannel = channel; + + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.id, 'town-square')); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + beforeEach(async () => { + await System.apiUpdateConfig({TeamSettings: {ExperimentalTownSquareIsReadOnly: false}}); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T143 should be able save and unsave a message from a read-only channel', async () => { + // # Post a message + const testMessage = Date.now().toString(); + await postMessage(testMessage); + + // # Set town square to read-only + await System.apiUpdateConfig({TeamSettings: {ExperimentalTownSquareIsReadOnly: true}}); + await goToChannel(testChannel.display_name); + await goToChannel(townSquareChannel.display_name); + + // # Save message from read-only channel + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + await openPostOptionsFor(post.id, testMessage); + await saveAction.tap(); + + // * Verify message appears in saved messages + const {postListPostItemPreHeaderText: savedPostPreHeaderText} = await getPostListPostItem(post.id, testMessage); + await expect(savedPostPreHeaderText).toHaveText('Saved'); + await openSettingsSidebar(); + await SavedMessagesScreen.open(); + const {searchResultPostItem} = await SavedMessagesScreen.getSearchResultPostItem(post.id, testMessage); + await expect(searchResultPostItem).toBeVisible(); + + // # Unsave message from read-only channel + await SavedMessagesScreen.close(); + await openPostOptionsFor(post.id, testMessage); + await unsaveAction.tap(); + + // * Verify message does not appear in saved messages + const {postListPostItemPreHeaderText: unsavedPostPreHeaderText} = await getPostListPostItem(post.id, testMessage); + await expect(unsavedPostPreHeaderText).not.toBeVisible(); + await openSettingsSidebar(); + await SavedMessagesScreen.open(); + await expect(element(by.text('No Saved messages yet'))).toBeVisible(); + + // # Close saved messages screen + await SavedMessagesScreen.close(); + }); +}); diff --git a/detox/e2e/test/smoke_test/search.e2e.js b/detox/e2e/test/smoke_test/search.e2e.js index ff33b27d3..6122115df 100644 --- a/detox/e2e/test/smoke_test/search.e2e.js +++ b/detox/e2e/test/smoke_test/search.e2e.js @@ -13,7 +13,6 @@ import { } from '@support/ui/component'; import { ChannelScreen, - PermalinkScreen, SearchScreen, ThreadScreen, } from '@support/ui/screen'; @@ -60,7 +59,7 @@ describe('Search', () => { await ChannelScreen.logout(); }); - it('MM-T3235 search on text, jump to result', async () => { + it('MM-T3235 search on text, go to result', async () => { // # Post message and search on text await postMessageAndSearchText(testMessage, testPartialSearchTerm); @@ -70,18 +69,14 @@ describe('Search', () => { await expect(searchResultPostItem).toBeVisible(); await searchResultPostItem.tap(); - // * Verify permalink post list has the message - await PermalinkScreen.toBeVisible(); - const {postListPostItem: permalinkPostItem} = await PermalinkScreen.getPostListPostItem(lastPost.post.id, testMessage); - await waitFor(permalinkPostItem).toBeVisible(); + // * Verify message opens in a thread + await expect(element(by.text(`${testChannel.display_name} Thread`))).toBeVisible(); + const {postListPostItem: threadPostItem} = await ThreadScreen.getPostListPostItem(lastPost.post.id, testMessage); + await expect(threadPostItem).toBeVisible(); - // # Jump to recent messages - await PermalinkScreen.jumpToRecentMessages(); - - // * Verify user is on channel where message is posted - await expect(ChannelScreen.channelNavBarTitle).toHaveText(testChannel.display_name); - const {postListPostItem: channelPostItem} = await ChannelScreen.getPostListPostItem(lastPost.post.id, testMessage); - await expect(channelPostItem).toBeVisible(); + // # Go back to channel + await ThreadScreen.back(); + await SearchScreen.cancel(); }); it('MM-T3236 search on sender, select from autocomplete, reply from search results', async () => {