MM-30430 Detox/E2E: MM-T3189, MM-T3231, MM-T3233 (#5293)

This commit is contained in:
Joseph Baylon 2021-04-08 16:15:10 -07:00 committed by GitHub
parent 9c043f1b9f
commit 7c36bfb6cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 156 additions and 48 deletions

View file

@ -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 &&
<MoreMessagesButton

View file

@ -6,7 +6,7 @@
"binaryPath": "../ios/Build/Products/Debug-iphonesimulator/Mattermost.app",
"type": "ios.simulator",
"device": {
"type": "iPhone 11"
"type": "iPhone 12"
}
},
"ios.sim.release": {
@ -14,13 +14,13 @@
"binaryPath": "../ios/Build/Products/Release-iphonesimulator/Mattermost.app",
"build": "cd ../fastlane && NODE_ENV=production bundle exec fastlane ios simulator && cd ../detox",
"device": {
"type": "iPhone 11"
"type": "iPhone 12"
}
},
"android.emu.debug": {
"type": "android.emulator",
"binaryPath": "../android/app/build/outputs/apk/debug/app-debug.apk",
"build": "cd .. && ./node_modules/.bin/jetify && cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ../detox",
"build": "cd .. && ./node_modules/.bin/jetify && cd android && ./gradlew clean assembleDebug assembleAndroidTest -DtestBuildType=debug && cd ../detox",
"device": {
"avdName": "detox_emu_api_30"
}
@ -28,7 +28,7 @@
"android.emu.release": {
"type": "android.emulator",
"binaryPath": "../android/app/build/outputs/apk/release/app-release.apk",
"build": "cd .. && ./node_modules/.bin/jetify && cd android && ./gradlew assembleRelease assembleAndroidTest -DtestBuildType=release && cd ../detox",
"build": "cd .. && ./node_modules/.bin/jetify && cd android && ./gradlew clean assembleRelease assembleAndroidTest -DtestBuildType=release && cd ../detox",
"device": {
"avdName": "detox_emu_api_30"
}

View file

@ -11,10 +11,10 @@ class Alert {
removeMembersTitle = isAndroid() ? element(by.text('Remove Members')) : element(by.label('Remove Members')).atIndex(0);
// alert buttons
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(0);
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(1);
deleteButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(0);
joinButton = isAndroid() ? element(by.text('JOIN')) : element(by.label('Join')).atIndex(0);
noButton = isAndroid() ? element(by.text('NO')) : element(by.label('No')).atIndex(0);
noButton = isAndroid() ? element(by.text('NO')) : element(by.label('No')).atIndex(1);
yesButton = isAndroid() ? element(by.text('YES')) : element(by.label('Yes')).atIndex(0);
}

View file

@ -47,7 +47,7 @@ class PostOptions {
await expect(this.postOptions).not.toBeVisible();
}
deletePost = async (confirm = true) => {
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();
}
}

View file

@ -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();
}

View file

@ -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) => {

View file

@ -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();
}
}

View file

@ -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) => {

View file

@ -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);

View file

@ -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 {

View file

@ -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();

View file

@ -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');
});
});

View file

@ -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