MM-30425 Detox/E2E: Add e2e for MM-T3216, MM-T3217, MM-T3220, MM-T3240, MM-T3269 (#5222)

* MM-30425 Detox/E2E: Add e2e for MM-T3216, MM-T3217, MM-T3220, MM-T3240, MM-T3269

* Added/fixed comments

* Fix snap test

* Stabilize for android

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Joseph Baylon 2021-03-16 14:34:13 -07:00 committed by GitHub
parent 89c23f4f91
commit 27cafc8288
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 224 additions and 45 deletions

View file

@ -239,7 +239,10 @@ export default class MarkdownImage extends ImageViewPort {
}
return (
<View style={style.container}>
<View
style={style.container}
testID='markdown_image'
>
{image}
</View>
);

View file

@ -3,6 +3,7 @@
exports[`ShowMoreButton should match, button snapshot 1`] = `
<View
style={Object {}}
testID="show_more.button.plus"
>
<CompassIcon
name="plus"
@ -20,6 +21,7 @@ exports[`ShowMoreButton should match, button snapshot 1`] = `
exports[`ShowMoreButton should match, button snapshot 2`] = `
<View
style={Object {}}
testID="show_more.button.minus"
>
<CompassIcon
name="minus"
@ -118,6 +120,7 @@ exports[`ShowMoreButton should match, full snapshot 1`] = `
"flexDirection": "row",
}
}
testID="show_more.button.plus"
>
<CompassIcon
name="plus"

View file

