diff --git a/detox/e2e/support/ui/component/alert.js b/detox/e2e/support/ui/component/alert.js index 86b6ddc27..82bd3e13d 100644 --- a/detox/e2e/support/ui/component/alert.js +++ b/detox/e2e/support/ui/component/alert.js @@ -13,6 +13,7 @@ class Alert { joinPrivateChannelTitle = isAndroid() ? element(by.text('Join private channel')) : element(by.label('Join private channel')).atIndex(0); leavePrivateChannelTitle = isAndroid() ? element(by.text('Leave Private Channel')) : element(by.label('Leave Private Channel')).atIndex(0); leavePublicChannelTitle = isAndroid() ? element(by.text('Leave Public Channel')) : element(by.label('Leave Public Channel')).atIndex(0); + messageLengthTitle = isAndroid() ? element(by.text('Message Length')) : element(by.label('Message Length')).atIndex(0); removeMembersTitle = isAndroid() ? element(by.text('Remove Members')) : element(by.label('Remove Members')).atIndex(0); // alert buttons diff --git a/detox/e2e/support/ui/component/post_options.js b/detox/e2e/support/ui/component/post_options.js index 16273e92c..cff563b39 100644 --- a/detox/e2e/support/ui/component/post_options.js +++ b/detox/e2e/support/ui/component/post_options.js @@ -26,6 +26,7 @@ class PostOptions { reactionPickerAction = element(by.id(this.testID.reactionPickerAction)); replyAction = element(by.id(this.testID.replyAction)); permalinkAction = element(by.id(this.testID.permalinkAction)); + copyAction = element(by.id(this.testID.copyAction)); deleteAction = element(by.id(this.testID.deleteAction)); editAction = element(by.id(this.testID.editAction)); saveAction = element(by.id(this.testID.saveAction)); diff --git a/detox/e2e/test/messaging/message_deletion.e2e.js b/detox/e2e/test/messaging/message_deletion.e2e.js new file mode 100644 index 000000000..f07b86a52 --- /dev/null +++ b/detox/e2e/test/messaging/message_deletion.e2e.js @@ -0,0 +1,66 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +// ******************************************************************* +// - [#] indicates a test step (e.g. # Go to a screen) +// - [*] indicates an assertion (e.g. * Check the title) +// - Use element testID when selecting an element. Create one if none. +// ******************************************************************* + +import {ChannelScreen} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; + +describe('Message Deletion', () => { + const { + channelScreen, + deletePost, + postMessage, + } = ChannelScreen; + let testChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + + const {channel} = await Channel.apiGetChannelByName(team.id, 'town-square'); + testChannel = channel; + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T115 should be able to delete message and cancel', async () => { + // # Post a message + const message = Date.now().toString(); + await postMessage(message); + + // # Delete post and cancel + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await deletePost(post.id, message, {confirm: false}); + + // * Verify post is not deleted + await expect(channelScreen).toBeVisible(); + await expect(element(by.text(message))).toBeVisible(); + }); + + it('MM-T116 should remove message from channel when deleted', async () => { + // # 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(); + }); +}); diff --git a/detox/e2e/test/messaging/message_draft.e2e.js b/detox/e2e/test/messaging/message_draft.e2e.js new file mode 100644 index 000000000..74161215a --- /dev/null +++ b/detox/e2e/test/messaging/message_draft.e2e.js @@ -0,0 +1,74 @@ +// 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 {Alert} from '@support/ui/component'; +import {ChannelScreen} from '@support/ui/screen'; +import {Setup} from '@support/server_api'; + +describe('Message Draft', () => { + const { + postInput, + sendButton, + sendButtonDisabled, + } = ChannelScreen; + + beforeAll(async () => { + const {user} = await Setup.apiInit(); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T107 should show warning message when message exceeds character limit', async () => { + // # Type message that exceeds character limit (16384) + let message = '1234567890'.repeat(1638) + '1234'; + await postInput.replaceText(message); + + // * Verify warning message is displayed and send button is disabled + await expect(Alert.messageLengthTitle).toBeVisible(); + await expect(element(by.text('Your current message is too long. Current character count: 16384/16383'))).toBeVisible(); + await Alert.okButton.tap(); + await expect(sendButtonDisabled).toBeVisible(); + + // # Type message that wit max character limit (16383) + message = '1234567890'.repeat(1638) + '123'; + await postInput.replaceText(message); + + // * Verify warning message is not displayed and send button is enabled + await expect(Alert.messageLengthTitle).not.toBeVisible(); + await expect(element(by.text('Your current message is too long. Current character count: 16383/16383'))).not.toBeVisible(); + await expect(sendButton).toBeVisible(); + + // # Clear input + await postInput.clearText(); + }); + + it('MM-T128 should save message draft when app is closed the re-opened', async () => { + // # Type a message draft + const message = Date.now().toString(); + await postInput.typeText(message); + + // * Verify message draft + await expect(postInput).toHaveText(message); + + // # Send app to home and re-open + await device.sendToHome(); + await device.launchApp({newInstance: false}); + + // * Verify message draft still exists + await expect(postInput).toHaveText(message); + + // # Clear input + await postInput.clearText(); + }); +}); diff --git a/detox/e2e/test/messaging/message_edit.e2e.js b/detox/e2e/test/messaging/message_edit.e2e.js new file mode 100644 index 000000000..67012f942 --- /dev/null +++ b/detox/e2e/test/messaging/message_edit.e2e.js @@ -0,0 +1,83 @@ +// 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, + EditPostScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; + +describe('Message Edit', () => { + const testMessage = Date.now().toString(); + const additionalText = ' additional text'; + const { + getPostListPostItem, + openPostOptionsFor, + postMessage, + } = ChannelScreen; + const { + messageInput, + saveButton, + } = EditPostScreen; + let testChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + const {channel} = await Channel.apiGetChannelByName(team.id, 'town-square'); + testChannel = channel; + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T100 should be able to edit a message and cancel', async () => { + // # Post a message + await postMessage(testMessage); + + // # Open edit post screen + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await openPostOptionsFor(post.id, testMessage); + await EditPostScreen.open(); + + // # Edit post and cancel + await messageInput.tap(); + await messageInput.typeText(additionalText); + await EditPostScreen.close(); + + // * Verify post is not edited + await ChannelScreen.toBeVisible(); + const {postListPostItem} = await getPostListPostItem(post.id, testMessage); + await expect(postListPostItem).toBeVisible(); + }); + + it('MM-T101 should be able to edit a message and save', async () => { + // # Open edit post screen + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await openPostOptionsFor(post.id, testMessage); + await EditPostScreen.open(); + + // # Edit post and save + await messageInput.tap(); + await messageInput.typeText(additionalText); + await saveButton.tap(); + + // * Verify post 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/message_posting.e2e.js b/detox/e2e/test/messaging/message_posting.e2e.js index 6502aeeb9..f93f09fb2 100644 --- a/detox/e2e/test/messaging/message_posting.e2e.js +++ b/detox/e2e/test/messaging/message_posting.e2e.js @@ -9,39 +9,107 @@ import moment from 'moment-timezone'; +import { + MainSidebar, + PostOptions, + TeamsList, +} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; import { Channel, Post, Setup, } from '@support/server_api'; -import {isAndroid} from '@support/utils'; +import { + getAdminAccount, + isAndroid, +} from '@support/utils'; describe('Messaging', () => { const { getPostListPostItem, goToChannel, + openTeamSidebar, postInput, + postMessage, sendButton, sendButtonDisabled, } = ChannelScreen; let testChannel; let townSquareChannel; + let testTeam; + let testUser; beforeAll(async () => { const {channel, team, user} = await Setup.apiInit(); testChannel = channel; + testTeam = team; + testUser = user; - ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.id, 'town-square')); + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(testTeam.id, 'town-square')); // # Open channel screen - await ChannelScreen.open(user); + await ChannelScreen.open(testUser); }); afterAll(async () => { await ChannelScreen.logout(); }); + it('MM-T108 should be able to post a long message', async () => { + // # Post a long message + const longMessage = 'The quick brown fox jumps over the lazy dog.'.repeat(10); + await postMessage(longMessage, {quickReplace: true}); + + // * Verify message is posted + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); + const {postListPostItem} = await getPostListPostItem(post.id, longMessage); + await expect(postListPostItem).toExist(); + }); + + it('MM-T151 should only have delete post option for system message as sysadmin', async () => { + const {getTeamByDisplayName} = MainSidebar; + const { + copyAction, + deleteAction, + editAction, + markUnreadAction, + permalinkAction, + pinAction, + reactionPickerAction, + replyAction, + saveAction, + } = PostOptions; + + // # Log in as sysadmin + await ChannelScreen.logout(); + await ChannelScreen.open(getAdminAccount()); + + // # Go to channel with system message + await openTeamSidebar(); + await waitFor(getTeamByDisplayName(testTeam.display_name)).toBeVisible().whileElement(by.id(TeamsList.testID.teamsList)).scroll(500, 'down'); + await getTeamByDisplayName(testTeam.display_name).tap(); + await goToChannel(testChannel.display_name); + + // * Verify only delete post option is present + const systemMessage = `@${testUser.username} added to the channel by you.`; + await element(by.text(systemMessage)).longPress(); + await expect(deleteAction).toBeVisible(); + await expect(reactionPickerAction).not.toExist(); + await expect(replyAction).not.toExist(); + await expect(permalinkAction).not.toExist(); + await expect(copyAction).not.toExist(); + await expect(editAction).not.toExist(); + await expect(saveAction).not.toExist(); + await expect(pinAction).not.toExist(); + await expect(markUnreadAction).not.toExist(); + + // # Log in as test user + await PostOptions.close(); + await ChannelScreen.logout(); + await ChannelScreen.open(testUser); + }); + it('MM-T3486 should post a message when send button is tapped', async () => { // * Verify post input is visible and send button is disabled await expect(postInput).toBeVisible();