Detox/E2E: Messaging (at-mentions, channel mentions, autocomplete etc) e2e tests in Gekidou (#6428)

* Detox/E2E: Messaging (at-mentions, channel mentions, autocomplete etc) e2e tests in Gekidou

* Enable other failing tests so they're visible

* Detox/E2E: Messaging markdown e2e tests in Gekidou (#6450)

* Detox/E2E: Messaging markdown e2e tests in Gekidou

* Added zephyr test ids

* Added markdown smoke test

* Enable disabled tests

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
Joseph Baylon
2022-07-05 10:01:46 -07:00
committed by GitHub
parent eefeb3b0a7
commit 6a3c600c8a
100 changed files with 3612 additions and 119 deletions

View File

@@ -308,6 +308,7 @@ const AtMention = ({
defaultMessage={item.defaultMessage}
id={item.id}
onPress={completeMention}
testID='autocomplete.special_mention_item'
/>
);
}, [completeMention]);
@@ -319,6 +320,7 @@ const AtMention = ({
name={item.name}
displayName={item.displayName}
onPress={completeMention}
testID='autocomplete.group_mention_item'
/>
);
}, [completeMention]);
@@ -326,9 +328,9 @@ const AtMention = ({
const renderAtMentions = useCallback((item: UserProfile | UserModel) => {
return (
<AtMentionItem
testID={`autocomplete.at_mention.item.${item}`}
onPress={completeMention}
user={item}
onPress={completeMention}
testID='autocomplete.at_mention_item'
/>
);
}, [completeMention]);

View File

@@ -60,12 +60,14 @@ type Props = {
name: string;
displayName: string;
onPress: (handle: string) => void;
testID?: string;
}
const GroupMentionItem = ({
onPress,
name,
displayName,
testID,
}: Props) => {
const insets = useSafeAreaInsets();
const theme = useTheme();
@@ -79,11 +81,14 @@ const GroupMentionItem = ({
onPress(name);
}, [onPress, name]);
const groupMentionItemTestId = `${testID}.${name}`;
return (
<TouchableWithFeedback
onPress={completeMention}
style={touchableStyle}
type={'opacity'}
testID={groupMentionItemTestId}
>
<View style={style.rowPicture}>
<CompassIcon
@@ -92,8 +97,18 @@ const GroupMentionItem = ({
/>
</View>
<View style={style.rowInfo}>
<Text style={style.rowDisplayName}>{`${displayName} `}</Text>
<Text style={style.rowName}>{`@${name}`}</Text>
<Text
style={style.rowDisplayName}
testID={`${groupMentionItemTestId}.display_name`}
>
{`${displayName} `}
</Text>
<Text
style={style.rowName}
testID={`${groupMentionItemTestId}.name`}
>
{`@${name}`}
</Text>
</View>
</TouchableWithFeedback>
);

View File

@@ -31,7 +31,6 @@ const AtMentionItem = ({
return (
<TouchableWithFeedback
testID={testID}
key={user.id}
onPress={completeMention}
underlayColor={changeOpacity(theme.buttonBg, 0.08)}
@@ -40,7 +39,7 @@ const AtMentionItem = ({
>
<UserItem
user={user}
testID='at_mention.user_item'
testID={testID}
/>
</TouchableWithFeedback>
);

View File

@@ -285,7 +285,7 @@ const ChannelMention = ({
<ChannelMentionItem
channel={item}
onPress={completeMention}
testID={`autocomplete.channel_mention.item.${item}`}
testID='autocomplete.channel_mention_item'
/>
);
}, [completeMention]);

View File

@@ -77,6 +77,7 @@ const ChannelMentionItem = ({
let component;
const isArchived = ('delete_at' in channel ? channel.delete_at : channel.deleteAt) > 0;
const channelMentionItemTestId = `${testID}.${channel.name}`;
if (isDMorGM(channel)) {
if (!displayName) {
@@ -88,15 +89,22 @@ const ChannelMentionItem = ({
key={channel.id}
onPress={completeMention}
style={rowStyle}
testID={testID}
testID={channelMentionItemTestId}
type={'opacity'}
>
<Text style={style.rowDisplayName}>{'@' + displayName}</Text>
<Text
style={style.rowDisplayName}
testID={`${channelMentionItemTestId}.display_name`}
>
{'@' + displayName}
</Text>
<BotTag
show={isBot}
testID={`${channelMentionItemTestId}.bot.tag`}
/>
<GuestTag
show={isGuest}
testID={`${channelMentionItemTestId}.guest.tag`}
/>
</TouchableWithFeedback>
);
@@ -107,7 +115,7 @@ const ChannelMentionItem = ({
onPress={completeMention}
style={margins}
underlayColor={changeOpacity(theme.buttonBg, 0.08)}
testID={testID}
testID={channelMentionItemTestId}
type={'native'}
>
<View style={style.row}>
@@ -120,8 +128,18 @@ const ChannelMentionItem = ({
size={18}
style={style.icon}
/>
<Text style={style.rowDisplayName}>{displayName}</Text>
<Text style={style.rowName}>{` ~${channel.name}`}</Text>
<Text
style={style.rowDisplayName}
testID={`${channelMentionItemTestId}.display_name`}
>
{displayName}
</Text>
<Text
style={style.rowName}
testID={`${channelMentionItemTestId}.name`}
>
{` ~${channel.name}`}
</Text>
</View>
</TouchableWithFeedback>
);

View File

@@ -169,6 +169,7 @@ const EmojiSuggestion = ({
const renderItem = useCallback(({item}: {item: string}) => {
const completeItemSuggestion = () => completeSuggestion(item);
const emojiSuggestionItemTestId = `autocomplete.emoji_suggestion_item.${item}`;
return (
<TouchableWithFeedback
@@ -182,9 +183,15 @@ const EmojiSuggestion = ({
emojiName={item}
textStyle={style.emojiText}
size={EMOJI_SIZE}
testID={emojiSuggestionItemTestId}
/>
</View>
<Text style={style.emojiName}>{`:${item}:`}</Text>
<Text
style={style.emojiName}
testID={`${emojiSuggestionItemTestId}.name`}
>
{`:${item}:`}
</Text>
</View>
</TouchableWithFeedback>
);
@@ -211,7 +218,6 @@ const EmojiSuggestion = ({
return (
<FlatList
testID='emoji_suggestion.list'
keyboardShouldPersistTaps='always'
style={flatListStyle}
data={data}
@@ -220,6 +226,7 @@ const EmojiSuggestion = ({
renderItem={renderItem}
nestedScrollEnabled={nestedScrollEnabled}
contentContainerStyle={containerStyle}
testID='autocomplete.emoji_suggestion.flat_list'
/>
);
};

View File

@@ -120,7 +120,7 @@ const AppSlashSuggestion = ({
<AtMentionItem
user={item.item as UserProfile | UserModel}
onPress={completeIgnoringSuggestion(item.Complete)}
testID={`autocomplete.at_mention.item.${item.item}`}
testID='autocomplete.slash_suggestion.at_mention_item'
/>
);
case COMMAND_SUGGESTION_CHANNEL:
@@ -131,7 +131,7 @@ const AppSlashSuggestion = ({
<ChannelMentionItem
channel={item.item as Channel | ChannelModel}
onPress={completeIgnoringSuggestion(item.Complete)}
testID={`autocomplete.channel_mention.item.${item.item}`}
testID='autocomplete.slash_suggestion.channel_mention_item'
/>
);
default:

View File

@@ -232,7 +232,6 @@ const SlashSuggestion = ({
return (
<FlatList
testID='slash_suggestion.list'
keyboardShouldPersistTaps='always'
style={listStyle}
data={dataSource}
@@ -240,6 +239,7 @@ const SlashSuggestion = ({
removeClippedSubviews={true}
renderItem={renderItem}
nestedScrollEnabled={nestedScrollEnabled}
testID='autocomplete.slash_suggestion.flat_list'
/>
);
};

View File

@@ -153,11 +153,14 @@ const SlashSuggestionItem = ({
}
}
const slashSuggestionItemTestId = `autocomplete.slash_suggestion_item.${suggestion}`;
return (
<TouchableWithFeedback
onPress={completeSuggestion}
style={touchableStyle}
underlayColor={changeOpacity(theme.buttonBg, 0.08)}
testID={slashSuggestionItemTestId}
type={'native'}
>
<View style={style.container}>
@@ -165,12 +168,18 @@ const SlashSuggestionItem = ({
{image}
</View>
<View style={style.suggestionContainer}>
<Text style={style.suggestionName}>{`${suggestionText}`}</Text>
<Text
style={style.suggestionName}
testID={`${slashSuggestionItemTestId}.name`}
>
{`${suggestionText}`}
</Text>
{Boolean(description) &&
<Text
ellipsizeMode='tail'
numberOfLines={1}
style={style.suggestionDescription}
testID={`${slashSuggestionItemTestId}.description`}
>
{description}
</Text>

View File

@@ -54,12 +54,14 @@ type Props = {
defaultMessage: string;
id: string;
onPress: (handle: string) => void;
testID?: string;
}
const SpecialMentionItem = ({
completeHandle,
defaultMessage,
id,
onPress,
testID,
}: Props) => {
const theme = useTheme();
const style = getStyleFromTheme(theme);
@@ -67,10 +69,13 @@ const SpecialMentionItem = ({
onPress(completeHandle);
}, [completeHandle, onPress]);
const specialMentionItemTestId = `${testID}.${id}`;
return (
<TouchableWithFeedback
onPress={completeMention}
underlayColor={changeOpacity(theme.buttonBg, 0.08)}
testID={specialMentionItemTestId}
type={'native'}
>
<View style={style.row}>
@@ -84,11 +89,17 @@ const SpecialMentionItem = ({
style={style.textWrapper}
numberOfLines={1}
>
<Text style={style.rowUsername}>{`@${completeHandle} - `}</Text>
<Text
style={style.rowUsername}
testID={`${specialMentionItemTestId}.name`}
>
{`@${completeHandle} - `}
</Text>
<FormattedText
id={id}
defaultMessage={defaultMessage}
style={style.rowFullname}
testID={`${specialMentionItemTestId}.display_name`}
/>
</Text>
</View>

View File

@@ -16,6 +16,7 @@ type Props = {
channelId: string;
channelType?: string;
inModal?: boolean;
testID?: string;
}
const OPTIONS_HEIGHT = 62;
@@ -31,7 +32,7 @@ const styles = StyleSheet.create({
},
});
const ChannelActions = ({channelId, channelType, inModal = false}: Props) => {
const ChannelActions = ({channelId, channelType, inModal = false, testID}: Props) => {
const onCopyLinkAnimationEnd = useCallback(() => {
if (!inModal) {
requestAnimationFrame(async () => {
@@ -45,17 +46,20 @@ const ChannelActions = ({channelId, channelType, inModal = false}: Props) => {
<FavoriteBox
channelId={channelId}
showSnackBar={!inModal}
testID={`${testID}.favorite.action`}
/>
<View style={styles.separator}/>
<MutedBox
channelId={channelId}
showSnackBar={!inModal}
testID={`${testID}.mute.action`}
/>
<View style={styles.separator}/>
{channelType && DIRECT_CHANNELS.includes(channelType) &&
<SetHeaderBox
channelId={channelId}
inModal={inModal}
testID={`${testID}.set_header.action`}
/>
}
{channelType && !DIRECT_CHANNELS.includes(channelType) &&
@@ -63,11 +67,13 @@ const ChannelActions = ({channelId, channelType, inModal = false}: Props) => {
<AddPeopleBox
channelId={channelId}
inModal={inModal}
testID={`${testID}.add_people.action`}
/>
<View style={styles.separator}/>
<CopyChannelLinkBox
channelId={channelId}
onAnimationEnd={onCopyLinkAnimationEnd}
testID={`${testID}.copy_channel_link.action`}
/>
</>
}

View File

@@ -208,8 +208,8 @@ const ChannelListItem = ({
<Text
ellipsizeMode='tail'
numberOfLines={1}
testID={`${testID}.${teamDisplayName}.display_name`}
style={[styles.teamName, isMuted && styles.teamNameMuted]}
testID={`${testID}.${channel.name}.team_display_name`}
>
{teamDisplayName}
</Text>
@@ -225,8 +225,8 @@ const ChannelListItem = ({
<Text
ellipsizeMode='tail'
numberOfLines={1}
testID={`${testID}.${teamDisplayName}.display_name`}
style={[styles.teamName, styles.teamNameTablet, isMuted && styles.teamNameMuted]}
testID={`${testID}.${channel.name}.team_display_name`}
>
{teamDisplayName}
</Text>

View File

@@ -177,7 +177,7 @@ const Markdown = ({
};
const renderBreak = () => {
return <Text>{'\n'}</Text>;
return <Text testID='markdown_break'>{'\n'}</Text>;
};
const renderChannelLink = ({context, channelName}: MarkdownChannelMentionRenderer) => {
@@ -197,7 +197,7 @@ const Markdown = ({
const renderCheckbox = ({isChecked}: {isChecked: boolean}) => {
return (
<Text>
<Text testID='markdown_checkbox'>
<CompassIcon
name={isChecked ? 'checkbox-marked' : 'checkbox-blank-outline'}
size={16}
@@ -236,7 +236,14 @@ const Markdown = ({
const renderCodeSpan = ({context, literal}: MarkdownBaseRenderer) => {
const {code} = textStyles;
return <Text style={computeTextStyle(textStyles, [baseTextStyle, code], context)}>{literal}</Text>;
return (
<Text
style={computeTextStyle(textStyles, [baseTextStyle, code], context)}
testID='markdown_code_span'
>
{literal}
</Text>
);
};
const renderEditedIndicator = ({context}: {context: string[]}) => {
@@ -288,7 +295,10 @@ const Markdown = ({
const renderHeading = ({children, level}: {children: ReactElement; level: string}) => {
if (disableHeading) {
return (
<Text style={style.bold}>
<Text
style={style.bold}
testID='markdown_heading'
>
{children}
</Text>
);
@@ -301,7 +311,10 @@ const Markdown = ({
const textStyle = textStyles[`heading${level}Text`];
return (
<View style={containerStyle}>
<View
style={containerStyle}
testID='markdown_heading'
>
<Text style={textStyle}>
{children}
</Text>
@@ -314,7 +327,10 @@ const Markdown = ({
if (props.isBlock) {
rendered = (
<View style={style.block}>
<View
style={style.block}
testID='markdown_html'
>
{rendered}
</View>
);
@@ -419,7 +435,10 @@ const Markdown = ({
}
return (
<View style={blockStyle}>
<View
style={blockStyle}
testID='markdown_paragraph'
>
<Text style={baseParagraphStyle}>
{children}
</Text>

View File

@@ -201,6 +201,7 @@ const MarkdownCodeBlock = ({language = '', content, textStyle}: MarkdownCodeBloc
<TouchableOpacity
onPress={handlePress}
onLongPress={handleLongPress}
testID='markdown_code_block'
>
<View style={style.container}>
<View>

View File

@@ -235,10 +235,7 @@ const MarkdownImage = ({
onLongPress={handleLinkLongPress}
onPress={onGestureEvent}
>
<Animated.View
style={[styles, {width, height}, style.container]}
testID='markdown_image'
>
<Animated.View style={[styles, {width, height}, style.container]}>
<ProgressiveImage
forwardRef={ref}
id={fileInfo.id!}

View File

@@ -196,6 +196,7 @@ const LatexCodeBlock = ({content, theme}: Props) => {
onPress={handlePress}
onLongPress={handleLongPress}
type={'opacity'}
testID='markdown_latex_code_block'
>
<View style={styles.container}>
<ErrorBoundary

View File

@@ -57,6 +57,7 @@ const LatexInline = ({content, maxMathWidth, theme}: Props) => {
<View
style={style.viewStyle}
key={content}
testID='markdown_latex_inline'
>
<MathView
style={[style.mathStyle, {maxWidth: maxMathWidth || '100%'}]}

View File

@@ -159,6 +159,7 @@ const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteU
<Text
onPress={handlePress}
onLongPress={handleLongPress}
testID='markdown_link'
>
{renderChildren}
</Text>

View File

@@ -41,9 +41,15 @@ const MarkdownListItem = ({index, level, bulletWidth, bulletStyle, children, con
}
return (
<View style={style.container}>
<View
style={style.container}
testID='markdown_list_item'
>
<View style={[{width: bulletWidth}, style.bullet]}>
<Text style={bulletStyle}>
<Text
style={bulletStyle}
testID='markdown_list_item.bullet'
>
{bullet}
</Text>
</View>

View File

@@ -53,7 +53,10 @@ const MarkdownTableCell = ({isLastCell, align, children}: MarkdownTableCellProps
}
return (
<View style={[cellStyle, textStyle]}>
<View
style={[cellStyle, textStyle]}
testID='markdown_table_cell'
>
{children}
</View>
);

View File

@@ -113,10 +113,7 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL,
disabled={disabled}
onPress={onGestureEvent}
>
<Animated.View
style={[styles, {width, height}]}
testID='markdown_table_image'
>
<Animated.View style={[styles, {width, height}]}>
<ProgressiveImage
id={fileId}
defaultSource={{uri: source}}
@@ -131,7 +128,10 @@ const MarkTableImage = ({disabled, imagesMetadata, location, postId, serverURL,
}
return (
<View style={style.container}>
<View
style={style.container}
testID='markdown_table_image'
>
{image}
</View>
);

View File

@@ -49,7 +49,14 @@ const MarkdownTableRow = ({isFirstRow, isLastRow, children}: MarkdownTableRowPro
isLastCell: true,
});
return <View style={rowStyle}>{renderChildren}</View>;
return (
<View
style={rowStyle}
testID='markdown_table_row'
>
{renderChildren}
</View>
);
};
export default MarkdownTableRow;

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.${author.id}.profile_picture`}
testID={`post_avatar.${author.id}.profile_picture`}
/>
);

View File

@@ -28,14 +28,14 @@ const HeaderTag = ({
return (
<BotTag
style={style.tag}
testID='post_header.bot_tag'
testID='post_header.bot.tag'
/>
);
} else if (isGuest) {
return (
<GuestTag
style={style.tag}
testID='post_header.guest_tag'
testID='post_header.guest.tag'
/>
);
} else if (isAutoResponder) {
@@ -44,6 +44,7 @@ const HeaderTag = ({
id={t('post_info.auto_responder')}
defaultMessage={'Automatic Reply'}
style={style.tag}
testID='post_header.auto_responder.tag'
/>
);
}

View File

@@ -18,6 +18,7 @@ exports[`renderSystemMessage uses renderer for Channel Display Name update 1`] =
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text
@@ -79,6 +80,7 @@ exports[`renderSystemMessage uses renderer for Channel Header update 1`] = `
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text
@@ -156,6 +158,7 @@ exports[`renderSystemMessage uses renderer for Guest added and join to channel 1
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text
@@ -217,6 +220,7 @@ exports[`renderSystemMessage uses renderer for Guest added and join to channel 2
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text
@@ -300,6 +304,7 @@ exports[`renderSystemMessage uses renderer for OLD archived channel without a us
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text
@@ -339,6 +344,7 @@ exports[`renderSystemMessage uses renderer for archived channel 1`] = `
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text
@@ -400,6 +406,7 @@ exports[`renderSystemMessage uses renderer for unarchived channel 1`] = `
},
]
}
testID="markdown_paragraph"
>
<Text>
<Text

View File

@@ -127,8 +127,8 @@ const UserItem = ({
<View
style={[style.rowInfo, {maxWidth: shared ? '75%' : '80%'}]}
>
{bot && <BotTag/>}
{guest && <GuestTag/>}
{bot && <BotTag testID={`${userItemTestId}.bot.tag`}/>}
{guest && <GuestTag testID={`${userItemTestId}.guest.tag`}/>}
{Boolean(name.length) &&
<Text
style={style.rowFullname}
@@ -143,6 +143,7 @@ const UserItem = ({
id='suggestion.mention.you'
defaultMessage=' (you)'
style={style.rowUsername}
testID={`${userItemTestId}.current_user_indicator`}
/>
}
{Boolean(user) &&

View File

@@ -198,9 +198,7 @@ export default function UserListRow({
const teammateDisplay = displayUsername(user, intl.locale, teammateNameDisplay);
const showTeammateDisplay = teammateDisplay !== username;
const itemTestID = `${testID}.${id}`;
const displayNameTestID = `${itemTestID}.display_name`;
const profilePictureTestID = `${itemTestID}.profile_picture`;
const userItemTestID = `${testID}.${id}`;
return (
<>
@@ -212,14 +210,14 @@ export default function UserListRow({
<View
ref={viewRef}
style={style.container}
testID={itemTestID}
testID={userItemTestID}
>
<View style={style.profileContainer}>
<ProfilePicture
author={user}
size={32}
iconSize={24}
testID={profilePictureTestID}
testID={`${userItemTestID}.profile_picture`}
/>
</View>
<View style={style.textContainer}>
@@ -228,15 +226,17 @@ export default function UserListRow({
style={style.username}
ellipsizeMode='tail'
numberOfLines={1}
testID={displayNameTestID}
testID={`${userItemTestID}.display_name`}
>
{usernameDisplay}
</Text>
<BotTag
show={Boolean(user.is_bot)}
testID={`${userItemTestID}.bot.tag`}
/>
<GuestTag
show={isGuest(user.roles)}
testID={`${userItemTestID}.guest.tag`}
/>
</View>
{showTeammateDisplay &&
@@ -245,6 +245,7 @@ export default function UserListRow({
style={style.displayName}
ellipsizeMode='tail'
numberOfLines={1}
testID={`${userItemTestID}.team_display_name`}
>
{teammateDisplay}
</Text>
@@ -254,6 +255,7 @@ export default function UserListRow({
<View>
<Text
style={style.deactivated}
testID={`${userItemTestID}.deactivated`}
>
{formatMessage({id: 'mobile.user_list.deactivated', defaultMessage: 'Deactivated'})}
</Text>

View File

@@ -46,27 +46,27 @@ const IntroOptions = ({channelId, header, favorite, people}: Props) => {
<AddPeopleBox
channelId={channelId}
containerStyle={[styles.item, styles.margin]}
testID='channel_post_list.intro.option_item.add_people'
testID='channel_post_list.intro_options.add_people.option'
/>
}
{header &&
<SetHeaderBox
channelId={channelId}
containerStyle={[styles.item, styles.margin]}
testID='channel_post_list.intro.option_item.set_header'
testID='channel_post_list.intro_options.set_header.option'
/>
}
{favorite &&
<FavoriteBox
channelId={channelId}
containerStyle={[styles.item, styles.margin]}
testID='channel_post_list.intro.option_item.favorite'
testID='channel_post_list.intro_options.favorite.option'
/>
}
<InfoBox
channelId={channelId}
containerStyle={styles.item}
testID='channel_post_list.intro.option_item.channel_details'
testID='channel_post_list.intro_options.channel_details.option'
/>
</View>
);

View File

@@ -39,14 +39,20 @@ const ChannelQuickAction = ({channelId}: Props) => {
return (
<View style={styles.container}>
<View style={styles.wrapper}>
<ChannelActions channelId={channelId}/>
<ChannelActions
channelId={channelId}
testID='channel.quick_actions'
/>
</View>
<InfoBox
channelId={channelId}
showAsLabel={true}
/>
<View style={styles.line}/>
<LeaveChannelLabel channelId={channelId}/>
<LeaveChannelLabel
channelId={channelId}
testID='channel.quick_actions.leave_channel.action'
/>
</View>
);
};

View File

@@ -54,11 +54,13 @@ const ChannelInfo = ({channelId, closeButtonId, componentId, type}: Props) => {
<SafeAreaView
edges={edges}
style={styles.flex}
testID='channel_info.screen'
>
<ScrollView
bounces={true}
alwaysBounceVertical={false}
contentContainerStyle={styles.content}
testID='channel_info.scrollview'
>
<Title
channelId={channelId}
@@ -67,6 +69,7 @@ const ChannelInfo = ({channelId, closeButtonId, componentId, type}: Props) => {
<ChannelActions
channelId={channelId}
inModal={true}
testID='channel_info.channel_actions'
/>
<Extra channelId={channelId}/>
<View style={styles.separator}/>

View File

@@ -130,6 +130,7 @@ const Archive = ({
icon='archive-outline'
destructive={true}
type='default'
testID='channel_info.options.archive_channel.option'
/>
);
};

View File

@@ -78,6 +78,7 @@ const ConvertPrivate = ({canConvert, channelId, displayName}: Props) => {
label={intl.formatMessage({id: 'channel_info.convert_private', defaultMessage: 'Convert to private channel'})}
icon='lock-outline'
type='default'
testID='channel_info.options.convert_private.option'
/>
);
};

View File

@@ -24,6 +24,7 @@ const DestructiveOptions = ({channelId, componentId, type}: Props) => {
<LeaveChannelLabel
channelId={channelId}
isOptionItem={true}
testID='channel_info.options.leave_channel.option'
/>
{type !== General.DM_CHANNEL && type !== General.GM_CHANNEL &&
<Archive

View File

@@ -28,6 +28,7 @@ const EditChannel = ({channelId}: Props) => {
label={title}
icon='pencil-outline'
type={Platform.select({ios: 'arrow', default: 'default'})}
testID='channel_info.options.edit_channel.option'
/>
);
};

View File

@@ -34,6 +34,7 @@ const IgnoreMentions = ({channelId, ignoring}: Props) => {
icon='at'
type='toggle'
selected={ignored}
testID={`channel_info.options.ignore_mentions.option.toggled.${ignored}`}
/>
);
};

View File

@@ -30,6 +30,7 @@ const Members = ({channelId, count}: Props) => {
icon='account-multiple-outline'
type={Platform.select({ios: 'arrow', default: 'default'})}
info={count.toString()}
testID='channel_info.options.members.option'
/>
);
};

View File

@@ -59,6 +59,7 @@ const NotificationPreference = ({channelId, notifyLevel}: Props) => {
icon='cellphone'
type={Platform.select({ios: 'arrow', default: 'default'})}
info={notificationLevelToText()}
testID='channel_info.options.notification_preference.option'
/>
);
};

View File

@@ -45,6 +45,7 @@ const PinnedMessages = ({channelId, count, displayName}: Props) => {
icon='pin-outline'
type={Platform.select({ios: 'arrow', default: 'default'})}
info={count.toString()}
testID='channel_info.options.pinned_messages.option'
/>
);
};

View File

@@ -50,21 +50,27 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
const DirectMessage = ({displayName, user}: Props) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const directMessageUserTestId = `channel_info.title.direct_message.${user?.id}`;
return (
<View style={styles.container}>
<View
style={styles.container}
testID={directMessageUserTestId}
>
<ProfilePicture
author={user}
size={64}
iconSize={64}
showStatus={true}
statusSize={24}
testID={`${directMessageUserTestId}.profile_picture`}
/>
<View style={styles.titleContainer}>
<View style={styles.displayName}>
<Text
numberOfLines={1}
style={styles.title}
testID={`${directMessageUserTestId}.display_name`}
>
{displayName}
</Text>
@@ -72,22 +78,30 @@ const DirectMessage = ({displayName, user}: Props) => {
<GuestTag
textStyle={styles.tag}
style={styles.tagContainer}
testID={`${directMessageUserTestId}.guest.tag`}
/>
}
{user?.isBot &&
<BotTag
textStyle={styles.tag}
style={styles.tagContainer}
testID={`${directMessageUserTestId}.bot.tag`}
/>
}
</View>
{Boolean(user?.position) &&
<Text style={styles.position}>
<Text
style={styles.position}
testID={`${directMessageUserTestId}.position`}
>
{user?.position}
</Text>
}
{Boolean(user?.isBot && user.props?.bot_description) &&
<Text style={styles.position}>
<Text
style={styles.position}
testID={`${directMessageUserTestId}.bot_description`}
>
{user?.props?.bot_description}
</Text>
}

View File

@@ -57,7 +57,10 @@ const GroupAvatars = ({users}: Props) => {
});
return (
<View style={[styles.container]}>
<View
style={[styles.container]}
testID='channel_info.title.group_message.group_avatars'
>
{group}
</View>
);

View File

@@ -40,7 +40,10 @@ const GroupMessage = ({currentUserId, displayName, members}: Props) => {
<GroupAvatars
userIds={userIds}
/>
<Text style={styles.title}>
<Text
style={styles.title}
testID='channel_info.title.group_message.display_name'
>
{displayName}
</Text>
</>

View File

@@ -28,14 +28,21 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
const PublicPrivate = ({displayName, purpose}: Props) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const publicPrivateTestId = 'channel_info.title.public_private';
return (
<>
<Text style={styles.title}>
<Text
style={styles.title}
testID={`${publicPrivateTestId}.display_name`}
>
{displayName}
</Text>
{Boolean(purpose) &&
<Text style={styles.purpose}>
<Text
style={styles.purpose}
testID={`${publicPrivateTestId}.purpose`}
>
{purpose}
</Text>
}

View File

@@ -220,6 +220,7 @@ const FilteredList = ({
<UserItem
onPress={onOpenDirectMessage}
user={item}
testID='find_channels.filtered_list.user_item'
/>
);
}
@@ -229,6 +230,7 @@ const FilteredList = ({
channel={item}
onPress={onJoinChannel}
showTeamName={showTeamName}
testID='find_channels.filtered_list.remote_channel_item'
/>
);
}, [onJoinChannel, onOpenDirectMessage, onSwitchToChannel, showTeamName, teammateDisplayNameSetting]);

View File

@@ -101,7 +101,7 @@ const RemoteChannelItem = ({onPress, channel, teamDisplayName, testID}: Props) =
<Text
ellipsizeMode='tail'
numberOfLines={1}
testID={`${testID}.${teamDisplayName}.display_name`}
testID={`${testID}.${channel.name}.team_display_name`}
style={styles.teamName}
>
{teamDisplayName}
@@ -112,7 +112,7 @@ const RemoteChannelItem = ({onPress, channel, teamDisplayName, testID}: Props) =
<Text
ellipsizeMode='tail'
numberOfLines={1}
testID={`${testID}.${teamDisplayName}.display_name`}
testID={`${testID}.${channel.name}.team_display_name`}
style={[styles.teamName, styles.teamNameTablet]}
>
{teamDisplayName}

View File

@@ -54,6 +54,7 @@ const UserItem = ({currentUserId, onPress, teammateDisplayNameSetting, testID, u
const styles = getStyleSheet(theme);
const isOwnDirectMessage = currentUserId === user.id;
const displayName = displayUsername(user, locale, teammateDisplayNameSetting);
const userItemTestId = `${testID}.${user.id}`;
const handleOnPress = useCallback(() => {
onPress(user.id, displayName);
@@ -64,7 +65,7 @@ const UserItem = ({currentUserId, onPress, teammateDisplayNameSetting, testID, u
<>
<View
style={styles.container}
testID={`${testID}.${user.id}`}
testID={userItemTestId}
>
<View style={styles.wrapper}>
<View style={styles.avatar}>
@@ -74,6 +75,7 @@ const UserItem = ({currentUserId, onPress, teammateDisplayNameSetting, testID, u
showStatus={true}
statusSize={12}
statusStyle={styles.status}
testID={`${userItemTestId}.profile_picture`}
/>
</View>
<View>
@@ -81,7 +83,7 @@ const UserItem = ({currentUserId, onPress, teammateDisplayNameSetting, testID, u
ellipsizeMode='tail'
numberOfLines={1}
style={styles.text}
testID={`${testID}.${user.id}.display_name`}
testID={`${userItemTestId}.display_name`}
>
{isOwnDirectMessage ? formatMessage({id: 'channel_header.directchannel.you', defaultMessage: '{displayName} (you)'}, {displayName}) : displayName}
</Text>

View File

@@ -52,6 +52,7 @@ const AccountUserInfo = ({user, showFullName, theme}: Props) => {
const nickName = user.nickname ? ` (${user.nickname})` : '';
const title = `${user.firstName} ${user.lastName}${nickName}`;
const userName = `@${user.username}`;
const accountUserInfoTestId = `account_user_info.${user.id}`;
return (
<View style={styles.container}>
@@ -62,10 +63,15 @@ const AccountUserInfo = ({user, showFullName, theme}: Props) => {
author={user}
statusStyle={styles.statusStyle}
statusSize={24}
testID={`account.${user.id}.profile_picture`}
testID={`${accountUserInfoTestId}.profile_picture`}
/>
{showFullName && <Text style={styles.textFullName}>{title}</Text>}
<Text style={showFullName ? styles.textUserName : styles.textFullName}>{`${userName}`}</Text>
<Text
style={showFullName ? styles.textUserName : styles.textFullName}
testID={`${accountUserInfoTestId}.username`}
>
{`${userName}`}
</Text>
</View>
);
};

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import {typography} from '@utils/typography';
type Props = {
title: string;
description: string;
testID?: string;
};
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
@@ -28,14 +29,24 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
},
}));
const UserProfileLabel = ({title, description}: Props) => {
const UserProfileLabel = ({title, description, testID}: Props) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
return (
<View style={styles.container}>
<Text style={styles.title}>{title}</Text>
<Text style={styles.description}>{description}</Text>
<Text
style={styles.title}
testID={`${testID}.title`}
>
{title}
</Text>
<Text
style={styles.description}
testID={`${testID}.description`}
>
{description}
</Text>
</View>
);
};

View File

@@ -77,12 +77,14 @@ const UserProfileOptions = ({location, type, userId, username}: Props) => {
<OptionBox
iconName='send'
onPress={openChannel}
testID='user_profile_options.send_message.option'
text={intl.formatMessage({id: 'channel_info.send_mesasge', defaultMessage: 'Send message'})}
/>
<View style={styles.divider}/>
<OptionBox
iconName='at'
onPress={mentionUser}
testID='user_profile_options.mention.option'
text={intl.formatMessage({id: 'channel_info.mention', defaultMessage: 'Mention'})}
/>
</View>
@@ -94,6 +96,7 @@ const UserProfileOptions = ({location, type, userId, username}: Props) => {
<TouchableOpacity
style={[buttonStyle, styles.singleButton]}
onPress={openChannel}
testID='user_profile_options.send_message.option'
>
<CompassIcon
color={theme.buttonBg}

View File

@@ -43,6 +43,7 @@ const UserProfileAvatar = ({enablePostIconOverride, user, userIconOverride}: Pro
showStatus={true}
size={96}
statusSize={24}
testID={`user_profile_avatar.${user.id}.profile_picture`}
/>
);
};

View File

@@ -89,6 +89,7 @@ const UserProfileTitle = ({
<Text
numberOfLines={1}
style={styles.displayName}
testID='user_profile.display_name'
>
{`${prefix}${displayName}`}
</Text>
@@ -96,6 +97,7 @@ const UserProfileTitle = ({
<Text
numberOfLines={1}
style={styles.username}
testID='user_profile.username'
>
{`@${user.username}`}
</Text>

View File

@@ -24,11 +24,19 @@ const styles = StyleSheet.create({
const UserProfileTag = ({isBot, isChannelAdmin, isGuest, isSystemAdmin, isTeamAdmin}: Props) => {
if (isBot) {
return (<BotTag style={styles.tag}/>);
return (
<BotTag
style={styles.tag}
testID='user_profile.bot.tag'
/>);
}
if (isGuest) {
return (<GuestTag style={styles.tag}/>);
return (
<GuestTag
style={styles.tag}
testID='user_profile.guest.tag'
/>);
}
if (isSystemAdmin) {
@@ -37,6 +45,7 @@ const UserProfileTag = ({isBot, isChannelAdmin, isGuest, isSystemAdmin, isTeamAd
id='user_profile.system_admin'
defaultMessage='System Admin'
style={styles.tag}
testID='user_profile.system_admin.tag'
/>
);
}
@@ -47,6 +56,7 @@ const UserProfileTag = ({isBot, isChannelAdmin, isGuest, isSystemAdmin, isTeamAd
id='user_profile.team_admin'
defaultMessage='Team Admin'
style={styles.tag}
testID='user_profile.team_admin.tag'
/>
);
}
@@ -57,6 +67,7 @@ const UserProfileTag = ({isBot, isChannelAdmin, isGuest, isSystemAdmin, isTeamAd
id='user_profile.channel_admin'
defaultMessage='Channel Admin'
style={styles.tag}
testID='user_profile.channel_admin.tag'
/>
);
}

View File

@@ -127,18 +127,21 @@ const UserProfile = ({
{Boolean(user.nickname) && !override && !user.isBot &&
<UserProfileLabel
description={user.nickname}
testID='user_profile.nickname'
title={formatMessage({id: 'channel_info.nickname', defaultMessage: 'Nickname'})}
/>
}
{Boolean(user.position) && !override && !user.isBot &&
<UserProfileLabel
description={user.position}
testID='user_profile.position'
title={formatMessage({id: 'channel_info.position', defaultMessage: 'Position'})}
/>
}
{Boolean(localTime) && !override && !user.isBot &&
<UserProfileLabel
description={localTime!}
testID='user_profile.local_time'
title={formatMessage({id: 'channel_info.local_time', defaultMessage: 'Local Time'})}
/>
}
@@ -153,7 +156,7 @@ const UserProfile = ({
componentId={Screens.USER_PROFILE}
initialSnapIndex={0}
snapPoints={snapPoints}
testID='post_options'
testID='user_profile'
/>
);
};

View File

@@ -102,6 +102,23 @@ export const apiCreateGroupChannel = async (baseUrl: string, userIds: string[] =
}
};
/**
* Delete a channel.
* See https://api.mattermost.com/#operation/DeleteChannel
* @param {string} baseUrl - the base server URL
* @param {string} channelId - The channel ID to be deleted
* @return {Object} returns {deleted} on success or {error, status} on error
*/
export const apiDeleteChannel = async (baseUrl: string, channelId: string): Promise<any> => {
try {
const response = await client.delete(`${baseUrl}/api/v4/channels/${channelId}`);
return {deleted: response.data};
} catch (err) {
return getResponseFromError(err);
}
};
/**
* Get a channel by name.
* See https://api.mattermost.com/#operation/GetChannelByName
@@ -172,6 +189,23 @@ export const apiGetUnreadMessages = async (baseUrl: string, userId: string, chan
}
};
/**
* Restore a channel.
* See https://api.mattermost.com/#operation/RestoreChannel
* @param {string} baseUrl - the base server URL
* @param {string} channelId - The channel ID to be restored
* @return {Object} returns {restored} on success or {error, status} on error
*/
export const apiRestoreChannel = async (baseUrl: string, channelId: string): Promise<any> => {
try {
const response = await client.post(`${baseUrl}/api/v4/channels/${channelId}/restore`);
return {restored: response.data};
} catch (err) {
return getResponseFromError(err);
}
};
/**
* Remove user from channel.
* See https://api.mattermost.com/#operation/RemoveUserFromChannel
@@ -231,9 +265,11 @@ export const Channel = {
apiCreateChannel,
apiCreateDirectChannel,
apiCreateGroupChannel,
apiDeleteChannel,
apiGetChannelByName,
apiGetChannelsForUser,
apiGetUnreadMessages,
apiRestoreChannel,
apiRemoveUserFromChannel,
apiViewChannel,
generateRandomChannel,

View File

@@ -5,6 +5,7 @@ import {isAndroid} from '@support/utils';
class Alert {
// alert titles
confirmSendingNotificationsTitle = isAndroid() ? element(by.text('Confirm sending notifications to entire channel')) : element(by.label('Confirm sending notifications to entire channel')).atIndex(0);
deletePostTitle = isAndroid() ? element(by.text('Delete Post')) : element(by.label('Delete Post')).atIndex(0);
logoutTitle = (serverDisplayName: string) => {
const title = `Are you sure you want to log out of ${serverDisplayName}?`;
@@ -21,6 +22,7 @@ class Alert {
// alert buttons
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).atIndex(1);
confirmButton = isAndroid() ? element(by.text('CONFIRM')) : element(by.label('Confirm')).atIndex(1);
deleteButton = isAndroid() ? element(by.text('DELETE')) : element(by.label('Delete')).atIndex(0);
logoutButton = isAndroid() ? element(by.text('LOG OUT')) : element(by.label('Log out')).atIndex(1);
markReadButton = isAndroid() ? element(by.text('MARK READ')) : element(by.label('Mark read')).atIndex(1);

View File

@@ -0,0 +1,125 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect} from 'detox';
import ProfilePicture from './profile_picture';
class Autocomplete {
testID = {
atMentionItemPrefix: 'autocomplete.at_mention_item.',
groupMentionItemPrefix: 'autocomplete.group_mention_item.',
specialMentionItemPrefix: 'autocomplete.special_mention_item.',
channelMentionItemPrefix: 'autocomplete.channel_mention_item.',
emojiSuggestionItemPrefix: 'autocomplete.emoji_suggestion_item.',
slashSuggestionItemPrefix: 'autocomplete.slash_suggestion_item.',
autocomplete: 'autocomplete',
sectionAtMentionList: 'autocomplete.at_mention.section_list',
sectionChannelMentionList: 'autocomplete.channel_mention.section_list',
flatEmojiSuggestionList: 'autocomplete.emoji_suggestion.flat_list',
flatSlashSuggestionList: 'autocomplete.slash_suggestion.flat_list',
};
autocomplete = element(by.id(this.testID.autocomplete));
sectionAtMentionList = element(by.id(this.testID.sectionAtMentionList));
sectionChannelMentionList = element(by.id(this.testID.sectionChannelMentionList));
flatEmojiSuggestionList = element(by.id(this.testID.flatEmojiSuggestionList));
flatSlashSuggestionList = element(by.id(this.testID.flatSlashSuggestionList));
getAtMentionItem = (userId: string) => {
const atMentionItemTestId = `${this.testID.atMentionItemPrefix}${userId}`;
const atMentionItemMatcher = by.id(atMentionItemTestId);
const atMentionItemProfilePictureMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.atMentionItemPrefix, userId);
const atMentionItemBotTagMatcher = by.id(`${atMentionItemTestId}.bot.tag`);
const atMentionItemGuestTagMatcher = by.id(`${atMentionItemTestId}.guest.tag`);
const atMentionItemUserDisplayNameMatcher = by.id(`${atMentionItemTestId}.display_name`);
const atMentionItemCurrentUserIndicatorMatcher = by.id(`${atMentionItemTestId}.current_user_indicator`);
const atMentionItemUsernameMatcher = by.id(`${atMentionItemTestId}.username`);
return {
atMentionItem: element(atMentionItemMatcher),
atMentionItemProfilePicture: element(atMentionItemProfilePictureMatcher),
atMentionItemBotTag: element(atMentionItemBotTagMatcher),
atMentionItemGuestTag: element(atMentionItemGuestTagMatcher),
atMentionItemUserDisplayName: element(atMentionItemUserDisplayNameMatcher),
atMentionItemCurrentUserIndicator: element(atMentionItemCurrentUserIndicatorMatcher),
atMentionItemUsername: element(atMentionItemUsernameMatcher),
};
};
getGrouptMentionItem = (groupName: string) => {
const groupMentionItemTestId = `${this.testID.groupMentionItemPrefix}${groupName}`;
const groupMentionItemMatcher = by.id(groupMentionItemTestId);
const groupMentionItemGroupDisplayNameMatcher = by.id(`${groupMentionItemTestId}.display_name`);
const groupMentionItemGroupNameMatcher = by.id(`${groupMentionItemTestId}.name`);
return {
groupMentionItem: element(groupMentionItemMatcher),
groupMentionItemGroupDisplayName: element(groupMentionItemGroupDisplayNameMatcher),
groupMentionItemGroupName: element(groupMentionItemGroupNameMatcher),
};
};
getSpecialMentionItem = (handleName: string) => {
const specialMentionItemTestId = `${this.testID.specialMentionItemPrefix}${handleName}`;
const specialMentionItemMatcher = by.id(specialMentionItemTestId);
const specialMentionItemGroupDisplayNameMatcher = by.id(`${specialMentionItemTestId}.display_name`);
const specialMentionItemGroupNameMatcher = by.id(`${specialMentionItemTestId}.name`);
return {
specialMentionItem: element(specialMentionItemMatcher),
specialMentionItemGroupDisplayName: element(specialMentionItemGroupDisplayNameMatcher),
specialMentionItemGroupName: element(specialMentionItemGroupNameMatcher),
};
};
getChannelMentionItem = (channelName: string) => {
const channelMentionItemTestId = `${this.testID.channelMentionItemPrefix}${channelName}`;
const channelMentionItemMatcher = by.id(channelMentionItemTestId);
const channelMentionItemChannelDisplayNameMatcher = by.id(`${channelMentionItemTestId}.display_name`);
const channelMentionItemChannelNameMatcher = by.id(`${channelMentionItemTestId}.name`);
return {
channelMentionItem: element(channelMentionItemMatcher),
channelMentionItemChannelDisplayName: element(channelMentionItemChannelDisplayNameMatcher),
channelMentionItemChannelName: element(channelMentionItemChannelNameMatcher),
};
};
getEmojiSuggestionItem = (emojiName: string) => {
const emojiSuggestionItemTestId = `${this.testID.emojiSuggestionItemPrefix}${emojiName}`;
const emojiSuggestionItemMatcher = by.id(emojiSuggestionItemTestId);
const emojiSuggestionItemEmojiNameMatcher = by.id(`${emojiSuggestionItemTestId}.name`);
return {
emojiSuggestionItem: element(emojiSuggestionItemMatcher),
emojiSuggestionItemEmojiName: element(emojiSuggestionItemEmojiNameMatcher),
};
};
getSlashSuggestionItem = (slashCommand: string) => {
const slashSuggestionItemTestId = `${this.testID.slashSuggestionItemPrefix}/${slashCommand}`;
const slashSuggestionItemMatcher = by.id(slashSuggestionItemTestId);
const slashSuggestionItemSlashCommandNameMatcher = by.id(`${slashSuggestionItemTestId}.name`);
const slashSuggestionItemSlashCommandDescriptionMatcher = by.id(`${slashSuggestionItemTestId}.description`);
return {
slashSuggestionItem: element(slashSuggestionItemMatcher),
slashSuggestionItemSlashCommandName: element(slashSuggestionItemSlashCommandNameMatcher),
slashSuggestionItemSlashCommandDescription: element(slashSuggestionItemSlashCommandDescriptionMatcher),
};
};
toBeVisible = async (isVisible = true) => {
if (isVisible) {
await expect(this.autocomplete.atIndex(0)).toBeVisible();
return this.autocomplete;
}
await expect(this.autocomplete).not.toBeVisible();
return null;
};
}
const autocomplete = new Autocomplete();
export default autocomplete;

View File

@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import Alert from './alert';
import Autocomplete from './autocomplete';
import CameraQuickAction from './camera_quick_action';
import FileQuickAction from './file_quick_action';
import ImageQuickAction from './image_quick_action';
@@ -18,6 +19,7 @@ import TeamSidebar from './team_sidebar';
export {
Alert,
Autocomplete,
CameraQuickAction,
FileQuickAction,
ImageQuickAction,

View File

@@ -5,20 +5,33 @@ import ProfilePicture from './profile_picture';
class Post {
testID = {
postProfilePicturePrefix: 'post_profile_picture.',
postAvatarPrefix: 'post_avatar.',
blockQuote: 'markdown_block_quote',
break: 'markdown_break',
checkbox: 'markdown_checkbox',
codeBlock: 'markdown_code_block',
codeSpan: 'markdown_code_span',
editedIndicator: 'edited_indicator',
emoji: 'markdown_emoji',
heading: 'markdown_heading',
html: 'markdown_html',
image: 'markdown_image',
message: 'markdown_text',
latexCodeBlock: 'markdown_latex_code_block',
latexInLine: 'markdown_latex_inline',
link: 'markdown_link',
listItem: 'markdown_list_item',
listItemBullet: 'markdown_list_item.bullet',
paragraph: 'markdown_paragraph',
postFooterFollowButton: 'post_footer.follow_thread.button',
postFooterFollowingButton: 'post_footer.following_thread.button',
postFooterReplyCount: 'post_footer.reply_count',
postHeaderCommentedOn: 'post_header.commented_on',
postHeaderDateTime: 'post_header.date_time',
postHeaderDisplayName: 'post_header.display_name',
postHeaderBotTag: 'post_header.bot_tag',
postHeaderGuestTag: 'post_header.guest_tag',
postHeaderBotTag: 'post_header.bot.tag',
postHeaderGuestTag: 'post_header.guest.tag',
postHeaderAutoResponderTag: 'post_header.auto_responder.tag',
postHeaderReply: 'post_header.reply',
postHeaderReplyCount: 'post_header.reply_count',
postPreHeaderText: 'post_pre_header.text',
@@ -26,37 +39,70 @@ class Post {
showLessButton: 'show_more.button.chevron-up',
showMoreButton: 'show_more.button.chevron-down',
table: 'markdown_table',
tableCell: 'markdown_table_cell',
tableExpandButton: 'markdown_table.expand.button',
tableImage: 'markdown_table_image',
tableRow: 'markdown_table_row',
thematicBreak: 'markdown_thematic_break',
};
getPost = (postItemSourceTestID: string, postId: string, postMessage: string, postProfileOptions: any = {}) => {
const postItemMatcher = this.getPostItemMatcher(postItemSourceTestID, postId, postMessage);
const postItemBlockQuoteMatcher = by.id(this.testID.blockQuote).withAncestor(postItemMatcher);
const postItemBreakMatcher = by.id(this.testID.break).withAncestor(postItemMatcher);
const postItemCheckboxMatcher = by.id(this.testID.checkbox).withAncestor(postItemMatcher);
const postItemCodeBlockMatcher = by.id(this.testID.codeBlock).withAncestor(postItemMatcher);
const postItemCodeSpanMatcher = by.id(this.testID.codeSpan).withAncestor(postItemMatcher);
const postItemEditedIndicator = by.id(this.testID.editedIndicator).withAncestor(postItemMatcher);
const postItemEmojiMatcher = by.id(this.testID.emoji).withAncestor(postItemMatcher);
const postItemHeadingMatcher = by.id(this.testID.heading).withAncestor(postItemMatcher);
const postItemHtmlMatcher = by.id(this.testID.html).withAncestor(postItemMatcher);
const postItemImageMatcher = by.id(this.testID.image).withAncestor(postItemMatcher);
const postItemMessageMatcher = by.id(this.testID.message).withAncestor(postItemMatcher);
const postItemLatexCodeBlockMatcher = by.id(this.testID.latexCodeBlock).withAncestor(postItemMatcher);
const postItemInlineLatexMatcher = by.id(this.testID.latexInLine).withAncestor(postItemMatcher);
const postItemLinkMatcher = by.id(this.testID.link).withAncestor(postItemMatcher);
const postItemListItemMatcher = by.id(this.testID.listItem).withAncestor(postItemMatcher);
const postItemListItemBulletMatcher = by.id(this.testID.listItemBullet).withAncestor(postItemMatcher);
const postItemParagraphMatcher = by.id(this.testID.paragraph).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);
const postItemTableMatcher = by.id(this.testID.table).withAncestor(postItemMatcher);
const postItemTableCellMatcher = by.id(this.testID.tableCell).withAncestor(postItemMatcher);
const postItemTableExpandButtonMatcher = by.id(this.testID.tableExpandButton).withAncestor(postItemMatcher);
const postItemTableImageMatcher = by.id(this.testID.tableImage).withAncestor(postItemMatcher);
const postItemTableRowMatcher = by.id(this.testID.tableRow).withAncestor(postItemMatcher);
const postItemThematicBreakMatcher = by.id(this.testID.thematicBreak).withAncestor(postItemMatcher);
const postItemUnreadDotBadgeMatcher = by.id(this.testID.postUnreadDotBadge).withAncestor(postItemMatcher);
return {
postItem: element(postItemMatcher),
postItemBlockQuote: element(postItemBlockQuoteMatcher),
postItemBreak: element(postItemBreakMatcher),
postItemCheckbox: element(postItemCheckboxMatcher),
postItemCodeBlock: element(postItemCodeBlockMatcher),
postItemCodeSpan: element(postItemCodeSpanMatcher),
postItemEditedIndicator: element(postItemEditedIndicator),
postItemEmoji: element(postItemEmojiMatcher),
postItemHeading: element(postItemHeadingMatcher),
postItemHtml: element(postItemHtmlMatcher),
postItemImage: element(postItemImageMatcher),
postItemMessage: element(postItemMessageMatcher),
postItemLatexCodeBlock: element(postItemLatexCodeBlockMatcher),
postItemInlineLatex: element(postItemInlineLatexMatcher),
postItemLink: element(postItemLinkMatcher),
postItemListItem: element(postItemListItemMatcher),
postItemListItemBullet: element(postItemListItemBulletMatcher),
postItemParagraph: element(postItemParagraphMatcher),
postItemPreHeaderText: element(postItemPreHeaderTextMatch),
postItemShowLessButton: element(postItemShowLessButtonMatcher),
postItemShowMoreButton: element(postItemShowMoreButtonMatcher),
postItemTable: element(postItemTableMatcher),
postItemTableCell: element(postItemTableCellMatcher),
postItemTableExpandButton: element(postItemTableExpandButtonMatcher),
postItemTableImage: element(postItemTableImageMatcher),
postItemTableRow: element(postItemTableRowMatcher),
postItemThematicBreak: element(postItemThematicBreakMatcher),
postItemUnreadDotBadge: element(postItemUnreadDotBadgeMatcher),
...this.getPostFooter(postItemMatcher),
@@ -83,6 +129,7 @@ class Post {
const postItemHeaderDisplayNameMatcher = by.id(this.testID.postHeaderDisplayName).withAncestor(postItemMatcher);
const postItemHeaderBotTagMatcher = by.id(this.testID.postHeaderBotTag).withAncestor(postItemMatcher);
const postItemHeaderGuestTagMatcher = by.id(this.testID.postHeaderGuestTag).withAncestor(postItemMatcher);
const postItemHeaderAutoResponderTagMatcher = by.id(this.testID.postHeaderAutoResponderTag).withAncestor(postItemMatcher);
const postItemHeaderReplyMatcher = by.id(this.testID.postHeaderReply).withAncestor(postItemMatcher);
const postItemHeaderReplyCountMatcher = by.id(this.testID.postHeaderReplyCount).withAncestor(postItemMatcher);
@@ -92,6 +139,7 @@ class Post {
postItemHeaderDisplayName: element(postItemHeaderDisplayNameMatcher),
postItemHeaderBotTag: element(postItemHeaderBotTagMatcher),
postItemHeaderGuestTag: element(postItemHeaderGuestTagMatcher),
postItemHeaderAutoResponderTag: element(postItemHeaderAutoResponderTagMatcher),
postItemHeaderReply: element(postItemHeaderReplyMatcher),
postItemHeaderReplyCount: element(postItemHeaderReplyCountMatcher),
};
@@ -115,7 +163,7 @@ class Post {
};
}
const profilePictureItemMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.postProfilePicturePrefix, postProfileOptions.userId);
const profilePictureItemMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.postAvatarPrefix, postProfileOptions.userId);
const profilePictureItemUserStatusMatcher = ProfilePicture.getProfilePictureItemUserStatusMatcher(profilePictureItemMatcher, postProfileOptions.userStatus);
const postItemProfilePictureMatcher = profilePictureItemMatcher.withAncestor(postItemMatcher);
const postItemProfilePictureUserStatusMatcher = profilePictureItemUserStatusMatcher.withAncestor(postItemMatcher);

View File

@@ -61,6 +61,10 @@ class PostList {
const {
postItem,
postItemBlockQuote,
postItemBreak,
postItemCheckbox,
postItemCodeBlock,
postItemCodeSpan,
postItemEditedIndicator,
postItemEmoji,
postItemFooterFollowButton,
@@ -71,17 +75,29 @@ class PostList {
postItemHeaderDisplayName,
postItemHeaderBotTag,
postItemHeaderGuestTag,
postItemHeaderAutoResponderTag,
postItemHeaderReply,
postItemHeaderReplyCount,
postItemHeading,
postItemHtml,
postItemImage,
postItemMessage,
postItemLatexCodeBlock,
postItemInlineLatex,
postItemLink,
postItemListItem,
postItemListItemBullet,
postItemParagraph,
postItemPreHeaderText,
postItemProfilePicture,
postItemProfilePictureUserStatus,
postItemShowLessButton,
postItemShowMoreButton,
postItemTable,
postItemTableCell,
postItemTableExpandButton,
postItemTableImage,
postItemTableRow,
postItemThematicBreak,
postItemUnreadDotBadge,
} = Post.getPost(this.testID.postListPostItem, postId, postMessage, postProfileOptions);
@@ -89,6 +105,10 @@ class PostList {
return {
postListPostItem: postItem,
postListPostItemBlockQuote: postItemBlockQuote,
postListPostItemBreak: postItemBreak,
postListPostItemCheckbox: postItemCheckbox,
postListPostItemCodeBlock: postItemCodeBlock,
postListPostItemCodeSpan: postItemCodeSpan,
postListPostItemEditedIndicator: postItemEditedIndicator,
postListPostItemEmoji: postItemEmoji,
postListPostItemFooterFollowButton: postItemFooterFollowButton,
@@ -99,17 +119,29 @@ class PostList {
postListPostItemHeaderDisplayName: postItemHeaderDisplayName,
postListPostItemHeaderBotTag: postItemHeaderBotTag,
postListPostItemHeaderGuestTag: postItemHeaderGuestTag,
postListPostItemHeaderAutoResponderTag: postItemHeaderAutoResponderTag,
postListPostItemHeaderReply: postItemHeaderReply,
postListPostItemHeaderReplyCount: postItemHeaderReplyCount,
postListPostItemHeading: postItemHeading,
postListPostItemHtml: postItemHtml,
postListPostItemImage: postItemImage,
postListPostItemMessage: postItemMessage,
postListPostItemLatexCodeBlock: postItemLatexCodeBlock,
postListPostItemInlineLatex: postItemInlineLatex,
postListPostItemLink: postItemLink,
postListPostItemListItem: postItemListItem,
postListPostItemListItemBullet: postItemListItemBullet,
postListPostItemParagraph: postItemParagraph,
postListPostItemPreHeaderText: postItemPreHeaderText,
postListPostItemProfilePicture: postItemProfilePicture,
postListPostItemProfilePictureUserStatus: postItemProfilePictureUserStatus,
postListPostItemShowLessButton: postItemShowLessButton,
postListPostItemShowMoreButton: postItemShowMoreButton,
postListPostItemTable: postItemTable,
postListPostItemTableCell: postItemTableCell,
postListPostItemTableExpandButton: postItemTableExpandButton,
postListPostItemTableImage: postItemTableImage,
postListPostItemTableRow: postItemTableRow,
postListPostItemThematicBreak: postItemThematicBreak,
postListPostItemUnreadDotBadge: postItemUnreadDotBadge,
};

View File

@@ -9,6 +9,7 @@ class BrowseChannelsScreen {
testID = {
browseChannelsScreen: 'browse_channels.screen',
closeButton: 'close.browse_channels.button',
createButton: 'browse_channels.create.button',
searchInput: 'browse_channels.search_bar.search.input',
searchClearButton: 'browse_channels.search_bar.search.clear.button',
searchCancelButton: 'browse_channels.search_bar.search.cancel.button',
@@ -21,6 +22,7 @@ class BrowseChannelsScreen {
browseChannelsScreen = element(by.id(this.testID.browseChannelsScreen));
closeButton = element(by.id(this.testID.closeButton));
createButton = element(by.id(this.testID.createButton));
searchInput = element(by.id(this.testID.searchInput));
searchClearButton = element(by.id(this.testID.searchClearButton));
searchCancelButton = element(by.id(this.testID.searchCancelButton));

View File

@@ -24,17 +24,19 @@ class ChannelScreen {
channelScreenPrefix: 'channel.',
channelScreen: 'channel.screen',
introDisplayName: 'channel_post_list.intro.display_name',
introOptionAddPeopleItem: 'channel_post_list.intro.option_item.add_people',
introOptionSetHeaderItem: 'channel_post_list.intro.option_item.set_header',
introOptionChannelDetailsItem: 'channel_post_list.intro.option_item.channel_details',
introAddPeopleOption: 'channel_post_list.intro_options.add_people.option',
introSetHeaderOption: 'channel_post_list.intro_options.set_header.option',
introFavoriteOption: 'channel_post_list.intro_options.set_header.option',
introChannelDetailsOption: 'channel_post_list.intro_options.channel_details.option',
flatPostList: 'channel.post_list.flat_list',
};
channelScreen = element(by.id(this.testID.channelScreen));
introDisplayName = element(by.id(this.testID.introDisplayName));
introOptionAddPeopleItem = element(by.id(this.testID.introOptionAddPeopleItem));
introOptionSetHeaderItem = element(by.id(this.testID.introOptionSetHeaderItem));
introOptionChannelDetailsItem = element(by.id(this.testID.introOptionChannelDetailsItem));
introAddPeopleOption = element(by.id(this.testID.introAddPeopleOption));
introSetHeaderOption = element(by.id(this.testID.introSetHeaderOption));
introFavoriteOption = element(by.id(this.testID.introFavoriteOption));
introChannelDetailsOption = element(by.id(this.testID.introChannelDetailsOption));
flatPostList = element(by.id(this.testID.flatPostList));
// convenience props
@@ -71,7 +73,7 @@ class ChannelScreen {
return this.postList.getNewMessagesDivider();
};
getPostListPostItem = (postId: string, text: string, postProfileOptions: any = {}) => {
getPostListPostItem = (postId: string, text = '', postProfileOptions: any = {}) => {
return this.postList.getPost(postId, text, postProfileOptions);
};

View File

@@ -0,0 +1,77 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ChannelScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
import {expect} from 'detox';
class ChannelInfoScreen {
testID = {
channelInfoScreen: 'channel_info.screen',
closeButton: 'close.channel_info.button',
scrollView: 'channel_info.scrollview',
favoriteAction: 'channel_info.channel_actions.favorite.action',
muteAction: 'channel_info.channel_actions.mute.action',
setHeaderAction: 'channel_info.channel_actions.set_header.action',
addPeopleAction: 'channel_info.channel_actions.add_people.action',
copyChannelLinkAction: 'channel_info.channel_actions.copy_channel_link.action',
ignoreMentionsOptionToggledOff: 'channel_info.options.ignore_mentions.option.toggled.false',
ignoreMentionsOptionToggledOn: 'channel_info.options.ignore_mentions.option.toggled.true',
notificationPreferenceOption: 'channel_info.options.notification_preference.option',
pinnedMessagesOption: 'channel_info.options.pinned_messages.option',
membersOption: 'channel_info.options.members.option',
editChannelOption: 'channel_info.options.edit_channel.option',
convertPrivateOption: 'channel_info.options.convert_private.option',
leaveChannelOption: 'channel_info.options.leave_channel.option',
archiveChannelOption: 'channel_info.options.archive_channel.option',
};
channelInfoScreen = element(by.id(this.testID.channelInfoScreen));
closeButton = element(by.id(this.testID.closeButton));
scrollView = element(by.id(this.testID.scrollView));
favoriteAction = element(by.id(this.testID.favoriteAction));
muteAction = element(by.id(this.testID.muteAction));
setHeaderAction = element(by.id(this.testID.setHeaderAction));
addPeopleAction = element(by.id(this.testID.addPeopleAction));
copyChannelLinkAction = element(by.id(this.testID.copyChannelLinkAction));
ignoreMentionsOptionToggledOff = element(by.id(this.testID.ignoreMentionsOptionToggledOff));
ignoreMentionsOptionToggledOn = element(by.id(this.testID.ignoreMentionsOptionToggledOn));
notificationPreferenceOption = element(by.id(this.testID.notificationPreferenceOption));
pinnedMessagesOption = element(by.id(this.testID.pinnedMessagesOption));
membersOption = element(by.id(this.testID.membersOption));
editChannelOption = element(by.id(this.testID.editChannelOption));
convertPrivateOption = element(by.id(this.testID.convertPrivateOption));
leaveChannelOption = element(by.id(this.testID.leaveChannelOption));
archiveChannelOption = element(by.id(this.testID.archiveChannelOption));
toBeVisible = async () => {
await waitFor(this.channelInfoScreen).toExist().withTimeout(timeouts.TEN_SEC);
return this.channelInfoScreen;
};
open = async () => {
// # Open channel info screen
await ChannelScreen.headerTitle.tap();
return this.toBeVisible();
};
close = async () => {
await this.closeButton.tap();
await expect(this.channelInfoScreen).not.toBeVisible();
};
toggleIgnoreMentionsOptionOn = async () => {
await this.ignoreMentionsOptionToggledOff.tap();
await expect(this.ignoreMentionsOptionToggledOn).toBeVisible();
};
toggleIgnoreMentionsOff = async () => {
await this.ignoreMentionsOptionToggledOn.tap();
await expect(this.ignoreMentionsOptionToggledOff).toBeVisible();
};
}
const channelInfoScreen = new ChannelInfoScreen();
export default channelInfoScreen;

View File

@@ -1,6 +1,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ProfilePicture} from '@support/ui/component';
import {ChannelListScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
import {expect} from 'detox';
@@ -45,7 +46,7 @@ class CreateDirectMessageScreen {
};
getUserItemProfilePicture = (userId: string) => {
return element(by.id(`create_direct_message.user_list.user_item.${userId}.profile_picture`));
return element(ProfilePicture.getProfilePictureItemMatcher('create_direct_message.user_list.user_item.', userId));
};
getUserItemDisplayName = (userId: string) => {

View File

@@ -2,6 +2,7 @@
// See LICENSE.txt for license information.
import {
ChannelInfoScreen,
ChannelScreen,
ChannelListScreen,
} from '@support/ui/screen';
@@ -12,6 +13,7 @@ class CreateOrEditChannelScreen {
testID = {
createOrEditChannelScreen: 'create_or_edit_channel.screen',
closeButton: 'close.create_or_edit_channel.button',
backButton: 'screen.back.button',
createButton: 'create_or_edit_channel.create.button',
saveButton: 'create_or_edit_channel.save.button',
scrollView: 'create_or_edit_channel.scrollview',
@@ -27,6 +29,7 @@ class CreateOrEditChannelScreen {
createOrEditChannelScreen = element(by.id(this.testID.createOrEditChannelScreen));
closeButton = element(by.id(this.testID.closeButton));
backButton = element(by.id(this.testID.backButton));
createButton = element(by.id(this.testID.createButton));
saveButton = element(by.id(this.testID.saveButton));
scrollView = element(by.id(this.testID.scrollView));
@@ -55,11 +58,23 @@ class CreateOrEditChannelScreen {
openEditChannel = async () => {
// # Open edit channel screen
await ChannelScreen.introOptionSetHeaderItem.tap();
await ChannelInfoScreen.editChannelOption.tap();
return this.toBeVisible();
};
openEditChannelHeader = async () => {
// # Open edit channel header screen
await ChannelScreen.introSetHeaderOption.tap();
return this.toBeVisible();
};
back = async () => {
await this.backButton.tap();
await expect(this.createOrEditChannelScreen).not.toBeVisible();
};
close = async () => {
await this.closeButton.tap();
await expect(this.createOrEditChannelScreen).not.toBeVisible();

View File

@@ -1,7 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {PostOptionsScreen} from '@support/ui/screen';
import {timeouts} from '@support/utils';
import {expect} from 'detox';
class EditPostScreen {
testID = {
@@ -26,6 +28,17 @@ class EditPostScreen {
return this.editPostScreen;
};
open = async () => {
await PostOptionsScreen.editPostOption.tap();
return this.toBeVisible();
};
close = async () => {
await this.closeButton.tap();
await expect(this.editPostScreen).not.toBeVisible();
};
}
const editPostScreen = new EditPostScreen();

View File

@@ -5,6 +5,7 @@ import AccountScreen from './account';
import BrowseChannelsScreen from './browse_channels';
import ChannelScreen from './channel';
import ChannelDropdownMenuScreen from './channel_dropdown_menu';
import ChannelInfoScreen from './channel_info';
import ChannelListScreen from './channel_list';
import CreateDirectMessageScreen from './create_direct_message';
import CreateOrEditChannelScreen from './create_or_edit_channel';
@@ -20,14 +21,17 @@ import PostOptionsScreen from './post_options';
import ReactionsScreen from './reactions';
import ServerScreen from './server';
import ServerListScreen from './server_list';
import TableScreen from './table';
import ThreadScreen from './thread';
import ThreadOptionsScreen from './thread_options';
import UserProfileScreen from './user_profile';
export {
AccountScreen,
BrowseChannelsScreen,
ChannelScreen,
ChannelDropdownMenuScreen,
ChannelInfoScreen,
ChannelListScreen,
CreateDirectMessageScreen,
CreateOrEditChannelScreen,
@@ -43,6 +47,8 @@ export {
ReactionsScreen,
ServerScreen,
ServerListScreen,
TableScreen,
ThreadScreen,
ThreadOptionsScreen,
UserProfileScreen,
};

View File

@@ -18,7 +18,7 @@ class PermalinkScreen {
postList = new PostList(this.testID.permalinkScreenPrefix);
getPostListPostItem = (postId: string, text: string, postProfileOptions = {}) => {
getPostListPostItem = (postId: string, text = '', postProfileOptions = {}) => {
return this.postList.getPost(postId, text, postProfileOptions);
};

View File

@@ -1,14 +1,15 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ProfilePicture} from '@support/ui/component';
import {expect} from 'detox';
class ReactionsScreen {
testID = {
reactorItemPrefix: 'reactors_list.user_item.',
reactorItemPrefix: 'reactions.reactor_item.',
reactionsScreen: 'reactions.screen',
reactionsBackdrop: 'reactions.backdrop',
flatReactorsList: 'reactors_list.flat_list',
flatReactorsList: 'reactions.reactors_list.flat_list',
};
reactionsScreen = element(by.id(this.testID.reactionsScreen));
@@ -30,16 +31,16 @@ class ReactionsScreen {
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 reactorItemUserProfilePictureMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.reactorItemPrefix, userId);
const reactorItemUserDisplayNameMatcher = by.id(`${reactorItemUserTestId}.display_name`);
const reactorItemUserUsernameMatcher = by.id(`${reactorItemUserTestId}.username`);
const reactorItemUsernameMatcher = by.id(`${reactorItemUserTestId}.username`);
return {
reactorItemEmojiAliases: element(reactorItemEmojiAliasesMatcher),
reactorItemUser: element(reactorItemUserMatcher),
reactorItemUserProfilePicture: element(reactorItemUserProfilePictureMatcher),
reactorItemUserDisplayName: element(reactorItemUserDisplayNameMatcher),
reactorItemUserUsername: element(reactorItemUserUsernameMatcher),
reactorItemUsername: element(reactorItemUsernameMatcher),
};
};
}

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {expect} from 'detox';
class TableScreen {
testID = {
tableScreen: 'table.screen',
tableScrollView: 'table.scroll_view',
backButton: 'screen.back.button',
};
tableScreen = element(by.id(this.testID.tableScreen));
tableScrollView = element(by.id(this.testID.tableScrollView));
backButton = element(by.id(this.testID.backButton));
toBeVisible = async () => {
await expect(this.tableScreen).toBeVisible();
return this.tableScreen;
};
back = async () => {
await this.backButton.tap();
await expect(this.tableScreen).not.toBeVisible();
};
}
const tableScreen = new TableScreen();
export default tableScreen;

View File

@@ -74,7 +74,7 @@ class ThreadScreen {
return this.postList.getThreadOverviewPostOptionsButton();
};
getPostListPostItem = (postId: string, text: string, postProfileOptions: any = {}) => {
getPostListPostItem = (postId: string, text = '', postProfileOptions: any = {}) => {
return this.postList.getPost(postId, text, postProfileOptions);
};

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ProfilePicture} from '@support/ui/component';
import {timeouts} from '@support/utils';
import {expect} from 'detox';
class UserProfileScreen {
testID = {
userProfileScreen: 'user_profile.screen',
userProfileAvatarPrefix: 'user_profile_avatar.',
userProfileBackdrop: 'user_profile.backdrop',
systemAdminTag: 'user_profile.system_admin.tag',
teamAdminTag: 'user_profile.team_admin.tag',
channelAdminTag: 'user_profile.channel_admin.tag',
userDisplayName: 'user_profile.display_name',
username: 'user_profile.username',
sendMessageProfileOption: 'user_profile_options.send_message.option',
mentionProfileOption: 'user_profile_options.mention.option',
userNicknameTitle: 'user_profile.nickname.title',
userNicknameDescription: 'user_profile.nickname.description',
userPositionTitle: 'user_profile.position.title',
userPositionDescription: 'user_profile.position.description',
userLocalTimeTitle: 'user_profile.local_time.title',
userLocalTimeDescription: 'user_profile.local_time.description',
};
userProfileScreen = element(by.id(this.testID.userProfileScreen));
userProfileBackdrop = element(by.id(this.testID.userProfileBackdrop));
userDisplayName = element(by.id(this.testID.userDisplayName));
username = element(by.id(this.testID.username));
sendMessageProfileOption = element(by.id(this.testID.sendMessageProfileOption));
mentionProfileOption = element(by.id(this.testID.mentionProfileOption));
userNicknameTitle = element(by.id(this.testID.userNicknameTitle));
userNicknameDescription = element(by.id(this.testID.userNicknameDescription));
userPositionTitle = element(by.id(this.testID.userPositionTitle));
userPositionDescription = element(by.id(this.testID.userPositionDescription));
userLocalTimeTitle = element(by.id(this.testID.userLocalTimeTitle));
userLocalTimeDescription = element(by.id(this.testID.userLocalTimeDescription));
getUserProfilePicture = (userId: string) => {
return element(ProfilePicture.getProfilePictureItemMatcher(this.testID.userProfileAvatarPrefix, userId));
};
toBeVisible = async () => {
await waitFor(this.userProfileScreen).toExist().withTimeout(timeouts.TEN_SEC);
return this.userProfileScreen;
};
close = async () => {
await this.userProfileBackdrop.tap({x: 5, y: 10});
await expect(this.userProfileScreen).not.toBeVisible();
};
}
const userProfileScreen = new UserProfileScreen();
export default userProfileScreen;

View File

@@ -0,0 +1,324 @@
// 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 {
Channel,
Setup,
Team,
User,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId, timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Autocomplete - At-Mention', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testTeam: any;
let testUser: any;
let testOtherUser: any;
let userAtMentionAutocomplete: any;
let otherUserAtMentionAutocomplete: any;
beforeAll(async () => {
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
testUser = user;
({atMentionItem: userAtMentionAutocomplete} = Autocomplete.getAtMentionItem(testUser.id));
({user: testOtherUser} = await User.apiCreateUser(siteOneUrl));
await Team.apiAddUserToTeam(siteOneUrl, testOtherUser.id, testTeam.id);
await Channel.apiAddUserToChannel(siteOneUrl, testOtherUser.id, testChannel.id);
({atMentionItem: otherUserAtMentionAutocomplete} = Autocomplete.getAtMentionItem(testOtherUser.id));
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(testUser);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen
await ChannelScreen.open(channelsCategory, testChannel.name);
});
beforeEach(async () => {
// # Clear post input
await ChannelScreen.postInput.clearText();
// * Verify autocomplete is not displayed
await Autocomplete.toBeVisible(false);
});
afterAll(async () => {
// # Log out
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4878_1 - should suggest user based on username', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username
await ChannelScreen.postInput.typeText(testUser.username);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_2 - should suggest user based on nickname', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in nickname
await ChannelScreen.postInput.typeText(testUser.nickname);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_3 - should suggest user based on first name', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in first name
await ChannelScreen.postInput.typeText(testUser.first_name);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_4 - should suggest user based on last name', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in last name
await ChannelScreen.postInput.typeText(testUser.last_name);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_5 - should suggest user based on lowercase first name', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in lowercase first name
await ChannelScreen.postInput.typeText(testUser.first_name.toLowerCase());
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_6 - should suggest user based on lowercase last name', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in lowercase last name
await ChannelScreen.postInput.typeText(testUser.last_name.toLowerCase());
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_7 - should suggest user based on full name with space', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in full name with space
await ChannelScreen.postInput.typeText(`${testUser.first_name} ${testUser.last_name}`);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_8 - should suggest user based on partial full name with space', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in partial full name with space
await ChannelScreen.postInput.typeText(`${testUser.first_name} ${testUser.last_name.substring(0, testUser.last_name.length - 6)}`);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_9 - should stop suggesting user after full name with trailing space -- KNOWN ISSUE: MM-45279', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in full name
await ChannelScreen.postInput.typeText(`${testUser.first_name} ${testUser.last_name}`);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(userAtMentionAutocomplete).toBeVisible();
// # Type in trailing space
await ChannelScreen.postInput.typeText(' ');
await wait(timeouts.ONE_SEC);
// * Verify at-mention autocomplete does not contain associated user suggestion
await expect(userAtMentionAutocomplete).not.toBeVisible();
});
it('MM-T4878_10 - should stop suggesting user when keyword is not associated with any user', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in keyword not associated with any user
await ChannelScreen.postInput.typeText(getRandomId());
// * Verify at-mention autocomplete does not contain associated user suggestion
await expect(userAtMentionAutocomplete).not.toBeVisible();
});
it('MM-T4878_11 - should be able to select at-mention multiple times', async () => {
// # Type in "@" to activate at-mention autocomplete
await expect(Autocomplete.sectionAtMentionList).not.toBeVisible();
await ChannelScreen.postInput.typeText('@');
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username and tap on user at-mention autocomplete
await ChannelScreen.postInput.typeText(testUser.username);
await userAtMentionAutocomplete.tap();
// * Verify at-mention list disappears
await expect(Autocomplete.sectionAtMentionList).not.toBeVisible();
// # Type in "@" again to re-activate at-mention list
await ChannelScreen.postInput.typeText('@');
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
});
it('MM-T4878_12 - should not be able to autocomplete deactivated user', async () => {
// # Deactivate another channel member user and type in "@" to activate at-mention autocomplete
await User.apiDeactivateUser(siteOneUrl, testOtherUser.id);
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username of deactivated user
await ChannelScreen.postInput.typeText(testOtherUser.username);
// * Verify at-mention autocomplete does not contain associated user suggestion
await expect(otherUserAtMentionAutocomplete).not.toBeVisible();
// # Reactivate user, clear post input, and type in "@" to activate at-mention list
await User.apiUpdateUserActiveStatus(siteOneUrl, testOtherUser.id, true);
await ChannelScreen.postInput.clearText();
await Autocomplete.toBeVisible(false);
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username of reactivated user
await ChannelScreen.postInput.typeText(testOtherUser.username);
// * Verify at-mention autocomplete contains associated user suggestion
await expect(otherUserAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_13 - should be able to autocomplete out of channel user', async () => {
// # Type in "@" to activate at-mention autocomplete
const {user: outOfChannelUser} = await User.apiCreateUser(siteOneUrl);
await Team.apiAddUserToTeam(siteOneUrl, outOfChannelUser.id, testTeam.id);
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username of out of channel user
await ChannelScreen.postInput.typeText(outOfChannelUser.username);
// * Verify at-mention autocomplete contains associated user suggestion
const {atMentionItem: outOfChannelUserAtMentionAutocomplete} = Autocomplete.getAtMentionItem(outOfChannelUser.id);
await expect(outOfChannelUserAtMentionAutocomplete).toBeVisible();
});
it('MM-T4878_14 - should include current user in autocomplete', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username of current user
await ChannelScreen.postInput.typeText(testUser.username);
// * Verify at-mention autocomplete contains current user
const {atMentionItemUserDisplayName, atMentionItemCurrentUserIndicator, atMentionItemUsername} = Autocomplete.getAtMentionItem(testUser.id);
await expect(atMentionItemUserDisplayName).toHaveText(`${testUser.first_name} ${testUser.last_name}`);
await expect(atMentionItemCurrentUserIndicator).toHaveText(' (you)');
await expect(atMentionItemUsername).toHaveText(` @${testUser.username}`);
});
});

View File

@@ -0,0 +1,266 @@
// 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 {
Channel,
Setup,
System,
Team,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId, timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Autocomplete - Channel Mention', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testOtherChannel: any;
let testTeam: any;
let channelMentionAutocomplete: any;
let otherChannelMentionAutocomplete: any;
beforeAll(async () => {
System.apiUpdateConfig(siteOneUrl, {
ServiceSettings: {
EnableAPIChannelDeletion: true,
},
});
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
({channelMentionItem: channelMentionAutocomplete} = Autocomplete.getChannelMentionItem(testChannel.name));
({channel: testOtherChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id}));
await Channel.apiAddUserToChannel(siteOneUrl, user.id, testOtherChannel.id);
({channelMentionItem: otherChannelMentionAutocomplete} = Autocomplete.getChannelMentionItem(testOtherChannel.name));
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen
await ChannelScreen.open(channelsCategory, testChannel.name);
});
beforeEach(async () => {
// # Clear post input
await ChannelScreen.postInput.clearText();
// * Verify autocomplete is not displayed
await Autocomplete.toBeVisible(false);
});
afterAll(async () => {
// # Log out
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4879_1 - should suggest channel based on channel name', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name
await ChannelScreen.postInput.typeText(testChannel.name);
// * Verify channel mention autocomplete contains associated channel suggestion
await expect(channelMentionAutocomplete).toBeVisible();
});
it('MM-T4879_2 - should suggest channel based on channel display name', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel display name
await ChannelScreen.postInput.typeText(testChannel.display_name);
// * Verify channel mention autocomplete contains associated channel suggestion
await expect(channelMentionAutocomplete).toBeVisible();
});
it('MM-T4879_3 - should suggest channel based on lowercase channel display name', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in lowercase channel display name
await ChannelScreen.postInput.typeText(testChannel.display_name.toLowerCase());
// * Verify channel mention autocomplete contains associated channel suggestion
await expect(channelMentionAutocomplete).toBeVisible();
});
it('MM-T4879_4 - should suggest channel based on partial channel display name', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in partial channel display name
await ChannelScreen.postInput.typeText(`${testChannel.display_name.substring(0, testChannel.display_name.length - 4)}`);
// * Verify channel mention autocomplete contains associated channel suggestion
await expect(channelMentionAutocomplete).toBeVisible();
});
it('MM-T4879_5 - should stop suggesting channel after channel display name with trailing space -- KNOWN ISSUE: MM-45279', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel display name
await ChannelScreen.postInput.typeText(testChannel.display_name);
// * Verify channel mention autocomplete contains associated channel suggestion
await expect(channelMentionAutocomplete).toBeVisible();
// # Type in trailing space
await ChannelScreen.postInput.typeText(' ');
await wait(timeouts.ONE_SEC);
// * Verify channel mention autocomplete does not contain associated channel suggestion
await expect(channelMentionAutocomplete).not.toBeVisible();
});
it('MM-T4879_6 - should stop suggesting channel when keyword is not associated with any channel', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in keyword not associated with any channel
await ChannelScreen.postInput.typeText(getRandomId());
// * Verify channel mention autocomplete does not contain associated channel suggestion
await expect(channelMentionAutocomplete).not.toBeVisible();
});
it('MM-T4879_7 - should be able to select channel mention multiple times', async () => {
// # Type in "~" to activate channel mention autocomplete
await expect(Autocomplete.sectionChannelMentionList).not.toBeVisible();
await ChannelScreen.postInput.typeText('~');
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name and tap on channel mention autocomplete
await ChannelScreen.postInput.typeText(testChannel.name);
await channelMentionAutocomplete.tap();
// * Verify channel mention list disappears
await expect(Autocomplete.sectionChannelMentionList).not.toBeVisible();
// # Type in "~" again to re-activate channel mention list
await ChannelScreen.postInput.typeText('~');
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
});
it('MM-T4879_8 - should not be able to autocomplete archived channel', async () => {
// # Archive another team channel and type in "~" to activate channel mention autocomplete
await Channel.apiDeleteChannel(siteOneUrl, testOtherChannel.id);
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name of archived channel
await ChannelScreen.postInput.typeText(testOtherChannel.name);
// * Verify channel mention autocomplete does not contain associated channel suggestion
await expect(otherChannelMentionAutocomplete).not.toBeVisible();
// # Unarchive channel, clear post input, and type in "~" to activate channel mention list
await Channel.apiRestoreChannel(siteOneUrl, testOtherChannel.id);
await ChannelScreen.postInput.clearText();
await Autocomplete.toBeVisible(false);
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name of unarchived channel
await ChannelScreen.postInput.typeText(testOtherChannel.name);
// * Verify channel mention autocomplete contains associated channel suggestion
await expect(otherChannelMentionAutocomplete).toBeVisible();
});
it('MM-T4879_9 - should not be able to autocomplete out of team channel', async () => {
// # Type in "~" to activate channel mention autocomplete
const {team: otherTeam} = await Team.apiCreateTeam(siteOneUrl);
const {channel: outOfTeamChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: otherTeam.id});
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name of out of team channel
await ChannelScreen.postInput.typeText(outOfTeamChannel.name);
// * Verify channel mention autocomplete does not contain associated channel suggestion
const {channelMentionItem: outOfTeamChannelChannelMentionAutocomplete} = Autocomplete.getChannelMentionItem(outOfTeamChannel.name);
await expect(outOfTeamChannelChannelMentionAutocomplete).not.toBeVisible();
});
it('MM-T4879_10 - should include current channel in autocomplete', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name of current channel
await ChannelScreen.postInput.typeText(testChannel.name);
// * Verify channel mention autocomplete contains current channel
const {channelMentionItemChannelDisplayName, channelMentionItemChannelName} = Autocomplete.getChannelMentionItem(testChannel.name);
await expect(channelMentionItemChannelDisplayName).toHaveText(`${testChannel.display_name}`);
await expect(channelMentionItemChannelName).toHaveText(` ~${testChannel.name}`);
});
});

View File

@@ -0,0 +1,103 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelInfoScreen,
ChannelListScreen,
ChannelScreen,
CreateOrEditChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Autocomplete - Edit Channel', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen, open channel info screen, and open edit channel screen
await ChannelScreen.open(channelsCategory, channel.name);
await ChannelInfoScreen.open();
await CreateOrEditChannelScreen.openEditChannel();
});
beforeEach(async () => {
// # Clear header input
await CreateOrEditChannelScreen.headerInput.clearText();
});
afterAll(async () => {
// # Log out
await CreateOrEditChannelScreen.back();
await ChannelInfoScreen.close();
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4885_1 - should render at-mention autocomplete in header input', async () => {
// * Verify at-mention list is not displayed
await expect(Autocomplete.sectionAtMentionList).not.toBeVisible();
// # Type in "@" to activate at-mention autocomplete
await CreateOrEditChannelScreen.headerInput.typeText('@');
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
});
it('MM-T4885_2 - should render channel mention autocomplete in header input', async () => {
// * Verify channel mention list is not displayed
await expect(Autocomplete.sectionChannelMentionList).not.toBeVisible();
// # Type in "~" to activate channel mention autocomplete
await CreateOrEditChannelScreen.headerInput.typeText('~');
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
});
it('MM-T4885_3 - should render emoji suggestion autocomplete in header input', async () => {
// * Verify emoji suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in ":" followed by 2 characters to activate emoji suggestion autocomplete
await CreateOrEditChannelScreen.headerInput.typeText(':sm');
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
});
it('MM-T4885_4 - should not render slash suggestion autocomplete in header input', async () => {
// * Verify slash suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in "/" to activate slash suggestion autocomplete
await CreateOrEditChannelScreen.headerInput.typeText('/');
// * Verify slash suggestion list is still not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
});
});

View File

@@ -0,0 +1,100 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
CreateOrEditChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Autocomplete - Edit Channel Header', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen and open edit channel header screen
await ChannelScreen.open(channelsCategory, channel.name);
await CreateOrEditChannelScreen.openEditChannelHeader();
});
beforeEach(async () => {
// # Clear header input
await CreateOrEditChannelScreen.headerInput.clearText();
});
afterAll(async () => {
// # Log out
await CreateOrEditChannelScreen.close();
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4884_1 - should render at-mention autocomplete in header input', async () => {
// * Verify at-mention list is not displayed
await expect(Autocomplete.sectionAtMentionList).not.toBeVisible();
// # Type in "@" to activate at-mention autocomplete
await CreateOrEditChannelScreen.headerInput.typeText('@');
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
});
it('MM-T4884_2 - should render channel mention autocomplete in header input', async () => {
// * Verify channel mention list is not displayed
await expect(Autocomplete.sectionChannelMentionList).not.toBeVisible();
// # Type in "~" to activate channel mention autocomplete
await CreateOrEditChannelScreen.headerInput.typeText('~');
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
});
it('MM-T4884_3 - should render emoji suggestion autocomplete in header input', async () => {
// * Verify emoji suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in ":" followed by 2 characters to activate emoji suggestion autocomplete
await CreateOrEditChannelScreen.headerInput.typeText(':sm');
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
});
it('MM-T4884_4 - should not render slash suggestion autocomplete in header input', async () => {
// * Verify slash suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in "/" to activate slash suggestion autocomplete
await CreateOrEditChannelScreen.headerInput.typeText('/');
// * Verify slash suggestion list is still not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
});
});

View File

@@ -0,0 +1,108 @@
// 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 {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
EditPostScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId} from '@support/utils';
import {expect} from 'detox';
describe('Autocomplete - Edit Post', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen, post a message, open post options for message, and open edit post screen
const message = `Messsage ${getRandomId()}`;
await ChannelScreen.open(channelsCategory, channel.name);
await ChannelScreen.postMessage(message);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, channel.id);
await ChannelScreen.openPostOptionsFor(post.id, message);
await EditPostScreen.open();
});
beforeEach(async () => {
// # Clear message input
await EditPostScreen.messageInput.clearText();
});
afterAll(async () => {
// # Log out
await EditPostScreen.close();
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4883_1 - should render at-mention autocomplete in message input', async () => {
// * Verify at-mention list is not displayed
await expect(Autocomplete.sectionAtMentionList).not.toBeVisible();
// # Type in "@" to activate at-mention autocomplete
await EditPostScreen.messageInput.typeText('@');
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
});
it('MM-T4883_2 - should render channel mention autocomplete in message input', async () => {
// * Verify channel mention list is not displayed
await expect(Autocomplete.sectionChannelMentionList).not.toBeVisible();
// # Type in "~" to activate channel mention autocomplete
await EditPostScreen.messageInput.typeText('~');
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
});
it('MM-T4883_3 - should render emoji suggestion autocomplete in message input', async () => {
// * Verify emoji suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in ":" followed by 2 characters to activate emoji suggestion autocomplete
await EditPostScreen.messageInput.typeText(':sm');
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
});
it('MM-T4883_4 - should not render slash suggestion autocomplete in message input', async () => {
// * Verify slash suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in "/" to activate slash suggestion autocomplete
await EditPostScreen.messageInput.typeText('/');
// * Verify slash suggestion list is still not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
});
});

View File

@@ -0,0 +1,167 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId, timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Autocomplete - Emoji Suggestion', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
const emojiName = 'fox_face';
const emojiNameFirst2Chars = emojiName.substring(0, 2);
const emojiName3rdToLastChars = emojiName.substring(2);
let emojiSuggestionAutocomplete: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
({emojiSuggestionItem: emojiSuggestionAutocomplete} = Autocomplete.getEmojiSuggestionItem(emojiName));
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen
await ChannelScreen.open(channelsCategory, channel.name);
});
beforeEach(async () => {
// # Clear post input
await ChannelScreen.postInput.clearText();
// * Verify autocomplete is not displayed
await Autocomplete.toBeVisible(false);
});
afterAll(async () => {
// # Log out
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4880_1 - should suggest emoji based on emoji name', async () => {
// # Type in ":" then first 2 characters of emoji name to activate emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
await Autocomplete.toBeVisible();
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in 3rd to last characters of emoji name
await ChannelScreen.postInput.typeText(emojiName3rdToLastChars);
// * Verify emoji suggestion autocomplete contains associated emoji suggestion
await expect(emojiSuggestionAutocomplete).toBeVisible();
});
it('MM-T4880_2 - should suggest emoji based on uppercase emoji name', async () => {
// # Type in ":" then uppercase first 2 characters of emoji name to activate emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars.toUpperCase()}`);
await Autocomplete.toBeVisible();
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in uppercase 3rd to last characters of emoji name
await ChannelScreen.postInput.typeText(emojiName3rdToLastChars.toUpperCase());
// * Verify emoji suggestion autocomplete contains associated emoji suggestion
await expect(emojiSuggestionAutocomplete).toBeVisible();
});
it('MM-T4880_3 - should suggest emoji based on partial emoji name', async () => {
// # Type in ":" then first 2 characters of emoji name to activate emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
await Autocomplete.toBeVisible();
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in partial emoji name
await ChannelScreen.postInput.typeText(`${emojiName.substring(2, 4)}`);
// * Verify emoji suggestion autocomplete contains associated emoji suggestion
await expect(emojiSuggestionAutocomplete).toBeVisible();
});
it('MM-T4880_4 - should stop suggesting emoji after emoji name with trailing space', async () => {
// # Type in ":" then first 2 characters of emoji name to activate emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
await Autocomplete.toBeVisible();
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in 3rd to last characters of emoji name
await ChannelScreen.postInput.typeText(emojiName3rdToLastChars);
// * Verify emoji suggestion autocomplete contains associated emoji suggestion
await expect(emojiSuggestionAutocomplete).toBeVisible();
// # Type in trailing space
await ChannelScreen.postInput.typeText(' ');
await wait(timeouts.ONE_SEC);
// * Verify emoji suggestion autocomplete does not contain associated emoji suggestion
await expect(emojiSuggestionAutocomplete).not.toBeVisible();
});
it('MM-T4880_5 - should stop suggesting emoji when keyword is not associated with any emoji', async () => {
// # Type in ":" then first 2 characters of emoji name to activate emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
await Autocomplete.toBeVisible();
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in keyword not associated with any emoji
await ChannelScreen.postInput.typeText(getRandomId());
// * Verify emoji suggestion autocomplete does not contain associated emoji suggestion
await expect(emojiSuggestionAutocomplete).not.toBeVisible();
});
it('MM-T4880_6 - should be able to select emoji suggestion multiple times', async () => {
// # Type in ":" then first 2 characters of emoji name to activate emoji suggestion autocomplete
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in 3rd to last characters of emoji name and tap on emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(emojiName3rdToLastChars);
await emojiSuggestionAutocomplete.tap();
// * Verify emoji suggestion list disappears
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in ":" then first 2 characters of emoji name again to re-activate emoji suggestion list
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
});
});

View File

@@ -0,0 +1,100 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Autocomplete - Post Draft', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen
await ChannelScreen.open(channelsCategory, channel.name);
});
beforeEach(async () => {
// # Clear post input
await ChannelScreen.postInput.clearText();
// * Verify autocomplete is not displayed
await Autocomplete.toBeVisible(false);
});
afterAll(async () => {
// # Log out
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4882_1 - should render at-mention autocomplete in post input', async () => {
// * Verify at-mention list is not displayed
await expect(Autocomplete.sectionAtMentionList).not.toBeVisible();
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
});
it('MM-T4882_2 - should render channel mention autocomplete in post input', async () => {
// * Verify channel mention list is not displayed
await expect(Autocomplete.sectionChannelMentionList).not.toBeVisible();
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
});
it('MM-T4882_3 - should render emoji suggestion autocomplete in post input', async () => {
// * Verify emoji suggestion list is not displayed
await expect(Autocomplete.flatEmojiSuggestionList).not.toBeVisible();
// # Type in ":" followed by 2 characters to activate emoji suggestion autocomplete
await ChannelScreen.postInput.typeText(':sm');
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
});
it('MM-T4882_4 - should render slash suggestion autocomplete in post input', async () => {
// * Verify slash suggestion list is not displayed
await expect(Autocomplete.flatSlashSuggestionList).not.toBeVisible();
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
});
});

View File

@@ -0,0 +1,165 @@
// 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 {Setup} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId, timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Autocomplete - Slash Suggestion', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
const slashCommand = 'away';
let slashSuggestionAutocomplete: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
({slashSuggestionItem: slashSuggestionAutocomplete} = Autocomplete.getSlashSuggestionItem(slashCommand));
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen
await ChannelScreen.open(channelsCategory, channel.name);
});
beforeEach(async () => {
// # Clear post input
await ChannelScreen.postInput.clearText();
// * Verify autocomplete is not displayed
await Autocomplete.toBeVisible(false);
});
afterAll(async () => {
// # Log out
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4881_1 - should suggest slash command based on slash command name', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
await Autocomplete.toBeVisible();
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in slash command name
await ChannelScreen.postInput.typeText(slashCommand);
// * Verify slash suggestion autocomplete contains associated slash command suggestion
await expect(slashSuggestionAutocomplete).toBeVisible();
});
it('MM-T4881_2 - should suggest slash command based on partial slash command name', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
await Autocomplete.toBeVisible();
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in partial slash command name
await ChannelScreen.postInput.typeText(`${slashCommand.substring(0, slashCommand.length - 2)}`);
// * Verify slash suggestion autocomplete contains associated slash command suggestion
await expect(slashSuggestionAutocomplete).toBeVisible();
});
it('MM-T4881_3 - should stop suggesting slash command after uppercase slash command name', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
await Autocomplete.toBeVisible();
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in uppercase slash command name
await ChannelScreen.postInput.typeText(slashCommand.toUpperCase());
// * Verify slash suggestion autocomplete does not contain associated slash command suggestion
await expect(slashSuggestionAutocomplete).not.toBeVisible();
});
it('MM-T4881_4 - should stop suggesting slash command after slash command name with trailing space', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
await Autocomplete.toBeVisible();
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in slash command name
await ChannelScreen.postInput.typeText(slashCommand);
// * Verify slash suggestion autocomplete contains associated slash command suggestion
await expect(slashSuggestionAutocomplete).toBeVisible();
// # Type in trailing space
await ChannelScreen.postInput.typeText(' ');
await wait(timeouts.ONE_SEC);
// * Verify slash suggestion autocomplete does not contain associated slash command suggestion
await expect(slashSuggestionAutocomplete).not.toBeVisible();
});
it('MM-T4881_5 - should stop suggesting slash command when keyword is not associated with any slash command', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
await Autocomplete.toBeVisible();
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in keyword not associated with any slash command
await ChannelScreen.postInput.typeText(getRandomId());
// * Verify slash suggestion autocomplete does not contain associated slash command suggestion
await expect(slashSuggestionAutocomplete).not.toBeVisible();
});
it('MM-T4881_6 - should not be able to select slash suggestion multiple times', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await expect(Autocomplete.flatSlashSuggestionList).not.toBeVisible();
await ChannelScreen.postInput.typeText('/');
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in slash command name and tap on slash suggestion autocomplete
await ChannelScreen.postInput.typeText(slashCommand);
await slashSuggestionAutocomplete.tap();
// * Verify slash suggestion list disappears
await expect(Autocomplete.flatSlashSuggestionList).not.toBeVisible();
// # Type in "/" again to re-activate slash suggestion list
await ChannelScreen.postInput.typeText('/');
// * Verify slash suggestion list is not displayed
await expect(Autocomplete.flatSlashSuggestionList).not.toBeVisible();
});
});

View File

@@ -57,9 +57,9 @@ describe('Channels - Channel Post List', () => {
await expect(ChannelScreen.backButton).toBeVisible();
await expect(ChannelScreen.headerTitle).toHaveText(testChannel.display_name);
await expect(ChannelScreen.introDisplayName).toHaveText(testChannel.display_name);
await expect(ChannelScreen.introOptionAddPeopleItem).toBeVisible();
await expect(ChannelScreen.introOptionSetHeaderItem).toBeVisible();
await expect(ChannelScreen.introOptionChannelDetailsItem).toBeVisible();
await expect(ChannelScreen.introAddPeopleOption).toBeVisible();
await expect(ChannelScreen.introSetHeaderOption).toBeVisible();
await expect(ChannelScreen.introChannelDetailsOption).toBeVisible();
await expect(ChannelScreen.postList.getFlatList()).toBeVisible();
await expect(ChannelScreen.postDraft).toBeVisible();
await expect(ChannelScreen.postInput).toBeVisible();

View File

@@ -82,7 +82,7 @@ describe('Channels - Create Channel and Edit Channel Header', () => {
await expect(ChannelScreen.introDisplayName).toHaveText(displayName);
// # Tap on set header option to edit the channel header
await ChannelScreen.introOptionSetHeaderItem.tap();
await ChannelScreen.introSetHeaderOption.tap();
// * Verify channel header is correct
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(header);
@@ -90,7 +90,7 @@ describe('Channels - Create Channel and Edit Channel Header', () => {
// # Edit the channel header, save, and re-open edit channel header screen
await CreateOrEditChannelScreen.headerInput.replaceText(`${header} edit`);
await CreateOrEditChannelScreen.saveButton.tap();
await CreateOrEditChannelScreen.openEditChannel();
await CreateOrEditChannelScreen.openEditChannelHeader();
// * Verify channel header has new value
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(`${header} edit`);
@@ -119,7 +119,7 @@ describe('Channels - Create Channel and Edit Channel Header', () => {
await expect(ChannelScreen.introDisplayName).toHaveText(displayName);
// # Tap on set header option to edit the channel header
await ChannelScreen.introOptionSetHeaderItem.tap();
await ChannelScreen.introSetHeaderOption.tap();
// * Verify channel header is correct
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(header);
@@ -127,7 +127,7 @@ describe('Channels - Create Channel and Edit Channel Header', () => {
// # Edit the channel header, save, and re-open edit channel header screen
await CreateOrEditChannelScreen.headerInput.replaceText(`${header} edit`);
await CreateOrEditChannelScreen.saveButton.tap();
await CreateOrEditChannelScreen.openEditChannel();
await CreateOrEditChannelScreen.openEditChannelHeader();
// * Verify channel header has new value
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(`${header} edit`);

View File

@@ -40,7 +40,7 @@ describe('Channels - Create Direct Message', () => {
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
await LoginScreen.login(testUser);
});
beforeEach(async () => {

View File

@@ -0,0 +1,160 @@
// 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 {
Channel,
Post,
Setup,
Team,
User,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {Alert} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
UserProfileScreen,
} from '@support/ui/screen';
import {timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - At-Mention', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testTeam: any;
let testUser: any;
let testOtherUser: any;
beforeAll(async () => {
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
testUser = user;
({user: testOtherUser} = await User.apiCreateUser(siteOneUrl));
await Team.apiAddUserToTeam(siteOneUrl, testOtherUser.id, testTeam.id);
await Channel.apiAddUserToChannel(siteOneUrl, testOtherUser.id, testChannel.id);
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(testUser);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4874_1 - should post at-mention as lowercase', async () => {
// # Open a channel screen and post a message with lowercase at-mention
const camelCaseUsernameMessage = `Message @${testUser.username.substring(0, 1).toUpperCase()}${testUser.username.substring(1)}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(camelCaseUsernameMessage);
// * Verify at-mention is posted as lowercase
const {post: lowerCasePost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(lowerCasePost.id, `Message @${testUser.username.toLowerCase()}`);
// # Post a message with uppercase at-mention
const upperCaseUsernameMessage = `Message @${testOtherUser.username.toUpperCase()}`;
await ChannelScreen.postMessage(upperCaseUsernameMessage);
// * Verify at-mention is posted as lowercase
const {post: upperCasePost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(upperCasePost.id, `Message @${testOtherUser.username.toLowerCase()}`);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4874_2 - should display confirmation dialog when posting @all, @channel, and @here', async () => {
// # Add more users to the channel, open a channel screen, and post @all
[...Array(3).keys()].forEach(async (key) => {
const {user} = await User.apiCreateUser(siteOneUrl, {prefix: `a-${key}-`});
await Team.apiAddUserToTeam(siteOneUrl, user.id, testTeam.id);
await Channel.apiAddUserToChannel(siteOneUrl, user.id, testChannel.id);
});
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postInput.replaceText('@all');
await ChannelScreen.sendButton.tap();
// * Verify confirmation dialog is displayed
await expect(Alert.confirmSendingNotificationsTitle).toBeVisible();
// # Tap on confirm button
await Alert.confirmButton.tap();
// * Verify @all is posted
const {post: atAllPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(atAllPost.id, '@all');
// # Post @channel
await ChannelScreen.postInput.clearText();
await ChannelScreen.postInput.replaceText('@channel');
await ChannelScreen.sendButton.tap();
// * Verify confirmation dialog is displayed
await expect(Alert.confirmSendingNotificationsTitle).toBeVisible();
// # Tap on confirm button
await Alert.confirmButton.tap();
// * Verify @channel is posted
const {post: atChannelPost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(atChannelPost.id, '@channel');
// # Post @here
await ChannelScreen.postInput.clearText();
await ChannelScreen.postInput.replaceText('@here');
await ChannelScreen.sendButton.tap();
// * Verify confirmation dialog is displayed
await expect(Alert.confirmSendingNotificationsTitle).toBeVisible();
// # Tap on confirm button
await Alert.confirmButton.tap();
// * Verify @here is posted
const {post: atHerePost} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(atHerePost.id, '@here');
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4874_3 - should be able to open user profile by tapping on at-mention', async () => {
// # Open a channel screen, post a message with at-mention, and tap on at-mention
const message = `@${testUser.username}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
await element(by.text(message)).tap({x: 5, y: 10});
await wait(timeouts.ONE_SEC);
// * Verify on user profile screen
await UserProfileScreen.toBeVisible();
await expect(UserProfileScreen.getUserProfilePicture(testUser.id)).toBeVisible();
await expect(UserProfileScreen.userDisplayName).toHaveText(`@${testUser.username}`);
// # Go back to channel list screen
await UserProfileScreen.close();
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,95 @@
// 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 {
Channel,
Post,
Setup,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Channel Link', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testTeam: any;
let testUser: any;
beforeAll(async () => {
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
testUser = user;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(testUser);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4877_1 - should be able to open joined channel by tapping on channel link from main channel', async () => {
// # Open a channel screen and post a channel link to target channel
await ChannelScreen.open(channelsCategory, testChannel.name);
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const channelLink = `${serverOneUrl}/${testTeam.name}/channels/${targetChannel.name}`;
await ChannelScreen.postMessage(channelLink);
// # Tap on channel link
await element(by.text(channelLink)).tap({x: 5, y: 10});
await wait(timeouts.ONE_SEC);
// * Verify redirected to target channel
await expect(ChannelScreen.headerTitle).toHaveText(targetChannel.display_name);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4877_2 - should be able to open joined channel by tapping on channel link from reply thread', async () => {
// # Open a channel screen, post a channel link to target channel, tap on post to open reply thread, and tap on channel link
await ChannelScreen.open(channelsCategory, testChannel.name);
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const channelLink = `${serverOneUrl}/${testTeam.name}/channels/${targetChannel.name}`;
await ChannelScreen.postMessage(channelLink);
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id);
await postListPostItem.tap({x: 1, y: 1});
await element(by.text(channelLink)).tap({x: 5, y: 10});
await wait(timeouts.ONE_SEC);
// * Verify redirected to target channel
await expect(ChannelScreen.headerTitle).toHaveText(targetChannel.display_name);
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,91 @@
// 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 {
Channel,
Post,
Setup,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Channel Mention', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testTeam: any;
let testUser: any;
beforeAll(async () => {
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
testUser = user;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(testUser);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
it('MM-T4875_1 - should post channel mention as channel display name', async () => {
// # Open a channel screen and post a channel name mention
await ChannelScreen.open(channelsCategory, testChannel.name);
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const channelNameMention = `~${targetChannel.name}`;
await ChannelScreen.postMessage(channelNameMention);
// * Verify post shows channel display name mention
const channelDisplayNameMention = `~${targetChannel.display_name}`;
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(post.id, channelDisplayNameMention);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4875_2 - should be able to open joined channel by tapping on channel mention', async () => {
// # Open a channel screen, post a channel name mention, and tap on channel display name mention
await ChannelScreen.open(channelsCategory, testChannel.name);
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const channelNameMention = `~${targetChannel.name}`;
const channelDisplayNameMention = `~${targetChannel.display_name}`;
await ChannelScreen.postMessage(channelNameMention);
await element(by.text(channelDisplayNameMention)).tap({x: 5, y: 10});
await wait(timeouts.ONE_SEC);
// * Verify redirected to target channel
await expect(ChannelScreen.headerTitle).toHaveText(targetChannel.display_name);
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -41,7 +41,7 @@ describe('Messaging - Emojis and Reactions', () => {
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
await LoginScreen.login(testUser);
});
beforeEach(async () => {
@@ -72,7 +72,7 @@ describe('Messaging - Emojis and Reactions', () => {
// # Open emoji picker screen and add a new reaction
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText('clown_face');
await EmojiPickerScreen.searchInput.replaceText('clown_face');
await element(by.text('🤡')).tap();
// * Verify new reaction is added to the message
@@ -103,7 +103,7 @@ describe('Messaging - Emojis and Reactions', () => {
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.openPostOptionsFor(post.id, message);
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText('fire');
await EmojiPickerScreen.searchInput.replaceText('fire');
await element(by.text('🔥')).tap();
// * Verify reaction is added to the message
@@ -115,11 +115,11 @@ describe('Messaging - Emojis and Reactions', () => {
// * Verify user who reacted with the emoji
await ReactionsScreen.toBeVisible();
const {reactorItemEmojiAliases, reactorItemUserProfilePicture, reactorItemUserDisplayName, reactorItemUserUsername} = ReactionsScreen.getReactorItem(testUser.id, 'fire');
const {reactorItemEmojiAliases, reactorItemUserProfilePicture, reactorItemUserDisplayName, reactorItemUsername} = 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}`);
await expect(reactorItemUsername).toHaveText(` @${testUser.username}`);
// # Go back to channel list screen
await ReactionsScreen.close();
@@ -168,7 +168,7 @@ describe('Messaging - Emojis and Reactions', () => {
const searchTerm = 'blahblahblahblah';
await ChannelScreen.openPostOptionsFor(post.id, message);
await EmojiPickerScreen.open();
await EmojiPickerScreen.searchInput.typeText(searchTerm);
await EmojiPickerScreen.searchInput.replaceText(searchTerm);
// * Verify empty search state for emoji picker
await expect(element(by.text(`No results for “${searchTerm}`))).toBeVisible();

View File

@@ -0,0 +1,67 @@
// 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,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Block Quote', () => {
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-T4898_1 - should be able to display markdown block quote', async () => {
// # Open a channel screen and post a markdown block quote
const message = 'this is a quote that i am making long so it wraps on mobile this is a quote that i am making long so it wraps on mobile this is a quote that i am making long so it wraps on mobile this is a quote that i am making long so it wraps on mobile';
const markdownBlockQuote = `>${message}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownBlockQuote);
// * Verify markdown block quote is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemBlockQuote} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemBlockQuote).toBeVisible();
await expect(element(by.text(message))).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,88 @@
// 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,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Code', () => {
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-T4895_1 - should be able to display markdown code block', async () => {
// # Open a channel screen and post a markdown code block
const line1 = 'let x = 10;';
const line2 = 'let y = 20;';
// eslint-disable-next-line no-template-curly-in-string
const line3 = 'console.log(`sum: ${x + y}`);';
const message = `${line1}\n${line2}\n${line3}`;
const markdownCodeBlock = `\`\`\`\n${message}\n\`\`\``;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownCodeBlock);
// * Verify markdown code block is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemCodeBlock} = ChannelScreen.getPostListPostItem(post.id);
await ChannelScreen.flatPostList.scrollTo('bottom');
await expect(postListPostItemCodeBlock).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4895_2- should be able to display markdown html', async () => {
// # Open a channel screen and post a html
const message = '<html>\n<body>\n<span>This is html block</span>\n</body>\n</html>';
const markdownHtml = `\`\`\`html\n${message}\n\`\`\``;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownHtml);
// * Verify markdown html is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemCodeBlock} = ChannelScreen.getPostListPostItem(post.id);
await ChannelScreen.flatPostList.scrollTo('bottom');
await expect(postListPostItemCodeBlock).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,67 @@
// 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,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Heading', () => {
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-T4893_1 - should be able to display markdown heading', async () => {
// # Open a channel screen and post a markdown heading
const message = 'Heading';
const markdownHeading = `## ${message}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownHeading);
// * Verify markdown heading is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemHeading} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemHeading).toBeVisible();
await expect(element(by.text(message))).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,80 @@
// 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,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Image', () => {
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-T4896_1 - should be able to display markdown image', async () => {
// # Open a channel screen and post a markdown image
const markdownImage = '![Mattermost](https://docs.mattermost.com/_images/icon-76x76.png)';
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownImage);
// * Verify markdown image is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemImage} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemImage).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4896_2 - should be able to display markdown image with link', async () => {
// # Open a channel screen and post a markdown image with link
const markdownImage = '[![Mattermost](https://docs.mattermost.com/_images/icon-76x76.png)](https://github.com/mattermost/mattermost-server)';
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownImage);
// * Verify markdown image with link is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemImage} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemImage).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,93 @@
// 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,
System,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Latex', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
beforeAll(async () => {
System.apiUpdateConfig(siteOneUrl, {
ServiceSettings: {
EnableLatex: true,
EnableInlineLatex: true,
},
});
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-T4900_1 - should be able to display markdown latex code block', async () => {
// # Open a channel screen and post a markdown latex code block
// eslint-disable-next-line no-useless-escape
const message = 'X_k = \sum_{n=0}^{2N-1} x_n \cos \left[\frac{\pi}{N} \left(n+\frac{1}{2}+\frac{N}{2}\right) \left(k+\frac{1}{2}\right) \right]';
const markdownLatexCodeBlock = `\`\`\`latex\n${message}\n\`\`\``;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownLatexCodeBlock);
// * Verify markdown latex code block is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemLatexCodeBlock} = ChannelScreen.getPostListPostItem(post.id);
await ChannelScreen.flatPostList.scrollTo('bottom');
await expect(postListPostItemLatexCodeBlock).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4900_2 - should be able to display markdown inline latex -- KNOWN ISSUE: MM-45466', async () => {
// # Open a channel screen and post a markdown inline latex
// eslint-disable-next-line no-useless-escape
const message = 'X_k = \sum_{n=0}^{2N-1} x_n \cos \left[\frac{\pi}{N} \left(n+\frac{1}{2}+\frac{N}{2}\right) \left(k+\frac{1}{2}\right) \right]';
const markdownInlineLatex = `$${message}$`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownInlineLatex);
// * Verify markdown inline latex is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemInlineLatex} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemInlineLatex).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,102 @@
// 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,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown List', () => {
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-T4894_1 - should be able to display markdown bullet list', async () => {
// # Open a channel screen and post a markdown bullet list
const item1 = 'item one';
const item2 = 'item two';
const item2SubPoint = 'item two sub-point';
const markdownBulletList = `* ${item1}\n- ${item2}\n + ${item2SubPoint}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownBulletList);
// * Verify markdown bullet list is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemListItem, postListPostItemListItemBullet} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('•');
await expect(element(by.text(item1))).toBeVisible();
await expect(postListPostItemListItem.atIndex(1)).toBeVisible();
await expect(postListPostItemListItemBullet.atIndex(1)).toHaveText('•');
await expect(element(by.text(item2))).toBeVisible();
await expect(postListPostItemListItem.atIndex(2)).toBeVisible();
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('◦');
await expect(element(by.text(item2SubPoint))).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4894_2 - should be able to display markdown ordered list', async () => {
// # Open a channel screen and post a markdown ordered list
const item1 = 'Item one';
const item2 = 'Item two';
const item3 = 'Item three';
const markdownOrderedList = `1. ${item1}\n1. ${item2}\n1. ${item3}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdownOrderedList);
// * Verify markdown ordered list is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemListItem, postListPostItemListItemBullet} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemListItem.atIndex(0)).toBeVisible();
await expect(postListPostItemListItemBullet.atIndex(0)).toHaveText('1.');
await expect(element(by.text(item1))).toBeVisible();
await expect(postListPostItemListItem.atIndex(1)).toBeVisible();
await expect(postListPostItemListItemBullet.atIndex(1)).toHaveText('2.');
await expect(element(by.text(item2))).toBeVisible();
await expect(postListPostItemListItem.atIndex(2)).toBeVisible();
await expect(postListPostItemListItemBullet.atIndex(2)).toHaveText('3.');
await expect(element(by.text(item3))).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,68 @@
// 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,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Separator', () => {
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-T4897_1 - should be able to display markdown separator', async () => {
// # Open a channel screen and post a markdown separator
const markdownSeparator = '---';
await ChannelScreen.open(channelsCategory, testChannel.name);
await Post.apiCreatePost(siteOneUrl, {
channelId: testChannel.id,
message: markdownSeparator,
});
// * Verify markdown separator is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemThematicBreak} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemThematicBreak).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,228 @@
// 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,
ServerScreen,
TableScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Messaging - Markdown Table', () => {
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-T4899_1 - should be able to display markdown table', async () => {
// # Open a channel screen and post a markdown table
const markdownTable =
'| A | B | C |\n' +
'|:---|:---|:---|\n' +
'| 1 | Name | Toast |\n' +
'| 2 | Name | Server |\n';
await ChannelScreen.open(channelsCategory, testChannel.name);
await Post.apiCreatePost(siteOneUrl, {
channelId: testChannel.id,
message: markdownTable,
});
// * Verify markdown table is displayed
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemTable} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemTable).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4899_2 - should be able to display markdown table with long text wrapped properly', async () => {
// # Open a channel screen and post a markdown table with long text
const markdownTable =
'| Left header that wraps | Center header that wraps | Right header that wraps |\n' +
'| :-- | :-: | --: |\n' +
'| Left text that wraps row | Center text that wraps row | Right text that wraps row |\n';
await ChannelScreen.open(channelsCategory, testChannel.name);
await Post.apiCreatePost(siteOneUrl, {
channelId: testChannel.id,
message: markdownTable,
});
// * Verify table is displayed with long text wrapped properly
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemTable, postListPostItemTableExpandButton} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemTable).toBeVisible();
await expect(element(by.text('Left header that wraps'))).toBeVisible();
await expect(element(by.text('Center header that wraps'))).toBeVisible();
await expect(element(by.text('Right header that wraps'))).toBeVisible();
await expect(element(by.text('Left text that wraps row'))).toBeVisible();
await expect(element(by.text('Center text that wraps row'))).toBeVisible();
await expect(element(by.text('Right text that wraps row'))).toBeVisible();
// # Expand to full view
await ChannelScreen.flatPostList.scrollTo('bottom');
await postListPostItemTableExpandButton.tap();
// * Verify on table screen with the markdown table
await TableScreen.toBeVisible();
await expect(element(by.text('Left header that wraps'))).toBeVisible();
await expect(element(by.text('Center header that wraps'))).toBeVisible();
await expect(element(by.text('Right header that wraps'))).toBeVisible();
await expect(element(by.text('Left text that wraps row'))).toBeVisible();
await expect(element(by.text('Center text that wraps row'))).toBeVisible();
await expect(element(by.text('Right text that wraps row'))).toBeVisible();
// # Go back to channel list screen
await TableScreen.back();
await ChannelScreen.back();
});
it('MM-T4899_3 - should be able to open markdown table in full view and allow horizontal scroll', async () => {
// # Open a channel screen and post a markdown table with more columns past horizontal view
const markdownTable =
'| Header | Header | Header | Header | Header | Header | Header | Header HS last |\n' +
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(7) +
'| Left | Center | Right | Right | Left | Center | Right | Right HS last |\n';
await ChannelScreen.open(channelsCategory, testChannel.name);
await Post.apiCreatePost(siteOneUrl, {
channelId: testChannel.id,
message: markdownTable,
});
// * Verify table is displayed with some right columns not visible
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemTable, postListPostItemTableExpandButton} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemTable).toBeVisible();
await expect(element(by.text('Header HS last'))).not.toBeVisible();
await expect(element(by.text('Right HS last'))).not.toBeVisible();
// # Expand to full view
await ChannelScreen.flatPostList.scrollTo('bottom');
await postListPostItemTableExpandButton.tap();
await TableScreen.toBeVisible();
await expect(element(by.text('Header HS last'))).not.toBeVisible();
await expect(element(by.text('Right HS last'))).not.toBeVisible();
// * Verify table screen is scrollable to the right
await TableScreen.tableScrollView.scrollTo('right');
await expect(element(by.text('Header HS last'))).toBeVisible();
await expect(element(by.text('Right HS last'))).toBeVisible();
// # Go back to channel list screen
await TableScreen.back();
await ChannelScreen.back();
});
it('MM-T4899_4 - should be able to open markdown table in full view and allow vertical scroll', async () => {
// # Open a channel screen and post a markdown table with more rows past vertical view
const markdownTable =
'| Header | Header | Header VS last |\n' +
'| :-- | :-: | --: |\n' +
'| Left | Center | Right |\n'.repeat(30) +
'| Left | Center | Right VS last |\n';
await ChannelScreen.open(channelsCategory, testChannel.name);
await Post.apiCreatePost(siteOneUrl, {
channelId: testChannel.id,
message: markdownTable,
});
// * Verify table is displayed with some bottom rows not visible
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemTable, postListPostItemTableExpandButton} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemTable).toBeVisible();
await expect(element(by.text('Header VS last'))).toBeVisible();
await expect(element(by.text('Right VS last'))).not.toBeVisible();
// # Expand to full view
await ChannelScreen.flatPostList.scrollTo('bottom');
await postListPostItemTableExpandButton.tap();
await TableScreen.toBeVisible();
await expect(element(by.text('Header VS last'))).toBeVisible();
await expect(element(by.text('Right VS last'))).not.toBeVisible();
// * Verify table screen is scrollable to the bottom
await TableScreen.tableScrollView.scrollTo('bottom');
await expect(element(by.text('Header VS last'))).not.toBeVisible();
await expect(element(by.text('Right VS last'))).toBeVisible();
// # Go back to channel list screen
await TableScreen.back();
await ChannelScreen.back();
});
it('MM-T4899_5 - should be able to open markdown table in full view and allow both horizontal and vertical scrolls', async () => {
// # Open a channel screen and post a markdown table with more columns and rows past horizontal and vertical views
const markdownTable =
'| Header | Header | Header | Header | Header | Header | Header | Header last |\n' +
'| :-- | :-: | --: | --: | :-- | :-: | --: | --: |\n' +
'| Left | Center | Right | Right | Left | Center | Right | Right |\n'.repeat(30) +
'| Left | Center | Right | Right | Left | Center | Right | Right last |\n';
await ChannelScreen.open(channelsCategory, testChannel.name);
await Post.apiCreatePost(siteOneUrl, {
channelId: testChannel.id,
message: markdownTable,
});
// * Verify table is displayed with some right columns and bottom rows not visible
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemTable, postListPostItemTableExpandButton} = ChannelScreen.getPostListPostItem(post.id);
await expect(postListPostItemTable).toBeVisible();
await expect(element(by.text('Header last'))).not.toBeVisible();
await expect(element(by.text('Right last'))).not.toBeVisible();
// # Expand to full view
await ChannelScreen.flatPostList.scrollTo('bottom');
await postListPostItemTableExpandButton.tap();
await TableScreen.toBeVisible();
await expect(element(by.text('Header last'))).not.toBeVisible();
await expect(element(by.text('Right last'))).not.toBeVisible();
// * Verify table screen is scrollable to the right and scrollable to the bottom
await TableScreen.tableScrollView.scrollTo('right');
await expect(element(by.text('Header last'))).toBeVisible();
await expect(element(by.text('Right last'))).not.toBeVisible();
await TableScreen.tableScrollView.scrollTo('bottom');
await expect(element(by.text('Header last'))).not.toBeVisible();
await expect(element(by.text('Right last'))).toBeVisible();
// # Go back to channel list screen
await TableScreen.back();
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,145 @@
// 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 {
Channel,
Post,
Setup,
User,
} from '@support/server_api';
import {
serverOneUrl,
siteOneUrl,
} from '@support/test_config';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
PermalinkScreen,
ServerScreen,
} from '@support/ui/screen';
import {getRandomId, timeouts, wait} from '@support/utils';
import {expect} from 'detox';
describe('Messaging - Permalink', () => {
const serverOneDisplayName = 'Server 1';
const channelsCategory = 'channels';
let testChannel: any;
let testTeam: any;
let testUser: any;
beforeAll(async () => {
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
testUser = user;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(testUser);
});
beforeEach(async () => {
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
});
afterAll(async () => {
// # Log out
await HomeScreen.logout();
});
const expectPermalinkTargetMessage = async (teamName: any, permalinkTargetPost: any, permalinkTargetChannelDiplayName: string) => {
// # Open a channel screen, post a permalink to target post, and tap on permalink
await ChannelScreen.open(channelsCategory, testChannel.name);
const permalinkLabel = `permalink-${getRandomId()}`;
const permalinkMessage = `[${permalinkLabel}](/${teamName}/pl/${permalinkTargetPost.id})`;
await ChannelScreen.postMessage(permalinkMessage);
await element(by.text(permalinkLabel)).tap({x: 5, y: 10});
await wait(timeouts.ONE_SEC);
// * Verify on permalink screen and target post is displayed
await PermalinkScreen.toBeVisible();
const {postListPostItem: permalinkPostListPostItem} = PermalinkScreen.getPostListPostItem(permalinkTargetPost.id, permalinkTargetPost.message);
await expect(permalinkPostListPostItem).toBeVisible();
// # Jump to recent messages
await PermalinkScreen.jumpToRecentMessages();
// * Verify on channel screen and target post is displayed
await expect(ChannelScreen.headerTitle).toHaveText(permalinkTargetChannelDiplayName);
const {postListPostItem: channelPostListPostItem} = ChannelScreen.getPostListPostItem(permalinkTargetPost.id, permalinkTargetPost.message);
await expect(channelPostListPostItem).toBeVisible();
};
it('MM-T4876_1 - should be able to jump to target public channel post by tapping on permalink with team name', async () => {
// # Post a target message in a target public channel
const permalinkTargetMessage = `Message ${getRandomId()}`;
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const permalinkTargetPost = await Post.apiCreatePost(siteOneUrl, {
channelId: targetChannel.id,
message: permalinkTargetMessage,
});
await expectPermalinkTargetMessage(testTeam.name, permalinkTargetPost.post, targetChannel.display_name);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4876_2 - should be able to jump to target public channel post by tapping on permalink with _redirect', async () => {
// # Post a target message in a target public channel
const permalinkTargetMessage = `Message ${getRandomId()}`;
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const permalinkTargetPost = await Post.apiCreatePost(siteOneUrl, {
channelId: targetChannel.id,
message: permalinkTargetMessage,
});
await expectPermalinkTargetMessage('_redirect', permalinkTargetPost.post, targetChannel.display_name);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4876_3 - should be able to jump to target DM post by tapping on permalink with team name', async () => {
// # Post a target message in a target DM channel
const {user: dmOtherUser} = await User.apiCreateUser(siteOneUrl, {prefix: 'testchannel-1'});
const {channel: targetChannel} = await Channel.apiCreateDirectChannel(siteOneUrl, [testUser.id, dmOtherUser.id]);
const permalinkTargetMessage = `Message ${getRandomId()}`;
const permalinkTargetPost = await Post.apiCreatePost(siteOneUrl, {
channelId: targetChannel.id,
message: permalinkTargetMessage,
});
await expectPermalinkTargetMessage(testTeam.name, permalinkTargetPost.post, dmOtherUser.username);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4876_4 - should be able to jump to target DM post by tapping on permalink with _redirect', async () => {
// # Post a target message in a target DM channel
const {user: dmOtherUser} = await User.apiCreateUser(siteOneUrl, {prefix: 'testchannel-1'});
const {channel: targetChannel} = await Channel.apiCreateDirectChannel(siteOneUrl, [testUser.id, dmOtherUser.id]);
const permalinkTargetMessage = `Message ${getRandomId()}`;
const permalinkTargetPost = await Post.apiCreatePost(siteOneUrl, {
channelId: targetChannel.id,
message: permalinkTargetMessage,
});
await expectPermalinkTargetMessage('_redirect', permalinkTargetPost.post, dmOtherUser.username);
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -0,0 +1,162 @@
// 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 {Autocomplete} from '@support/ui/component';
import {
ChannelListScreen,
ChannelScreen,
HomeScreen,
LoginScreen,
ServerScreen,
} from '@support/ui/screen';
import {expect} from 'detox';
describe('Autocomplete - At-Mention', () => {
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(testUser);
// * Verify on channel list screen
await ChannelListScreen.toBeVisible();
// # Open a channel screen
await ChannelScreen.open(channelsCategory, testChannel.name);
});
beforeEach(async () => {
// # Clear post input
await ChannelScreen.postInput.clearText();
// * Verify autocomplete is not displayed
await Autocomplete.toBeVisible(false);
});
afterAll(async () => {
// # Log out
await ChannelScreen.back();
await HomeScreen.logout();
});
it('MM-T4886_1 - should be able to select and post at-mention suggestion', async () => {
// # Type in "@" to activate at-mention autocomplete
await ChannelScreen.postInput.typeText('@');
await Autocomplete.toBeVisible();
// * Verify at-mention list is displayed
await expect(Autocomplete.sectionAtMentionList).toBeVisible();
// # Type in username
await ChannelScreen.postInput.typeText(testUser.username);
// * Verify at-mention autocomplete contains associated user suggestion
const {atMentionItem} = Autocomplete.getAtMentionItem(testUser.id);
await expect(atMentionItem).toBeVisible();
// # Select and post at-mention suggestion
await atMentionItem.tap();
await ChannelScreen.sendButton.tap();
// * Verify at-mention suggestion is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(post.id, `@${testUser.username}`);
});
it('MM-T4886_2 - should be able to select and post channel mention suggestion', async () => {
// # Type in "~" to activate channel mention autocomplete
await ChannelScreen.postInput.typeText('~');
await Autocomplete.toBeVisible();
// * Verify channel mention list is displayed
await expect(Autocomplete.sectionChannelMentionList).toBeVisible();
// # Type in channel name
await ChannelScreen.postInput.typeText(testChannel.name);
// * Verify channel mention autocomplete contains associated channel suggestion
const {channelMentionItem} = Autocomplete.getChannelMentionItem(testChannel.name);
await expect(channelMentionItem).toBeVisible();
// # Select and post channel mention suggestion
await channelMentionItem.tap();
await ChannelScreen.sendButton.tap();
// * Verify channel mention suggestion is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(post.id, `~${testChannel.display_name}`);
});
it('MM-T4886_3 - should be able to select and post emoji suggestion', async () => {
// # Type in ":" then first 2 characters of emoji name to activate emoji suggestion autocomplete
const emojiName = 'fox_face';
const emojiNameFirst2Chars = emojiName.substring(0, 2);
const emojiName3rdToLastChars = emojiName.substring(2);
await ChannelScreen.postInput.typeText(`:${emojiNameFirst2Chars}`);
await Autocomplete.toBeVisible();
// * Verify emoji suggestion list is displayed
await expect(Autocomplete.flatEmojiSuggestionList).toBeVisible();
// # Type in 3rd to last characters of emoji name
await ChannelScreen.postInput.typeText(emojiName3rdToLastChars);
// * Verify emoji suggestion autocomplete contains associated emoji suggestion
const {emojiSuggestionItem} = Autocomplete.getEmojiSuggestionItem(emojiName);
await expect(emojiSuggestionItem).toBeVisible();
// # Select and post emoji suggestion
await emojiSuggestionItem.tap();
await ChannelScreen.sendButton.tap();
// * Verify emoji suggestion is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(post.id, '🦊');
});
it('MM-T4886_4 - should be able to select and post slash suggestion', async () => {
// # Type in "/" to activate slash suggestion autocomplete
await ChannelScreen.postInput.typeText('/');
await Autocomplete.toBeVisible();
// * Verify slash suggestion list is displayed
await expect(Autocomplete.flatSlashSuggestionList).toBeVisible();
// # Type in slash command name
const slashCommand = 'away';
await ChannelScreen.postInput.typeText(slashCommand);
// * Verify slash suggestion autocomplete contains associated slash command suggestion
const {slashSuggestionItem} = Autocomplete.getSlashSuggestionItem(slashCommand);
await expect(slashSuggestionItem).toBeVisible();
// # Select and post slash suggestion
await slashSuggestionItem.tap();
await ChannelScreen.sendButton.tap();
// * Verify slash suggestion is posted
await expect(element(by.text('You are now away'))).toBeVisible();
});
});

View File

@@ -8,6 +8,7 @@
// *******************************************************************
import {
Channel,
Post,
Setup,
} from '@support/server_api';
@@ -35,14 +36,18 @@ describe('Smoke Test - Messaging', () => {
const savedText = 'Saved';
const pinnedText = 'Pinned';
let testChannel: any;
let testTeam: any;
let testUser: any;
beforeAll(async () => {
const {channel, user} = await Setup.apiInit(siteOneUrl);
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
testChannel = channel;
testTeam = team;
testUser = user;
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
await LoginScreen.login(testUser);
});
beforeEach(async () => {
@@ -135,7 +140,7 @@ describe('Smoke Test - Messaging', () => {
// # 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 EmojiPickerScreen.searchInput.replaceText('clown_face');
await element(by.text('🤡')).tap();
// * Verify reaction is added to the message
@@ -198,4 +203,64 @@ describe('Smoke Test - Messaging', () => {
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4786_5 - should be able to post a message with at-mention and channel mention', async () => {
// # Open a channel screen and post a message with at-mention and channel mention
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const message = `Message @${testUser.username} ~${targetChannel.name}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(message);
// * Verify at-mention is posted as lowercase and channel mention is posted as display name
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(post.id, `Message @${testUser.username.toLowerCase()} ~${targetChannel.display_name}`);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4786_6 - should be able to post labeled permalink and labeled channel link', async () => {
// # Post a target message in a target channel
const permalinkTargetMessage = `Message ${getRandomId()}`;
const {channel: targetChannel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
await Channel.apiAddUserToChannel(siteOneUrl, testUser.id, targetChannel.id);
const permalinkTargetPost = await Post.apiCreatePost(siteOneUrl, {
channelId: targetChannel.id,
message: permalinkTargetMessage,
});
// # Open a channel screen and post a message with labeled permalink to the target message and labeled channel link to the target channel
await ChannelScreen.open(channelsCategory, testChannel.name);
const permalinkLabel = `permalink-${getRandomId()}`;
const permalinkMessage = `[${permalinkLabel}](/${testTeam.name}/pl/${permalinkTargetPost.id})`;
const channelLinkLabel = `channel-link-${getRandomId()}`;
const channelLinkMessage = `[${channelLinkLabel}](${serverOneUrl}/${testTeam.name}/channels/${targetChannel.name})`;
const message = `Message ${permalinkMessage} ${channelLinkMessage}`;
await ChannelScreen.postMessage(message);
// * Verify permalink and channel link are posted as labeled links
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
await ChannelScreen.hasPostMessage(post.id, `Message ${permalinkLabel} ${channelLinkLabel}`);
// # Go back to channel list screen
await ChannelScreen.back();
});
it('MM-T4786_7 - should be able to post a message with markdown', async () => {
// # Open a channel screen and post a message with markdown
const message = `Message ${getRandomId()}`;
const markdown = `#### ${message}`;
await ChannelScreen.open(channelsCategory, testChannel.name);
await ChannelScreen.postMessage(markdown);
// * Verify message with markdown is posted
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
const {postListPostItemHeading} = ChannelScreen.getPostListPostItem(post.id, message);
await expect(postListPostItemHeading).toBeVisible();
await expect(element(by.text(message))).toBeVisible();
// # Go back to channel list screen
await ChannelScreen.back();
});
});

View File

@@ -47,7 +47,7 @@ describe('Threads - Global Threads', () => {
// # Log in to server
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
await LoginScreen.login(user);
await LoginScreen.login(testUser);
});
beforeEach(async () => {

View File

@@ -113,7 +113,7 @@ describe('Threads - Open Thread in Channel', () => {
await expect(GlobalThreadsScreen.getThreadItem(parentPost.id)).toBeVisible();
// # Open thread options for thread, tap on copy link option, go back to channel list screen, go to another channel, post the permalink, and tap on permalink
const permalinkLabel = `permalink-${Date.now().toString()}`;
const permalinkLabel = `permalink-${getRandomId()}`;
const permalinkMessage = `[${permalinkLabel}](/_redirect/pl/${parentPost.id})`;
await GlobalThreadsScreen.back();
await ChannelScreen.open(channelsCategory, 'off-topic');