From 7c36bfb6cc8a430bfcd1514538627aaeef7f4ed7 Mon Sep 17 00:00:00 2001 From: Joseph Baylon Date: Thu, 8 Apr 2021 16:15:10 -0700 Subject: [PATCH] MM-30430 Detox/E2E: MM-T3189, MM-T3231, MM-T3233 (#5293) --- app/components/post_list/post_list.js | 2 + detox/.detoxrc.json | 8 +-- detox/e2e/support/ui/component/alert.js | 4 +- .../e2e/support/ui/component/post_options.js | 11 +++- detox/e2e/support/ui/screen/channel.js | 6 +- .../support/ui/screen/channel_add_members.js | 10 ++- detox/e2e/support/ui/screen/channel_info.js | 10 ++- .../e2e/support/ui/screen/channel_members.js | 10 ++- detox/e2e/support/ui/screen/permalink.js | 2 + detox/e2e/support/ui/screen/thread.js | 6 +- .../test/smoke_test/message_deletion.e2e.js | 66 ++++++++++++++----- .../test/smoke_test/message_posting.e2e.js | 63 ++++++++++++++++-- ios/Podfile.lock | 6 +- 13 files changed, 156 insertions(+), 48 deletions(-) diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js index 40c9d1f2b..6c67024d8 100644 --- a/app/components/post_list/post_list.js +++ b/app/components/post_list/post_list.js @@ -481,6 +481,7 @@ export default class PostList extends PureComponent { initialIndex, deepLinkURL, showMoreMessagesButton, + testID, } = this.props; const refreshControl = ( @@ -526,6 +527,7 @@ export default class PostList extends PureComponent { minimumViewTime: 100, }} onViewableItemsChanged={this.onViewableItemsChanged} + testID={testID} /> {showMoreMessagesButton && { + deletePost = async ({confirm = true} = {}) => { // # Swipe up panel on Android if (isAndroid()) { await this.slideUpPanel.swipe('up'); @@ -60,13 +60,18 @@ class PostOptions { deleteButton, } = Alert; await expect(deletePostTitle).toBeVisible(); + await expect(cancelButton).toBeVisible(); + await expect(deleteButton).toBeVisible(); if (confirm) { deleteButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.postOptions).not.toBeVisible(); } else { cancelButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.postOptions).toBeVisible(); + await this.close(); } - await wait(timeouts.ONE_SEC); - await expect(this.postOptions).not.toBeVisible(); } } diff --git a/detox/e2e/support/ui/screen/channel.js b/detox/e2e/support/ui/screen/channel.js index 331967b3a..6ee0479fb 100644 --- a/detox/e2e/support/ui/screen/channel.js +++ b/detox/e2e/support/ui/screen/channel.js @@ -25,6 +25,7 @@ class ChannelScreen { testID = { channelScreenPrefix: 'channel.', channelScreen: 'channel.screen', + channelPostList: 'channel.post_list', mainSidebarDrawerButton: 'main_sidebar_drawer.button', mainSidebarDrawerButtonBadge: 'main_sidebar_drawer.button.badge', mainSidebarDrawerButtonBadgeUnreadCount: 'main_sidebar_drawer.button.badge.unread_count', @@ -37,6 +38,7 @@ class ChannelScreen { } channelScreen = element(by.id(this.testID.channelScreen)); + channelPostList = element(by.id(this.testID.channelPostList)); mainSidebarDrawerButton = element(by.id(this.testID.mainSidebarDrawerButton)); mainSidebarDrawerButtonBadge = element(by.id(this.testID.mainSidebarDrawerButtonBadge)); mainSidebarDrawerButtonBadgeUnreadCount = element(by.id(this.testID.mainSidebarDrawerButtonBadgeUnreadCount)); @@ -131,11 +133,11 @@ class ChannelScreen { await this.closeMainSidebar(); } - deletePost = async (postId, text, confirm = true) => { + deletePost = async (postId, text, {confirm = true} = {}) => { await this.openPostOptionsFor(postId, text); // # Delete post - await PostOptions.deletePost(confirm); + await PostOptions.deletePost({confirm}); await this.toBeVisible(); } diff --git a/detox/e2e/support/ui/screen/channel_add_members.js b/detox/e2e/support/ui/screen/channel_add_members.js index 1e1a7f527..41bd47fe4 100644 --- a/detox/e2e/support/ui/screen/channel_add_members.js +++ b/detox/e2e/support/ui/screen/channel_add_members.js @@ -72,7 +72,7 @@ class ChannelAddMembersScreen { await expect(this.channelAddMembersScreen).not.toBeVisible(); } - removeMembers = async (displayUsernameList, confirm = true) => { + removeMembers = async (displayUsernameList, {confirm = true} = {}) => { displayUsernameList.forEach(async (displayUsername) => { await this.getUserByDisplayUsername(`@${displayUsername}`).tap(); }); @@ -84,13 +84,17 @@ class ChannelAddMembersScreen { yesButton, } = Alert; await expect(removeMembersTitle).toBeVisible(); + await expect(noButton).toBeVisible(); + await expect(yesButton).toBeVisible(); if (confirm) { yesButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.channelAddMembersScreen).not.toBeVisible(); } else { noButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.channelAddMembersScreen).toBeVisible(); } - await wait(timeouts.ONE_SEC); - await expect(this.channelAddMembersScreen).not.toBeVisible(); } hasUserDisplayUsernameAtIndex = async (index, displayUsername) => { diff --git a/detox/e2e/support/ui/screen/channel_info.js b/detox/e2e/support/ui/screen/channel_info.js index f16ea0bfa..58f4307ed 100644 --- a/detox/e2e/support/ui/screen/channel_info.js +++ b/detox/e2e/support/ui/screen/channel_info.js @@ -78,7 +78,7 @@ class ChannelInfoScreen { await expect(this.channelInfoScreen).not.toBeVisible(); } - archiveChannel = async (confirm = true) => { + archiveChannel = async ({confirm = true} = {}) => { await this.channelInfoScrollView.scrollTo('bottom'); await this.archiveAction.tap(); const { @@ -87,13 +87,17 @@ class ChannelInfoScreen { yesButton, } = Alert; await expect(archivePublicChannelTitle).toBeVisible(); + await expect(noButton).toBeVisible(); + await expect(yesButton).toBeVisible(); if (confirm) { yesButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.channelInfoScreen).not.toBeVisible(); } else { noButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.channelInfoScreen).toBeVisible(); } - await wait(timeouts.ONE_SEC); - await expect(this.channelInfoScreen).not.toBeVisible(); } } diff --git a/detox/e2e/support/ui/screen/channel_members.js b/detox/e2e/support/ui/screen/channel_members.js index ab8498796..0c39da3ba 100644 --- a/detox/e2e/support/ui/screen/channel_members.js +++ b/detox/e2e/support/ui/screen/channel_members.js @@ -72,7 +72,7 @@ class ChannelMembersScreen { await expect(this.channelMembersScreen).not.toBeVisible(); } - removeMembers = async (displayUsernameList, confirm = true) => { + removeMembers = async (displayUsernameList, {confirm = true} = {}) => { displayUsernameList.forEach(async (displayUsername) => { await this.getUserByDisplayUsername(`@${displayUsername}`).tap(); }); @@ -84,13 +84,17 @@ class ChannelMembersScreen { yesButton, } = Alert; await expect(removeMembersTitle).toBeVisible(); + await expect(noButton).toBeVisible(); + await expect(yesButton).toBeVisible(); if (confirm) { yesButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.channelMembersScreen).not.toBeVisible(); } else { noButton.tap(); + await wait(timeouts.ONE_SEC); + await expect(this.channelMembersScreen).toBeVisible(); } - await wait(timeouts.ONE_SEC); - await expect(this.channelMembersScreen).not.toBeVisible(); } hasUserDisplayUsernameAtIndex = async (index, displayUsername) => { diff --git a/detox/e2e/support/ui/screen/permalink.js b/detox/e2e/support/ui/screen/permalink.js index 0fbdc41a7..a45986e40 100644 --- a/detox/e2e/support/ui/screen/permalink.js +++ b/detox/e2e/support/ui/screen/permalink.js @@ -8,10 +8,12 @@ class PermalinkScreen { testID = { permalinkScreenPrefix: 'permalink.', permalinkScreen: 'permalink.screen', + permalinkPostList: 'permalink.post_list', searchJump: 'permalink.search.jump', } permalinkScreen = element(by.id(this.testID.permalinkScreen)); + permalinkPostList = element(by.id(this.testID.permalinkPostList)); searchJump = element(by.id(this.testID.searchJump)); postList = new PostList(this.testID.permalinkScreenPrefix); diff --git a/detox/e2e/support/ui/screen/thread.js b/detox/e2e/support/ui/screen/thread.js index 90a6edee6..e5091a517 100644 --- a/detox/e2e/support/ui/screen/thread.js +++ b/detox/e2e/support/ui/screen/thread.js @@ -18,10 +18,12 @@ class ThreadScreen { testID = { threadScreenPrefix: 'thread.', threadScreen: 'thread.screen', + threadPostList: 'thread.post_list', backButton: 'screen.back.button', } threadScreen = element(by.id(this.testID.threadScreen)); + threadPostList = element(by.id(this.testID.threadPostList)); backButton = element(by.id(this.testID.backButton)); // convenience props @@ -72,11 +74,11 @@ class ThreadScreen { await expect(this.threadScreen).not.toBeVisible(); } - deletePost = async (postId, text, confirm = true, isParentPost = true) => { + deletePost = async (postId, text, {confirm = true, isParentPost = true} = {}) => { await this.openPostOptionsFor(postId, text); // # Delete post - await PostOptions.deletePost(confirm); + await PostOptions.deletePost({confirm}); if (isParentPost) { await expect(this.threadScreen).not.toBeVisible(); } else { diff --git a/detox/e2e/test/smoke_test/message_deletion.e2e.js b/detox/e2e/test/smoke_test/message_deletion.e2e.js index 4e2dafb14..e6032051e 100644 --- a/detox/e2e/test/smoke_test/message_deletion.e2e.js +++ b/detox/e2e/test/smoke_test/message_deletion.e2e.js @@ -18,6 +18,12 @@ import { } from '@support/server_api'; describe('Message Deletion', () => { + const { + channelScreen, + deletePost, + openReplyThreadFor, + postMessage, + } = ChannelScreen; let testChannel; beforeAll(async () => { @@ -34,13 +40,21 @@ describe('Message Deletion', () => { await ChannelScreen.logout(); }); - it('MM-T3232 should remove message from channel when deleted', async () => { - const { - channelScreen, - deletePost, - postMessage, - } = ChannelScreen; + it('MM-T3231 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-T3232 should remove message from channel when deleted', async () => { // # Post a message const message = Date.now().toString(); await postMessage(message); @@ -54,16 +68,36 @@ describe('Message Deletion', () => { 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; + it('MM-T3233 should be able to delete reply post from reply thread view', async () => { + // # Post a message + const parentMessage = Date.now().toString(); + await postMessage(parentMessage); + // # Open reply thread for post + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await openReplyThreadFor(post.id, parentMessage); + + // # Post a reply message + const replyMessage = `reply ${parentMessage}`; + await ThreadScreen.postMessage(replyMessage); + + // # Delete reply post from reply thread + const {post: replyPost} = await Post.apiGetLastPostInChannel(testChannel.id); + await ThreadScreen.deletePost(replyPost.id, replyMessage, {isParentPost: false}); + + // * Verify reply post is deleted but parent post is still visible in reply thread + await ThreadScreen.toBeVisible(); + await expect(element(by.text(replyMessage))).not.toBeVisible(); + await expect(element(by.text(parentMessage))).toBeVisible(); + + // * Verify reply post is deleted but parent post is still visible in main thread + await ThreadScreen.back(); + await expect(channelScreen).toBeVisible(); + await expect(element(by.text(replyMessage))).not.toBeVisible(); + await expect(element(by.text(parentMessage))).toBeVisible(); + }); + + it('MM-T3234 should be able to delete parent post from reply thread view', async () => { // # Post a message const message = Date.now().toString(); await postMessage(message); @@ -73,7 +107,7 @@ describe('Message Deletion', () => { await openReplyThreadFor(post.id, message); // # Delete parent post from reply thread - await deletePost(post.id, message); + await ThreadScreen.deletePost(post.id, message); // * Verify post is deleted await expect(channelScreen).toBeVisible(); diff --git a/detox/e2e/test/smoke_test/message_posting.e2e.js b/detox/e2e/test/smoke_test/message_posting.e2e.js index 6f45daf38..2d6cc2297 100644 --- a/detox/e2e/test/smoke_test/message_posting.e2e.js +++ b/detox/e2e/test/smoke_test/message_posting.e2e.js @@ -7,6 +7,8 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* +import moment from 'moment-timezone'; + import { ChannelScreen, LongPostScreen, @@ -16,22 +18,25 @@ import { Post, Setup, } from '@support/server_api'; +import {isAndroid} from '@support/utils'; describe('Message Posting', () => { const longMessage = 'The quick brown fox jumps over the lazy dog.'.repeat(30); const { getLongPostItem, getPostListPostItem, + goToChannel, postMessage, } = ChannelScreen; let testChannel; + let townSquareChannel; beforeAll(async () => { - const {team, user} = await Setup.apiInit(); - - const {channel} = await Channel.apiGetChannelByName(team.name, 'town-square'); + const {channel, team, user} = await Setup.apiInit(); testChannel = channel; + ({channel: townSquareChannel} = await Channel.apiGetChannelByName(team.name, 'town-square')); + // # Open channel screen await ChannelScreen.open(user); }); @@ -45,7 +50,7 @@ describe('Message Posting', () => { await postMessage(longMessage, {quickReplace: true}); // * Verify message is posted - const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); const { postListPostItem, postListPostItemShowMoreButton, @@ -56,7 +61,7 @@ describe('Message Posting', () => { it('MM-T3229 should be able to open long post via show more', async () => { // # Open long post via show more - const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); const {postListPostItemShowMoreButton} = await getPostListPostItem(post.id, longMessage); await postListPostItemShowMoreButton.tap(); @@ -74,7 +79,7 @@ describe('Message Posting', () => { await postMessage(message, {quickReplace: true}); // * Verify message is posted - const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); const {postListPostItemImage} = await getPostListPostItem(post.id); await expect(postListPostItemImage).toBeVisible(); }); @@ -85,8 +90,52 @@ describe('Message Posting', () => { await postMessage(message); // * Verify message is posted - const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + const {post} = await Post.apiGetLastPostInChannel(townSquareChannel.id); const {postListPostItem} = await getPostListPostItem(post.id, '🦊'); await expect(postListPostItem).toBeVisible(); }); + + it('MM-T3189 should be able to scroll up in channel with long history', async () => { + // # Post messages + await goToChannel(testChannel.display_name); + const size = 50; + const firstMessageDate = moment().subtract(size + 1, 'days').toDate(); + const firstMessage = `${firstMessageDate} first`; + const firstPost = await Post.apiCreatePost({ + channelId: testChannel.id, + message: firstMessage, + createAt: firstMessageDate.getTime(), + }); + [...Array(size).keys()].forEach(async (key) => { + const messageDate = moment().subtract(key + 1, 'days').toDate(); + const message = `${messageDate}-${key}.`.repeat(10); + await Post.apiCreatePost({ + channelId: testChannel.id, + message, + createAt: messageDate.getTime(), + }); + }); + const lastMessageDate = moment().toDate(); + const lastMessage = `${lastMessageDate} last`; + const lastPost = await Post.apiCreatePost({ + channelId: testChannel.id, + message: lastMessage, + createAt: lastMessageDate.getTime(), + }); + + // Detox is having trouble scrolling + if (isAndroid()) { + return; + } + + // * Verify last message is posted + await goToChannel(townSquareChannel.display_name); + await goToChannel(testChannel.display_name); + const {postListPostItem: lastPostItem} = await getPostListPostItem(lastPost.post.id, lastMessage); + await waitFor(lastPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(1000, 'down'); + + // * Verify user can scroll up multiple times until first matching post is seen + const {postListPostItem: firstPostItem} = await getPostListPostItem(firstPost.post.id, firstMessage); + await waitFor(firstPostItem).toBeVisible().whileElement(by.id(ChannelScreen.testID.channelPostList)).scroll(1000, 'up'); + }); }); diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 1b132b7c7..6f390dd27 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -651,10 +651,10 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872 - DoubleConversion: cf9b38bf0b2d048436d9a82ad2abe1404f11e7de + DoubleConversion: cde416483dac037923206447da6e1454df403714 FBLazyVector: 49cbe4b43e445b06bf29199b6ad2057649e4c8f5 - FBReactNativeSpec: a804c9d6c798f94831713302354003ee54ea18cb - glog: 73c2498ac6884b13ede40eda8228cb1eee9d9d62 + FBReactNativeSpec: 4862b23f27a9cdb8fdb1710af411fa677dcdd3cf + glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 jail-monkey: 80c9e34da2cd54023e5ad08bf7051ec75bd43d5b libwebp: 946cb3063cea9236285f7e9a8505d806d30e07f3 MMKV: 22e5136f7d00197bc0fc9694b7f71519f0d1ca12