forked from Ivasoft/mattermost-mobile
Detox/E2E: Create Channel and Channel Post List e2e in Gekidou
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,7 +6,6 @@ server.PID
|
||||
mattermost.keystore
|
||||
tmp/
|
||||
.env
|
||||
env.d.ts
|
||||
|
||||
*/**/compass-icons.ttf
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ function SendButton({
|
||||
sendMessage,
|
||||
}: Props) {
|
||||
const theme = useTheme();
|
||||
const sendButtonTestID = `${testID}.send.button`;
|
||||
const sendButtonTestID = disabled ? `${testID}.send.button.disabled` : `${testID}.send.button`;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
const viewStyle = useMemo(() => {
|
||||
|
||||
@@ -363,7 +363,7 @@ const PostList = ({
|
||||
scrollEventThrottle={60}
|
||||
style={styles.flex}
|
||||
viewabilityConfig={VIEWABILITY_CONFIG}
|
||||
testID={testID}
|
||||
testID={`${testID}.flat_list`}
|
||||
/>
|
||||
</PostListRefreshControl>
|
||||
{showMoreMessages &&
|
||||
|
||||
@@ -93,6 +93,7 @@ const SectionItem = ({testID = 'sectionItem', action, actionType, actionValue, l
|
||||
<Switch
|
||||
onValueChange={action}
|
||||
value={selected}
|
||||
testID={`${testID}.toggled.${selected}`}
|
||||
/>
|
||||
);
|
||||
} else if (actionType === ActionTypes.ARROW) {
|
||||
|
||||
@@ -32,6 +32,7 @@ export default function AddTeamSlideUp({otherTeams, canCreateTeams, showTitle =
|
||||
onPress={onPressCreate}
|
||||
showButton={canCreateTeams}
|
||||
showTitle={showTitle}
|
||||
testID='add_team_slide_up'
|
||||
title={intl.formatMessage({id: 'mobile.add_team.join_team', defaultMessage: 'Join Another Team'})}
|
||||
>
|
||||
<TeamList teams={otherTeams}/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {RUNNING_E2E} from '@env';
|
||||
import Emm from '@mattermost/react-native-emm';
|
||||
import {Alert, Linking, Platform} from 'react-native';
|
||||
import {Alert, Linking, LogBox, Platform} from 'react-native';
|
||||
import {Notifications} from 'react-native-notifications';
|
||||
|
||||
import {appEntry, pushNotificationEntry, upgradeEntry} from '@actions/remote/entry';
|
||||
@@ -219,3 +220,9 @@ export const getLaunchPropsFromNotification = async (notification: NotificationW
|
||||
|
||||
return launchProps;
|
||||
};
|
||||
|
||||
// Ignore all notifications if running e2e
|
||||
const isRunningE2e = RUNNING_E2E === 'true';
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`RUNNING_E2E: ${RUNNING_E2E}, isRunningE2e: ${isRunningE2e}`);
|
||||
LogBox.ignoreAllLogs(isRunningE2e);
|
||||
|
||||
@@ -23,9 +23,10 @@ type SlideUpPanelProps = {
|
||||
initialSnapIndex?: number;
|
||||
renderContent: () => ReactNode;
|
||||
snapPoints?: Array<string | number>;
|
||||
testID?: string;
|
||||
}
|
||||
|
||||
const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderContent, snapPoints = ['90%', '50%', 50]}: SlideUpPanelProps) => {
|
||||
const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderContent, snapPoints = ['90%', '50%', 50], testID}: SlideUpPanelProps) => {
|
||||
const sheetRef = useRef<RNBottomSheet>(null);
|
||||
const dimensions = useWindowDimensions();
|
||||
const isTablet = useIsTablet();
|
||||
@@ -121,6 +122,7 @@ const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderCo
|
||||
width: isTablet ? '100%' : Math.min(dimensions.width, 450),
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
testID={`${testID}.screen`}
|
||||
>
|
||||
{renderContent()}
|
||||
</View>
|
||||
|
||||
@@ -66,6 +66,7 @@ export default function DropdownSlideup({
|
||||
<BottomSheetContent
|
||||
showButton={false}
|
||||
showTitle={!isTablet}
|
||||
testID='dropdown_slideup'
|
||||
title={intl.formatMessage({id: 'browse_channels.dropdownTitle', defaultMessage: 'Show'})}
|
||||
>
|
||||
<SlideUpPanelItem
|
||||
|
||||
@@ -144,6 +144,7 @@ const Channel = ({channelId, componentId, displayName, isOwnDirectMessage, membe
|
||||
keyboardTracker={postDraftRef}
|
||||
scrollViewNativeID={channelId}
|
||||
accessoriesContainerID={ACCESSORIES_CONTAINER_NATIVE_ID}
|
||||
testID='channel.post_draft'
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -224,9 +224,10 @@ export default function ChannelInfoForm({
|
||||
<SafeAreaView
|
||||
edges={['bottom', 'left', 'right']}
|
||||
style={styles.container}
|
||||
testID='create_or_edit_channel.screen'
|
||||
>
|
||||
<KeyboardAwareScrollView
|
||||
testID={'edit_channel_info.scrollview'}
|
||||
testID={'create_or_edit_channel.scrollview'}
|
||||
|
||||
// @ts-expect-error legacy ref
|
||||
ref={scrollViewRef}
|
||||
@@ -244,7 +245,7 @@ export default function ChannelInfoForm({
|
||||
<View>
|
||||
{showSelector && (
|
||||
<SectionItem
|
||||
testID='makePrivate'
|
||||
testID='channel_info_form.make_private'
|
||||
label={makePrivateLabel}
|
||||
description={makePrivateDescription}
|
||||
action={handlePress}
|
||||
@@ -269,7 +270,7 @@ export default function ChannelInfoForm({
|
||||
returnKeyType='next'
|
||||
showErrorIcon={false}
|
||||
spellCheck={false}
|
||||
testID='edit_channel_info.displayname.input'
|
||||
testID='channel_info_form.display_name.input'
|
||||
value={displayName}
|
||||
ref={nameInput}
|
||||
containerStyle={styles.textInput}
|
||||
@@ -288,7 +289,7 @@ export default function ChannelInfoForm({
|
||||
returnKeyType='next'
|
||||
showErrorIcon={false}
|
||||
spellCheck={false}
|
||||
testID='edit_channel_info.purpose.input'
|
||||
testID='channel_info_form.purpose.input'
|
||||
value={purpose}
|
||||
ref={purposeInput}
|
||||
containerStyle={styles.textInput}
|
||||
@@ -298,6 +299,7 @@ export default function ChannelInfoForm({
|
||||
style={styles.helpText}
|
||||
id='channel_modal.descriptionHelp'
|
||||
defaultMessage='Describe how this channel should be used.'
|
||||
testID='channel_info_form.purpose.description'
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
@@ -315,7 +317,7 @@ export default function ChannelInfoForm({
|
||||
returnKeyType='next'
|
||||
showErrorIcon={false}
|
||||
spellCheck={false}
|
||||
testID='edit_channel_info.header.input'
|
||||
testID='channel_info_form.header.input'
|
||||
value={header}
|
||||
onLayout={onHeaderLayout}
|
||||
ref={headerInput}
|
||||
@@ -326,6 +328,7 @@ export default function ChannelInfoForm({
|
||||
style={styles.helpText}
|
||||
id='channel_modal.headerHelp'
|
||||
defaultMessage={'Specify text to appear in the channel header beside the channel name. For example, include frequently used links by typing link text [Link Title](http://example.com).'}
|
||||
testID='channel_info_form.header.description'
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
@@ -58,7 +58,7 @@ const isDirect = (channel?: ChannelModel): boolean => {
|
||||
};
|
||||
|
||||
const makeCloseButton = (icon: ImageResource) => {
|
||||
return buildNavigationButton(CLOSE_BUTTON_ID, 'close.more_direct_messages.button', icon);
|
||||
return buildNavigationButton(CLOSE_BUTTON_ID, 'close.create_or_edit_channel.button', icon);
|
||||
};
|
||||
|
||||
const CreateOrEditChannel = ({
|
||||
@@ -111,7 +111,7 @@ const CreateOrEditChannel = ({
|
||||
const rightButton = useMemo(() => {
|
||||
const base = buildNavigationButton(
|
||||
editing ? EDIT_BUTTON_ID : CREATE_BUTTON_ID,
|
||||
'edit_channel.save.button',
|
||||
editing ? 'create_or_edit_channel.save.button' : 'create_or_edit_channel.create.button',
|
||||
undefined,
|
||||
editing ? formatMessage({id: 'mobile.edit_channel', defaultMessage: 'Save'}) : formatMessage({id: 'mobile.create_channel', defaultMessage: 'Create'}),
|
||||
);
|
||||
|
||||
@@ -32,7 +32,7 @@ const CopyPermalinkOption = ({teamName, post}: Props) => {
|
||||
defaultMessage='Copy Link'
|
||||
onPress={handleCopyLink}
|
||||
iconName='link-variant'
|
||||
testID='post.options.copy.permalink'
|
||||
testID='post_options.copy.permalink.option'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ const CopyTextOption = ({postMessage}: Props) => {
|
||||
defaultMessage='Copy Text'
|
||||
iconName='content-copy'
|
||||
onPress={handleCopyText}
|
||||
testID='post.options.copy.text'
|
||||
testID='post_options.copy.text.option'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -50,7 +50,7 @@ const DeletePostOption = ({combinedPost, post}: Props) => {
|
||||
defaultMessage='Delete'
|
||||
iconName='trash-can-outline'
|
||||
onPress={onPress}
|
||||
testID='post.options.delete.post'
|
||||
testID='post_options.delete.post.option'
|
||||
isDestructive={true}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -48,7 +48,7 @@ const EditOption = ({post, canDelete}: Props) => {
|
||||
defaultMessage='Edit'
|
||||
onPress={onPress}
|
||||
iconName='pencil-outline'
|
||||
testID='post.options.edit'
|
||||
testID='post_options.edit.post.option'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,7 +48,7 @@ const FollowThreadOption = ({thread}: FollowThreadOptionProps) => {
|
||||
<BaseOption
|
||||
i18nId={id}
|
||||
defaultMessage={defaultMessage}
|
||||
testID='post.options.follow.thread'
|
||||
testID='post_options.follow.thread.option'
|
||||
iconName={icon}
|
||||
onPress={handleToggleFollow}
|
||||
/>
|
||||
|
||||
@@ -29,7 +29,7 @@ const MarkAsUnreadOption = ({postId}: Props) => {
|
||||
defaultMessage='Mark as Unread'
|
||||
iconName='mark-as-unread'
|
||||
onPress={onPress}
|
||||
testID='post.options.mark.unread'
|
||||
testID='post_options.mark.unread.option'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ const PinChannelOption = ({isPostPinned, postId}: PinChannelProps) => {
|
||||
defaultMessage={defaultMessage}
|
||||
iconName='pin-outline'
|
||||
onPress={onPress}
|
||||
testID={`post.options.${key}.channel`}
|
||||
testID={`post.options.${key}.channel.option`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -31,7 +31,7 @@ const ReplyOption = ({post}: Props) => {
|
||||
defaultMessage='Reply'
|
||||
iconName='reply-outline'
|
||||
onPress={handleReply}
|
||||
testID='post.options.reply'
|
||||
testID='post_options.reply.post.option'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ const SaveOption = ({isSaved, postId}: CopyTextProps) => {
|
||||
defaultMessage={defaultMessage}
|
||||
iconName='bookmark-outline'
|
||||
onPress={onHandlePress}
|
||||
testID={id}
|
||||
testID={`post.options.${defaultMessage.toLocaleLowerCase()}.channel.option`}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -140,6 +140,7 @@ const PostOptions = ({
|
||||
componentId={Screens.POST_OPTIONS}
|
||||
initialSnapIndex={0}
|
||||
snapPoints={[((snapPoints + additionalSnapPoints) * ITEM_HEIGHT), 10]}
|
||||
testID='post_options'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -37,6 +37,7 @@ const PickReaction = ({openEmojiPicker, width, height}: PickReactionProps) => {
|
||||
style={[styles.container, {
|
||||
width, height,
|
||||
}]}
|
||||
testID='post_options.reaction_bar.pick_reaction'
|
||||
>
|
||||
<CompassIcon
|
||||
onPress={openEmojiPicker}
|
||||
|
||||
@@ -31,8 +31,9 @@ type ReactionProps = {
|
||||
emoji: string;
|
||||
iconSize: number;
|
||||
containerSize: number;
|
||||
testID?: string;
|
||||
}
|
||||
const Reaction = ({onPressReaction, emoji, iconSize, containerSize}: ReactionProps) => {
|
||||
const Reaction = ({onPressReaction, emoji, iconSize, containerSize, testID}: ReactionProps) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyleSheet(theme);
|
||||
const handleReactionPressed = useCallback(() => {
|
||||
@@ -53,6 +54,7 @@ const Reaction = ({onPressReaction, emoji, iconSize, containerSize}: ReactionPro
|
||||
key={emoji}
|
||||
onPress={handleReactionPressed}
|
||||
style={highlightedStyle}
|
||||
testID={`${testID}.${emoji}`}
|
||||
>
|
||||
<View
|
||||
style={reactionStyle}
|
||||
|
||||
@@ -84,6 +84,7 @@ const ReactionBar = ({recentEmojis = [], postId}: QuickReactionProps) => {
|
||||
emoji={emoji}
|
||||
iconSize={iconSize}
|
||||
containerSize={containerSize}
|
||||
testID='post_options.reaction_bar.reaction'
|
||||
/>
|
||||
);
|
||||
})
|
||||
|
||||
@@ -80,6 +80,7 @@ const Reactions = ({initialEmoji, reactions}: Props) => {
|
||||
componentId={Screens.REACTIONS}
|
||||
initialSnapIndex={1}
|
||||
snapPoints={['90%', '50%', 10]}
|
||||
testID='reactions'
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -53,6 +53,7 @@ const Thread = ({rootPost}: ThreadProps) => {
|
||||
accessoriesContainerID={THREAD_ACCESSORIES_CONTAINER_NATIVE_ID}
|
||||
rootId={rootPost!.id}
|
||||
keyboardTracker={postDraftRef}
|
||||
testID='thread.post_draft'
|
||||
/>
|
||||
</>
|
||||
}
|
||||
|
||||
@@ -44,6 +44,14 @@ module.exports = {
|
||||
'@websocket': './app/client/websocket',
|
||||
},
|
||||
}],
|
||||
['module:react-native-dotenv', {
|
||||
moduleName: '@env',
|
||||
path: '.env',
|
||||
blacklist: null,
|
||||
whitelist: null,
|
||||
safe: false,
|
||||
allowUndefined: true,
|
||||
}],
|
||||
'react-native-reanimated/plugin',
|
||||
],
|
||||
exclude: ['**/*.png', '**/*.jpg', '**/*.gif'],
|
||||
|
||||
@@ -5,6 +5,7 @@ import {isAndroid} from '@support/utils';
|
||||
|
||||
class Alert {
|
||||
// alert titles
|
||||
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}?`;
|
||||
|
||||
@@ -18,6 +19,7 @@ class Alert {
|
||||
|
||||
// alert buttons
|
||||
cancelButton = isAndroid() ? element(by.text('CANCEL')) : element(by.label('Cancel')).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);
|
||||
removeButton = isAndroid() ? element(by.text('REMOVE')) : element(by.label('Remove')).atIndex(1);
|
||||
}
|
||||
|
||||
20
detox/e2e/support/ui/component/camera_quick_action.ts
Normal file
20
detox/e2e/support/ui/component/camera_quick_action.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class CameraQuickAction {
|
||||
testID = {
|
||||
cameraActionSuffix: 'post_draft.quick_actions.camera_action',
|
||||
cameraActionDisabledSuffix: 'post_draft.quick_actions.camera_action.disabled',
|
||||
};
|
||||
|
||||
getCameraQuickAction = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.cameraActionSuffix}`));
|
||||
};
|
||||
|
||||
getCameraQuickActionDisabled = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.cameraActionDisabledSuffix}`));
|
||||
};
|
||||
}
|
||||
|
||||
const cameraQuickAction = new CameraQuickAction();
|
||||
export default cameraQuickAction;
|
||||
20
detox/e2e/support/ui/component/file_quick_action.ts
Normal file
20
detox/e2e/support/ui/component/file_quick_action.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class FileQuickAction {
|
||||
testID = {
|
||||
fileActionSuffix: 'post_draft.quick_actions.file_action',
|
||||
fileActionDisabledSuffix: 'post_draft.quick_actions.file_action.disabled',
|
||||
};
|
||||
|
||||
getFileQuickAction = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.fileActionSuffix}`));
|
||||
};
|
||||
|
||||
getFileQuickActionDisabled = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.fileActionDisabledSuffix}`));
|
||||
};
|
||||
}
|
||||
|
||||
const fileQuickAction = new FileQuickAction();
|
||||
export default fileQuickAction;
|
||||
20
detox/e2e/support/ui/component/image_quick_action.ts
Normal file
20
detox/e2e/support/ui/component/image_quick_action.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class ImageQuickAction {
|
||||
testID = {
|
||||
imageActionSuffix: 'post_draft.quick_actions.image_action',
|
||||
imageActionDisabledSuffix: 'post_draft.quick_actions.image_action.disabled',
|
||||
};
|
||||
|
||||
getImageQuickAction = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.imageActionSuffix}`));
|
||||
};
|
||||
|
||||
getImageQuickActionDisabled = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.imageActionDisabledSuffix}`));
|
||||
};
|
||||
}
|
||||
|
||||
const imageQuickAction = new ImageQuickAction();
|
||||
export default imageQuickAction;
|
||||
@@ -2,11 +2,29 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Alert from './alert';
|
||||
import CameraQuickAction from './camera_quick_action';
|
||||
import FileQuickAction from './file_quick_action';
|
||||
import ImageQuickAction from './image_quick_action';
|
||||
import InputQuickAction from './input_quick_action';
|
||||
import NavigationHeader from './navigation_header';
|
||||
import PlusMenu from './plus_menu';
|
||||
import Post from './post';
|
||||
import PostDraft from './post_draft';
|
||||
import PostList from './post_list';
|
||||
import ProfilePicture from './profile_picture';
|
||||
import SendButton from './send_button';
|
||||
|
||||
export {
|
||||
Alert,
|
||||
CameraQuickAction,
|
||||
FileQuickAction,
|
||||
ImageQuickAction,
|
||||
InputQuickAction,
|
||||
NavigationHeader,
|
||||
PlusMenu,
|
||||
Post,
|
||||
PostDraft,
|
||||
PostList,
|
||||
ProfilePicture,
|
||||
SendButton,
|
||||
};
|
||||
|
||||
30
detox/e2e/support/ui/component/input_quick_action.ts
Normal file
30
detox/e2e/support/ui/component/input_quick_action.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class InputQuickAction {
|
||||
testID = {
|
||||
atInputActionSuffix: 'post_draft.quick_actions.at_input_action',
|
||||
atInputActionDisabledSuffix: 'post_draft.quick_actions.at_input_action.disabled',
|
||||
slashInputActionSuffix: 'post_draft.quick_actions.slash_input_action',
|
||||
slashInputActionDisabledSuffix: 'post_draft.quick_actions.slash_input_action.disabled',
|
||||
};
|
||||
|
||||
getAtInputQuickAction = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.atInputActionSuffix}`));
|
||||
};
|
||||
|
||||
getAtInputQuickActionDisabled = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.atInputActionDisabledSuffix}`));
|
||||
};
|
||||
|
||||
getSlashInputQuickAction = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.slashInputActionSuffix}`));
|
||||
};
|
||||
|
||||
getSlashInputQuickActionDisabled = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.slashInputActionDisabledSuffix}`));
|
||||
};
|
||||
}
|
||||
|
||||
const inputQuickAction = new InputQuickAction();
|
||||
export default inputQuickAction;
|
||||
109
detox/e2e/support/ui/component/post.ts
Normal file
109
detox/e2e/support/ui/component/post.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import ProfilePicture from './profile_picture';
|
||||
|
||||
class Post {
|
||||
testID = {
|
||||
postProfilePicturePrefix: 'post_profile_picture.profile_picture.',
|
||||
blockQuote: 'markdown_block_quote',
|
||||
emoji: 'markdown_emoji',
|
||||
image: 'markdown_image',
|
||||
message: 'markdown_text',
|
||||
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',
|
||||
postHeaderReply: 'post_header.reply',
|
||||
postHeaderReplyCount: 'post_header.reply.count',
|
||||
postPreHeaderText: 'post_pre_header.text',
|
||||
showLessButton: 'show_more.button.chevron-up',
|
||||
showMoreButton: 'show_more.button.chevron-down',
|
||||
table: 'markdown_table',
|
||||
tableExpandButton: 'markdown_table.expand.button',
|
||||
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 postItemEmojiMatcher = by.id(this.testID.emoji).withAncestor(postItemMatcher);
|
||||
const postItemImageMatcher = by.id(this.testID.image).withAncestor(postItemMatcher);
|
||||
const postItemMessageMatcher = by.id(this.testID.message).withAncestor(postItemMatcher);
|
||||
const postItemPreHeaderTextMatch = by.id(this.testID.postPreHeaderText).withAncestor(postItemMatcher);
|
||||
const postItemShowLessButtonMatcher = by.id(this.testID.showLessButton).withAncestor(postItemMatcher);
|
||||
const postItemShowMoreButtonMatcher = by.id(this.testID.showMoreButton).withAncestor(postItemMatcher);
|
||||
const postItemTableMatcher = by.id(this.testID.table).withAncestor(postItemMatcher);
|
||||
const postItemTableExpandButtonMatcher = by.id(this.testID.tableExpandButton).withAncestor(postItemMatcher);
|
||||
const postItemThematicBreakMatcher = by.id(this.testID.thematicBreak).withAncestor(postItemMatcher);
|
||||
|
||||
return {
|
||||
postItem: element(postItemMatcher),
|
||||
postItemBlockQuote: element(postItemBlockQuoteMatcher),
|
||||
postItemEmoji: element(postItemEmojiMatcher),
|
||||
postItemImage: element(postItemImageMatcher),
|
||||
postItemMessage: element(postItemMessageMatcher),
|
||||
postItemPreHeaderText: element(postItemPreHeaderTextMatch),
|
||||
postItemShowLessButton: element(postItemShowLessButtonMatcher),
|
||||
postItemShowMoreButton: element(postItemShowMoreButtonMatcher),
|
||||
postItemTable: element(postItemTableMatcher),
|
||||
postItemTableExpandButton: element(postItemTableExpandButtonMatcher),
|
||||
postItemThematicBreak: element(postItemThematicBreakMatcher),
|
||||
...this.getPostHeader(postItemMatcher),
|
||||
...this.getPostProfilePicture(postItemMatcher, postProfileOptions),
|
||||
};
|
||||
};
|
||||
|
||||
getPostHeader = (postItemMatcher: Detox.NativeMatcher) => {
|
||||
const postItemHeaderCommentedOnMatcher = by.id(this.testID.postHeaderCommentedOn).withAncestor(postItemMatcher);
|
||||
const postItemHeaderDateTimeMatcher = by.id(this.testID.postHeaderDateTime).withAncestor(postItemMatcher);
|
||||
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 postItemHeaderReplyMatcher = by.id(this.testID.postHeaderReply).withAncestor(postItemMatcher);
|
||||
const postItemHeaderReplyCountMatcher = by.id(this.testID.postHeaderReplyCount).withAncestor(postItemMatcher);
|
||||
|
||||
return {
|
||||
postItemHeaderCommentedOn: element(postItemHeaderCommentedOnMatcher),
|
||||
postItemHeaderDateTime: element(postItemHeaderDateTimeMatcher),
|
||||
postItemHeaderDisplayName: element(postItemHeaderDisplayNameMatcher),
|
||||
postItemHeaderBotTag: element(postItemHeaderBotTagMatcher),
|
||||
postItemHeaderGuestTag: element(postItemHeaderGuestTagMatcher),
|
||||
postItemHeaderReply: element(postItemHeaderReplyMatcher),
|
||||
postItemHeaderReplyCount: element(postItemHeaderReplyCountMatcher),
|
||||
};
|
||||
};
|
||||
|
||||
getPostItemMatcher = (postItemSourceTestID: string, postId: string, postMessage: string) => {
|
||||
const postTestID = `${postItemSourceTestID}.${postId}`;
|
||||
const baseMatcher = by.id(postTestID);
|
||||
return postMessage ? baseMatcher.withDescendant(by.text(postMessage)) : baseMatcher;
|
||||
};
|
||||
|
||||
getPostMessage = (postItemSourceTestID: string) => {
|
||||
return element(by.id(this.testID.message).withAncestor(by.id(postItemSourceTestID)));
|
||||
};
|
||||
|
||||
getPostProfilePicture = (postItemMatcher: Detox.NativeMatcher, postProfileOptions = {userId: null, userStatus: 'online'}) => {
|
||||
if (Object.keys(postProfileOptions).length === 0 || !postProfileOptions.userId) {
|
||||
return {
|
||||
postItemProfilePicture: null,
|
||||
postItemProfilePictureUserStatus: null,
|
||||
};
|
||||
}
|
||||
|
||||
const profilePictureItemMatcher = ProfilePicture.getProfilePictureItemMatcher(this.testID.postProfilePicturePrefix, postProfileOptions.userId);
|
||||
const profilePictureItemUserStatusMatcher = ProfilePicture.getProfilePictureItemUserStatusMatcher(profilePictureItemMatcher, postProfileOptions.userStatus);
|
||||
const postItemProfilePictureMatcher = profilePictureItemMatcher.withAncestor(postItemMatcher);
|
||||
const postItemProfilePictureUserStatusMatcher = profilePictureItemUserStatusMatcher.withAncestor(postItemMatcher);
|
||||
|
||||
return {
|
||||
postItemProfilePicture: element(postItemProfilePictureMatcher),
|
||||
postItemProfilePictureUserStatus: element(postItemProfilePictureUserStatusMatcher),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const post = new Post();
|
||||
export default post;
|
||||
30
detox/e2e/support/ui/component/post_draft.ts
Normal file
30
detox/e2e/support/ui/component/post_draft.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class PostDraft {
|
||||
testID = {
|
||||
postDraftSuffix: 'post_draft',
|
||||
postDraftArchivedSuffix: 'post_draft.archived',
|
||||
postDraftReadOnlySuffix: 'post_draft.read_only',
|
||||
postInputSuffix: 'post_draft.post.input',
|
||||
};
|
||||
|
||||
getPostDraft = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.postDraftSuffix}`));
|
||||
};
|
||||
|
||||
getPostDraftArchived = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.postDraftArchivedSuffix}`));
|
||||
};
|
||||
|
||||
getPostDraftReadOnly = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.postDraftReadOnlySuffix}`));
|
||||
};
|
||||
|
||||
getPostInput = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.postInputSuffix}`));
|
||||
};
|
||||
}
|
||||
|
||||
const postDraft = new PostDraft();
|
||||
export default postDraft;
|
||||
83
detox/e2e/support/ui/component/post_list.ts
Normal file
83
detox/e2e/support/ui/component/post_list.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import Post from './post';
|
||||
|
||||
class PostList {
|
||||
testID: any;
|
||||
|
||||
constructor(screenPrefix: string) {
|
||||
this.testID = {
|
||||
flatList: `${screenPrefix}post_list.flat_list`,
|
||||
moreMessagesButton: `${screenPrefix}post_list.more_messages_button`,
|
||||
newMessagesDivider: `${screenPrefix}post_list.new_messages_line`,
|
||||
postListPostItem: `${screenPrefix}post_list.post`,
|
||||
};
|
||||
}
|
||||
|
||||
getFlatList = () => {
|
||||
return element(by.id(this.testID.flatList));
|
||||
};
|
||||
|
||||
getMoreMessagesButton = () => {
|
||||
return element(by.id(this.testID.moreMessagesButton));
|
||||
};
|
||||
|
||||
getNewMessagesDivider = () => {
|
||||
return element(by.id(this.testID.newMessagesDivider));
|
||||
};
|
||||
|
||||
getPost = (postId: string, postMessage: string, postProfileOptions = {}) => {
|
||||
const {
|
||||
postItem,
|
||||
postItemBlockQuote,
|
||||
postItemEmoji,
|
||||
postItemHeaderCommentedOn,
|
||||
postItemHeaderDateTime,
|
||||
postItemHeaderDisplayName,
|
||||
postItemHeaderBotTag,
|
||||
postItemHeaderGuestTag,
|
||||
postItemHeaderReply,
|
||||
postItemHeaderReplyCount,
|
||||
postItemImage,
|
||||
postItemMessage,
|
||||
postItemPreHeaderText,
|
||||
postItemProfilePicture,
|
||||
postItemProfilePictureUserStatus,
|
||||
postItemShowLessButton,
|
||||
postItemShowMoreButton,
|
||||
postItemTable,
|
||||
postItemTableExpandButton,
|
||||
postItemThematicBreak,
|
||||
} = Post.getPost(this.testID.postListPostItem, postId, postMessage, postProfileOptions);
|
||||
|
||||
return {
|
||||
postListPostItem: postItem,
|
||||
postListPostItemBlockQuote: postItemBlockQuote,
|
||||
postListPostItemEmoji: postItemEmoji,
|
||||
postListPostItemHeaderCommentedOn: postItemHeaderCommentedOn,
|
||||
postListPostItemHeaderDateTime: postItemHeaderDateTime,
|
||||
postListPostItemHeaderDisplayName: postItemHeaderDisplayName,
|
||||
postListPostItemHeaderBotTag: postItemHeaderBotTag,
|
||||
postListPostItemHeaderGuestTag: postItemHeaderGuestTag,
|
||||
postListPostItemHeaderReply: postItemHeaderReply,
|
||||
postListPostItemHeaderReplyCount: postItemHeaderReplyCount,
|
||||
postListPostItemImage: postItemImage,
|
||||
postListPostItemMessage: postItemMessage,
|
||||
postListPostItemPreHeaderText: postItemPreHeaderText,
|
||||
postListPostItemProfilePicture: postItemProfilePicture,
|
||||
postListPostItemProfilePictureUserStatus: postItemProfilePictureUserStatus,
|
||||
postListPostItemShowLessButton: postItemShowLessButton,
|
||||
postListPostItemShowMoreButton: postItemShowMoreButton,
|
||||
postListPostItemTable: postItemTable,
|
||||
postListPostItemTableExpandButton: postItemTableExpandButton,
|
||||
postListPostItemThematicBreak: postItemThematicBreak,
|
||||
};
|
||||
};
|
||||
|
||||
getPostMessageAtIndex = (index: number) => {
|
||||
return Post.getPostMessage(this.testID.postListPostItem).atIndex(index);
|
||||
};
|
||||
}
|
||||
|
||||
export default PostList;
|
||||
21
detox/e2e/support/ui/component/profile_picture.ts
Normal file
21
detox/e2e/support/ui/component/profile_picture.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class ProfilePicture {
|
||||
testID = {
|
||||
userStatusIconPrefix: 'user_status.icon.',
|
||||
};
|
||||
|
||||
getProfilePictureItemMatcher = (profilePictureSourcePrefix: string, userId: string) => {
|
||||
const profilePictureTestID = `${profilePictureSourcePrefix}${userId}`;
|
||||
return by.id(profilePictureTestID);
|
||||
};
|
||||
|
||||
getProfilePictureItemUserStatusMatcher(profilePictureItemMatcher: Detox.NativeMatcher, userStatus = 'online') {
|
||||
const userStatusIconTestID = `${this.testID.userStatusIconPrefix}${userStatus}`;
|
||||
return by.id(userStatusIconTestID).withAncestor(profilePictureItemMatcher);
|
||||
}
|
||||
}
|
||||
|
||||
const profilePicture = new ProfilePicture();
|
||||
export default profilePicture;
|
||||
20
detox/e2e/support/ui/component/send_button.ts
Normal file
20
detox/e2e/support/ui/component/send_button.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
class SendButton {
|
||||
testID = {
|
||||
sendButtonSuffix: 'post_draft.send_action.send.button',
|
||||
sendButtonDisabledSuffix: 'post_draft.send_action.send.button.disabled',
|
||||
};
|
||||
|
||||
getSendButton = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.sendButtonSuffix}`));
|
||||
};
|
||||
|
||||
getSendButtonDisabled = (screenPrefix: string) => {
|
||||
return element(by.id(`${screenPrefix}${this.testID.sendButtonDisabledSuffix}`));
|
||||
};
|
||||
}
|
||||
|
||||
const sendButton = new SendButton();
|
||||
export default sendButton;
|
||||
@@ -1,13 +1,27 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {NavigationHeader} from '@support/ui/component';
|
||||
import {ChannelListScreen} from '@support/ui/screen';
|
||||
import {
|
||||
CameraQuickAction,
|
||||
FileQuickAction,
|
||||
ImageQuickAction,
|
||||
InputQuickAction,
|
||||
NavigationHeader,
|
||||
PostDraft,
|
||||
PostList,
|
||||
SendButton,
|
||||
} from '@support/ui/component';
|
||||
import {
|
||||
ChannelListScreen,
|
||||
PostOptionsScreen,
|
||||
ThreadScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
class ChannelScreen {
|
||||
testID = {
|
||||
channelScreenPrefix: 'channel.',
|
||||
channelScreen: 'channel.screen',
|
||||
introDisplayName: 'channel_post_list.intro.display_name',
|
||||
introOptionAddPeopleItem: 'channel_post_list.intro.option_item.add_people',
|
||||
@@ -20,13 +34,49 @@ class ChannelScreen {
|
||||
introOptionAddPeopleItem = element(by.id(this.testID.introOptionAddPeopleItem));
|
||||
introOptionSetHeaderItem = element(by.id(this.testID.introOptionSetHeaderItem));
|
||||
introOptionChannelDetailsItem = element(by.id(this.testID.introOptionChannelDetailsItem));
|
||||
|
||||
// convenience props
|
||||
backButton = NavigationHeader.backButton;
|
||||
headerTitle = NavigationHeader.headerTitle;
|
||||
atInputQuickAction = InputQuickAction.getAtInputQuickAction(this.testID.channelScreenPrefix);
|
||||
atInputQuickActionDisabled = InputQuickAction.getAtInputQuickActionDisabled(this.testID.channelScreenPrefix);
|
||||
slashInputQuickAction = InputQuickAction.getSlashInputQuickAction(this.testID.channelScreenPrefix);
|
||||
slashInputQuickActionDisabled = InputQuickAction.getSlashInputQuickActionDisabled(this.testID.channelScreenPrefix);
|
||||
fileQuickAction = FileQuickAction.getFileQuickAction(this.testID.channelScreenPrefix);
|
||||
fileQuickActionDisabled = FileQuickAction.getFileQuickActionDisabled(this.testID.channelScreenPrefix);
|
||||
imageQuickAction = ImageQuickAction.getImageQuickAction(this.testID.channelScreenPrefix);
|
||||
imageQuickActionDisabled = ImageQuickAction.getImageQuickActionDisabled(this.testID.channelScreenPrefix);
|
||||
cameraQuickAction = CameraQuickAction.getCameraQuickAction(this.testID.channelScreenPrefix);
|
||||
cameraQuickActionDisabled = CameraQuickAction.getCameraQuickActionDisabled(this.testID.channelScreenPrefix);
|
||||
postDraft = PostDraft.getPostDraft(this.testID.channelScreenPrefix);
|
||||
postDraftArchived = PostDraft.getPostDraftArchived(this.testID.channelScreenPrefix);
|
||||
postDraftReadOnly = PostDraft.getPostDraftReadOnly(this.testID.channelScreenPrefix);
|
||||
postInput = PostDraft.getPostInput(this.testID.channelScreenPrefix);
|
||||
sendButton = SendButton.getSendButton(this.testID.channelScreenPrefix);
|
||||
sendButtonDisabled = SendButton.getSendButtonDisabled(this.testID.channelScreenPrefix);
|
||||
|
||||
postList = new PostList(this.testID.channelScreenPrefix);
|
||||
|
||||
getIntroOptionItemLabel = (introOptionItemTestId: string) => {
|
||||
return element(by.id(`${introOptionItemTestId}.label`));
|
||||
};
|
||||
|
||||
getMoreMessagesButton = () => {
|
||||
return this.postList.getMoreMessagesButton();
|
||||
};
|
||||
|
||||
getNewMessagesDivider = () => {
|
||||
return this.postList.getNewMessagesDivider();
|
||||
};
|
||||
|
||||
getPostListPostItem = (postId: string, text: string, postProfileOptions: any = {}) => {
|
||||
return this.postList.getPost(postId, text, postProfileOptions);
|
||||
};
|
||||
|
||||
getPostMessageAtIndex = (index: number) => {
|
||||
return this.postList.getPostMessageAtIndex(index);
|
||||
};
|
||||
|
||||
toBeVisible = async () => {
|
||||
await waitFor(this.channelScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
|
||||
@@ -44,6 +94,48 @@ class ChannelScreen {
|
||||
await this.backButton.tap();
|
||||
await expect(this.channelScreen).not.toBeVisible();
|
||||
};
|
||||
|
||||
openPostOptionsFor = async (postId: string, text: string) => {
|
||||
const {postListPostItem} = this.getPostListPostItem(postId, text);
|
||||
await expect(postListPostItem).toBeVisible();
|
||||
|
||||
// # Open post options
|
||||
await postListPostItem.longPress();
|
||||
await PostOptionsScreen.toBeVisible();
|
||||
};
|
||||
|
||||
openReplyThreadFor = async (postId: string, text: string) => {
|
||||
await this.openPostOptionsFor(postId, text);
|
||||
|
||||
// # Open reply thread screen
|
||||
await PostOptionsScreen.replyPostOption.tap();
|
||||
await ThreadScreen.toBeVisible();
|
||||
};
|
||||
|
||||
postMessage = async (message: string) => {
|
||||
// # Post message
|
||||
await this.postInput.tap();
|
||||
await this.postInput.replaceText(message);
|
||||
await this.tapSendButton();
|
||||
};
|
||||
|
||||
tapSendButton = async () => {
|
||||
// # Tap send button
|
||||
await this.sendButton.tap();
|
||||
await expect(this.sendButton).not.toExist();
|
||||
await expect(this.sendButtonDisabled).toBeVisible();
|
||||
};
|
||||
|
||||
hasPostMessage = async (postId: string, postMessage: string) => {
|
||||
const {postListPostItem} = this.getPostListPostItem(postId, postMessage);
|
||||
await expect(postListPostItem).toBeVisible();
|
||||
};
|
||||
|
||||
hasPostMessageAtIndex = async (index: number, postMessage: string) => {
|
||||
await expect(
|
||||
this.getPostMessageAtIndex(index),
|
||||
).toHaveText(postMessage);
|
||||
};
|
||||
}
|
||||
|
||||
const channelScreen = new ChannelScreen();
|
||||
|
||||
@@ -25,6 +25,8 @@ class ChannelListScreen {
|
||||
headerPlusButton = element(by.id(this.testID.headerPlusButton));
|
||||
findChannelsInput = element(by.id(this.testID.findChannelsInput));
|
||||
threadsButton = element(by.id(this.testID.threadsButton));
|
||||
|
||||
// convenience props
|
||||
browseChannelsItem = PlusMenu.browseChannelsItem;
|
||||
createNewChannelItem = PlusMenu.createNewChannelItem;
|
||||
openDirectMessageItem = PlusMenu.openDirectMessageItem;
|
||||
|
||||
80
detox/e2e/support/ui/screen/create_or_edit_channel.ts
Normal file
80
detox/e2e/support/ui/screen/create_or_edit_channel.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {
|
||||
ChannelScreen,
|
||||
ChannelListScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
class CreateOrEditChannelScreen {
|
||||
testID = {
|
||||
createOrEditChannelScreen: 'create_or_edit_channel.screen',
|
||||
closeButton: 'close.create_or_edit_channel.button',
|
||||
createButton: 'create_or_edit_channel.create.button',
|
||||
saveButton: 'create_or_edit_channel.save.button',
|
||||
scrollView: 'create_or_edit_channel.scrollview',
|
||||
makePrivateToggledOff: 'channel_info_form.make_private.toggled.false',
|
||||
makePrivateToggledOn: 'channel_info_form.make_private.toggled.true',
|
||||
makePrivateDescription: 'channel_info_form.make_private.description',
|
||||
displayNameInput: 'channel_info_form.display_name.input',
|
||||
purposeInput: 'channel_info_form.purpose.input',
|
||||
purposeDescription: 'channel_info_form.purpose.description',
|
||||
headerInput: 'channel_info_form.header.input',
|
||||
headerDescription: 'channel_info_form.header.description',
|
||||
};
|
||||
|
||||
createOrEditChannelScreen = element(by.id(this.testID.createOrEditChannelScreen));
|
||||
closeButton = element(by.id(this.testID.closeButton));
|
||||
createButton = element(by.id(this.testID.createButton));
|
||||
saveButton = element(by.id(this.testID.saveButton));
|
||||
scrollView = element(by.id(this.testID.scrollView));
|
||||
makePrivateToggledOff = element(by.id(this.testID.makePrivateToggledOff));
|
||||
makePrivateToggledOn = element(by.id(this.testID.makePrivateToggledOn));
|
||||
makePrivateDescription = element(by.id(this.testID.makePrivateDescription));
|
||||
displayNameInput = element(by.id(this.testID.displayNameInput));
|
||||
purposeInput = element(by.id(this.testID.purposeInput));
|
||||
purposeDescription = element(by.id(this.testID.purposeDescription));
|
||||
headerInput = element(by.id(this.testID.headerInput));
|
||||
headerDescription = element(by.id(this.testID.headerDescription));
|
||||
|
||||
toBeVisible = async () => {
|
||||
await waitFor(this.createOrEditChannelScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
|
||||
return this.createOrEditChannelScreen;
|
||||
};
|
||||
|
||||
openCreateChannel = async () => {
|
||||
// # Open create channel screen
|
||||
await ChannelListScreen.headerPlusButton.tap();
|
||||
await ChannelListScreen.createNewChannelItem.tap();
|
||||
|
||||
return this.toBeVisible();
|
||||
};
|
||||
|
||||
openEditChannel = async () => {
|
||||
// # Open edit channel screen
|
||||
await ChannelScreen.introOptionSetHeaderItem.tap();
|
||||
|
||||
return this.toBeVisible();
|
||||
};
|
||||
|
||||
close = async () => {
|
||||
await this.closeButton.tap();
|
||||
await expect(this.createOrEditChannelScreen).not.toBeVisible();
|
||||
};
|
||||
|
||||
toggleMakePrivateOn = async () => {
|
||||
await this.makePrivateToggledOff.tap();
|
||||
await expect(this.makePrivateToggledOn).toBeVisible();
|
||||
};
|
||||
|
||||
toggleMakePrivateOff = async () => {
|
||||
await this.makePrivateToggledOn.tap();
|
||||
await expect(this.makePrivateToggledOff).toBeVisible();
|
||||
};
|
||||
}
|
||||
|
||||
const createOrEditChannelScreen = new CreateOrEditChannelScreen();
|
||||
export default createOrEditChannelScreen;
|
||||
@@ -22,7 +22,7 @@ class HomeScreen {
|
||||
accountTab = element(by.id(this.testID.accountTab));
|
||||
|
||||
toBeVisible = async () => {
|
||||
await waitFor(this.channelListTab).toBeVisible().withTimeout(timeouts.TEN_SEC);
|
||||
await waitFor(this.channelListTab).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
|
||||
return this.channelListTab;
|
||||
};
|
||||
|
||||
@@ -6,11 +6,14 @@ import BrowseChannelsScreen from './browse_channels';
|
||||
import ChannelScreen from './channel';
|
||||
import ChannelListScreen from './channel_list';
|
||||
import CreateDirectMessageScreen from './create_direct_message';
|
||||
import CreateOrEditChannelScreen from './create_or_edit_channel';
|
||||
import EditServerScreen from './edit_server';
|
||||
import HomeScreen from './home';
|
||||
import LoginScreen from './login';
|
||||
import PostOptionsScreen from './post_options';
|
||||
import ServerScreen from './server';
|
||||
import ServerListScreen from './server_list';
|
||||
import ThreadScreen from './thread';
|
||||
|
||||
export {
|
||||
AccountScreen,
|
||||
@@ -18,9 +21,12 @@ export {
|
||||
ChannelScreen,
|
||||
ChannelListScreen,
|
||||
CreateDirectMessageScreen,
|
||||
CreateOrEditChannelScreen,
|
||||
EditServerScreen,
|
||||
HomeScreen,
|
||||
LoginScreen,
|
||||
PostOptionsScreen,
|
||||
ServerScreen,
|
||||
ServerListScreen,
|
||||
ThreadScreen,
|
||||
};
|
||||
|
||||
79
detox/e2e/support/ui/screen/post_options.ts
Normal file
79
detox/e2e/support/ui/screen/post_options.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Alert} from '@support/ui/component';
|
||||
import {timeouts, wait} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
class PostOptionsScreen {
|
||||
testID = {
|
||||
reactionEmojiPrefix: 'post_options.reaction_bar.reaction.',
|
||||
postOptionsScreen: 'post_options.screen',
|
||||
pickReaction: 'post_options.reaction_bar.pick_reaction',
|
||||
replyPostOption: 'post_options.reply.post.option',
|
||||
copyLinkOption: 'post_options.copy.permalink.option',
|
||||
saveChannelOption: 'post_options.save.channel.option',
|
||||
unsaveChannelOption: 'post_options.unsave.channel.option',
|
||||
copyTextOption: 'post_options.copy.text.option',
|
||||
pinChannelOption: 'post_options.pin.channel.option',
|
||||
unpinChannelOption: 'post_options.unpin.channel.option',
|
||||
editPostOption: 'post_options.edit.post.option',
|
||||
deletePostOption: 'post_options.delete.post.option',
|
||||
followThreadOption: 'post_options.follow.thread.option',
|
||||
markUnreadOption: 'post_options.mark.unread.option',
|
||||
};
|
||||
|
||||
postOptionsScreen = element(by.id(this.testID.postOptionsScreen));
|
||||
pickReaction = element(by.id(this.testID.pickReaction));
|
||||
replyPostOption = element(by.id(this.testID.replyPostOption));
|
||||
copyLinkOption = element(by.id(this.testID.copyLinkOption));
|
||||
saveChannelOption = element(by.id(this.testID.saveChannelOption));
|
||||
unsaveChannelOption = element(by.id(this.testID.unsaveChannelOption));
|
||||
copyTextOption = element(by.id(this.testID.copyTextOption));
|
||||
pinChannelOption = element(by.id(this.testID.pinChannelOption));
|
||||
unpinChannelOption = element(by.id(this.testID.unpinChannelOption));
|
||||
editPostOption = element(by.id(this.testID.editPostOption));
|
||||
deletePostOption = element(by.id(this.testID.deletePostOption));
|
||||
followThreadOption = element(by.id(this.testID.followThreadOption));
|
||||
markUnreadOption = element(by.id(this.testID.markUnreadOption));
|
||||
|
||||
getReactionEmoji = (emojiName: string) => {
|
||||
return element(by.id(`${this.testID.reactionEmojiPrefix}${emojiName}`));
|
||||
};
|
||||
|
||||
toBeVisible = async () => {
|
||||
await waitFor(this.postOptionsScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
|
||||
return postOptionsScreen;
|
||||
};
|
||||
|
||||
close = async () => {
|
||||
await this.postOptionsScreen.tap({x: 5, y: 10});
|
||||
await expect(this.postOptionsScreen).not.toBeVisible();
|
||||
};
|
||||
|
||||
deletePost = async ({confirm = true} = {}) => {
|
||||
await this.deletePostOption.tap({x: 1, y: 1});
|
||||
const {
|
||||
deletePostTitle,
|
||||
cancelButton,
|
||||
deleteButton,
|
||||
} = Alert;
|
||||
await expect(deletePostTitle).toBeVisible();
|
||||
await expect(cancelButton).toBeVisible();
|
||||
await expect(deleteButton).toBeVisible();
|
||||
if (confirm) {
|
||||
await deleteButton.tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await expect(this.postOptionsScreen).not.toBeVisible();
|
||||
} else {
|
||||
await cancelButton.tap();
|
||||
await wait(timeouts.ONE_SEC);
|
||||
await expect(this.postOptionsScreen).toBeVisible();
|
||||
await this.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const postOptionsScreen = new PostOptionsScreen();
|
||||
export default postOptionsScreen;
|
||||
31
detox/e2e/support/ui/screen/thread.ts
Normal file
31
detox/e2e/support/ui/screen/thread.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {NavigationHeader} from '@support/ui/component';
|
||||
import {timeouts} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
class ThreadScreen {
|
||||
testID = {
|
||||
threadScreen: 'channel.screen',
|
||||
};
|
||||
|
||||
threadScreen = element(by.id(this.testID.threadScreen));
|
||||
|
||||
// convenience props
|
||||
backButton = NavigationHeader.backButton;
|
||||
|
||||
toBeVisible = async () => {
|
||||
await waitFor(this.threadScreen).toExist().withTimeout(timeouts.TEN_SEC);
|
||||
|
||||
return this.threadScreen;
|
||||
};
|
||||
|
||||
back = async () => {
|
||||
await this.backButton.tap();
|
||||
await expect(this.threadScreen).not.toBeVisible();
|
||||
};
|
||||
}
|
||||
|
||||
const threadScreen = new ThreadScreen();
|
||||
export default threadScreen;
|
||||
@@ -77,9 +77,9 @@ describe('Channels - Browse Channels', () => {
|
||||
await expect(BrowseChannelsScreen.getChannelItemDisplayName(channel.name)).toHaveText(channel.display_name);
|
||||
|
||||
// # Tap on the new channel item
|
||||
await BrowseChannelsScreen.getChannelItem(channel.name).tap();
|
||||
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
||||
|
||||
// * Verify on new channel screen
|
||||
// * Verify on newly joined channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(channel.display_name);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(channel.display_name);
|
||||
@@ -88,7 +88,7 @@ describe('Channels - Browse Channels', () => {
|
||||
await ChannelScreen.back();
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
||||
// * Verify new channel is added to channel list
|
||||
// * Verify newly joined channel is added to channel list
|
||||
await expect(ChannelListScreen.getChannelListItemDisplayName(channelsCategory, channel.name)).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
ChannelScreen,
|
||||
ChannelListScreen,
|
||||
CreateDirectMessageScreen,
|
||||
CreateOrEditChannelScreen,
|
||||
HomeScreen,
|
||||
LoginScreen,
|
||||
ServerScreen,
|
||||
@@ -142,8 +143,16 @@ describe('Channels - Channel List', () => {
|
||||
await CreateDirectMessageScreen.close();
|
||||
});
|
||||
|
||||
xit('MM-T4728_6 - should be able to go to create channel screen', async () => {
|
||||
// NOT YET IMPLEMENTED
|
||||
it('MM-T4728_6 - should be able to go to create channel screen', async () => {
|
||||
// # Tap on plus menu button and tap on create new channel item
|
||||
await ChannelListScreen.headerPlusButton.tap();
|
||||
await ChannelListScreen.createNewChannelItem.tap();
|
||||
|
||||
// * Verify on create channel screen
|
||||
await CreateOrEditChannelScreen.toBeVisible();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await CreateOrEditChannelScreen.close();
|
||||
});
|
||||
|
||||
xit('MM-T4728_7 - should be able to go to threads screen', async () => {
|
||||
|
||||
98
detox/e2e/test/channels/channel_post_list.e2e.ts
Normal file
98
detox/e2e/test/channels/channel_post_list.e2e.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
// 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 {
|
||||
ChannelScreen,
|
||||
ChannelListScreen,
|
||||
HomeScreen,
|
||||
LoginScreen,
|
||||
PostOptionsScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Channel Post List', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
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-T4773_1 - should match elements on channel screen', async () => {
|
||||
// # Open a channel screen
|
||||
await ChannelScreen.open('channels', testChannel.name);
|
||||
|
||||
// * Verify basic elements on channel screen
|
||||
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.postList.getFlatList()).toBeVisible();
|
||||
await expect(ChannelScreen.postDraft).toBeVisible();
|
||||
await expect(ChannelScreen.postInput).toBeVisible();
|
||||
await expect(ChannelScreen.atInputQuickAction).toBeVisible();
|
||||
await expect(ChannelScreen.slashInputQuickAction).toBeVisible();
|
||||
await expect(ChannelScreen.fileQuickAction).toBeVisible();
|
||||
await expect(ChannelScreen.imageQuickAction).toBeVisible();
|
||||
await expect(ChannelScreen.cameraQuickAction).toBeVisible();
|
||||
await expect(ChannelScreen.sendButtonDisabled).toBeVisible();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
||||
it('MM-T4773_2 - should be able to add a message to post list and delete a message from post list', async () => {
|
||||
// # Open a channel screen and post a message
|
||||
const message = `Message ${getRandomId()}`;
|
||||
await ChannelScreen.open('channels', testChannel.name);
|
||||
await ChannelScreen.postMessage(message);
|
||||
|
||||
// * Verify message is added to post list
|
||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
|
||||
await expect(postListPostItem).toExist();
|
||||
|
||||
// # Open post options for the message that was just posted, tap delete option and confirm
|
||||
await ChannelScreen.openPostOptionsFor(post.id, message);
|
||||
await PostOptionsScreen.deletePost({confirm: true});
|
||||
|
||||
// * Verify message is deleted from post list
|
||||
await expect(postListPostItem).not.toExist();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
});
|
||||
139
detox/e2e/test/channels/create_and_edit_header.e2e.ts
Normal file
139
detox/e2e/test/channels/create_and_edit_header.e2e.ts
Normal file
@@ -0,0 +1,139 @@
|
||||
// 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 {
|
||||
ChannelListScreen,
|
||||
ChannelScreen,
|
||||
CreateOrEditChannelScreen,
|
||||
HomeScreen,
|
||||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Channels - Create Channel and Edit Channel Header', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
|
||||
beforeAll(async () => {
|
||||
const {user} = await Setup.apiInit(siteOneUrl);
|
||||
|
||||
// # 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-T4731_1 - should match elements on create channel screen', async () => {
|
||||
// # Open create channel screen
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
|
||||
// * Verify basic elements on create channel screen
|
||||
await expect(CreateOrEditChannelScreen.closeButton).toBeVisible();
|
||||
await expect(CreateOrEditChannelScreen.createButton).toBeVisible();
|
||||
await expect(CreateOrEditChannelScreen.makePrivateToggledOff).toBeVisible();
|
||||
await expect(CreateOrEditChannelScreen.makePrivateDescription).toHaveText('When a channel is set to private, only invited team members can access and participate in that channel');
|
||||
await expect(CreateOrEditChannelScreen.displayNameInput).toBeVisible();
|
||||
await expect(CreateOrEditChannelScreen.purposeInput).toBeVisible();
|
||||
await expect(CreateOrEditChannelScreen.purposeDescription).toHaveText('Describe how this channel should be used.');
|
||||
await expect(CreateOrEditChannelScreen.headerInput).toBeVisible();
|
||||
await expect(CreateOrEditChannelScreen.headerDescription).toHaveText('Specify text to appear in the channel header beside the channel name. For example, include frequently used links by typing link text [Link Title](http://example.com).');
|
||||
|
||||
// # Go back to channel list screen
|
||||
await CreateOrEditChannelScreen.close();
|
||||
});
|
||||
|
||||
it('MM-T4731_2 - should be able to create a public channel and edit the channel header', async () => {
|
||||
// # Open create channel screen, toggle make private off, fill out channel info, and tap create button
|
||||
const suffix = getRandomId();
|
||||
const displayName = `Channel ${suffix}`;
|
||||
const purpose = `Purpose ${suffix}`;
|
||||
const header = `Header ${suffix}`;
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await expect(CreateOrEditChannelScreen.makePrivateToggledOff).toBeVisible();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(displayName);
|
||||
await CreateOrEditChannelScreen.purposeInput.replaceText(purpose);
|
||||
await CreateOrEditChannelScreen.headerInput.replaceText(header);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
|
||||
// * Verify on newly created public channel
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(displayName);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(displayName);
|
||||
|
||||
// # Tap on set header option to edit the channel header
|
||||
await ChannelScreen.introOptionSetHeaderItem.tap();
|
||||
|
||||
// * Verify channel header is correct
|
||||
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(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();
|
||||
|
||||
// * Verify channel header has new value
|
||||
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(`${header} edit`);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await CreateOrEditChannelScreen.close();
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
||||
it('MM-T4731_3 - should be able to create a private channel and edit the channel header', async () => {
|
||||
// # Open create channel screen, toggle make private on, fill out channel info, and tap create button
|
||||
const suffix = getRandomId();
|
||||
const displayName = `Channel ${suffix}`;
|
||||
const purpose = `Purpose ${suffix}`;
|
||||
const header = `Header ${suffix}`;
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.toggleMakePrivateOn();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(displayName);
|
||||
await CreateOrEditChannelScreen.purposeInput.replaceText(purpose);
|
||||
await CreateOrEditChannelScreen.headerInput.replaceText(header);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
|
||||
// * Verify on newly created private channel
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(displayName);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(displayName);
|
||||
|
||||
// # Tap on set header option to edit the channel header
|
||||
await ChannelScreen.introOptionSetHeaderItem.tap();
|
||||
|
||||
// * Verify channel header is correct
|
||||
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(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();
|
||||
|
||||
// * Verify channel header has new value
|
||||
await expect(CreateOrEditChannelScreen.headerInput).toHaveValue(`${header} edit`);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await CreateOrEditChannelScreen.close();
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,7 @@
|
||||
// *******************************************************************
|
||||
|
||||
import {
|
||||
Channel,
|
||||
Setup,
|
||||
Team,
|
||||
User,
|
||||
@@ -30,10 +31,12 @@ describe('Channels - Create Direct Message', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
const directMessagesCategory = 'direct_messages';
|
||||
let testTeam: any;
|
||||
let testUser: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const {team, user} = await Setup.apiInit(siteOneUrl);
|
||||
testTeam = team;
|
||||
testUser = user;
|
||||
|
||||
// # Log in to server
|
||||
await ServerScreen.connectToServer(serverOneUrl, serverOneDisplayName);
|
||||
@@ -75,7 +78,7 @@ describe('Channels - Create Direct Message', () => {
|
||||
|
||||
// # Open create direct message screen and search for the new user
|
||||
await CreateDirectMessageScreen.open();
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(newUser.username);
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(newUserDisplayName);
|
||||
|
||||
// * Verify search returns the new user item
|
||||
await expect(CreateDirectMessageScreen.getUserItemDisplayName(newUser.id)).toBeVisible();
|
||||
@@ -99,7 +102,8 @@ describe('Channels - Create Direct Message', () => {
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
||||
// * Verify direct message channel for the new user is added to direct message list
|
||||
await expect(ChannelListScreen.getChannelListItemDisplayName(directMessagesCategory, newUser.id)).toHaveText(newUserDisplayName);
|
||||
const {channel: directMessageChannel} = await Channel.apiCreateDirectChannel(siteOneUrl, [testUser.id, newUser.id]);
|
||||
await expect(ChannelListScreen.getChannelListItemDisplayName(directMessagesCategory, directMessageChannel.name)).toHaveText(newUserDisplayName);
|
||||
});
|
||||
|
||||
it('MM-T4730_3 - should be able to create a group message', async () => {
|
||||
@@ -112,8 +116,8 @@ describe('Channels - Create Direct Message', () => {
|
||||
// * Verify no group message channel for the new users appears on channel list screen
|
||||
const firstNewUserDisplayName = firstNewUser.username;
|
||||
const secondNewUserDisplayName = secondNewUser.username;
|
||||
const groupDisplayName = `${firstNewUserDisplayName}, ${secondNewUserDisplayName}`;
|
||||
await expect(ChannelListScreen.getChannelListItemDisplayName(directMessagesCategory, groupDisplayName)).not.toBeVisible();
|
||||
const groupDisplayName = `${firstNewUserDisplayName}, ${secondNewUserDisplayName}, ${testUser.username}`;
|
||||
await expect(element(by.text(groupDisplayName))).not.toBeVisible();
|
||||
|
||||
// # Open create direct message screen, search for the first new user and tap on the first new user item
|
||||
await CreateDirectMessageScreen.open();
|
||||
@@ -143,6 +147,6 @@ describe('Channels - Create Direct Message', () => {
|
||||
await ChannelListScreen.toBeVisible();
|
||||
|
||||
// * Verify group message channel for the other two new users is added to direct message list
|
||||
await expect(ChannelListScreen.getChannelListItemDisplayName(directMessagesCategory, `${firstNewUser.id}__${secondNewUser.id}`)).toHaveText(groupDisplayName);
|
||||
await expect(element(by.text(groupDisplayName))).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
130
detox/e2e/test/smoke_test/channels.e2e.ts
Normal file
130
detox/e2e/test/smoke_test/channels.e2e.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
// 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 {
|
||||
BrowseChannelsScreen,
|
||||
ChannelScreen,
|
||||
ChannelListScreen,
|
||||
CreateDirectMessageScreen,
|
||||
CreateOrEditChannelScreen,
|
||||
HomeScreen,
|
||||
LoginScreen,
|
||||
ServerScreen,
|
||||
} from '@support/ui/screen';
|
||||
import {getRandomId} from '@support/utils';
|
||||
import {expect} from 'detox';
|
||||
|
||||
describe('Smoke Test - Channels', () => {
|
||||
const serverOneDisplayName = 'Server 1';
|
||||
const channelsCategory = 'channels';
|
||||
let testChannel: any;
|
||||
let testTeam: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const {channel, team, user} = await Setup.apiInit(siteOneUrl);
|
||||
testChannel = channel;
|
||||
testTeam = team;
|
||||
|
||||
// # 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-T4774_1 - should be able to join a new channel and switch to an existing channel', async () => {
|
||||
// # As admin, create a new channel so that user can join, then open browse channels screen and join the new channel
|
||||
const {channel} = await Channel.apiCreateChannel(siteOneUrl, {teamId: testTeam.id});
|
||||
await BrowseChannelsScreen.open();
|
||||
await BrowseChannelsScreen.searchInput.replaceText(channel.name);
|
||||
await BrowseChannelsScreen.getChannelItem(channel.name).multiTap(2);
|
||||
|
||||
// * Verify on newly joined channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(channel.display_name);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(channel.display_name);
|
||||
|
||||
// # Go back to channel list screen and switch to an existing channel
|
||||
await ChannelScreen.back();
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
|
||||
// * Verify on the other channel screen
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(testChannel.display_name);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(testChannel.display_name);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
||||
it('MM-T4774_2 - should be able to create a channel and create a direct message', async () => {
|
||||
// # Open create channel screen and create a new channel
|
||||
const displayName = `Channel ${getRandomId()}`;
|
||||
await CreateOrEditChannelScreen.openCreateChannel();
|
||||
await CreateOrEditChannelScreen.displayNameInput.replaceText(displayName);
|
||||
await CreateOrEditChannelScreen.createButton.tap();
|
||||
|
||||
// * Verify on newly created public channel
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(displayName);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(displayName);
|
||||
|
||||
// # As admin, create a new user to open direct message with, then go back to channel list screen, open create direct message screen and open direct message with new user
|
||||
const {user: newUser} = await User.apiCreateUser(siteOneUrl);
|
||||
const newUserDisplayName = newUser.username;
|
||||
await Team.apiAddUserToTeam(siteOneUrl, newUser.id, testTeam.id);
|
||||
await ChannelScreen.back();
|
||||
await CreateDirectMessageScreen.open();
|
||||
await CreateDirectMessageScreen.searchInput.replaceText(newUserDisplayName);
|
||||
await CreateDirectMessageScreen.getUserItem(newUser.id).tap();
|
||||
await CreateDirectMessageScreen.startButton.tap();
|
||||
|
||||
// * Verify on direct message channel screen for the new user
|
||||
await ChannelScreen.toBeVisible();
|
||||
await expect(ChannelScreen.headerTitle).toHaveText(newUserDisplayName);
|
||||
await expect(ChannelScreen.introDisplayName).toHaveText(newUserDisplayName);
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
|
||||
it('MM-T4774_3 - should be able to post a message in a channel', async () => {
|
||||
// # Open a channel screen and post a message
|
||||
const message = `Message ${getRandomId()}`;
|
||||
await ChannelScreen.open(channelsCategory, testChannel.name);
|
||||
await ChannelScreen.postMessage(message);
|
||||
|
||||
// * Verify message is posted
|
||||
const {post} = await Post.apiGetLastPostInChannel(siteOneUrl, testChannel.id);
|
||||
const {postListPostItem} = ChannelScreen.getPostListPostItem(post.id, message);
|
||||
await expect(postListPostItem).toExist();
|
||||
|
||||
// # Go back to channel list screen
|
||||
await ChannelScreen.back();
|
||||
});
|
||||
});
|
||||
43
package-lock.json
generated
43
package-lock.json
generated
@@ -56,6 +56,7 @@
|
||||
"react-native-create-thumbnail": "1.5.1",
|
||||
"react-native-device-info": "8.7.0",
|
||||
"react-native-document-picker": "8.1.0",
|
||||
"react-native-dotenv": "3.4.0",
|
||||
"react-native-elements": "3.4.2",
|
||||
"react-native-exception-handler": "2.10.10",
|
||||
"react-native-fast-image": "8.5.11",
|
||||
@@ -118,6 +119,7 @@
|
||||
"@types/react-native": "0.67.3",
|
||||
"@types/react-native-background-timer": "2.0.0",
|
||||
"@types/react-native-button": "3.0.1",
|
||||
"@types/react-native-dotenv": "0.2.0",
|
||||
"@types/react-native-share": "3.3.3",
|
||||
"@types/react-native-video": "5.0.13",
|
||||
"@types/react-syntax-highlighter": "13.5.2",
|
||||
@@ -5766,6 +5768,12 @@
|
||||
"@types/react-native": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-native-dotenv": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-native-dotenv/-/react-native-dotenv-0.2.0.tgz",
|
||||
"integrity": "sha512-ZxX+dU/yoQc0jTk+/NWttkiuXceJyN5FpOSqDl0WynN5GDzxwH7OMruQ47qcY8llo2RD3irjvzJ9BwC8gDiq0A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/react-native-share": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-native-share/-/react-native-share-3.3.3.tgz",
|
||||
@@ -9432,6 +9440,14 @@
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
|
||||
"integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/dtrace-provider": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
@@ -19041,6 +19057,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-dotenv": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.0.tgz",
|
||||
"integrity": "sha512-Z/pvS0QGSDHcxnUmJ/uHLgC1/DZL/UpKSOHp3mCKyV3vQd385CBahrEBca8pD3eabrsDjg8xO0uRxnRG3s1lbw==",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-elements": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.2.tgz",
|
||||
@@ -27593,6 +27617,12 @@
|
||||
"@types/react-native": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-native-dotenv": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-native-dotenv/-/react-native-dotenv-0.2.0.tgz",
|
||||
"integrity": "sha512-ZxX+dU/yoQc0jTk+/NWttkiuXceJyN5FpOSqDl0WynN5GDzxwH7OMruQ47qcY8llo2RD3irjvzJ9BwC8gDiq0A==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react-native-share": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-native-share/-/react-native-share-3.3.3.tgz",
|
||||
@@ -30487,6 +30517,11 @@
|
||||
"domhandler": "^4.2.0"
|
||||
}
|
||||
},
|
||||
"dotenv": {
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.0.tgz",
|
||||
"integrity": "sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q=="
|
||||
},
|
||||
"dtrace-provider": {
|
||||
"version": "0.8.8",
|
||||
"resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.8.tgz",
|
||||
@@ -38027,6 +38062,14 @@
|
||||
"invariant": "^2.2.4"
|
||||
}
|
||||
},
|
||||
"react-native-dotenv": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.0.tgz",
|
||||
"integrity": "sha512-Z/pvS0QGSDHcxnUmJ/uHLgC1/DZL/UpKSOHp3mCKyV3vQd385CBahrEBca8pD3eabrsDjg8xO0uRxnRG3s1lbw==",
|
||||
"requires": {
|
||||
"dotenv": "^16.0.0"
|
||||
}
|
||||
},
|
||||
"react-native-elements": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.2.tgz",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"react-native-create-thumbnail": "1.5.1",
|
||||
"react-native-device-info": "8.7.0",
|
||||
"react-native-document-picker": "8.1.0",
|
||||
"react-native-dotenv": "3.4.0",
|
||||
"react-native-elements": "3.4.2",
|
||||
"react-native-exception-handler": "2.10.10",
|
||||
"react-native-fast-image": "8.5.11",
|
||||
@@ -116,6 +117,7 @@
|
||||
"@types/react-native": "0.67.3",
|
||||
"@types/react-native-background-timer": "2.0.0",
|
||||
"@types/react-native-button": "3.0.1",
|
||||
"@types/react-native-dotenv": "0.2.0",
|
||||
"@types/react-native-share": "3.3.3",
|
||||
"@types/react-native-video": "5.0.13",
|
||||
"@types/react-syntax-highlighter": "13.5.2",
|
||||
|
||||
1
env.d.ts → types/env.d.ts
vendored
1
env.d.ts → types/env.d.ts
vendored
@@ -3,4 +3,5 @@
|
||||
|
||||
// So that typescript doesn't complain about importing `@env` through react-native-dotenv
|
||||
declare module '@env' {
|
||||
export const RUNNING_E2E: string;
|
||||
}
|
||||
Reference in New Issue
Block a user