@ -35,7 +35,10 @@ export default class ShowMoreButton extends PureComponent {
}
return (
<View style={style.button}>
<View
style={style.button}
testID={`show_more.button.${iconName}`}
>
<CompassIcon
name={iconName}
size={18}

View file

@ -6,23 +6,32 @@ import ProfilePicture from './profile_picture';
class Post {
testID = {
postProfilePicturePrefix: 'post_profile_picture.profile_picture.',
image: 'markdown_image',
message: 'markdown_text',
postHeaderDateTime: 'post_header.date_time',
postHeaderDisplayName: 'post_header.display_name',
postHeaderGuestTag: 'post_header.guest_tag',
postHeaderReply: 'post_header.reply',
postPreHeaderText: 'post_pre_header.text',
showLessButton: 'show_more.button.minus',
showMoreButton: 'show_more.button.plus',
}
getPost = (postItemSourceTestID, postId, postMessage, postProfileOptions = {}) => {
const postItemMatcher = this.getPostItemMatcher(postItemSourceTestID, postId, postMessage);
const postItemImageMatcher = by.id(this.testID.image).withAncestor(postItemMatcher);
const postItemMessageMatcher = by.id(this.testID.message).withAncestor(postItemMatcher);
const postItemPreHeaderTextMatch = by.id(this.testID.postPreHeaderText).withAncestor(postItemMatcher);
const postItemShowLessButtonMatcher = by.id(this.testID.showLessButton).withAncestor(postItemMatcher);
const postItemShowMoreButtonMatcher = by.id(this.testID.showMoreButton).withAncestor(postItemMatcher);
return {
postItem: element(postItemMatcher),
postItemImage: element(postItemImageMatcher),
postItemMessage: element(postItemMessageMatcher),
postItemPreHeaderText: element(postItemPreHeaderTextMatch),
postItemShowLessButton: element(postItemShowLessButtonMatcher),
postItemShowMoreButton: element(postItemShowMoreButtonMatcher),
...this.getPostHeader(postItemMatcher),
...this.getPostProfilePicture(postItemMatcher, postProfileOptions),
};

View file

@ -17,10 +17,13 @@ class PostList {
postItemHeaderDisplayName,
postItemHeaderGuestTag,
postItemHeaderReply,
postItemImage,
postItemMessage,
postItemPreHeaderText,
postItemProfilePicture,
postItemProfilePictureUserStatus,
postItemShowLessButton,
postItemShowMoreButton,
} = Post.getPost(this.testID.postListPostItem, postId, postMessage, postProfileOptions);
return {
@ -29,10 +32,13 @@ class PostList {
postListPostItemHeaderDisplayName: postItemHeaderDisplayName,
postListPostItemHeaderGuestTag: postItemHeaderGuestTag,
postListPostItemHeaderReply: postItemHeaderReply,
postListPostItemImage: postItemImage,
postListPostItemMessage: postItemMessage,
postListPostItemPreHeaderText: postItemPreHeaderText,
postListPostItemProfilePicture: postItemProfilePicture,
postListPostItemProfilePictureUserStatus: postItemProfilePictureUserStatus,
postListPostItemShowLessButton: postItemShowLessButton,
postListPostItemShowMoreButton: postItemShowMoreButton,
};
}

View file

@ -175,10 +175,14 @@ class ChannelScreen {
await ThreadScreen.toBeVisible();
}
postMessage = async (message) => {
postMessage = async (message, {quickReplace = false} = {}) => {
// # Post message
await this.postInput.tap();
await this.postInput.typeText(message);
if (quickReplace) {
await this.postInput.replaceText(message);
} else {
await this.postInput.typeText(message);
}
await this.tapSendButton();
}

View file

@ -15,9 +15,12 @@ class LongPostScreen {
postItemHeaderDisplayName,
postItemHeaderGuestTag,
postItemHeaderReply,
postItemImage,
postItemMessage,
postItemProfilePicture,
postItemProfilePictureUserStatus,
postItemShowLessButton,
postItemShowMoreButton,
} = Post.getPost(this.testID.longPostItem, postId, postMessage, postProfileOptions);
return {
@ -26,9 +29,12 @@ class LongPostScreen {
longPostItemHeaderDisplayName: postItemHeaderDisplayName,
longPostItemHeaderGuestTag: postItemHeaderGuestTag,
longPostItemHeaderReply: postItemHeaderReply,
longPostItemImage: postItemImage,
longPostItemMessage: postItemMessage,
longPostItemProfilePicture: postItemProfilePicture,
longPostItemProfilePictureUserStatus: postItemProfilePictureUserStatus,
longPostItemShowLessButton: postItemShowLessButton,
longPostItemShowMoreButton: postItemShowMoreButton,
};
}

View file

@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import {
PostOptions,
RecentItem,
SearchBar,
} from '@support/ui/component';
@ -75,6 +76,15 @@ class SearchScreen {
await expect(this.clearButton).not.toExist();
}
openPostOptionsFor = async (postId, text) => {
const {searchResultPostItem} = await this.getSearchResultPostItem(postId, text);
await expect(searchResultPostItem).toBeVisible();
// # Open post options
await searchResultPostItem.longPress();
await PostOptions.toBeVisible();
}
hasSearchResultPostMessageAtIndex = async (index, postMessage) => {
await expect(
this.getSearchResultPostMessageAtIndex(index),

View file

@ -15,9 +15,12 @@ class SearchResultPostScreen {
postItemHeaderDisplayName,
postItemHeaderGuestTag,
postItemHeaderReply,
postItemImage,
postItemMessage,
postItemProfilePicture,
postItemProfilePictureUserStatus,
postItemShowLessButton,
postItemShowMoreButton,
} = Post.getPost(this.testID.searchResultPostItem, postId, postMessage, postProfileOptions);
return {
@ -26,9 +29,12 @@ class SearchResultPostScreen {
searchResultPostItemHeaderDisplayName: postItemHeaderDisplayName,
searchResultPostItemHeaderGuestTag: postItemHeaderGuestTag,
searchResultPostItemHeaderReply: postItemHeaderReply,
searchResultPostItemImage: postItemImage,
searchResultPostItemMessage: postItemMessage,
searchResultPostItemProfilePicture: postItemProfilePicture,
searchResultPostItemProfilePictureUserStatus: postItemProfilePictureUserStatus,
searchResultPostItemShowLessButton: postItemShowLessButton,
searchResultPostItemShowMoreButton: postItemShowMoreButton,
};
}

View file

@ -93,10 +93,14 @@ class ThreadScreen {
await PostOptions.toBeVisible();
}
postMessage = async (message) => {
postMessage = async (message, {quickReplace = false} = {}) => {
// # Post message
await this.postInput.tap();
await this.postInput.typeText(message);
if (quickReplace) {
await this.postInput.replaceText(message);
} else {
await this.postInput.typeText(message);
}
await this.tapSendButton();
}

View file

@ -23,6 +23,17 @@ import {getRandomId} from '@support/utils';
describe('Direct Messages', () => {
const searchTerm = getRandomId();
const {
channelNavBarTitle,
closeMainSidebar,
openMainSidebar,
} = ChannelScreen;
const {
getUserAtIndex,
searchInput,
startButton,
} = MoreDirectMessagesScreen;
const {getChannelByDisplayName} = MainSidebar;
let testUser;
let testOtherUser;
let townSquareChannel;
@ -45,18 +56,6 @@ describe('Direct Messages', () => {
});
it('MM-T3215 should be able to close direct message', async () => {
const {
channelNavBarTitle,
closeMainSidebar,
openMainSidebar,
} = ChannelScreen;
const {
getUserAtIndex,
searchInput,
startButton,
} = MoreDirectMessagesScreen;
const {getChannelByDisplayName} = MainSidebar;
// # Create a DM with the other user
await openMainSidebar();
await MoreDirectMessagesScreen.open();
@ -81,4 +80,15 @@ describe('Direct Messages', () => {
await expect(getChannelByDisplayName(testOtherUser.username)).not.toBeVisible();
await closeMainSidebar();
});
it('MM-T3216 should be able to open direct message with self', async () => {
// # Open a DM with self
await openMainSidebar();
await MoreDirectMessagesScreen.open();
await searchInput.typeText(testUser.username);
await getUserAtIndex(0).tap();
// * Verify DM channel with self is displayed
await expect(channelNavBarTitle).toHaveText(`${testUser.username} (you) `);
});
});

View file

@ -0,0 +1,63 @@
// 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 Posting', () => {
const {
getPostListPostItem,
postMessage,
} = ChannelScreen;
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-T3217 should be able to post a long message', async () => {
// # Post a long message
const message = 'The quick brown fox jumps over the lazy dog.'.repeat(30);
await postMessage(message, {quickReplace: true});
// * Verify message is posted
const {post} = await Post.apiGetLastPostInChannel(testChannel.id);
const {
postListPostItem,
postListPostItemShowMoreButton,
} = await getPostListPostItem(post.id, message);
await expect(postListPostItem).toExist();
await expect(postListPostItemShowMoreButton).toExist();
});
it('MM-T3269 should be able to post a markdown image', async () => {
// # Post a markdown image
const message = '![alt text that displays if loading fails](https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png)';
await postMessage(message, {quickReplace: true});
// * Verify message is posted
const {post} = await Post.apiGetLastPostInChannel(testChannel.id);
const {postListPostItemImage} = await getPostListPostItem(post.id);
await expect(postListPostItemImage).toBeVisible();
});
});

View file

@ -28,6 +28,8 @@ describe('Saved Messages', () => {
postMessage,
} = ChannelScreen;
const {
postOptions,
reactionPickerAction,
saveAction,
unsaveAction,
} = PostOptions;
@ -87,4 +89,21 @@ describe('Saved Messages', () => {
// # Close saved messages screen
await SavedMessagesScreen.close();
});
it('MM-T3220 should not be able to add a reaction on saved messages', async () => {
// # Save message from channel post list
const {post} = await Post.apiGetLastPostInChannel(testChannel.id);
await openPostOptionsFor(post.id, message);
await saveAction.tap();
// * Verify add a reaction is not available in post options
await openSettingsSidebar();
await SavedMessagesScreen.open();
await SavedMessagesScreen.openPostOptionsFor(post.id, message);
await expect(reactionPickerAction).not.toBeVisible();
await postOptions.tap();
// # Close saved messages screen
await SavedMessagesScreen.close();
});
});

View file

@ -7,7 +7,10 @@
// - Use element testID when selecting an element. Create one if none.
// *******************************************************************
import {Autocomplete} from '@support/ui/component';
import {
Autocomplete,
PostOptions,
} from '@support/ui/component';
import {
ChannelScreen,
PermalinkScreen,
@ -21,12 +24,18 @@ import {
} from '@support/server_api';
describe('Search', () => {
const testMessage = `test ${Date.now().toString()}`;
const partialSearchTerm = testMessage.split('-')[0];
const {
atMentionSuggestionList,
channelMentionSuggestionList,
} = Autocomplete;
const {
getRecentSearchItem,
getSearchResultPostItem,
searchFromModifier,
searchInModifier,
} = SearchScreen;
let testMessage;
let testPartialSearchTerm;
let testUser;
let testChannel;
@ -41,17 +50,22 @@ describe('Search', () => {
await ChannelScreen.open(user);
});
beforeEach(async () => {
testPartialSearchTerm = Date.now().toString();
testMessage = `${testPartialSearchTerm} test`;
});
afterAll(async () => {
await ChannelScreen.logout();
});
it('MM-T3235 search on text, jump to result', async () => {
// # Post message and search on text
await postMessageAndSearchText(testMessage, partialSearchTerm);
await postMessageAndSearchText(testMessage, testPartialSearchTerm);
// # Open permalink from search result post item
const lastPost = await Post.apiGetLastPostInChannel(testChannel.id);
const {searchResultPostItem} = await SearchScreen.getSearchResultPostItem(lastPost.post.id, testMessage);
const {searchResultPostItem} = await getSearchResultPostItem(lastPost.post.id, testMessage);
await expect(searchResultPostItem).toBeVisible();
await searchResultPostItem.tap();
@ -71,14 +85,14 @@ describe('Search', () => {
it('MM-T3236 search on sender, select from autocomplete, reply from search results', async () => {
// # Post message and search on sender
await postMessageAndSearchFrom(testMessage, testUser, atMentionSuggestionList, partialSearchTerm);
await postMessageAndSearchFrom(testMessage, testUser, atMentionSuggestionList, testPartialSearchTerm);
// * Verify search result post has the message
const lastPost = await Post.apiGetLastPostInChannel(testChannel.id);
const {
searchResultPostItem,
searchResultPostItemHeaderReply,
} = await SearchScreen.getSearchResultPostItem(lastPost.post.id, testMessage);
} = await getSearchResultPostItem(lastPost.post.id, testMessage);
await expect(searchResultPostItem).toBeVisible();
// # Open reply thread from search result post item
@ -101,11 +115,11 @@ describe('Search', () => {
it('MM-T3237 search on channel, select from autocomplete, search in combination with a text string as well', async () => {
// # Post message and search in channel
await postMessageAndSearchIn(testMessage, testChannel, channelMentionSuggestionList, partialSearchTerm);
await postMessageAndSearchIn(testMessage, testChannel, channelMentionSuggestionList, testPartialSearchTerm);
// * Verify search result post has the message
const lastPost = await Post.apiGetLastPostInChannel(testChannel.id);
const {searchResultPostItem} = await SearchScreen.getSearchResultPostItem(lastPost.post.id, testMessage);
const {searchResultPostItem} = await getSearchResultPostItem(lastPost.post.id, testMessage);
await expect(searchResultPostItem).toBeVisible();
// # Go back to channel
@ -113,37 +127,56 @@ describe('Search', () => {
});
it('MM-T3238 delete one previous search, tap on another', async () => {
const {
getRecentSearchItem,
searchInModifier,
} = SearchScreen;
// # Post message and search in channel
await postMessageAndSearchIn(testMessage, testChannel, channelMentionSuggestionList, testPartialSearchTerm);
// # Open search screen
await SearchScreen.open();
// # Post message and search on sender
await SearchScreen.cancel();
await postMessageAndSearchFrom(testMessage, testUser, atMentionSuggestionList, testPartialSearchTerm);
// # Delete one previous text search
const previousTextSearch = await getRecentSearchItem(testMessage);
const fromSearchTerms = `${searchFromModifier} ${testUser.username} ${testPartialSearchTerm}`;
const previousTextSearch = await getRecentSearchItem(fromSearchTerms);
await previousTextSearch.recentSearchItemRemoveButton.tap();
// * Verify previous text search is removed
await expect(previousTextSearch.recentSearchItem).not.toBeVisible();
// # Tap on previous in search
const inSearchTerms = `${searchInModifier} ${testChannel.name} ${partialSearchTerm}`;
const inSearchTerms = `${searchInModifier} ${testChannel.name} ${testPartialSearchTerm}`;
const previousInSearch = await getRecentSearchItem(inSearchTerms);
await previousInSearch.recentSearchItem.tap();
// * Verify search result post has the message
const lastPost = await Post.apiGetLastPostInChannel(testChannel.id);
const {searchResultPostItem} = await SearchScreen.getSearchResultPostItem(lastPost.post.id, testMessage);
const {searchResultPostItem} = await getSearchResultPostItem(lastPost.post.id, testMessage);
await expect(searchResultPostItem).toBeVisible();
// # Go back to channel
await SearchScreen.cancel();
});
it('MM-T3240 should not be able to add a reaction on search results', async () => {
const {
postOptions,
reactionPickerAction,
} = PostOptions;
// # Post message and search on text
await postMessageAndSearchText(testMessage, testPartialSearchTerm);
// * Verify add a reaction is not visible
const lastPost = await Post.apiGetLastPostInChannel(testChannel.id);
await SearchScreen.openPostOptionsFor(lastPost.post.id, testMessage);
await expect(reactionPickerAction).not.toBeVisible();
await postOptions.tap();
// # Go back to channel
await SearchScreen.cancel();
});
});
async function postMessageAndSearchFrom(testMessage, testUser, atMentionSuggestionList, partialSearchTerm) {
async function postMessageAndSearchFrom(testMessage, testUser, atMentionSuggestionList, testPartialSearchTerm) {
const {
getRecentSearchItem,
searchFromModifier,
@ -171,19 +204,19 @@ async function postMessageAndSearchFrom(testMessage, testUser, atMentionSuggesti
await userAtMentionAutocomplete.tap();
// # Type end of search term
await searchInput.typeText(partialSearchTerm);
await searchInput.typeText(testPartialSearchTerm);
// # Search user
await searchInput.tapReturnKey();
await expect(atMentionSuggestionList).not.toExist();
// * Verify recent search item is displayed
const searchTerms = `${searchFromModifier} ${testUser.username} ${partialSearchTerm}`;
const searchTerms = `${searchFromModifier} ${testUser.username} ${testPartialSearchTerm}`;
const {recentSearchItem} = await getRecentSearchItem(searchTerms);
await expect(recentSearchItem).toBeVisible();
}
async function postMessageAndSearchIn(testMessage, testChannel, channelMentionSuggestionList, partialSearchTerm) {
async function postMessageAndSearchIn(testMessage, testChannel, channelMentionSuggestionList, testPartialSearchTerm) {
const {
getRecentSearchItem,
searchInModifier,
@ -211,19 +244,19 @@ async function postMessageAndSearchIn(testMessage, testChannel, channelMentionSu
await channelMentionAutocomplete.tap();
// # Type end of search term
await searchInput.typeText(partialSearchTerm);
await searchInput.typeText(testPartialSearchTerm);
// # Search channel
await searchInput.tapReturnKey();
await expect(channelMentionSuggestionList).not.toExist();
// * Verify recent search item is displayed
const searchTerms = `${searchInModifier} ${testChannel.name} ${partialSearchTerm}`;
const searchTerms = `${searchInModifier} ${testChannel.name} ${testPartialSearchTerm}`;
const {recentSearchItem} = await getRecentSearchItem(searchTerms);
await expect(recentSearchItem).toBeVisible();
}
async function postMessageAndSearchText(testMessage, partialSearchTerm) {
async function postMessageAndSearchText(testMessage, testPartialSearchTerm) {
const {
getRecentSearchItem,
searchInput,
@ -237,12 +270,12 @@ async function postMessageAndSearchText(testMessage, partialSearchTerm) {
// # Type beginning of search term
await searchInput.clearText();
await searchInput.typeText(partialSearchTerm);
await searchInput.typeText(testPartialSearchTerm);
// # Search text
await searchInput.tapReturnKey();
// * Verify recent search item is displayed
const {recentSearchItem} = await getRecentSearchItem(testMessage);
const {recentSearchItem} = await getRecentSearchItem(testPartialSearchTerm);
await expect(recentSearchItem).toBeVisible();
}