Detox/E2E: Messaging post options e2e tests in Gekidou (#6405)

* Detox/E2E: Messaging post options e2e tests in Gekidou

* Simplify find channels search bar
This commit is contained in:
Joseph Baylon 2022-06-17 18:01:05 -07:00 committed by GitHub
parent 07df681193
commit 7a71d864e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
61 changed files with 953 additions and 70 deletions

View file

@ -443,7 +443,7 @@ const AtMention = ({
renderSectionHeader={renderSectionHeader}
style={[style.listView, {maxHeight: maxListHeight}]}
sections={sections}
testID='at_mention_suggestion.list'
testID='autocomplete.at_mention.section_list'
/>
);
};

View file

@ -40,7 +40,7 @@ const AtMentionItem = ({
>
<UserItem
user={user}
testID='at_mention'
testID='at_mention.user_item'
/>
</TouchableWithFeedback>
);

View file

@ -349,7 +349,7 @@ const ChannelMention = ({
renderSectionHeader={renderSectionHeader}
style={listStyle}
sections={sections}
testID='channel_mention_suggestion.list'
testID='autocomplete.channel_mention.section_list'
/>
);
};

View file

@ -34,7 +34,7 @@ const InfoBox = ({channelId, containerStyle, showAsLabel = false, testID}: Props
leftButtons: [{
id: closeButtonId,
icon: closeButton,
testID: closeButtonId,
testID: 'close.channel_info.button',
}],
},
modal: {swipeToDismiss: false},

View file

@ -34,7 +34,7 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
},
]
}
testID="channel_list_item.hello.collapsed.true"
testID="channel_item.hello"
>
<View
style={
@ -103,7 +103,7 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
false,
]
}
testID="channel_list_item.hello.display_name"
testID="channel_item.hello.display_name"
>
Hello!
</Text>
@ -147,7 +147,7 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
},
]
}
testID="channel_list_item.hello.collapsed.true"
testID="channel_item.hello"
>
<View
style={
@ -216,7 +216,7 @@ exports[`components/channel_list/categories/body/channel_item should match snaps
false,
]
}
testID="channel_list_item.hello.display_name"
testID="channel_item.hello.display_name"
>
Hello!
</Text>

View file

@ -37,7 +37,7 @@ describe('components/channel_list/categories/body/channel_item', () => {
membersCount={0}
isMuted={false}
currentUserId={'id'}
testID='channel_list_item'
testID='channel_item'
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}
@ -57,7 +57,7 @@ describe('components/channel_list/categories/body/channel_item', () => {
membersCount={3}
isMuted={false}
currentUserId={'id'}
testID='channel_list_item'
testID='channel_item'
onPress={() => undefined}
isUnread={myChannel.isUnread}
mentionsCount={myChannel.mentionsCount}

View file

@ -179,7 +179,7 @@ const ChannelListItem = ({
<>
<View
style={containerStyle}
testID={`${testID}.${channel.name}.collapsed.${!isActive}`}
testID={`${testID}.${channel.name}`}
>
<View style={styles.wrapper}>
<ChannelIcon

View file

@ -35,7 +35,7 @@ const CustomStatus = ({customStatus, customStatusExpired, isCustomStatusEnabled,
<CustomStatusEmoji
customStatus={customStatus!}
style={[style.customStatusEmoji, isInfo && style.info]}
testID={`channel_list_item.custom_status.${customStatus!.emoji}-${customStatus!.text}`}
testID={`channel_item.custom_status.${customStatus!.emoji}-${customStatus!.text}`}
/>
);
};

View file

@ -100,11 +100,9 @@ const EmojiPicker = ({customEmojis, customEmojisEnabled, onEmojiPress, recentEmo
<SafeAreaView
style={styles.flex}
edges={edges}
testID={`${testID}.screen`}
>
<View
style={styles.searchBar}
testID={testID}
>
<View style={styles.searchBar}>
<SearchBar
autoCapitalize='none'
keyboardAppearance={getKeyboardAppearanceFromTheme(theme)}

View file

@ -53,6 +53,7 @@ const EmojiSectionBar = ({currentIndex, sections, scrollToIndex}: Props) => {
scrollViewNativeID={SCROLLVIEW_NATIVE_ID}
normalList={true}
style={styles.container}
testID='emoji_picker.emoji_sections.section_bar'
>
<View style={styles.background}>
<View style={styles.pane}>

View file

@ -227,6 +227,7 @@ const EmojiSections = ({customEmojis, customEmojisEnabled, onEmojiPress, recentE
sections={sections}
contentContainerStyle={{paddingBottom: 50}}
windowSize={100}
testID='emoji_picker.emoji_sections.section_list'
/>
<EmojiSectionBar
currentIndex={sectionIndex}

View file

@ -113,7 +113,7 @@ const Avatar = ({author, enablePostIconOverride, isAutoReponse, location, post}:
size={ViewConstant.PROFILE_PICTURE_SIZE}
iconSize={24}
showStatus={!isAutoReponse || author.isBot}
testID='post_profile_picture.profile_picture'
testID={`post_profile_picture.${author.id}.profile_picture`}
/>
);

View file

@ -101,7 +101,7 @@ const ProfilePicture = ({
return (
<View
style={containerStyle}
testID={`${testID}.${author?.id}`}
testID={testID}
>
<Image
author={author}

View file

@ -108,14 +108,19 @@ const UserItem = ({
const name = getName(user, showFullName, isCurrentUser, intl);
const customStatus = getUserCustomStatus(user);
const userItemTestId = `${testID}.${user?.id}`;
return (
<View style={[style.row, containerStyle]}>
<View
style={[style.row, containerStyle]}
testID={userItemTestId}
>
<View style={style.rowPicture}>
<ProfilePicture
author={user}
size={24}
showStatus={false}
testID={`${testID}.profile_picture`}
testID={`${userItemTestId}.profile_picture`}
/>
</View>
<View
@ -127,7 +132,7 @@ const UserItem = ({
<Text
style={style.rowFullname}
numberOfLines={1}
testID={`${testID}.name`}
testID={`${userItemTestId}.display_name`}
>
{name}
</Text>
@ -143,7 +148,7 @@ const UserItem = ({
<Text
style={style.rowUsername}
numberOfLines={1}
testID='at_mention_item.username'
testID={`${userItemTestId}.username`}
>
{` @${user!.username}`}
</Text>
@ -153,6 +158,7 @@ const UserItem = ({
<CustomStatusEmoji
customStatus={customStatus!}
style={style.icon}
testID={testID}
/>
)}
{shared && (

View file

@ -53,7 +53,7 @@ const Member = ({channelId, containerStyle, size = 72, showStatus = true, theme,
iconSize={48}
showStatus={showStatus}
statusSize={24}
testID='channel_intro.profile_picture'
testID={`channel_intro.${user.id}.profile_picture`}
/>
</TouchableWithFeedback>
);

View file

@ -111,7 +111,7 @@ const ChannelHeader = ({
leftButtons: [{
id: closeButtonId,
icon: closeButton,
testID: closeButtonId,
testID: 'close.channel_info.button',
}],
},
modal: {swipeToDismiss: false},

View file

@ -21,7 +21,7 @@ const EmojiPickerScreen = ({closeButton, componentId, onEmojiPress}: Props) => {
{
icon: closeButton,
id: 'close-add-reaction',
testID: 'close.add_reaction.button',
testID: 'close.emoji_picker.button',
},
],
rightButtons: [],
@ -50,7 +50,12 @@ const EmojiPickerScreen = ({closeButton, componentId, onEmojiPress}: Props) => {
close();
}, []);
return <EmojiPicker onEmojiPress={handleEmojiPress}/>;
return (
<EmojiPicker
onEmojiPress={handleEmojiPress}
testID='emoji_picker'
/>
);
};
export default EmojiPickerScreen;

View file

@ -45,6 +45,7 @@ type Props = {
term: string;
usersMatch: UserModel[];
usersMatchStart: UserModel[];
testID?: string;
}
const style = StyleSheet.create({
@ -77,7 +78,7 @@ const sortByUserOrChannel = <T extends Channel |UserModel>(locale: string, teamm
const FilteredList = ({
archivedChannels, close, channelsMatch, channelsMatchStart, currentTeamId,
keyboardHeight, loading, onLoading, restrictDirectMessage, showTeamName,
teamIds, teammateDisplayNameSetting, term, usersMatch, usersMatchStart,
teamIds, teammateDisplayNameSetting, term, usersMatch, usersMatchStart, testID,
}: Props) => {
const bounce = useRef<DebouncedFunc<() => void>>();
const mounted = useRef(false);
@ -211,6 +212,7 @@ const FilteredList = ({
isInfo={true}
onPress={onSwitchToChannel}
showTeamName={showTeamName}
testID='find_channels.filtered_list.channel_item'
/>
);
} else if ('username' in item) {
@ -304,6 +306,7 @@ const FilteredList = ({
renderItem={renderItem}
data={data}
showsVerticalScrollIndicator={false}
testID={`${testID}.flat_list`}
/>
</Animated.View>
);

View file

@ -83,7 +83,10 @@ const FindChannels = ({closeButtonId, componentId}: Props) => {
}, []);
return (
<View style={styles.container}>
<View
style={styles.container}
testID='find_channels.screen'
>
<SearchBar
autoCapitalize='none'
autoFocus={true}
@ -99,6 +102,7 @@ const FindChannels = ({closeButtonId, componentId}: Props) => {
selectionColor={color}
showLoading={loading}
value={term}
testID='find_channels.search_bar'
/>
{term === '' && <QuickOptions close={close}/>}
<View style={styles.listContainer}>
@ -106,6 +110,7 @@ const FindChannels = ({closeButtonId, componentId}: Props) => {
<UnfilteredList
close={close}
keyboardHeight={keyboardHeight}
testID='find_channels.unfiltered_list'
/>
}
{Boolean(term) &&
@ -115,6 +120,7 @@ const FindChannels = ({closeButtonId, componentId}: Props) => {
loading={loading}
onLoading={setLoading}
term={term}
testID='find_channels.filtered_list'
/>
}
</View>

View file

@ -76,6 +76,7 @@ const QuickOptions = ({canCreateChannels, canJoinChannels, close}: Props) => {
iconName='globe'
onPress={browseChannels}
text={intl.formatMessage({id: 'find_channels.directory', defaultMessage: 'Directory'})}
testID='find_channels.quick_options.directory.option'
/>
<View style={styles.separator}/>
</>
@ -84,6 +85,7 @@ const QuickOptions = ({canCreateChannels, canJoinChannels, close}: Props) => {
iconName='account-outline'
onPress={openDirectMessage}
text={intl.formatMessage({id: 'find_channels.open_dm', defaultMessage: 'Open a DM'})}
testID='find_channels.quick_options.open_dm.option'
/>
{canCreateChannels &&
<>
@ -92,6 +94,7 @@ const QuickOptions = ({canCreateChannels, canJoinChannels, close}: Props) => {
iconName='plus'
onPress={createNewChannel}
text={intl.formatMessage({id: 'find_channels.new_channel', defaultMessage: 'New Channel'})}
testID='find_channels.quick_options.new_channel.option'
/>
</>
}

View file

@ -21,6 +21,7 @@ type Props = {
recentChannels: ChannelModel[];
showTeamName: boolean;
unreadChannels: ChannelModel[];
testID?: string;
}
const sectionNames = {
@ -57,7 +58,7 @@ const buildSections = (unreadChannels: ChannelModel[], recentChannels: ChannelMo
return sections;
};
const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, unreadChannels}: Props) => {
const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, unreadChannels, testID}: Props) => {
const intl = useIntl();
const serverUrl = useServerUrl();
const [sections, setSections] = useState(buildSections(unreadChannels, recentChannels));
@ -79,6 +80,7 @@ const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, un
isInfo={true}
onPress={onPress}
showTeamName={showTeamName}
testID='find_channels.unfiltered_list.channel_item'
/>
);
}, [onPress, showTeamName]);
@ -102,6 +104,7 @@ const UnfilteredList = ({close, keyboardHeight, recentChannels, showTeamName, un
sections={sections}
showsVerticalScrollIndicator={false}
stickySectionHeadersEnabled={true}
testID={`${testID}.section_list`}
/>
</Animated.View>
);

View file

@ -60,9 +60,9 @@ const AccountUserInfo = ({user, showFullName, theme}: Props) => {
iconSize={28}
showStatus={true}
author={user}
testID={'account.profile_picture'}
statusStyle={styles.statusStyle}
statusSize={24}
testID={`account.${user.id}.profile_picture`}
/>
{showFullName && <Text style={styles.textFullName}>{title}</Text>}
<Text style={showFullName ? styles.textUserName : styles.textFullName}>{`${userName}`}</Text>

View file

@ -537,6 +537,7 @@ exports[`components/categories_list should render team error 1`] = `
"padding": 8,
}
}
testID="channel_list_subheader.search_field.button"
>
<Icon
name="magnify"

