diff --git a/detox/e2e/support/ui/component/alert.js b/detox/e2e/support/ui/component/alert.js new file mode 100644 index 000000000..0385d08c7 --- /dev/null +++ b/detox/e2e/support/ui/component/alert.js @@ -0,0 +1,16 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {isAndroid} from '@support/utils'; + +class Alert { + // alert titles + deletePostTitle = isAndroid() ? element(by.text('Delete Post')) : element(by.label('Delete Post')).atIndex(0); + + // alert buttons + cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(0); + deleteButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(0); +} + +const alert = new Alert(); +export default alert; diff --git a/detox/e2e/support/ui/component/index.js b/detox/e2e/support/ui/component/index.js index 6578873fb..9b413d7ef 100644 --- a/detox/e2e/support/ui/component/index.js +++ b/detox/e2e/support/ui/component/index.js @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import Alert from './alert'; import Autocomplete from './autocomplete'; import CameraQuickAction from './camera_quick_action'; import EditChannelInfo from './edit_channel_info'; @@ -20,6 +21,7 @@ import SendButton from './send_button'; import SettingsSidebar from './settings_sidebar'; export { + Alert, Autocomplete, CameraQuickAction, EditChannelInfo, diff --git a/detox/e2e/support/ui/component/post_options.js b/detox/e2e/support/ui/component/post_options.js index 09418d404..aeb132cf9 100644 --- a/detox/e2e/support/ui/component/post_options.js +++ b/detox/e2e/support/ui/component/post_options.js @@ -1,6 +1,9 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import Alert from './alert'; +import {isAndroid, timeouts, wait} from '@support/utils'; + class PostOptions { testID = { postOptions: 'post.options', @@ -43,6 +46,28 @@ class PostOptions { await this.postOptions.tap(); await expect(this.postOptions).not.toBeVisible(); } + + deletePost = async (confirm = true) => { + // # Swipe up panel on Android + if (isAndroid()) { + await this.slideUpPanel.swipe('up'); + } + + await this.deleteAction.tap(); + const { + deletePostTitle, + cancelButton, + deleteButton, + } = Alert; + await expect(deletePostTitle).toBeVisible(); + if (confirm) { + deleteButton.tap(); + } else { + cancelButton.tap(); + } + await wait(timeouts.ONE_SEC); + await expect(this.postOptions).not.toBeVisible(); + } } const postOptions = new PostOptions(); diff --git a/detox/e2e/support/ui/screen/channel.js b/detox/e2e/support/ui/screen/channel.js index f488c3560..b7c938c92 100644 --- a/detox/e2e/support/ui/screen/channel.js +++ b/detox/e2e/support/ui/screen/channel.js @@ -17,6 +17,7 @@ import { LoginScreen, LongPostScreen, SelectServerScreen, + ThreadScreen, } from '@support/ui/screen'; import {isAndroid} from '@support/utils'; @@ -130,6 +131,14 @@ class ChannelScreen { await this.closeMainSidebar(); } + deletePost = async (postId, text, confirm = true) => { + await this.openPostOptionsFor(postId, text); + + // # Delete post + await PostOptions.deletePost(confirm); + await this.toBeVisible(); + } + openMainSidebar = async () => { // # Open main sidebar await this.mainSidebarDrawerButton.tap(); @@ -158,6 +167,14 @@ class ChannelScreen { await PostOptions.toBeVisible(); } + openReplyThreadFor = async (postId, text) => { + await this.openPostOptionsFor(postId, text); + + // # Open reply thread screen + await PostOptions.replyAction.tap(); + await ThreadScreen.toBeVisible(); + } + postMessage = async (message) => { // # Post message await this.postInput.tap(); diff --git a/detox/e2e/support/ui/screen/thread.js b/detox/e2e/support/ui/screen/thread.js index e8781a5a9..7458a4278 100644 --- a/detox/e2e/support/ui/screen/thread.js +++ b/detox/e2e/support/ui/screen/thread.js @@ -8,6 +8,7 @@ import { InputQuickAction, PostDraft, PostList, + PostOptions, SendButton, } from '@support/ui/component'; import {LongPostScreen} from '@support/ui/screen'; @@ -71,6 +72,27 @@ class ThreadScreen { await expect(this.threadScreen).not.toBeVisible(); } + deletePost = async (postId, text, confirm = true, isParentPost = true) => { + await this.openPostOptionsFor(postId, text); + + // # Delete post + await PostOptions.deletePost(confirm); + if (isParentPost) { + await expect(this.threadScreen).not.toBeVisible(); + } else { + this.toBeVisible(); + } + } + + openPostOptionsFor = async (postId, text) => { + const {postListPostItem} = await this.getPostListPostItem(postId, text); + await expect(postListPostItem).toBeVisible(); + + // # Open post options + await postListPostItem.longPress(); + await PostOptions.toBeVisible(); + } + postMessage = async (message) => { // # Post message await this.postInput.tap(); diff --git a/detox/e2e/test/autocomplete/edit_post.e2e.js b/detox/e2e/test/autocomplete/edit_post.e2e.js index d5b72f445..8bd4adca5 100644 --- a/detox/e2e/test/autocomplete/edit_post.e2e.js +++ b/detox/e2e/test/autocomplete/edit_post.e2e.js @@ -35,19 +35,18 @@ describe('Autocomplete', () => { }); it('MM-T3391 should render autocomplete in post edit screen', async () => { + const { + openPostOptionsFor, + postMessage, + } = ChannelScreen; + + // # Post a message const testMessage = Date.now().toString(); - const {postInput} = ChannelScreen; - - // # Type a message - await postInput.tap(); - await postInput.typeText(testMessage); - - // # Tap send button - await ChannelScreen.tapSendButton(); + await postMessage(testMessage); // # Open edit post screen const {post} = await Post.apiGetLastPostInChannel(testChannel.id); - await ChannelScreen.openPostOptionsFor(post.id, testMessage); + await openPostOptionsFor(post.id, testMessage); await EditPostScreen.open(); const {atMentionSuggestionList} = Autocomplete; diff --git a/detox/e2e/test/messaging/recent_emojis.e2e.js b/detox/e2e/test/messaging/recent_emojis.e2e.js index 870742a8a..ff9a31ccc 100644 --- a/detox/e2e/test/messaging/recent_emojis.e2e.js +++ b/detox/e2e/test/messaging/recent_emojis.e2e.js @@ -24,16 +24,9 @@ describe('Messaging', () => { }); it('MM-T3495 should include post message emojis in Recent Emojis section and Recently Used section', async () => { - // * Verify channel screen is visible - await ChannelScreen.toBeVisible(); - - const { - postMessage, - } = ChannelScreen; - - // # Type message on post input + // # Post a message const message = 'The quick brown fox :fox_face: jumps over the lazy dog :dog:'; - await postMessage(message); + await ChannelScreen.postMessage(message); // * Verify message is posted await expect(element(by.text('The quick brown fox 🦊 jumps over the lazy dog 🐶'))).toBeVisible(); diff --git a/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js b/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js index 7c066fed4..bac9c764a 100644 --- a/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js +++ b/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js @@ -28,19 +28,14 @@ describe('Messaging', () => { }); it('MM-T109 User can\'t send the same message repeatedly', async () => { - const message = Date.now().toString(); - const { - postInput, + postMessage, sendButtonDisabled, } = ChannelScreen; - // # Type a message - await postInput.tap(); - await postInput.typeText(message); - - // # Tap send button - await ChannelScreen.tapSendButton(); + // # Post a message + const message = Date.now().toString(); + await postMessage(message); // # Then tap send button repeatedly await expect(sendButtonDisabled).toBeVisible(); diff --git a/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js b/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js index cd683e379..e1b36c2a3 100644 --- a/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js +++ b/detox/e2e/test/messaging/tapping_channel_url_link_joins_channel.e2e.js @@ -26,32 +26,34 @@ describe('Messaging', () => { }); it('MM-T3471 Tapping channel URL link joins public channel', async () => { + const { + channelNavBarTitle, + logout, + openMainSidebar, + postMessage, + } = ChannelScreen; + const {getChannelByDisplayName} = MainSidebar; + // # Go to the Town Square channel - const {channelNavBarTitle} = ChannelScreen; - await ChannelScreen.openMainSidebar(); - let channelItem = MainSidebar.getChannelByDisplayName('Town Square'); + await openMainSidebar(); + let channelItem = getChannelByDisplayName('Town Square'); await channelItem.tap(); await expect(channelNavBarTitle).toHaveText('Town Square'); - // # There's no way to get a channel permalink on mobile so we make one - // manually + // # There's no way to get a channel permalink on mobile so we make one manually const channelPermalink = `${serverUrl}/${testTeam.name}/channels/${testChannel.name}`; // # Post the channel permalink to the test channel in Town Square - const {postInput} = ChannelScreen; - await expect(postInput).toBeVisible(); - await postInput.tap(); - await postInput.typeText(channelPermalink); - await ChannelScreen.tapSendButton(); + await postMessage(channelPermalink); await expect(element(by.text(channelPermalink))).toBeVisible(); // # Create another user in the same team, log in and go to town square const {user: otherUser} = await User.apiCreateUser({prefix: 'TestUser'}); await Team.apiAddUserToTeam(otherUser.id, testTeam.id); - await ChannelScreen.logout(); + await logout(); await ChannelScreen.open(otherUser); - await ChannelScreen.openMainSidebar(); - channelItem = MainSidebar.getChannelByDisplayName('Town Square'); + await openMainSidebar(); + channelItem = getChannelByDisplayName('Town Square'); await channelItem.tap(); await expect(channelNavBarTitle).toHaveText('Town Square'); diff --git a/detox/e2e/test/notifications/in_app_notification.e2e.js b/detox/e2e/test/notifications/in_app_notification.e2e.js index 4517ca40e..2eba53002 100644 --- a/detox/e2e/test/notifications/in_app_notification.e2e.js +++ b/detox/e2e/test/notifications/in_app_notification.e2e.js @@ -46,19 +46,18 @@ describe('in-app Notification', () => { }); it('MM-T3440 should render an in-app notification', async () => { + const { + postMessage, + openPostOptionsFor, + } = ChannelScreen; + + // # Post a message const testMessage = Date.now().toString(); - const {postInput} = ChannelScreen; - - // # Type a message - await postInput.tap(); - await postInput.typeText(testMessage); - - // # Tap send button - await ChannelScreen.tapSendButton(); + await postMessage(testMessage); // # Open add reaction screen const {post} = await Post.apiGetLastPostInChannel(testChannel2.id); - await ChannelScreen.openPostOptionsFor(post.id, testMessage); + await openPostOptionsFor(post.id, testMessage); await AddReactionScreen.open(); // # Close add reaction screen diff --git a/detox/e2e/test/smoke_test/message_deletion.e2e.js b/detox/e2e/test/smoke_test/message_deletion.e2e.js new file mode 100644 index 000000000..4e2dafb14 --- /dev/null +++ b/detox/e2e/test/smoke_test/message_deletion.e2e.js @@ -0,0 +1,82 @@ +// 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, + ThreadScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; + +describe('Message Deletion', () => { + let testChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + + const {channel} = await Channel.apiGetChannelByName(team.name, 'town-square'); + testChannel = channel; + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T3232 should remove message from channel when deleted', async () => { + const { + channelScreen, + deletePost, + postMessage, + } = ChannelScreen; + + // # Post a message + const message = Date.now().toString(); + await postMessage(message); + + // # Delete post + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await deletePost(post.id, message); + + // * Verify post is deleted + await expect(channelScreen).toBeVisible(); + await expect(element(by.text(message))).not.toBeVisible(); + }); + + it('MM-T3234 should be able to delete parent post from reply thread view', async () => { + const { + channelScreen, + openReplyThreadFor, + postMessage, + } = ChannelScreen; + const { + deletePost, + } = ThreadScreen; + + // # Post a message + const message = Date.now().toString(); + await postMessage(message); + + // # Open reply thread for post + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await openReplyThreadFor(post.id, message); + + // # Delete parent post from reply thread + await deletePost(post.id, message); + + // * Verify post is deleted + await expect(channelScreen).toBeVisible(); + await expect(element(by.text(message))).not.toBeVisible(); + }); +});