View file

@ -48,9 +48,9 @@ const CategoryBody = ({sortedChannels, unreadIds, unreadsOnTop, category, limit,
return (
<ChannelItem
channel={item}
testID={`category.${category.displayName.replace(/ /g, '_').toLocaleLowerCase()}.channel_list_item`}
onPress={onChannelSwitch}
key={item.id}
testID={`channel_list.category.${category.displayName.replace(/ /g, '_').toLocaleLowerCase()}.channel_item`}
/>
);
}, [onChannelSwitch]);

View file

@ -17,7 +17,7 @@ exports[`components/channel_list/categories/header should match snapshot 1`] = `
"opacity": 1,
}
}
testID="category_header.custom.collapsed.false"
testID="channel_list.category_header.custom.collapsed.false"
>
<View
style={
@ -75,7 +75,7 @@ exports[`components/channel_list/categories/header should match snapshot 1`] = `
"textTransform": "uppercase",
}
}
testID="category_header.custom.display_name"
testID="channel_list.category_header.custom.display_name"
>
Test Category
</Text>

View file

@ -97,7 +97,7 @@ const CategoryHeader = ({category, hasChannels}: Props) => {
return (
<TouchableOpacity
onPress={toggleCollapse}
testID={`category_header.${category.type}.collapsed.${category.collapsed}`}
testID={`channel_list.category_header.${category.type}.collapsed.${category.collapsed}`}
>
<View style={[styles.container, category.muted ? styles.muted : null]}>
<AnimatedCompassIcon
@ -107,7 +107,7 @@ const CategoryHeader = ({category, hasChannels}: Props) => {
/>
<Text
style={styles.heading}
testID={`category_header.${category.type}.display_name`}
testID={`channel_list.category_header.${category.type}.display_name`}
>
{displayName}
</Text>

View file

@ -51,6 +51,7 @@ const UnreadCategories = ({onChannelSwitch, onlyUnreads, unreadChannels, unreadT
<ChannelItem
channel={item}
onPress={onChannelSwitch}
testID='channel_list.category.unreads.channel_item'
/>
);
}, [onChannelSwitch]);

View file

@ -23,6 +23,7 @@ exports[`Search Field should match snapshot 1`] = `
"padding": 8,
}
}
testID="channel_list_subheader.search_field.button"
>
<Icon
name="magnify"

View file

@ -54,6 +54,7 @@ const SearchField = () => {
style={styles.container}
onPress={onPress}
underlayColor={changeOpacity(theme.sidebarText, 0.32)}
testID='channel_list_subheader.search_field.button'
>
<>
<CompassIcon

View file

@ -81,6 +81,7 @@ export const loginAnimationOptions = () => {
export const bottomSheetModalOptions = (theme: Theme, closeButtonId?: string) => {
if (closeButtonId) {
const closeButton = CompassIcon.getImageSourceSync('close', 24, theme.centerChannelColor);
const closeButtonTestId = `${closeButtonId.replace('close-', 'close.').replace(/-/g, '_')}.button`;
return {
modalPresentationStyle: OptionsModalPresentationStyle.formSheet,
modal: {
@ -90,7 +91,7 @@ export const bottomSheetModalOptions = (theme: Theme, closeButtonId?: string) =>
leftButtons: [{
id: closeButtonId,
icon: closeButton,
testID: closeButtonId,
testID: closeButtonTestId,
}],
leftButtonColor: changeOpacity(theme.centerChannelColor, 0.56),
background: {
@ -724,7 +725,7 @@ export async function findChannels(title: string, theme: Theme) {
leftButtons: [{
id: closeButtonId,
icon: closeButton,
testID: closeButtonId,
testID: 'close.find_channels.button',
}],
};

View file

@ -37,7 +37,7 @@ const PickReaction = ({openEmojiPicker, width, height}: PickReactionProps) => {
style={[styles.container, {
width, height,
}]}
testID='post_options.reaction_bar.pick_reaction'
testID='post_options.reaction_bar.pick_reaction.button'
>
<CompassIcon
onPress={openEmojiPicker}

View file

@ -34,6 +34,7 @@ const EmojiAliases = ({emoji}: Props) => {
ellipsizeMode='tail'
numberOfLines={1}
style={style.title}
testID={`emoji_aliases.${emoji}`}
>
{aliases}
</Text>

View file

@ -66,6 +66,7 @@ const ReactorsList = ({location, reactions}: Props) => {
scrollEnabled={enabled}
scrollEventThrottle={60}
{...panResponder.panHandlers}
testID='reactors_list.flat_list'
/>
);
};

View file

@ -45,6 +45,7 @@ const Reactor = ({channelId, location, user}: Props) => {
<UserItem
containerStyle={style.container}
user={user}
testID='reactors_list.user_item'
/>
</TouchableOpacity>
);

View file

@ -235,6 +235,7 @@ function unsupportedServerAlert(intl: IntlShape) {
function buildServerModalOptions(theme: Theme, closeButtonId: string) {
const closeButton = CompassIcon.getImageSourceSync('close', 24, changeOpacity(theme.centerChannelColor, 0.56));
const closeButtonTestId = `${closeButtonId.replace('close-', 'close.').replace(/-/g, '_')}.button`;
return {
layout: {
backgroundColor: theme.centerChannelBg,
@ -251,7 +252,7 @@ function buildServerModalOptions(theme: Theme, closeButtonId: string) {
leftButtons: [{
id: closeButtonId,
icon: closeButton,
testID: closeButtonId,
testID: closeButtonTestId,
}],
leftButtonColor: undefined,
title: {color: theme.sidebarHeaderTextColor},

View file

@ -12,6 +12,7 @@ import Post from './post';
import PostDraft from './post_draft';
import PostList from './post_list';
import ProfilePicture from './profile_picture';
import SearchBar from './search_bar';
import SendButton from './send_button';
import TeamSidebar from './team_sidebar';
@ -27,6 +28,7 @@ export {
PostDraft,
PostList,
ProfilePicture,
SearchBar,
SendButton,
TeamSidebar,
};

View file

@ -5,7 +5,7 @@ import ProfilePicture from './profile_picture';
class Post {
testID = {
postProfilePicturePrefix: 'post_profile_picture.profile_picture.',
postProfilePicturePrefix: 'post_profile_picture.',
blockQuote: 'markdown_block_quote',
editedIndicator: 'edited_indicator',
emoji: 'markdown_emoji',

View file

@ -7,7 +7,7 @@ class ProfilePicture {
};
getProfilePictureItemMatcher = (profilePictureSourcePrefix: string, userId: string) => {
const profilePictureTestID = `${profilePictureSourcePrefix}${userId}`;
const profilePictureTestID = `${profilePictureSourcePrefix}${userId}.profile_picture`;
return by.id(profilePictureTestID);
};

View file

@ -0,0 +1,35 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {isAndroid} from '@support/utils';
class SearchBar {
testID = {
searchBarSuffix: 'search_bar',
searchInputSuffix: 'search_bar.search.input',
cancelButtonSuffix: 'search_bar.search.cancel.button',
clearButtonSuffix: 'search_bar.search.clear.button',
};
getSearchBar = (screenPrefix: string) => {
return element(by.id(`${screenPrefix}${this.testID.searchBarSuffix}`));
};
getSearchInput = (screenPrefix: string) => {
return element(by.id(`${screenPrefix}${this.testID.searchInputSuffix}`)).atIndex(0);
};
getCancelButton = (screenPrefix: string) => {
if (isAndroid()) {
return element(by.id(`${screenPrefix}${this.testID.cancelButtonSuffix}`)).atIndex(0);
}
return element(by.text('Cancel').withAncestor(by.id(`${screenPrefix}${this.testID.searchBarSuffix}`))).atIndex(0);
};
getClearButton = (screenPrefix: string) => {
return element(by.id(`${screenPrefix}${this.testID.clearButtonSuffix}`)).atIndex(0);
};
}
const searchBar = new SearchBar();
export default searchBar;

View file

@ -87,7 +87,7 @@ class ChannelScreen {
open = async (categoryKey: string, channelName: string) => {
// # Open channel screen
await ChannelListScreen.getChannelListItemDisplayName(categoryKey, channelName).tap();
await ChannelListScreen.getChannelItemDisplayName(categoryKey, channelName).tap();
return this.toBeVisible();
};

View file

@ -16,6 +16,7 @@ class ChannelListScreen {
headerServerDisplayName: 'channel_list_header.server_display_name',
headerChevronButton: 'channel_list_header.chevron.button',
headerPlusButton: 'channel_list_header.plus.button',
subheaderSearchFieldButton: 'channel_list_subheader.search_field.button',
findChannelsInput: 'channel_list.search_field.find_channels.input',
threadsButton: 'channel_list.threads.button',
};
@ -26,6 +27,7 @@ class ChannelListScreen {
headerServerDisplayName = element(by.id(this.testID.headerServerDisplayName));
headerChevronButton = element(by.id(this.testID.headerChevronButton));
headerPlusButton = element(by.id(this.testID.headerPlusButton));
subheaderSearchFieldButton = element(by.id(this.testID.subheaderSearchFieldButton));
findChannelsInput = element(by.id(this.testID.findChannelsInput));
threadsButton = element(by.id(this.testID.threadsButton));
@ -36,19 +38,23 @@ class ChannelListScreen {
openDirectMessageItem = PlusMenu.openDirectMessageItem;
getCategoryCollapsed = (categoryKey: string) => {
return element(by.id(`category_header.${categoryKey}.collapsed.true`));
return element(by.id(`channel_list.category_header.${categoryKey}.collapsed.true`));
};
getCategoryExpanded = (categoryKey: string) => {
return element(by.id(`category_header.${categoryKey}.collapsed.false`));
return element(by.id(`channel_list.category_header.${categoryKey}.collapsed.false`));
};
getCategoryHeaderDisplayName = (categoryKey: string) => {
return element(by.id(`category_header.${categoryKey}.display_name`));
return element(by.id(`channel_list.category_header.${categoryKey}.display_name`));
};
getChannelListItemDisplayName = (categoryKey: string, channelName: string) => {
return element(by.id(`category.${categoryKey}.channel_list_item.${channelName}.display_name`));
getChannelItem = (categoryKey: string, channelName: string) => {
return element(by.id(`channel_list.category.${categoryKey}.channel_item.${channelName}`));
};
getChannelItemDisplayName = (categoryKey: string, channelName: string) => {
return element(by.id(`channel_list.category.${categoryKey}.channel_item.${channelName}.display_name`));
};
getTeamItemSelected = (teamId: string) => {

View file

@ -2,11 +2,12 @@
// See LICENSE.txt for license information.
import {timeouts} from '@support/utils';
import {expect} from 'detox';
class EditServerScreen {
testID = {
editServerScreen: 'edit_server.screen',
closeButton: 'close-server-edit',
closeButton: 'close.server_edit.button',
headerTitle: 'edit_server_header.title',
headerDescription: 'edit_server_header.description',
serverDisplayNameInput: 'edit_server_form.server_display_name.input',
@ -32,6 +33,11 @@ class EditServerScreen {
return this.editServerScreen;
};
close = async () => {
await this.closeButton.tap();
await expect(this.editServerScreen).not.toBeVisible();
};
}
const editServerScreen = new EditServerScreen();

View file

@ -0,0 +1,44 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {SearchBar} from '@support/ui/component';
import {PostOptionsScreen} from '@support/ui/screen';
import {expect} from 'detox';
class EmojiPickerScreen {
testID = {
emojiPickerScreen: 'emoji_picker.screen',
emojiPickerPrefix: 'emoji_picker.',
closeButton: 'close.emoji_picker.button',
};
emojiPickerScreen = element(by.id(this.testID.emojiPickerScreen));
closeButton = element(by.id(this.testID.closeButton));
// convenience props
searchBar = SearchBar.getSearchBar(this.testID.emojiPickerPrefix);
searchInput = SearchBar.getSearchInput(this.testID.emojiPickerPrefix);
cancelButton = SearchBar.getCancelButton(this.testID.emojiPickerPrefix);
clearButton = SearchBar.getClearButton(this.testID.emojiPickerPrefix);
toBeVisible = async () => {
await expect(this.emojiPickerScreen).toBeVisible();
return this.emojiPickerScreen;
};
open = async () => {
// # Open add reaction screen
await PostOptionsScreen.pickReactionButton.tap();
return this.toBeVisible();
};
close = async () => {
await this.closeButton.tap();
await expect(this.emojiPickerScreen).not.toBeVisible();
};
}
const emojiPickerScreen = new EmojiPickerScreen();
export default emojiPickerScreen;

View file

@ -0,0 +1,71 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {SearchBar} from '@support/ui/component';
import {ChannelListScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
import {expect} from 'detox';
class FindChannelsScreen {
testID = {
findChannelsScreen: 'find_channels.screen',
findChannelsPrefix: 'find_channels.',
closeButton: 'close.find_channels.button',
directoryQuickOption: 'find_channels.quick_options.directory.option',
openDirectMessageQuickOption: 'find_channels.quick_options.open_dm.option',
newChannelQuickOption: 'find_channels.quick_options.new_channel.option',
sectionUnfilteredChannelList: 'find_channels.unfiltered_list.section_list',
flatFilteredChannelList: 'find_channels.filtered_list.flat_list',
};
findChannelsScreen = element(by.id(this.testID.findChannelsScreen));
closeButton = element(by.id(this.testID.closeButton));
directoryQuickOption = element(by.id(this.testID.directoryQuickOption));
openDirectMessageQuickOption = element(by.id(this.testID.openDirectMessageQuickOption));
newChannelQuickOption = element(by.id(this.testID.newChannelQuickOption));
sectionUnfilteredChannelList = element(by.id(this.testID.sectionUnfilteredChannelList));
flatFilteredChannelList = element(by.id(this.testID.flatFilteredChannelList));
// convenience props
searchBar = SearchBar.getSearchBar(this.testID.findChannelsPrefix);
searchInput = SearchBar.getSearchInput(this.testID.findChannelsPrefix);
cancelButton = SearchBar.getCancelButton(this.testID.findChannelsPrefix);
clearButton = SearchBar.getClearButton(this.testID.findChannelsPrefix);
getUnfilteredChannelItem = (channelName: string) => {
return element(by.id(`find_channels.unfiltered_list.channel_item.${channelName}`));
};
getUnfilteredChannelItemDisplayName = (channelName: string) => {
return element(by.id(`find_channels.unfiltered_list.channel_item.${channelName}.display_name`));
};
getFilteredChannelItem = (channelName: string) => {
return element(by.id(`find_channels.filtered_list.channel_item.${channelName}`));
};
getFilteredChannelItemDisplayName = (channelName: string) => {
return element(by.id(`find_channels.filtered_list.channel_item.${channelName}.display_name`));
};
toBeVisible = async () => {
await waitFor(this.findChannelsScreen).toExist().withTimeout(timeouts.TEN_SEC);
return this.findChannelsScreen;
};
open = async () => {
// # Open find channels screen
await ChannelListScreen.subheaderSearchFieldButton.tap();
return this.toBeVisible();
};
close = async () => {
await this.closeButton.tap();
await expect(this.findChannelsScreen).not.toBeVisible();
};
}
const findChannelsScreen = new FindChannelsScreen();
export default findChannelsScreen;

View file

@ -10,11 +10,14 @@ import CreateDirectMessageScreen from './create_direct_message';
import CreateOrEditChannelScreen from './create_or_edit_channel';
import EditPostScreen from './edit_post';
import EditServerScreen from './edit_server';
import EmojiPickerScreen from './emoji_picker';
import FindChannelsScreen from './find_channels';
import GlobalThreadsScreen from './global_threads';
import HomeScreen from './home';
import LoginScreen from './login';
import PermalinkScreen from './permalink';
import PostOptionsScreen from './post_options';
import ReactionsScreen from './reactions';
import ServerScreen from './server';
import ServerListScreen from './server_list';
import ThreadScreen from './thread';
@ -30,11 +33,14 @@ export {
CreateOrEditChannelScreen,
EditPostScreen,
EditServerScreen,
EmojiPickerScreen,
FindChannelsScreen,
GlobalThreadsScreen,
HomeScreen,
LoginScreen,
PermalinkScreen,
PostOptionsScreen,
ReactionsScreen,
ServerScreen,
ServerListScreen,
ThreadScreen,

View file

@ -10,7 +10,7 @@ class PostOptionsScreen {
reactionEmojiPrefix: 'post_options.reaction_bar.reaction.',
postOptionsScreen: 'post_options.screen',
postOptionsBackdrop: 'post_options.backdrop',
pickReaction: 'post_options.reaction_bar.pick_reaction',
pickReactionButton: 'post_options.reaction_bar.pick_reaction.button',
replyPostOption: 'post_options.reply_post.option',
followThreadOption: 'post_options.follow_thread.option',
followingThreadOption: 'post_options.following_thread.option',
@ -27,7 +27,7 @@ class PostOptionsScreen {
postOptionsScreen = element(by.id(this.testID.postOptionsScreen));
postOptionsBackdrop = element(by.id(this.testID.postOptionsBackdrop));
pickReaction = element(by.id(this.testID.pickReaction));
pickReactionButton = element(by.id(this.testID.pickReactionButton));
replyPostOption = element(by.id(this.testID.replyPostOption));
followThreadOption = element(by.id(this.testID.followThreadOption));
followingThreadOption = element(by.id(this.testID.followingThreadOption));

View file

@ -0,0 +1,48 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect} from 'detox';
class ReactionsScreen {
testID = {
reactorItemPrefix: 'reactors_list.user_item.',
reactionsScreen: 'reactions.screen',
reactionsBackdrop: 'reactions.backdrop',
flatReactorsList: 'reactors_list.flat_list',
};
reactionsScreen = element(by.id(this.testID.reactionsScreen));
reactionsBackdrop = element(by.id(this.testID.reactionsBackdrop));
flatReactorsList = element(by.id(this.testID.flatReactorsList));
toBeVisible = async () => {
await expect(this.reactionsScreen).toExist();
return reactionsScreen;
};
close = async () => {
await this.reactionsBackdrop.tap({x: 5, y: 10});
await expect(this.reactionsScreen).not.toBeVisible();
};
getReactorItem = (userId: string, emojiName: string) => {
const reactorItemEmojiAliasesMatcher = by.id(`emoji_aliases.${emojiName}`);
const reactorItemUserTestId = `${this.testID.reactorItemPrefix}${userId}`;
const reactorItemUserMatcher = by.id(reactorItemUserTestId);
const reactorItemUserProfilePictureMatcher = by.id(`${reactorItemUserTestId}.profile_picture`);
const reactorItemUserDisplayNameMatcher = by.id(`${reactorItemUserTestId}.display_name`);
const reactorItemUserUsernameMatcher = by.id(`${reactorItemUserTestId}.username`);
return {
reactorItemEmojiAliases: element(reactorItemEmojiAliasesMatcher),
reactorItemUser: element(reactorItemUserMatcher),
reactorItemUserProfilePicture: element(reactorItemUserProfilePictureMatcher),
reactorItemUserDisplayName: element(reactorItemUserDisplayNameMatcher),
reactorItemUserUsername: element(reactorItemUserUsernameMatcher),
};
};
}
const reactionsScreen = new ReactionsScreen();
export default reactionsScreen;

View file

@ -7,7 +7,7 @@ import {expect} from 'detox';
class ServerScreen {
testID = {
serverScreen: 'server.screen',
closeButton: 'close-server',
closeButton: 'close.server.button',
headerTitleAddServer: 'server_header.title.add_server',
headerTitleConnectToServer: 'server_header.title.connect_to_server',
headerWelcome: 'server_header.welcome',

View file

@ -67,7 +67,7 @@ describe('Channels - Browse Channels', () => {
const {channel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
// * Verify new channel does not appear on channel list screen
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, channel.display_name)).not.toExist();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, channel.display_name)).not.toExist();
// # Open browse channels screen and search for the new channel name to join
await BrowseChannelsScreen.open();
@ -89,6 +89,20 @@ describe('Channels - Browse Channels', () => {
await ChannelListScreen.toBeVisible();
// * Verify newly joined channel is added to channel list
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, channel.name)).toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, channel.name)).toBeVisible();
});
it('MM-T4729_3 - should display empty search state for browse channels', async () => {
// # Open browse channels screen and search for a non-existent channel
const searchTerm = 'blahblahblahblah';
await BrowseChannelsScreen.open();
await BrowseChannelsScreen.searchInput.replaceText(searchTerm);
// * Verify empty search state for browse channels
await expect(element(by.text(`No results for “${searchTerm}`))).toBeVisible();
await expect(element(by.text('Check the spelling or try another search.'))).toBeVisible();
// # Go back to channel list screen
await BrowseChannelsScreen.close();
});
});

View file

@ -22,6 +22,7 @@ import {
ChannelListScreen,
CreateDirectMessageScreen,
CreateOrEditChannelScreen,
FindChannelsScreen,
GlobalThreadsScreen,
HomeScreen,
LoginScreen,
@ -75,15 +76,15 @@ describe('Channels - Channel List', () => {
await expect(ChannelListScreen.headerPlusButton).toBeVisible();
await expect(ChannelListScreen.threadsButton).toBeVisible();
await expect(ChannelListScreen.getCategoryHeaderDisplayName(channelsCategory)).toHaveText('CHANNELS');
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, testChannel.name)).toHaveText(testChannel.display_name);
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, offTopicChannelName)).toHaveText('Off-Topic');
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, townSquareChannelName)).toHaveText('Town Square');
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name)).toHaveText(testChannel.display_name);
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName)).toHaveText('Off-Topic');
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName)).toHaveText('Town Square');
await expect(ChannelListScreen.getCategoryHeaderDisplayName(directMessagesCategory)).toHaveText('DIRECT MESSAGES');
});
it('MM-T4728_2 - should be able to switch between channels', async () => {
// # Tap on a first channel
await ChannelListScreen.getChannelListItemDisplayName(channelsCategory, testChannel.name).tap();
await ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name).tap();
// * Verify on first channel
await ChannelScreen.toBeVisible();
@ -93,7 +94,7 @@ describe('Channels - Channel List', () => {
// # Go back to channel list screen and tap on a second channel
await ChannelScreen.back();
await ChannelListScreen.toBeVisible();
await ChannelListScreen.getChannelListItemDisplayName(channelsCategory, offTopicChannelName).tap();
await ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName).tap();
// * Verify on second channel
await ChannelScreen.toBeVisible();
@ -117,18 +118,18 @@ describe('Channels - Channel List', () => {
// * Verify category is collapsed and channels are not listed
await expect(ChannelListScreen.getCategoryCollapsed(channelsCategory)).toBeVisible();
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, testChannel.name)).not.toBeVisible();
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, offTopicChannelName)).not.toBeVisible();
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, townSquareChannelName)).not.toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name)).not.toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName)).not.toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName)).not.toBeVisible();
// # Toggle channels category to expand
await ChannelListScreen.getCategoryCollapsed(channelsCategory).tap();
// * Verify category is expanded and all channels are listed
await expect(ChannelListScreen.getCategoryExpanded(channelsCategory)).toBeVisible();
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, testChannel.name)).toBeVisible();
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, offTopicChannelName)).toBeVisible();
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, townSquareChannelName)).toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, testChannel.name)).toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, offTopicChannelName)).toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(channelsCategory, townSquareChannelName)).toBeVisible();
});
it('MM-T4728_4 - should be able to go to browse channels screen', async () => {
@ -179,8 +180,15 @@ describe('Channels - Channel List', () => {
await GlobalThreadsScreen.back();
});
xit('MM-T4728_8 - should be able to find channels', async () => {
// NOT YET IMPLEMENTED
it('MM-T4728_8 - should be able to go to find channels screen', async () => {
// # Tap on search field button
await ChannelListScreen.subheaderSearchFieldButton.tap();
// * Verify on find channels screen
await FindChannelsScreen.toBeVisible();
// # Go back to channel list screen
await FindChannelsScreen.close();
});
it('MM-T4728_9 - should be able to switch between teams', async () => {

View file

@ -75,7 +75,7 @@ describe('Channels - Create Direct Message', () => {
// * Verify no direct message channel for the new user appears on channel list screen
const newUserDisplayName = newUser.username;
await expect(ChannelListScreen.getChannelListItemDisplayName(directMessagesCategory, newUserDisplayName)).not.toBeVisible();
await expect(ChannelListScreen.getChannelItemDisplayName(directMessagesCategory, newUserDisplayName)).not.toBeVisible();
// # Open create direct message screen and search for the new user
await CreateDirectMessageScreen.open();
@ -105,7 +105,7 @@ describe('Channels - Create Direct Message', () => {
// * Verify direct message channel for the new user is added to direct message list
const {channel: directMessageChannel} = await Channel.apiCreateDirectChannel(siteOneUrl, [testUser.id, newUser.id]);
await expect(ChannelListScreen.getChannelListItemDisplayName(directMessagesCategory, directMessageChannel.name)).toHaveText(newUserDisplayName);
await expect(ChannelListScreen.getChannelItemDisplayName(directMessagesCategory, directMessageChannel.name)).toHaveText(newUserDisplayName);
});
it('MM-T4730_3 - should be able to create a group message', async () => {
@ -152,4 +152,18 @@ describe('Channels - Create Direct Message', () => {
// * Verify group message channel for the other two new users is added to direct message list
await expect(element(by.text(groupDisplayName))).toBeVisible();
});
it('MM-T4730_4 - should display empty search state for create direct message', async () => {
// # Open create direct message screen and search for a non-existent user
const searchTerm = 'blahblahblahblah';
await CreateDirectMessageScreen.open();
await CreateDirectMessageScreen.searchInput.replaceText(searchTerm);
// * Verify empty search state for create direct message
await expect(element(by.text(`No results for “${searchTerm}`))).toBeVisible();
await expect(element(by.text('Check the spelling or try another search.'))).toBeVisible();
// # Go back to channel list screen
await CreateDirectMessageScreen.close();
});
});

View file

@ -0,0 +1,181 @@
// 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 {
Post,
Setup,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
EmojiPickerScreen,
HomeScreen,
LoginScreen,
PostOptionsScreen,
ReactionsScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Emojis and Reactions', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testUser: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testUser = user;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4862_1 - should be able to view recent reactions and add new reaction via post options', async () => {
// # Open a channel screen, post a message, and open post options for message
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.openPostOptionsFor(post.id, message);
// * Verify six default reactions are displayed
await expect(PostOptionsScreen.getReactionEmoji('+1')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('smiley')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('white_check_mark')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('heart')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('eyes')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('raised_hands')).toBeVisible();
// # Open emoji picker screen and add a new reaction
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText('clown_face');
await element(by.text('🤡')).tap();
// * Verify new reaction is added to the message
await expect(element(by.text('🤡').withAncestor(by.id(`channel.post_list.post.${post.id}`)))).toBeVisible();
// # Open post options for message
await ChannelScreen.openPostOptionsFor(post.id, message);
// * Verify recent reactions are displayed, newest reaction first and then the first five default reactions
await expect(PostOptionsScreen.getReactionEmoji('clown_face')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('+1')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('smiley')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('white_check_mark')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('heart')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('eyes')).toBeVisible();
await expect(PostOptionsScreen.getReactionEmoji('raised_hands')).not.toBeVisible();
// # Go back to channel list screen
await PostOptionsScreen.close();
await ChannelScreen.back();
});
it('MM-T4862_2 - should be able to long press on a reaction to view the list of users who reacted', async () => {
// # Open a channel screen, post a message, open post options for message, open emoji picker screen, and add a reaction
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.openPostOptionsFor(post.id, message);
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText('fire');
await element(by.text('🔥')).tap();
// * Verify reaction is added to the message
const reaction = element(by.text('🔥').withAncestor(by.id(`channel.post_list.post.${post.id}`)));
await expect(reaction).toBeVisible();
// # Long press on the reaction
await reaction.longPress();
// * Verify user who reacted with the emoji
await ReactionsScreen.toBeVisible();
const {reactorItemEmojiAliases, reactorItemUserProfilePicture, reactorItemUserDisplayName, reactorItemUserUsername} = ReactionsScreen.getReactorItem(testUser.id, 'fire');
await expect(reactorItemEmojiAliases).toHaveText(':fire:');
await expect(reactorItemUserProfilePicture).toBeVisible();
await expect(reactorItemUserDisplayName).toHaveText(`${testUser.first_name} ${testUser.last_name}`);
await expect(reactorItemUserUsername).toHaveText(` @${testUser.username}`);
// # Go back to channel list screen
await ReactionsScreen.close();
await ChannelScreen.back();
});
it('MM-T4862_3 - should be able to include emojis in a message and be able to find them in emoji bar and recently used section', async () => {
// # Open a channel screen and post a message that includes emojis
const message = 'The quick brown fox :fox_face: jumps over the lazy dog :dog:';
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
// * Verify message is posted with emojis
const resolvedMessage = 'The quick brown fox 🦊 jumps over the lazy dog 🐶';
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, resolvedMessage);
await expect(postListPostItem).toBeVisible();
// # Open post options for message
await ChannelScreen.openPostOptionsFor(post.id, resolvedMessage);
await PostOptionsScreen.toBeVisible();
// * Verify emojis exist in emoji bar
await expect(element(by.text('🦊'))).toExist();
await expect(element(by.text('🐶'))).toExist();
// # Open emoji picker screen
await EmojiPickerScreen.open();
// * Verify emojis exist in recently used section
await expect(element(by.text('Recently Used'))).toExist();
await expect(element(by.text('🦊'))).toExist();
await expect(element(by.text('🐶'))).toExist();
// # Go back to channel list screen
await EmojiPickerScreen.close();
await ChannelScreen.back();
});
it('MM-T4862_4 - should display empty search state for emoji picker', async () => {
// # Open a channel screen, post a message, open post options for message, open emoji picker screen, and search for a non-existent emoji
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const searchTerm = 'blahblahblahblah';
await ChannelScreen.openPostOptionsFor(post.id, message);
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText(searchTerm);
// * Verify empty search state for emoji picker
await expect(element(by.text(`No results for “${searchTerm}`))).toBeVisible();
await expect(element(by.text('Check the spelling or try another search.'))).toBeVisible();
// # Go back to channel list screen
await EmojiPickerScreen.close();
await ChannelScreen.back();
});
});

View file

@ -0,0 +1,105 @@
// 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 {
Post,
Setup,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
PostOptionsScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Follow and Unfollow Message', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4863_1 - should be able to follow/unfollow a message via post options', async () => {
// # Open a channel screen and post a message
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
// * Verify message is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItem).toBeVisible();
// # Open post options for message and tap on follow message option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.followThreadOption.tap();
// * Verify message is followed by user via post footer
const {postListPostItemFooterFollowingButton} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemFooterFollowingButton).toBeVisible();
// # Open post options for message and tap on unfollow message option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.followingThreadOption.tap();
// * Verify message is not followed by user via post footer
await expect(postListPostItemFooterFollowingButton).not.toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4863_2 - should be able to unfollow a message via post footer', async () => {
// # Open a channel screen, post a message, open post options for message, and tap on follow message option
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.followThreadOption.tap();
// * Verify message is followed by user via post footer
const {postListPostItemFooterFollowingButton} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemFooterFollowingButton).toBeVisible();
// # Tap on following button via post footer
await postListPostItemFooterFollowingButton.tap();
// * Verify message is not followed by user via post footer
await expect(postListPostItemFooterFollowingButton).not.toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View file

@ -85,7 +85,7 @@ describe('Messaging - Message Reply', () => {
await ChannelScreen.back();
});
it('MM-T4785_2 - should be able to open reply thread by tapping the post', async () => {
it('MM-T4785_2 - should be able to open reply thread by tapping on the post', async () => {
// # Open a channel screen and post a message
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
@ -96,7 +96,7 @@ describe('Messaging - Message Reply', () => {
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItem).toExist();
// # Tap the post
// # Tap on post to open thread
await postListPostItem.tap();
// * Verify on reply thread screen

View file

@ -0,0 +1,111 @@
// 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 {
Post,
Setup,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
PostOptionsScreen,
ServerScreen,
ThreadScreen,
} from '@support/ui/screen';
import {getRandomId} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Pin and Unpin Message', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
const pinnedText = 'Pinned';
let testChannel: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4865_1 - should be able to pin/unpin a message via post options on channel screen', async () => {
// # Open a channel screen and post a message
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
// * Verify message is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItem).toBeVisible();
// # Open post options for message and tap on pin to channel option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.pinPostOption.tap();
// * Verify pinned text is displayed on the post pre-header
const {postListPostItemPreHeaderText} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
// # Open post options for message and tap on unpin from channel option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.unpinPostOption.tap();
// * Verify pinned text is not displayed on the post pre-header
await expect(postListPostItemPreHeaderText).not.toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4865_2 - should be able to pin/unpin a message via post options on thread screen', async () => {
// # Open a channel screen, post a message, tap on post to open thread, open post options for message, and tap on pin to channel option
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
await postListPostItem.tap();
await ThreadScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.pinPostOption.tap();
// * Verify pinned text is displayed on the post pre-header
const {postListPostItemPreHeaderText} = ThreadScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemPreHeaderText).toHaveText(pinnedText);
// # Open post options for message and tap on unpin from channel option
await ThreadScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.unpinPostOption.tap();
// * Verify pinned text is not displayed on the post pre-header
await expect(postListPostItemPreHeaderText).not.toBeVisible();
// # Go back to channel list screen
await ThreadScreen.back();
await ChannelScreen.back();
});
});

View file

@ -0,0 +1,111 @@
// 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 {
Post,
Setup,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
PostOptionsScreen,
ServerScreen,
ThreadScreen,
} from '@support/ui/screen';
import {getRandomId} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Save and Unsave Message', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
const savedText = 'Saved';
let testChannel: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4864_1 - should be able to save/unsave a message via post options on channel screen', async () => {
// # Open a channel screen and post a message
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
// * Verify message is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItem).toBeVisible();
// # Open post options for message and tap on save option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.savePostOption.tap();
// * Verify saved text is displayed on the post pre-header
const {postListPostItemPreHeaderText} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemPreHeaderText).toHaveText(savedText);
// # Open post options for message and tap on unsave option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.unsavePostOption.tap();
// * Verify saved text is not displayed on the post pre-header
await expect(postListPostItemPreHeaderText).not.toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4864_2 - should be able to save/unsave a message via post options on thread screen', async () => {
// # Open a channel screen, post a message, tap on post to open thread, open post options for message, and tap on save option
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
await postListPostItem.tap();
await ThreadScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.savePostOption.tap();
// * Verify saved text is displayed on the post pre-header
const {postListPostItemPreHeaderText} = ThreadScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemPreHeaderText).toHaveText(savedText);
// # Open post options for message and tap on unsave option
await ThreadScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.unsavePostOption.tap();
// * Verify saved text is not displayed on the post pre-header
await expect(postListPostItemPreHeaderText).not.toBeVisible();
// # Go back to channel list screen
await ThreadScreen.back();
await ChannelScreen.back();
});
});

View file

@ -85,6 +85,7 @@ describe('Server Login - Connect to Server', () => {
await expect(serverUrlInputError).toHaveText(connectionError);
// # Connect with invalid host and valid server display name
await device.reloadReactNative();
await serverUrlInput.replaceText('wrong.host.badssl.com');
await serverDisplayNameInput.replaceText('Server 1');
await connectButton.tap();

View file

@ -91,6 +91,7 @@ describe('Server Login - Server List', () => {
// * Verify on channel list screen of the second server
await ChannelListScreen.toBeVisible();
await device.reloadReactNative();
await expect(ChannelListScreen.headerServerDisplayName).toHaveText(serverTwoDisplayName);
// # Open server list screen

View file

@ -19,6 +19,7 @@ import {
ChannelScreen,
ChannelListScreen,
EditPostScreen,
EmojiPickerScreen,
HomeScreen,
LoginScreen,
PostOptionsScreen,
@ -31,6 +32,8 @@ import {expect} from 'detox';
describe('Smoke Test - Messaging', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
const savedText = 'Saved';
const pinnedText = 'Pinned';
let testChannel: any;
beforeAll(async () => {
@ -116,4 +119,83 @@ describe('Smoke Test - Messaging', () => {
await ThreadScreen.back();
await ChannelScreen.back();
});
it('MM-T4786_3 - should be able to include emojis in a message and add reaction to a message', async () => {
// # Open a channel screen and post a message that includes emojis
const message = 'The quick brown fox :fox_face: jumps over the lazy dog :dog:';
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
// * Verify message is posted with emojis
const resolvedMessage = 'The quick brown fox 🦊 jumps over the lazy dog 🐶';
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, resolvedMessage);
await expect(postListPostItem).toBeVisible();
// # Open post options for message, open emoji picker screen, and add a reaction
await ChannelScreen.openPostOptionsFor(post.id, resolvedMessage);
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText('clown_face');
await element(by.text('🤡')).tap();
// * Verify reaction is added to the message
await expect(element(by.text('🤡').withAncestor(by.id(`channel.post_list.post.${post.id}`)))).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4786_4 - should be able to follow/unfollow a message, save/unsave a message, and pin/unpin a message', async () => {
// # Open a channel screen, post a message, open post options for message, and tap on follow message option
const message = `Message ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.followThreadOption.tap();
// * Verify message is followed by user via post footer
const {postListPostItem, postListPostItemFooterFollowingButton} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemFooterFollowingButton).toBeVisible();
// # Tap on following button via post footer
await postListPostItemFooterFollowingButton.tap();
// * Verify message is not followed by user via post footer
await expect(postListPostItemFooterFollowingButton).not.toBeVisible();
// # Open post options for message and tap on save option
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.savePostOption.tap();
// * Verify saved text is displayed on the post pre-header
const {postListPostItemPreHeaderText: channelPostListPostItemPreHeaderText} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(channelPostListPostItemPreHeaderText).toHaveText(savedText);
// # Tap on post to open thread and tap on thread overview unsave button
await postListPostItem.tap();
await ThreadScreen.getThreadOverviewUnsaveButton().tap();
// * Verify saved text is not displayed on the post pre-header
await expect(channelPostListPostItemPreHeaderText).not.toBeVisible();
// # Open post options for message and tap on pin to channel option
await ThreadScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.pinPostOption.tap();
// * Verify pinned text is displayed on the post pre-header
const {postListPostItemPreHeaderText: threadPostListPostItemPreHeaderText} = ThreadScreen.getPostListPostItem(post.id, message);
await expect(threadPostListPostItemPreHeaderText).toHaveText(pinnedText);
// # Go back to channel, open post options for message, and tap on unpin from channel option
await ThreadScreen.back();
await ChannelScreen.openPostOptionsFor(post.id, message);
await PostOptionsScreen.unpinPostOption.tap();
// * Verify pinned text is not displayed on the post pre-header
await expect(channelPostListPostItemPreHeaderText).not.toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View file

@ -72,6 +72,7 @@ describe('Smoke Test - Server Login', () => {
// * Verify on channel list screen of the second server
await ChannelListScreen.toBeVisible();
await device.reloadReactNative();
await expect(ChannelListScreen.headerServerDisplayName).toHaveText(serverTwoDisplayName);
// # Go back to first server

View file

@ -83,6 +83,7 @@ describe('Smoke Test - Threads', () => {
// # Go back to channel list screen, then go to global threads screen, tap on all your threads button, open thread options for thread, tap on mark as unread option, and tap on unread threads button
await ThreadScreen.back();
await ChannelScreen.back();
await device.reloadReactNative();
await GlobalThreadsScreen.open();
await GlobalThreadsScreen.headerAllThreadsButton.tap();
await GlobalThreadsScreen.openThreadOptionsFor(parentPost.id);