diff --git a/app/components/pasteable_text_input/__snapshots__/index.test.js.snap b/app/components/pasteable_text_input/__snapshots__/index.test.js.snap index 8e1dbe0ea..d5fe97c90 100644 --- a/app/components/pasteable_text_input/__snapshots__/index.test.js.snap +++ b/app/components/pasteable_text_input/__snapshots__/index.test.js.snap @@ -5,7 +5,6 @@ exports[`PasteableTextInput should render pasteable text input 1`] = ` allowFontScaling={true} onPaste={[Function]} rejectResponderTermination={true} - testID="post.input" underlineColorAndroid="transparent" > My Text diff --git a/app/components/pasteable_text_input/index.js b/app/components/pasteable_text_input/index.js index 239a90178..0b492e92c 100644 --- a/app/components/pasteable_text_input/index.js +++ b/app/components/pasteable_text_input/index.js @@ -53,11 +53,11 @@ export class PasteableTextInput extends React.PureComponent { } render() { - const {forwardRef, ...props} = this.props; + const {testID, forwardRef, ...props} = this.props; return ( + @@ -534,7 +537,7 @@ exports[`PostDraft Should render the DraftInput 1`] = ` "paddingRight": 8, } } - testID="disabled_send.button" + testID="post_draft.send.button.disabled" > + diff --git a/app/components/post_draft/post_draft.js b/app/components/post_draft/post_draft.js index d8d1745e1..35aea2103 100644 --- a/app/components/post_draft/post_draft.js +++ b/app/components/post_draft/post_draft.js @@ -90,6 +90,7 @@ export default class PostDraft extends PureComponent { const draftInput = ( { ); expect(toJSON()).toMatchSnapshot(); - expect(getByTestId('post.input')).toBeTruthy(); + expect(getByTestId('post_draft.post.input')).toBeTruthy(); expect(queryByText('Close Channel')).toBeNull(); }); @@ -79,7 +79,7 @@ describe('PostDraft', () => { expect(toJSON()).toMatchSnapshot(); // Should not render text input - expect(queryByTestId('post.input')).toBeNull(); + expect(queryByTestId('post_draft.post.input')).toBeNull(); // Should match text description expect(getByText('You are viewing an ')).toBeTruthy(); @@ -100,7 +100,7 @@ describe('PostDraft', () => { expect(toJSON()).toMatchSnapshot(); // Should not render text input - expect(queryByTestId('post.input')).toBeNull(); + expect(queryByTestId('post_draft.post.input')).toBeNull(); // Should match text description expect(getByText('You are viewing an ')).toBeTruthy(); @@ -121,7 +121,7 @@ describe('PostDraft', () => { expect(toJSON()).toMatchSnapshot(); // Should not render text input - expect(queryByTestId('post.input')).toBeNull(); + expect(queryByTestId('post_draft.post.input')).toBeNull(); // Should match text description expect(getByText('This channel is read-only.')).toBeTruthy(); @@ -140,7 +140,7 @@ describe('PostDraft', () => { expect(toJSON()).toMatchSnapshot(); // Should not render text input - expect(queryByTestId('post.input')).toBeNull(); + expect(queryByTestId('post_draft.post.input')).toBeNull(); // Should match text description expect(getByText('This channel is read-only.')).toBeTruthy(); diff --git a/app/components/post_draft/post_input/__snapshots__/post_input.test.js.snap b/app/components/post_draft/post_input/__snapshots__/post_input.test.js.snap index b3d028d84..8aeff2e88 100644 --- a/app/components/post_draft/post_input/__snapshots__/post_input.test.js.snap +++ b/app/components/post_draft/post_input/__snapshots__/post_input.test.js.snap @@ -25,6 +25,7 @@ exports[`PostInput should match, full snapshot 1`] = ` "paddingTop": 6, } } + testID="post_draft.post.input" textContentType="none" underlineColorAndroid="transparent" /> diff --git a/app/components/post_draft/post_input/post_input.js b/app/components/post_draft/post_input/post_input.js index ac5079844..a337a32f6 100644 --- a/app/components/post_draft/post_input/post_input.js +++ b/app/components/post_draft/post_input/post_input.js @@ -278,6 +278,7 @@ export default class PostInput extends PureComponent { return ( { const style = getStyle(theme); return ( - + @@ -32,7 +32,7 @@ function SendButton(props) { return ( diff --git a/app/components/reaction_picker/reaction_picker.js b/app/components/reaction_picker/reaction_picker.js index da1072565..0306526fb 100644 --- a/app/components/reaction_picker/reaction_picker.js +++ b/app/components/reaction_picker/reaction_picker.js @@ -83,7 +83,7 @@ export default class ReactionPicker extends PureComponent { {list} + { }); render() { - const {canManageUsers, groupConstrained, isLandscape, theme} = this.props; + const {testID, canManageUsers, groupConstrained, isLandscape, theme} = this.props; if (canManageUsers && !groupConstrained) { return ( <> } diff --git a/app/screens/channel_info/convert_private/convert_private.tsx b/app/screens/channel_info/convert_private/convert_private.tsx index ea5d199b0..32d0fe1b2 100644 --- a/app/screens/channel_info/convert_private/convert_private.tsx +++ b/app/screens/channel_info/convert_private/convert_private.tsx @@ -14,6 +14,7 @@ import {t} from '@utils/i18n'; import {preventDoubleTap} from '@utils/tap'; interface ConvertPrivateProps { + testID?: string; canConvert: boolean; channelId: string; convertChannelToPrivate: (channelId: string) => Promise; @@ -79,7 +80,7 @@ export default class ConvertPrivate extends PureComponent { }); render() { - const {canConvert, isLandscape, theme} = this.props; + const {testID, canConvert, isLandscape, theme} = this.props; if (!canConvert) { return null; @@ -89,6 +90,7 @@ export default class ConvertPrivate extends PureComponent { <> void; isFavorite: boolean; @@ -17,7 +18,7 @@ interface FavoriteProps { theme: Theme; } -const Favorite = ({channelId, favoriteChannel, isLandscape, isFavorite, unfavoriteChannel, theme}: FavoriteProps) => { +const Favorite = ({testID, channelId, favoriteChannel, isLandscape, isFavorite, unfavoriteChannel, theme}: FavoriteProps) => { const [favorite, setFavorite] = useState(isFavorite); const handleFavorite = preventDoubleTap(() => { @@ -28,6 +29,7 @@ const Favorite = ({channelId, favoriteChannel, isLandscape, isFavorite, unfavori return ( { +const IgnoreMentions = ({testID, channelId, ignore, isLandscape, updateChannelNotifyProps, userId, theme}: IgnoreMentionsProps) => { const [mentions, setMentions] = useState(ignore); const handleIgnoreChannelMentions = preventDoubleTap(() => { @@ -32,6 +33,7 @@ const IgnoreMentions = ({channelId, ignore, isLandscape, updateChannelNotifyProp return ( { }); render() { - const {isDirectMessage, canManageUsers, isLandscape, membersCount, separator, theme} = this.props; + const {testID, isDirectMessage, canManageUsers, isLandscape, membersCount, separator, theme} = this.props; if (isDirectMessage) { return null; @@ -51,6 +52,7 @@ export default class ManageMembers extends PureComponent { <> {separator && } { +const Mute = ({testID, channelId, isChannelMuted, isLandscape, updateChannelNotifyProps, userId, theme}: MuteProps) => { const [muted, setMuted] = useState(isChannelMuted); const handleMuteChannel = preventDoubleTap(() => { @@ -31,6 +32,7 @@ const Mute = ({channelId, isChannelMuted, isLandscape, updateChannelNotifyProps, return ( { }); render() { - const {pinnedCount, isLandscape, theme} = this.props; + const {testID, pinnedCount, isLandscape, theme} = this.props; return ( ); }; @@ -477,7 +477,7 @@ export default class MoreDirectMessages extends PureComponent { } return ( - + @@ -519,7 +519,7 @@ export default class MoreDirectMessages extends PureComponent { onLoadMore={this.getProfiles} onRowPress={this.handleSelectProfile} renderItem={this.renderItem} - testID='more_dms.list' + testID='more_direct_messages.list' theme={theme} /> diff --git a/app/screens/notification/index.tsx b/app/screens/notification/index.tsx index 5add8a6a8..567bba22a 100644 --- a/app/screens/notification/index.tsx +++ b/app/screens/notification/index.tsx @@ -127,7 +127,7 @@ const Notification = ({componentId, notification}: NotificationProps) => { style={styles.container} useNativeDriver={true} animation={animation} - testID='in_app_notification' + testID='in_app_notification.screen' > + { const {formatMessage} = this.context.intl; const {isLandscape, theme} = this.props; + const testID = `post.options.${key}.action`; return ( diff --git a/app/screens/post_options/post_options.test.js b/app/screens/post_options/post_options.test.js index 744b0b7c5..87f47eb7e 100644 --- a/app/screens/post_options/post_options.test.js +++ b/app/screens/post_options/post_options.test.js @@ -45,7 +45,7 @@ describe('PostOptions', () => { currentUserId: 'user1', deviceHeight: 600, hasBeenDeleted: false, - isFlagged: false, + isFlagged: true, isSystemMessage: false, managedConfig: {}, post, diff --git a/app/screens/recent_mentions/__snapshots__/recent_mentions.test.js.snap b/app/screens/recent_mentions/__snapshots__/recent_mentions.test.js.snap index 2791e9cf0..72c7170c8 100644 --- a/app/screens/recent_mentions/__snapshots__/recent_mentions.test.js.snap +++ b/app/screens/recent_mentions/__snapshots__/recent_mentions.test.js.snap @@ -7,6 +7,7 @@ exports[`RecentMentions should match snapshot 1`] = ` "flex": 1, } } + testID="recent_mentions.screen" > + {component} diff --git a/app/screens/search/search_result_post/__snapshots__/search_result_post.test.js.snap b/app/screens/search/search_result_post/__snapshots__/search_result_post.test.js.snap new file mode 100644 index 000000000..e71bd7b1d --- /dev/null +++ b/app/screens/search/search_result_post/__snapshots__/search_result_post.test.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`SearchResultPost should match snapshot 1`] = ` + +`; diff --git a/app/screens/search/search_result_post/search_result_post.js b/app/screens/search/search_result_post/search_result_post.js index dc8e60261..6092822ca 100644 --- a/app/screens/search/search_result_post/search_result_post.js +++ b/app/screens/search/search_result_post/search_result_post.js @@ -42,8 +42,11 @@ export default class SearchResultPost extends PureComponent { postComponentProps.skipPinnedHeader = this.props.skipPinnedHeader; } + const testID = `search_result_post.post.${this.props.postId}`; + return ( { + const baseProps = { + goToThread: jest.fn(), + onHashtagPress: jest.fn(), + onPermalinkPress: jest.fn(), + previewPost: jest.fn(), + postId: 'post-id', + isDeleted: false, + highlightPinnedOrFlagged: false, + managedConfig: {}, + showFullDate: false, + skipFlaggedHeader: false, + skipPinnedHeader: false, + }; + + test('should match snapshot', async () => { + const wrapper = shallowWithIntl(); + + expect(wrapper.getElement()).toMatchSnapshot(); + }); +}); diff --git a/app/screens/settings/general/settings.js b/app/screens/settings/general/settings.js index 36b3854dd..c61bbae53 100644 --- a/app/screens/settings/general/settings.js +++ b/app/screens/settings/general/settings.js @@ -211,6 +211,7 @@ class Settings extends PureComponent { isLandscape={isLandscape} /> } } } { + try { + const payload = { + channel_id: channelId, + message, + parent_id: rootId, + root_id: rootId, + props, + create_at: createAt, + }; + const response = await client.post('/api/v4/posts', payload); + + return {post: response.data}; + } catch (err) { + return getResponseFromError(err); + } +}; + /** * Get posts for a channel. * See https://api.mattermost.com/#tag/posts/paths/~1channels~1{channel_id}~1posts/get @@ -47,6 +75,7 @@ export const apiGetLastPostInChannel = async (channelId) => { }; export const Post = { + apiCreatePost, apiGetLastPostInChannel, apiGetPostsInChannel, }; diff --git a/detox/e2e/support/ui/component/index.js b/detox/e2e/support/ui/component/index.js index 9d41dc115..230ff9c3a 100644 --- a/detox/e2e/support/ui/component/index.js +++ b/detox/e2e/support/ui/component/index.js @@ -6,7 +6,10 @@ import CameraQuickAction from './camera_quick_action'; import FileQuickAction from './file_quick_action'; import ImageQuickAction from './image_quick_action'; import MainSidebar from './main_sidebar'; +import Post from './post'; +import PostDraft from './post_draft'; import PostOptions from './post_options'; +import SendButton from './send_button'; import SettingsSidebar from './settings_sidebar'; export { @@ -15,6 +18,9 @@ export { FileQuickAction, ImageQuickAction, MainSidebar, + Post, + PostDraft, PostOptions, + SendButton, SettingsSidebar, }; diff --git a/detox/e2e/support/ui/component/main_sidebar.js b/detox/e2e/support/ui/component/main_sidebar.js index 0f25a7851..d929e50e2 100644 --- a/detox/e2e/support/ui/component/main_sidebar.js +++ b/detox/e2e/support/ui/component/main_sidebar.js @@ -6,15 +6,21 @@ class MainSidebar { mainSidebar: 'main.sidebar', channelItemDisplayName: 'channel_item.display_name', channelsList: 'channels.list', - addChannel: 'action_button_sidebar.channels', - addDirectChannel: 'action_button_sidebar.direct', + openMoreChannelsButton: 'action_button_sidebar.channels', + openCreatePrivateChannelButton: 'action_button_sidebar.pg', + openMoreDirectMessagesButton: 'action_button_sidebar.direct', } mainSidebar = element(by.id(this.testID.mainSidebar)); channelItemDisplayName = element(by.id(this.testID.channelItemDisplayName)); channelsList = element(by.id(this.testID.channelsList)); - addChannel = element(by.id(this.testID.addChannel)); - addDirectChannel = element(by.id(this.testID.addDirectChannel)); + openMoreChannelsButton = element(by.id(this.testID.openMoreChannelsButton)); + openCreatePrivateChannelButton = element(by.id(this.testID.openCreatePrivateChannelButton)); + openMoreDirectMessagesButton = element(by.id(this.testID.openMoreDirectMessagesButton)); + + getChannelByDisplayName = (displayName) => { + return element(by.text(displayName).withAncestor(by.id(this.testID.channelsList))); + } toBeVisible = async () => { await expect(this.mainSidebar).toBeVisible(); @@ -22,10 +28,6 @@ class MainSidebar { return this.mainSidebar; } - getChannelByDisplayName = (displayName) => { - return element(by.text(displayName).withAncestor(by.id(this.testID.channelsList))); - } - hasChannelAtIndex = async (index, channelDisplayName) => { await expect( element(by.id(this.testID.channelItemDisplayName).withAncestor(by.id(this.testID.channelsList))).atIndex(index), diff --git a/detox/e2e/support/ui/component/post.js b/detox/e2e/support/ui/component/post.js new file mode 100644 index 000000000..baae556b4 --- /dev/null +++ b/detox/e2e/support/ui/component/post.js @@ -0,0 +1,18 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +class Post { + testID = { + postPrefix: 'post.', + } + + getPost = (postTypePrefix, postId, text) => { + if (text) { + return element(by.id(`${postTypePrefix}${this.testID.postPrefix}${postId}`).withDescendant(by.text(text))); + } + return element(by.id(`${postTypePrefix}${this.testID.postPrefix}${postId}`)); + } +} + +const post = new Post(); +export default post; diff --git a/detox/e2e/support/ui/component/post_draft.js b/detox/e2e/support/ui/component/post_draft.js new file mode 100644 index 000000000..22342d1e9 --- /dev/null +++ b/detox/e2e/support/ui/component/post_draft.js @@ -0,0 +1,34 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +class PostDraft { + testID = { + postDraft: 'post_draft', + postDraftArchived: 'post_draft.archived', + postDraftReadOnly: 'post_draft.archived', + postInput: 'post_draft.post.input', + } + + postDraft = element(by.id(this.testID.postDraft)); + postDraftArchived = element(by.id(this.testID.postDraftArchived)); + postDraftReadOnly = element(by.id(this.testID.postDraftReadOnly)); + postInput = element(by.id(this.testID.postInput)); + + toBeVisible = async (options = {archived: false, readOnly: false}) => { + if (options.archived) { + await expect(this.postDraftArchived).toBeVisible(); + return this.postDraftArchived; + } + + if (options.readOnly) { + await expect(this.postDraftReadOnly).toBeVisible(); + return this.postDraftReadOnly; + } + + await expect(this.postDraft).toBeVisible(); + return this.postDraft; + } +} + +const postDraft = new PostDraft(); +export default postDraft; diff --git a/detox/e2e/support/ui/component/post_options.js b/detox/e2e/support/ui/component/post_options.js index f7ceb08cf..09418d404 100644 --- a/detox/e2e/support/ui/component/post_options.js +++ b/detox/e2e/support/ui/component/post_options.js @@ -4,17 +4,45 @@ class PostOptions { testID = { postOptions: 'post.options', - openReactionPicker: 'open.reaction_picker', + reactionPickerAction: 'post.options.reaction_picker.action', + replyAction: 'post.options.reply.action', + permalinkAction: 'post.options.permalink.action', + copyAction: 'post.options.copy.action', + deleteAction: 'post.options.delete.action', + editAction: 'post.options.edit.action', + saveAction: 'post.options.flagged.action', + unsaveAction: 'post.options.unflag.action', + pinAction: 'post.options.pin.action', + unpinAction: 'post.options.unpin.action', + markUnreadAction: 'post.options.markUnread.action', + openAddReactionButton: 'open.add_reaction.button', + slideUpPanel: 'slide_up_panel', } postOptions = element(by.id(this.testID.postOptions)); - openReactionPicker = element(by.id(this.testID.openReactionPicker)); + reactionPickerAction = element(by.id(this.testID.reactionPickerAction)); + replyAction = element(by.id(this.testID.replyAction)); + permalinkAction = element(by.id(this.testID.permalinkAction)); + deleteAction = element(by.id(this.testID.deleteAction)); + editAction = element(by.id(this.testID.editAction)); + saveAction = element(by.id(this.testID.saveAction)); + unsaveAction = element(by.id(this.testID.unsaveAction)); + pinAction = element(by.id(this.testID.pinAction)); + unpinAction = element(by.id(this.testID.unpinAction)); + markUnreadAction = element(by.id(this.testID.markUnreadAction)); + openAddReactionButton = element(by.id(this.testID.openAddReactionButton)); + slideUpPanel = element(by.id(this.testID.slideUpPanel)); toBeVisible = async () => { await expect(this.postOptions).toBeVisible(); return postOptions; } + + close = async () => { + await this.postOptions.tap(); + await expect(this.postOptions).not.toBeVisible(); + } } const postOptions = new PostOptions(); diff --git a/detox/e2e/support/ui/component/send_button.js b/detox/e2e/support/ui/component/send_button.js new file mode 100644 index 000000000..ca6205220 --- /dev/null +++ b/detox/e2e/support/ui/component/send_button.js @@ -0,0 +1,25 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +class SendButton { + testID = { + sendButton: 'post_draft.send.button', + sendButtonDisabled: 'post_draft.send.button.disabled', + } + + sendButton = element(by.id(this.testID.sendButton)); + sendButtonDisabled = element(by.id(this.testID.sendButtonDisabled)); + + toBeVisible = async (options = {disabled: false}) => { + if (options.disabled) { + await expect(this.sendButtonDisabled).toBeVisible(); + return this.sendButtonDisabled; + } + + await expect(this.sendButton).toBeVisible(); + return this.sendButton; + } +} + +const sendButton = new SendButton(); +export default sendButton; diff --git a/detox/e2e/support/ui/component/settings_sidebar.js b/detox/e2e/support/ui/component/settings_sidebar.js index 9d559a584..8262bf4aa 100644 --- a/detox/e2e/support/ui/component/settings_sidebar.js +++ b/detox/e2e/support/ui/component/settings_sidebar.js @@ -4,11 +4,21 @@ class SettingsSidebar { testID = { settingsSidebar: 'settings.sidebar', + userInfoAction: 'settings.sidebar.user_info.action', + statusAction: 'settings.sidebar.status.action', + recentMentionsAction: 'settings.sidebar.recent_mentions.action', + savedMessagesAction: 'settings.sidebar.saved_messages.action', + editProfileAction: 'settings.sidebar.edit_profile.action', settingsAction: 'settings.sidebar.settings.action', logoutAction: 'settings.sidebar.logout.action', } settingsSidebar = element(by.id(this.testID.settingsSidebar)); + userInfoAction = element(by.id(this.testID.userInfoAction)); + statusAction = element(by.id(this.testID.statusAction)); + recentMentionsAction = element(by.id(this.testID.recentMentionsAction)); + savedMessagesAction = element(by.id(this.testID.savedMessagesAction)); + editProfileAction = element(by.id(this.testID.editProfileAction)); settingsAction = element(by.id(this.testID.settingsAction)); logoutAction = element(by.id(this.testID.logoutAction)); diff --git a/detox/e2e/support/ui/screen/add_reaction.js b/detox/e2e/support/ui/screen/add_reaction.js index cc3e312f0..e882b9a37 100644 --- a/detox/e2e/support/ui/screen/add_reaction.js +++ b/detox/e2e/support/ui/screen/add_reaction.js @@ -18,10 +18,9 @@ class AddReactionScreen { return this.addReactionScreen; } - open = async (message) => { - await element(by.text(message)).longPress(); - await PostOptions.toBeVisible(); - await PostOptions.openReactionPicker.tap(); + open = async () => { + // # Open add reaction screen + await PostOptions.openAddReactionButton.tap(); return this.toBeVisible(); } diff --git a/detox/e2e/support/ui/screen/channel.js b/detox/e2e/support/ui/screen/channel.js index b04128b61..efaad997f 100644 --- a/detox/e2e/support/ui/screen/channel.js +++ b/detox/e2e/support/ui/screen/channel.js @@ -1,8 +1,22 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {SettingsSidebar} from '@support/ui/component'; -import {LoginScreen, SelectServerScreen} from '@support/ui/screen'; +import { + CameraQuickAction, + FileQuickAction, + ImageQuickAction, + MainSidebar, + PostDraft, + PostOptions, + SendButton, + SettingsSidebar, +} from '@support/ui/component'; +import { + LoginScreen, + LongPostScreen, + PostListScreen, + SelectServerScreen, +} from '@support/ui/screen'; class ChannelScreen { testID = { @@ -12,9 +26,6 @@ class ChannelScreen { channelNavBarTitle: 'channel.nav_bar.title', channelSearchButton: 'channel.search.button', channelTitleButton: 'channel.title.button', - disabledSendButton: 'disabled_send.button', - postInput: 'post.input', - sendButton: 'send.button', settingsSidebarDrawerButton: 'settings_sidebar_drawer.button', } @@ -24,11 +35,30 @@ class ChannelScreen { channelNavBarTitle = element(by.id(this.testID.channelNavBarTitle)); channelSearchButton = element(by.id(this.testID.channelSearchButton)); channelTitleButton = element(by.id(this.testID.channelTitleButton)); - disabledSendButton = element(by.id(this.testID.disabledSendButton)); - postInput = element(by.id(this.testID.postInput)); - sendButton = element(by.id(this.testID.sendButton)); settingsSidebarDrawerButton = element(by.id(this.testID.settingsSidebarDrawerButton)); + // convenience props + cameraQuickAction = CameraQuickAction.cameraQuickAction; + cameraQuickActionDisabled = CameraQuickAction.cameraQuickActionDisabled; + imageQuickAction = ImageQuickAction.imageQuickAction; + imageQuickActionDisabled = ImageQuickAction.imageQuickActionDisabled; + fileQuickAction = FileQuickAction.fileQuickAction; + fileQuickActionDisabled = FileQuickAction.fileQuickActionDisabled; + postDraft = PostDraft.postDraft; + postDraftArchived = PostDraft.postDraftArchived; + postDraftReadOnly = PostDraft.postDraftReadOnly; + postInput = PostDraft.postInput; + sendButton = SendButton.sendButton; + sendButtonDisabled = SendButton.sendButtonDisabled; + + getLongPostPostItem = (postId, text) => { + return LongPostScreen.getPost(postId, text); + } + + getPostListPostItem = (postId, text) => { + return PostListScreen.getPost(postId, text); + } + toBeVisible = async () => { await expect(this.channelScreen).toBeVisible(); @@ -36,6 +66,7 @@ class ChannelScreen { } open = async (user) => { + // # Open channel screen await SelectServerScreen.connectToServer(); await LoginScreen.login(user); @@ -43,10 +74,38 @@ class ChannelScreen { } logout = async () => { - await this.settingsSidebarDrawerButton.tap(); + await this.openSettingsSidebar(); await SettingsSidebar.logoutAction.tap(); await SelectServerScreen.toBeVisible(); } + + openMainSidebar = async () => { + // # Open main sidebar + await this.mainSidebarDrawerButton.tap(); + await MainSidebar.toBeVisible(); + } + + openSettingsSidebar = async () => { + // # Open settings sidebar + await this.settingsSidebarDrawerButton.tap(); + await SettingsSidebar.toBeVisible(); + } + + openPostOptionsFor = async (postId, text) => { + const post = await this.getPostListPostItem(postId, text); + await expect(post).toBeVisible(); + + // # Open post options + await post.longPress(); + await PostOptions.toBeVisible(); + } + + tapSendButton = async () => { + // # Tap send button + await this.sendButton.tap(); + await expect(this.sendButton).not.toExist(); + await expect(this.sendButtonDisabled).toBeVisible(); + } } const channelScreen = new ChannelScreen(); diff --git a/detox/e2e/support/ui/screen/channel_info.js b/detox/e2e/support/ui/screen/channel_info.js index 115500377..d208da170 100644 --- a/detox/e2e/support/ui/screen/channel_info.js +++ b/detox/e2e/support/ui/screen/channel_info.js @@ -9,14 +9,28 @@ class ChannelInfoScreen { channelInfoScreen: 'channel_info.screen', closeChannelInfoButton: 'close.channel_info.button', channelIconGMMemberCount: 'channel_icon.gm_member_count', + favoritePreferenceAction: 'channel_info.favorite.action', + mutePreferenceAction: 'channel_info.mute.action', + ignoreMentionsPreferenceAction: 'channel_info.ignore_mentions.action', notificationPreferenceAction: 'channel_info.notification_preference.action', + pinnedAction: 'channel_info.pinned.action', + manageMembersAction: 'channel_info.manage_members.action', + addMembersAction: 'channel_info.add_members.action', + convertPrivateAction: 'channel_info.convert_private.action', editChannelAction: 'channel_info.edit_channel.action', } channelInfoScreen = element(by.id(this.testID.channelInfoScreen)); closeChannelInfoButton = element(by.id(this.testID.closeChannelInfoButton)); channelIconGMMemberCount = element(by.id(this.testID.channelIconGMMemberCount)); + favoritePreferenceAction = element(by.id(this.testID.favoritePreferenceAction)); + mutePreferenceAction = element(by.id(this.testID.mutePreferenceAction)); + ignoreMentionsPreferenceAction = element(by.id(this.testID.ignoreMentionsPreferenceAction)); notificationPreferenceAction = element(by.id(this.testID.notificationPreferenceAction)); + pinnedAction = element(by.id(this.testID.pinnedAction)); + manageMembersAction = element(by.id(this.testID.manageMembersAction)); + addMembersAction = element(by.id(this.testID.addMembersAction)); + convertPrivateAction = element(by.id(this.testID.convertPrivateAction)); editChannelAction = element(by.id(this.testID.editChannelAction)); toBeVisible = async () => { diff --git a/detox/e2e/support/ui/screen/create_channel.js b/detox/e2e/support/ui/screen/create_channel.js index ff64b87bd..459fed5d0 100644 --- a/detox/e2e/support/ui/screen/create_channel.js +++ b/detox/e2e/support/ui/screen/create_channel.js @@ -1,8 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {ChannelInfoScreen} from '@support/ui/screen'; - class CreateChannelScreen { testID = { createChannelScreen: 'create_channel.screen', @@ -26,16 +24,6 @@ class CreateChannelScreen { return this.createChannelScreen; } - open = async () => { - // # Open channel info screen - await ChannelInfoScreen.open(); - - // # Tap on Edit Channel - await ChannelInfoScreen.editChannelAction.tap(); - - return this.toBeVisible(); - } - back = async () => { await this.backButton.tap(); await expect(this.createChannelScreen).not.toBeVisible(); diff --git a/detox/e2e/support/ui/screen/direct_channels.js b/detox/e2e/support/ui/screen/direct_channels.js deleted file mode 100644 index da3b2dc48..000000000 --- a/detox/e2e/support/ui/screen/direct_channels.js +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. -// See LICENSE.txt for license information. - -import {ChannelScreen} from '@support/ui/component'; -import {MainSidebar} from '@support/ui/screen'; - -class DirectChannelsScreen { - testID = { - directChannelsScreen: 'direct_channels.screen', - startButton: 'start-conversation', - } - - directChannelsScreen = element(by.id(this.testID.directChannelsScreen)); - startButton = element(by.id(this.testID.startButton)); - - getUserAtIndex = (index) => { - return element(by.id('more_dms.user').withAncestor(by.id('more_dms.list'))).atIndex(index); - } - - toBeVisible = async () => { - await expect(this.directChannelsScreen).toBeVisible(); - - return this.directChannelsScreen; - } - - open = async () => { - await ChannelScreen.mainSidebarDrawerButton.tap(); - await MainSidebar.addDirectChannel.tap(); - - return this.toBeVisible(); - } -} - -const directChannelsScreen = new DirectChannelsScreen(); -export default directChannelsScreen; diff --git a/detox/e2e/support/ui/screen/edit_channel.js b/detox/e2e/support/ui/screen/edit_channel.js index 6546e90dc..5b7b03b05 100644 --- a/detox/e2e/support/ui/screen/edit_channel.js +++ b/detox/e2e/support/ui/screen/edit_channel.js @@ -1,7 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {ChannelScreen} from '@support/ui/screen'; +import {ChannelInfoScreen} from '@support/ui/screen'; class EditChannelScreen { testID = { @@ -21,9 +21,8 @@ class EditChannelScreen { } open = async () => { - // # Open channel menu then tap 'Edit Channel' - await ChannelScreen.channelTitleButton.tap(); - await element(by.text('Edit Channel')).tap(); + // # Open edit channel screen + await ChannelInfoScreen.editChannelAction.tap(); return this.toBeVisible(); } diff --git a/detox/e2e/support/ui/screen/edit_post.js b/detox/e2e/support/ui/screen/edit_post.js index 2221c8a9f..2eb0c16df 100644 --- a/detox/e2e/support/ui/screen/edit_post.js +++ b/detox/e2e/support/ui/screen/edit_post.js @@ -1,6 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. +import {PostOptions} from '@support/ui/component'; import {isAndroid} from '@support/utils'; class EditPostScreen { @@ -10,26 +11,30 @@ class EditPostScreen { editPostClose: 'edit_post.close', } + editPostScreen = element(by.id(this.testID.editPostScreen)); editPostInput = element(by.id(this.testID.editPostInput)); editPostClose = element(by.id(this.testID.editPostClose)); toBeVisible = async () => { - await expect(this.editPostScreen).toBeVisible(); + if (isAndroid()) { + await expect(this.editPostScreen).toBeVisible(); + } else { + await expect(this.editPostScreen).toExist(); + } return this.editPostScreen; } - open = async (message) => { - // # Open edit post screen - await element(by.text(message)).longPress(); - + open = async () => { // # Swipe up panel on Android if (isAndroid()) { - const slide = element(by.id('slide_up_panel')); - await slide.swipe('up'); + await PostOptions.slideUpPanel.swipe('up'); } - await element(by.text('Edit')).tap(); + // # Open edit post screen + await PostOptions.editAction.tap(); + + return this.toBeVisible(); } back = async () => { diff --git a/detox/e2e/support/ui/screen/general_settings.js b/detox/e2e/support/ui/screen/general_settings.js index 456a7644e..75ca32c32 100644 --- a/detox/e2e/support/ui/screen/general_settings.js +++ b/detox/e2e/support/ui/screen/general_settings.js @@ -8,11 +8,25 @@ class GeneralSettingsScreen { generalSettingsScreen: 'general_settings.screen', closeSettingsButton: 'close.settings.button', notificationsAction: 'general_settings.notifications.action', + displayAction: 'general_settings.display.action', + selectTeamAction: 'general_settings.select_team.action', + advancedAction: 'general_settings.advanced.action', + checkForUpgradeAction: 'general_settings.checkForUpgrade.action', + aboutAction: 'general_settings.about.action', + helpAction: 'general_settings.help.action', + reportAction: 'general_settings.report.action', } generalSettingsScreen = element(by.id(this.testID.generalSettingsScreen)); closeSettingsButton = element(by.id(this.testID.closeSettingsButton)); notificationsAction = element(by.id(this.testID.notificationsAction)); + displayAction = element(by.id(this.testID.displayAction)); + selectTeamAction = element(by.id(this.testID.selectTeamAction)); + advancedAction = element(by.id(this.testID.advancedAction)); + checkForUpgradeAction = element(by.id(this.testID.checkForUpgradeAction)); + aboutAction = element(by.id(this.testID.aboutAction)); + helpAction = element(by.id(this.testID.helpAction)); + reportAction = element(by.id(this.testID.reportAction)); toBeVisible = async () => { await expect(this.generalSettingsScreen).toBeVisible(); diff --git a/detox/e2e/support/ui/screen/index.js b/detox/e2e/support/ui/screen/index.js index a7978a536..6e30b45f3 100644 --- a/detox/e2e/support/ui/screen/index.js +++ b/detox/e2e/support/ui/screen/index.js @@ -6,15 +6,19 @@ import ChannelInfoScreen from './channel_info'; import ChannelNotificationPreferenceScreen from './channel_notification_preference'; import ChannelScreen from './channel'; import CreateChannelScreen from './create_channel'; -import DirectChannelsScreen from './direct_channels'; import EditChannelScreen from './edit_channel'; import EditPostScreen from './edit_post'; import GeneralSettingsScreen from './general_settings'; import LoginScreen from './login'; +import LongPostScreen from './long_post'; import MoreChannelsScreen from './more_channels'; +import MoreDirectMessagesScreen from './more_direct_messages'; import NotificationScreen from './notification'; import NotificationSettingsMobileScreen from './notification_settings_mobile'; import NotificationSettingsScreen from './notification_settings'; +import PostListScreen from './post_list'; +import RecentMentionsScreen from './recent_mentions'; +import SearchResultPostScreen from './search_result_post'; import SearchScreen from './search'; import SelectServerScreen from './select_server'; @@ -24,15 +28,19 @@ export { ChannelNotificationPreferenceScreen, ChannelScreen, CreateChannelScreen, - DirectChannelsScreen, EditChannelScreen, EditPostScreen, GeneralSettingsScreen, LoginScreen, + LongPostScreen, MoreChannelsScreen, + MoreDirectMessagesScreen, NotificationScreen, NotificationSettingsMobileScreen, NotificationSettingsScreen, + PostListScreen, + RecentMentionsScreen, + SearchResultPostScreen, SearchScreen, SelectServerScreen, }; diff --git a/detox/e2e/support/ui/screen/login.js b/detox/e2e/support/ui/screen/login.js index 8319b888f..110456fdb 100644 --- a/detox/e2e/support/ui/screen/login.js +++ b/detox/e2e/support/ui/screen/login.js @@ -25,6 +25,7 @@ class LoginScreen { } open = async () => { + // # Open login screen await SelectServerScreen.connectToServer(); return this.toBeVisible(); diff --git a/detox/e2e/support/ui/screen/long_post.js b/detox/e2e/support/ui/screen/long_post.js new file mode 100644 index 000000000..ab61d3e42 --- /dev/null +++ b/detox/e2e/support/ui/screen/long_post.js @@ -0,0 +1,17 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Post} from '@support/ui/component'; + +class LongPostScreen { + testID = { + longPostScreenPrefix: 'long_post.', + } + + getPost = (postId, text) => { + return Post.getPost(this.testID.longPostScreenPrefix, postId, text); + } +} + +const longPostScreen = new LongPostScreen(); +export default longPostScreen; diff --git a/detox/e2e/support/ui/screen/more_channels.js b/detox/e2e/support/ui/screen/more_channels.js index 2c107b834..a755c2921 100644 --- a/detox/e2e/support/ui/screen/more_channels.js +++ b/detox/e2e/support/ui/screen/more_channels.js @@ -1,8 +1,7 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {ChannelScreen} from '@support/ui/component'; -import {MainSidebar} from '@support/ui/screen'; +import {MainSidebar} from '@support/ui/component'; class MoreChannelsScreen { testID = { @@ -20,8 +19,8 @@ class MoreChannelsScreen { } open = async () => { - await ChannelScreen.mainSidebarDrawerButton.tap(); - await MainSidebar.addChannel.tap(); + // # Open more channels screen + await MainSidebar.openMoreChannelsButton.tap(); return this.toBeVisible(); } diff --git a/detox/e2e/support/ui/screen/more_direct_messages.js b/detox/e2e/support/ui/screen/more_direct_messages.js new file mode 100644 index 000000000..677a8e2f8 --- /dev/null +++ b/detox/e2e/support/ui/screen/more_direct_messages.js @@ -0,0 +1,36 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {MainSidebar} from '@support/ui/component'; + +class MoreDirectMessagesScreen { + testID = { + moreDirectMessagesScreen: 'more_direct_messages.screen', + startButton: 'more_direct_messages.start.button', + list: 'more_direct_messages.list', + user: 'more_direct_messages.user', + } + + moreDirectMessagesScreen = element(by.id(this.testID.moreDirectMessagesScreen)); + startButton = element(by.id(this.testID.startButton)); + + getUserAtIndex = (index) => { + return element(by.id(this.testID.user).withAncestor(by.id(this.testID.list))).atIndex(index); + } + + toBeVisible = async () => { + await expect(this.moreDirectMessagesScreen).toBeVisible(); + + return this.moreDirectMessagesScreen; + } + + open = async () => { + // # Open more direct messages screen + await MainSidebar.openMoreDirectMessagesButton.tap(); + + return this.toBeVisible(); + } +} + +const moreDirectMessagesScreen = new MoreDirectMessagesScreen(); +export default moreDirectMessagesScreen; diff --git a/detox/e2e/support/ui/screen/notification.js b/detox/e2e/support/ui/screen/notification.js index 0a4dd348b..11e49f538 100644 --- a/detox/e2e/support/ui/screen/notification.js +++ b/detox/e2e/support/ui/screen/notification.js @@ -3,21 +3,21 @@ class NotificationScreen { testID = { - inAppNotification: 'in_app_notification', + inAppNotificationScreen: 'in_app_notification.screen', inAppNotificationIcon: 'in_app_notification.icon', inAppNotificationTitle: 'in_app_notification.title', inAppNotificationMessage: 'in_app_notification.message', } - inAppNotification = element(by.id(this.testID.inAppNotification)); + inAppNotificationScreen = element(by.id(this.testID.inAppNotificationScreen)); inAppNotificationIcon = element(by.id(this.testID.inAppNotificationIcon)); inAppNotificationTitle = element(by.id(this.testID.inAppNotificationTitle)); inAppNotificationMessage = element(by.id(this.testID.inAppNotificationMessage)); toBeVisible = async () => { - await expect(this.inAppNotification).toBeVisible(); + await expect(this.inAppNotificationScreen).toBeVisible(); - return this.inAppNotification; + return this.inAppNotificationScreen; } } diff --git a/detox/e2e/support/ui/screen/notification_settings.js b/detox/e2e/support/ui/screen/notification_settings.js index 866043f1f..c5f2596e8 100644 --- a/detox/e2e/support/ui/screen/notification_settings.js +++ b/detox/e2e/support/ui/screen/notification_settings.js @@ -22,7 +22,7 @@ class NotificationSettingsScreen { open = async () => { // # Open notification settings screen - await GeneralSettingsScreen.notificationAction.tap(); + await GeneralSettingsScreen.notificationsAction.tap(); return this.toBeVisible(); } diff --git a/detox/e2e/support/ui/screen/notification_settings_mobile.js b/detox/e2e/support/ui/screen/notification_settings_mobile.js index f33b2f5bc..58ece2bfc 100644 --- a/detox/e2e/support/ui/screen/notification_settings_mobile.js +++ b/detox/e2e/support/ui/screen/notification_settings_mobile.js @@ -30,6 +30,19 @@ class NotificationSettingsMobileScreen { pushModalCancelButton = element(by.id(this.testID.pushModalCancelButton)); pushModalSaveButton = element(by.id(this.testID.pushModalSaveButton)); + getPushActionFor = (pushKey) => { + switch (pushKey) { + case 'all': + return this.allAction; + case 'mentions': + return this.mentionsAction; + case 'never': + return this.neverAction; + default: + throw new Error('Not a valid push option: ' + pushKey); + } + } + toBeVisible = async () => { await expect(this.notificationSettingsMobileScreen).toBeVisible(); @@ -47,19 +60,6 @@ class NotificationSettingsMobileScreen { await this.backButton.tap(); await expect(this.notificationSettingsMobileScreen).not.toBeVisible(); } - - getPushActionFor(pushKey) { - switch (pushKey) { - case 'all': - return this.allAction; - case 'mentions': - return this.mentionsAction; - case 'never': - return this.neverAction; - default: - throw new Error('Not a valid push option: ' + pushKey); - } - } } const notificationSettingsMobileScreen = new NotificationSettingsMobileScreen(); diff --git a/detox/e2e/support/ui/screen/post_list.js b/detox/e2e/support/ui/screen/post_list.js new file mode 100644 index 000000000..13093e9b7 --- /dev/null +++ b/detox/e2e/support/ui/screen/post_list.js @@ -0,0 +1,17 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Post} from '@support/ui/component'; + +class PostListScreen { + testID = { + postListScreenPrefix: 'post_list.', + } + + getPost = (postId, text) => { + return Post.getPost(this.testID.postListScreenPrefix, postId, text); + } +} + +const postListScreen = new PostListScreen(); +export default postListScreen; diff --git a/detox/e2e/support/ui/screen/recent_mentions.js b/detox/e2e/support/ui/screen/recent_mentions.js new file mode 100644 index 000000000..8d800a7c0 --- /dev/null +++ b/detox/e2e/support/ui/screen/recent_mentions.js @@ -0,0 +1,52 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import { + PostOptions, + SettingsSidebar, +} from '@support/ui/component'; +import {SearchResultPostScreen} from '@support/ui/screen'; + +class RecentMentionsScreen { + testID = { + recentMentionsScreen: 'recent_mentions.screen', + closeSettingsButton: 'close.settings.button', + } + + recentMentionsScreen = element(by.id(this.testID.recentMentionsScreen)); + closeSettingsButton = element(by.id(this.testID.closeSettingsButton)); + + getSearchResultPostItem = (postId, text) => { + return SearchResultPostScreen.getPost(postId, text); + } + + toBeVisible = async () => { + await expect(this.recentMentionsScreen).toBeVisible(); + + return this.recentMentionsScreen; + } + + open = async () => { + // # Open recent mentions screen + await SettingsSidebar.recentMentionsAction.tap(); + + return this.toBeVisible(); + } + + close = async () => { + await this.closeSettingsButton.tap(); + await expect(this.recentMentionsScreen).not.toBeVisible(); + } + + openPostOptionsFor = async (postId, text) => { + const post = await this.getSearchResultPostItem(postId, text); + await expect(post).toBeVisible(); + + // # Open post options + await post.longPress(); + await PostOptions.toBeVisible(); + } +} + +const recentMentionsScreen = new RecentMentionsScreen(); +export default recentMentionsScreen; diff --git a/detox/e2e/support/ui/screen/search.js b/detox/e2e/support/ui/screen/search.js index cff318845..f3b86bd81 100644 --- a/detox/e2e/support/ui/screen/search.js +++ b/detox/e2e/support/ui/screen/search.js @@ -26,6 +26,7 @@ class SearchScreen { } open = async () => { + // # Open search screen await ChannelScreen.channelSearchButton.tap(); return this.toBeVisible(); diff --git a/detox/e2e/support/ui/screen/search_result_post.js b/detox/e2e/support/ui/screen/search_result_post.js new file mode 100644 index 000000000..0af5d4f7d --- /dev/null +++ b/detox/e2e/support/ui/screen/search_result_post.js @@ -0,0 +1,17 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {Post} from '@support/ui/component'; + +class SearchResultPostScreen { + testID = { + searchResultPostScreenPrefix: 'search_result_post.', + } + + getPost = (postId, text) => { + return Post.getPost(this.testID.searchResultPostScreenPrefix, postId, text); + } +} + +const searchResultPostScreen = new SearchResultPostScreen(); +export default searchResultPostScreen; diff --git a/detox/e2e/test/autocomplete/at_mention.e2e.js b/detox/e2e/test/autocomplete/at_mention.e2e.js index cbfd86692..49142fdac 100644 --- a/detox/e2e/test/autocomplete/at_mention.e2e.js +++ b/detox/e2e/test/autocomplete/at_mention.e2e.js @@ -20,6 +20,8 @@ describe('Autocomplete', () => { beforeAll(async () => { ({user} = await Setup.apiInit()); userAtMentionAutocomplete = Autocomplete.getAtMentionItem(user.id); + + // # Open channel screen await ChannelScreen.open(user); }); @@ -171,21 +173,21 @@ describe('Autocomplete', () => { it('MM-T3409_11 should be able to select at mention multiple times', async () => { // # Type "@" to activate at mention autocomplete - await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist(); + await expect(atMentionSuggestionList).not.toExist(); await postInput.typeText('@'); - await expect(element(by.id('autocomplete.at_mention.list'))).toExist(); + await expect(atMentionSuggestionList).toExist(); // # Type username await postInput.typeText(user.username); // # Tap user - await element(by.id(`autocomplete.at_mention.item.${user.id}`)).tap(); + await userAtMentionAutocomplete.tap(); // * expect mention autocomplete to dissappear - await expect(element(by.id('autocomplete.at_mention.list'))).not.toExist(); + await expect(atMentionSuggestionList).not.toExist(); // # Type "@" again to re-activate mention autocomplete await postInput.typeText('@'); - await expect(element(by.id('autocomplete.at_mention.list'))).toExist(); + await expect(atMentionSuggestionList).toExist(); }); }); diff --git a/detox/e2e/test/autocomplete/edit_channel.e2e.js b/detox/e2e/test/autocomplete/edit_channel.e2e.js index e128211ab..928cc5630 100644 --- a/detox/e2e/test/autocomplete/edit_channel.e2e.js +++ b/detox/e2e/test/autocomplete/edit_channel.e2e.js @@ -18,6 +18,8 @@ import {Setup} from '@support/server_api'; describe('Autocomplete', () => { beforeAll(async () => { const {user} = await Setup.apiInit(); + + // # Open channel screen await ChannelScreen.open(user); }); @@ -26,7 +28,8 @@ describe('Autocomplete', () => { }); it('MM-T3390 should render autocomplete in channel header edit screen', async () => { - // # Open edit channel modal + // # Open edit channel screen + await ChannelInfoScreen.open(); await EditChannelScreen.open(); // # Activate at_mention autocomplete diff --git a/detox/e2e/test/autocomplete/edit_post.e2e.js b/detox/e2e/test/autocomplete/edit_post.e2e.js index 0bdf097ae..d5b72f445 100644 --- a/detox/e2e/test/autocomplete/edit_post.e2e.js +++ b/detox/e2e/test/autocomplete/edit_post.e2e.js @@ -8,13 +8,25 @@ // ******************************************************************* import {Autocomplete} from '@support/ui/component'; -import {ChannelScreen, EditPostScreen} from '@support/ui/screen'; -import {isAndroid, timeouts, wait} from '@support/utils'; -import {Setup} from '@support/server_api'; +import { + ChannelScreen, + EditPostScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; describe('Autocomplete', () => { + let testChannel; + beforeAll(async () => { - const {user} = await Setup.apiInit(); + const {team, user} = await Setup.apiInit(); + const {channel} = await Channel.apiGetChannelByName(team.name, 'town-square'); + testChannel = channel; + + // # Open channel screen await ChannelScreen.open(user); }); @@ -23,23 +35,20 @@ describe('Autocomplete', () => { }); it('MM-T3391 should render autocomplete in post edit screen', async () => { - const message = Date.now().toString(); - const {postInput, sendButton} = ChannelScreen; + const testMessage = Date.now().toString(); + const {postInput} = ChannelScreen; // # Type a message await postInput.tap(); - await postInput.typeText(message); + await postInput.typeText(testMessage); - // # Tap the send button - await sendButton.tap(); + // # Tap send button + await ChannelScreen.tapSendButton(); - // # Explicitly wait on Android before verifying error message - if (isAndroid()) { - await wait(timeouts.ONE_SEC); - } - - // # Open edit screen - await EditPostScreen.open(message); + // # Open edit post screen + const {post} = await Post.apiGetLastPostInChannel(testChannel.id); + await ChannelScreen.openPostOptionsFor(post.id, testMessage); + await EditPostScreen.open(); const {atMentionSuggestionList} = Autocomplete; const {editPostInput, editPostClose} = EditPostScreen; diff --git a/detox/e2e/test/autocomplete/post_draft.e2e.js b/detox/e2e/test/autocomplete/post_draft.e2e.js index b88883a5a..b2f2b8ebe 100644 --- a/detox/e2e/test/autocomplete/post_draft.e2e.js +++ b/detox/e2e/test/autocomplete/post_draft.e2e.js @@ -22,6 +22,8 @@ describe('Autocomplete', () => { beforeAll(async () => { const {user} = await Setup.apiInit(); + + // # Open channel screen await ChannelScreen.open(user); }); diff --git a/detox/e2e/test/autocomplete/search.e2e.js b/detox/e2e/test/autocomplete/search.e2e.js index 05775ba28..862c0162d 100644 --- a/detox/e2e/test/autocomplete/search.e2e.js +++ b/detox/e2e/test/autocomplete/search.e2e.js @@ -20,9 +20,9 @@ describe('Autocomplete', () => { beforeAll(async () => { const {user} = await Setup.apiInit(); - await ChannelScreen.open(user); // # Open search screen + await ChannelScreen.open(user); await SearchScreen.open(); }); diff --git a/detox/e2e/test/channel_info/header.e2e.js b/detox/e2e/test/channel_info/header.e2e.js index 865b58e8d..73b0b3513 100644 --- a/detox/e2e/test/channel_info/header.e2e.js +++ b/detox/e2e/test/channel_info/header.e2e.js @@ -7,11 +7,10 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {MainSidebar} from '@support/ui/component'; import { ChannelInfoScreen, ChannelScreen, - DirectChannelsScreen, + MoreDirectMessagesScreen, } from '@support/ui/screen'; import {timeouts, wait} from '@support/utils'; import {Setup} from '@support/server_api'; @@ -19,6 +18,8 @@ import {Setup} from '@support/server_api'; describe('Channel Info Header', () => { beforeAll(async () => { const {user} = await Setup.apiInit(); + + // # Open channel screen await ChannelScreen.open(user); }); @@ -27,13 +28,9 @@ describe('Channel Info Header', () => { }); it('MM-T3406 should render correct GM member count in channel info header', async () => { - // # Open main sidebar - await ChannelScreen.mainSidebarDrawerButton.tap(); - await MainSidebar.toBeVisible(); - - // # Open Direct Channels screen - await MainSidebar.addDirectChannel.tap(); - await DirectChannelsScreen.toBeVisible(); + // # Open more direct messages screen + await ChannelScreen.openMainSidebar(); + await MoreDirectMessagesScreen.open(); // # Wait for some profiles to load await wait(timeouts.ONE_SEC); @@ -41,7 +38,7 @@ describe('Channel Info Header', () => { const { getUserAtIndex, startButton, - } = DirectChannelsScreen; + } = MoreDirectMessagesScreen; // # Select 3 profiles await getUserAtIndex(0).tap(); diff --git a/detox/e2e/test/channels/create_public_channel.e2e.js b/detox/e2e/test/channels/create_public_channel.e2e.js index 22f0c9ce9..cfd2dd20b 100644 --- a/detox/e2e/test/channels/create_public_channel.e2e.js +++ b/detox/e2e/test/channels/create_public_channel.e2e.js @@ -9,7 +9,6 @@ import jestExpect from 'expect'; -import {MainSidebar} from '@support/ui/component'; import { ChannelScreen, ChannelInfoScreen, @@ -22,6 +21,7 @@ describe('Channels', () => { beforeAll(async () => { const {user} = await Setup.apiInit(); + // # Open channel screen await ChannelScreen.open(user); }); @@ -30,14 +30,11 @@ describe('Channels', () => { }); it('MM-T3201 Create public channel', async () => { - // # Go to channel sidebar list - await ChannelScreen.mainSidebarDrawerButton.tap(); - - // # Tap on + public channels - await MainSidebar.addChannel.tap(); + // # Open more channels screen + await ChannelScreen.openMainSidebar(); + await MoreChannelsScreen.open(); // * Expect a list of public channels, initially empty - await MoreChannelsScreen.toBeVisible(); await expect(element(by.text('No more channels to join'))).toBeVisible(); // # Tap to create new channel diff --git a/detox/e2e/test/files_and_attachments/disable_upload.e2e.js b/detox/e2e/test/files_and_attachments/disable_upload.e2e.js index 11aa83577..98858461b 100644 --- a/detox/e2e/test/files_and_attachments/disable_upload.e2e.js +++ b/detox/e2e/test/files_and_attachments/disable_upload.e2e.js @@ -7,11 +7,6 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import { - CameraQuickAction, - FileQuickAction, - ImageQuickAction, -} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; import { Setup, @@ -35,9 +30,15 @@ describe('Disable Upload', () => { }); it('MM-T3453 should disable file, image, camera icons when mobile file upload is disabled', async () => { + const { + cameraQuickActionDisabled, + fileQuickActionDisabled, + imageQuickActionDisabled, + } = ChannelScreen; + // * Verify disabled file, image, camera icons are visible - await expect(CameraQuickAction.cameraQuickActionDisabled).toBeVisible(); - await expect(FileQuickAction.fileQuickActionDisabled).toBeVisible(); - await expect(ImageQuickAction.imageQuickActionDisabled).toBeVisible(); + await expect(cameraQuickActionDisabled).toBeVisible(); + await expect(fileQuickActionDisabled).toBeVisible(); + await expect(imageQuickActionDisabled).toBeVisible(); }); }); diff --git a/detox/e2e/test/files_and_attachments/enable_upload.e2e.js b/detox/e2e/test/files_and_attachments/enable_upload.e2e.js index 139e51db9..625743d82 100644 --- a/detox/e2e/test/files_and_attachments/enable_upload.e2e.js +++ b/detox/e2e/test/files_and_attachments/enable_upload.e2e.js @@ -7,11 +7,6 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import { - CameraQuickAction, - FileQuickAction, - ImageQuickAction, -} from '@support/ui/component'; import {ChannelScreen} from '@support/ui/screen'; import { Setup, @@ -35,9 +30,15 @@ describe('Enable Upload', () => { }); it('MM-T3483 should enable file, image, camera icons when mobile file upload is enabled', async () => { + const { + cameraQuickAction, + fileQuickAction, + imageQuickAction, + } = ChannelScreen; + // * Verify enabled file, image, camera icons are visible - await expect(CameraQuickAction.cameraQuickAction).toBeVisible(); - await expect(FileQuickAction.fileQuickAction).toBeVisible(); - await expect(ImageQuickAction.imageQuickAction).toBeVisible(); + await expect(cameraQuickAction).toBeVisible(); + await expect(fileQuickAction).toBeVisible(); + await expect(imageQuickAction).toBeVisible(); }); }); diff --git a/detox/e2e/test/main_sidebar/select_channel.e2e.js b/detox/e2e/test/main_sidebar/select_channel.e2e.js index 3332a587b..3033c38c6 100644 --- a/detox/e2e/test/main_sidebar/select_channel.e2e.js +++ b/detox/e2e/test/main_sidebar/select_channel.e2e.js @@ -19,6 +19,7 @@ describe('Select channel', () => { const {user, channel} = await Setup.apiInit(); newChannel = channel; + // # Open channel screen await ChannelScreen.open(user); }); @@ -27,13 +28,10 @@ describe('Select channel', () => { }); it('MM-T3412 should close the sidebar menu when selecting the same channel', async () => { - const {mainSidebarDrawerButton, channelNavBarTitle} = ChannelScreen; + const {channelNavBarTitle} = ChannelScreen; // # Open main sidebar (with at least one unread channel) - await mainSidebarDrawerButton.tap(); - - // * Main sidebar should be visible - await MainSidebar.toBeVisible(); + await ChannelScreen.openMainSidebar(); // # Tap a channel to view it const channelItem = MainSidebar.getChannelByDisplayName(newChannel.display_name); @@ -43,7 +41,7 @@ describe('Select channel', () => { await expect(channelNavBarTitle).toHaveText(newChannel.display_name); // # Open main sidebar again and select the same channel - await mainSidebarDrawerButton.tap(); + await ChannelScreen.openMainSidebar(); await channelItem.tap(); // * Drawer should not be visible on Android diff --git a/detox/e2e/test/messaging/message_posting.e2e.js b/detox/e2e/test/messaging/message_posting.e2e.js index ff37a80af..8c77ce014 100644 --- a/detox/e2e/test/messaging/message_posting.e2e.js +++ b/detox/e2e/test/messaging/message_posting.e2e.js @@ -14,6 +14,7 @@ describe('Messaging', () => { beforeAll(async () => { const {user} = await Setup.apiInit(); + // # Open channel screen await ChannelScreen.open(user); }); @@ -21,28 +22,34 @@ describe('Messaging', () => { await ChannelScreen.logout(); }); - it('should post a message on tap to paper send button', async () => { + it('MM-T3486 should post a message when send button is tapped', async () => { // * Verify channel screen is visible await ChannelScreen.toBeVisible(); - const {postInput, sendButton} = ChannelScreen; + const { + postInput, + sendButton, + sendButtonDisabled, + } = ChannelScreen; - // * Post input should exist while send button should not + // * Verify post input is visible and send button is disabled await expect(postInput).toBeVisible(); - await expect(sendButton).not.toExist(); + await expect(sendButtonDisabled).toBeVisible(); // # Tap on post input await postInput.tap(); - // # Type text on post input - const text = Date.now().toString(); - await postInput.typeText(text); + // # Type message on post input + const message = Date.now().toString(); + await postInput.typeText(message); + + // * Verify send button is enabled + await expect(sendButton).toBeVisible(); // # Tap send button - await expect(sendButton).toBeVisible(); await sendButton.tap(); - // * Verify text to exist - await expect(element(by.text(text))).toBeVisible(); + // * Verify message is posted + await expect(element(by.text(message))).toBeVisible(); }); }); diff --git a/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js b/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js index 40b749228..7c066fed4 100644 --- a/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js +++ b/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js @@ -19,6 +19,7 @@ describe('Messaging', () => { beforeAll(async () => { ({team, user} = await Setup.apiInit()); + // # Open channel screen await ChannelScreen.open(user); }); @@ -29,21 +30,21 @@ describe('Messaging', () => { it('MM-T109 User can\'t send the same message repeatedly', async () => { const message = Date.now().toString(); - const {disabledSendButton, sendButton, postInput} = ChannelScreen; + const { + postInput, + sendButtonDisabled, + } = ChannelScreen; // # Type a message await postInput.tap(); await postInput.typeText(message); - // # Tap the send button - await expect(sendButton).toBeVisible(); - await sendButton.tap(); - await expect(sendButton).not.toExist(); + // # Tap send button + await ChannelScreen.tapSendButton(); // # Then tap send button repeatedly - await expect(disabledSendButton).toBeVisible(); - await expect(disabledSendButton).toExist(); - await disabledSendButton.multiTap(3); + await expect(sendButtonDisabled).toBeVisible(); + await sendButtonDisabled.multiTap(3); // * Check that message is successfully posted await expect(element(by.text(message))).toExist(); diff --git a/detox/e2e/test/notifications/channel_notification_default.e2e.js b/detox/e2e/test/notifications/channel_notification_default.e2e.js index dd6457498..10e49ab6f 100644 --- a/detox/e2e/test/notifications/channel_notification_default.e2e.js +++ b/detox/e2e/test/notifications/channel_notification_default.e2e.js @@ -7,10 +7,7 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import { - MainSidebar, - SettingsSidebar, -} from '@support/ui/component'; +import {MainSidebar} from '@support/ui/component'; import { ChannelInfoScreen, ChannelNotificationPreferenceScreen, @@ -28,15 +25,14 @@ describe('Channel Notification Preference - Default', () => { beforeAll(async () => { const {user, channel} = await Setup.apiInit(); testChannel = channel; + + // # Open channel screen await ChannelScreen.open(user); }); beforeEach(async () => { - // # Open main sidebar - await ChannelScreen.mainSidebarDrawerButton.tap(); - await MainSidebar.toBeVisible(); - // # Go to channel + await ChannelScreen.openMainSidebar(); await MainSidebar.getChannelByDisplayName(testChannel.display_name).tap(); }); @@ -53,11 +49,9 @@ describe('Channel Notification Preference - Default', () => { // # Set global notifications to mentions await setGlobalNotificationsTo('mentions'); - // # Open channel info screen + // # Open on notification preference screen await ChannelInfoScreen.open(); - - // # Tap on Mobile Notifications - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); // # Tap on For all activity option await element(by.text(`${ChannelNotificationPreferenceScreen.optionDefaultText} (Mentions)`)).tap(); @@ -76,7 +70,7 @@ describe('Channel Notification Preference - Default', () => { // * Verify Global default (Never) is displayed await ChannelInfoScreen.open(); - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); await expect(element(by.text(`${ChannelNotificationPreferenceScreen.optionDefaultText} (Never)`))).toBeVisible(); // # Go back to channel info screen @@ -88,7 +82,7 @@ describe('Channel Notification Preference - Default', () => { // * Verify Global default (All) is displayed await ChannelInfoScreen.open(); - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); await expect(element(by.text(`${ChannelNotificationPreferenceScreen.optionDefaultText} (All)`))).toBeVisible(); // # Tap on back button @@ -97,21 +91,11 @@ describe('Channel Notification Preference - Default', () => { }); async function setGlobalNotificationsTo(pushKey) { - // # Open settings sidebar - await ChannelScreen.settingsSidebarDrawerButton.tap(); - await SettingsSidebar.toBeVisible(); - - // # Open general settings screen - await SettingsSidebar.settingsAction.tap(); - await GeneralSettingsScreen.toBeVisible(); - - // # Open notification settings screen - await GeneralSettingsScreen.notificationsAction.tap(); - await NotificationSettingsScreen.toBeVisible(); - // # Open notifications settings mobile screen - await NotificationSettingsScreen.mobileAction.tap(); - await NotificationSettingsMobileScreen.toBeVisible(); + await ChannelScreen.openSettingsSidebar(); + await GeneralSettingsScreen.open(); + await NotificationSettingsScreen.open(); + await NotificationSettingsMobileScreen.open(); // # Tap on Send Notifications option if Android if (isAndroid()) { diff --git a/detox/e2e/test/notifications/channel_notification_preference.e2e.js b/detox/e2e/test/notifications/channel_notification_preference.e2e.js index 0fc5a8bd9..500c5cad1 100644 --- a/detox/e2e/test/notifications/channel_notification_preference.e2e.js +++ b/detox/e2e/test/notifications/channel_notification_preference.e2e.js @@ -12,7 +12,7 @@ import { ChannelInfoScreen, ChannelNotificationPreferenceScreen, ChannelScreen, - DirectChannelsScreen, + MoreDirectMessagesScreen, } from '@support/ui/screen'; import {Setup} from '@support/server_api'; import {isAndroid} from '@support/utils'; @@ -23,15 +23,14 @@ describe('Channel Notification Preference', () => { beforeAll(async () => { const {user, channel} = await Setup.apiInit(); testChannel = channel; + + // # Open channel screen await ChannelScreen.open(user); }); beforeEach(async () => { - // # Open main sidebar - await ChannelScreen.mainSidebarDrawerButton.tap(); - await MainSidebar.toBeVisible(); - // # Go to channel + await ChannelScreen.openMainSidebar(); await MainSidebar.getChannelByDisplayName(testChannel.display_name).tap(); }); @@ -45,11 +44,9 @@ describe('Channel Notification Preference', () => { }); it('MM-T3375_1 should display Mobile Notifications option for non-DM Channels', async () => { - // # Open channel info screen + // # Open on notification preference screen await ChannelInfoScreen.open(); - - // # Tap on Mobile Notifications - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); const { titleText, @@ -73,18 +70,14 @@ describe('Channel Notification Preference', () => { }); it('MM-T3375_2 should not display Mobile Notifications option for DM Channel', async () => { - // # Open main sidebar - await ChannelScreen.mainSidebarDrawerButton.tap(); - await MainSidebar.toBeVisible(); - - // # Open Direct Channels screen - await MainSidebar.addDirectChannel.tap(); - await DirectChannelsScreen.toBeVisible(); + // # Open more direct messages screen + await ChannelScreen.openMainSidebar(); + await MoreDirectMessagesScreen.open(); const { getUserAtIndex, startButton, - } = DirectChannelsScreen; + } = MoreDirectMessagesScreen; // # Select 1 profile await getUserAtIndex(0).tap(); @@ -100,11 +93,9 @@ describe('Channel Notification Preference', () => { }); it('MM-T3377 should be able to select For all activity option and display All next to Mobile Notifications', async () => { - // # Open channel info screen + // # Open on notification preference screen await ChannelInfoScreen.open(); - - // # Tap on Mobile Notifications - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); // # Tap on For all activity option await element(by.text(ChannelNotificationPreferenceScreen.optionAllText)).tap(); @@ -118,11 +109,9 @@ describe('Channel Notification Preference', () => { }); it('MM-T3378 should be able to select Only mentions and direct messages option and display Mentions next to Mobile Notifications', async () => { - // # Open channel info screen + // # Open on notification preference screen await ChannelInfoScreen.open(); - - // # Tap on Mobile Notifications - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); // # Tap on For all activity option await element(by.text(ChannelNotificationPreferenceScreen.optionMentionsText)).tap(); @@ -136,11 +125,9 @@ describe('Channel Notification Preference', () => { }); it('MM-T3379 should be able to select Never option and display Never next to Mobile Notifications', async () => { - // # Open channel info screen + // # Open on notification preference screen await ChannelInfoScreen.open(); - - // # Tap on Mobile Notifications - await ChannelInfoScreen.notificationPreferenceAction.tap(); + await ChannelNotificationPreferenceScreen.open(); // # Tap on For all activity option await element(by.text(ChannelNotificationPreferenceScreen.optionNeverText)).tap(); diff --git a/detox/e2e/test/notifications/in_app_notification.e2e.js b/detox/e2e/test/notifications/in_app_notification.e2e.js index e6e329e39..4517ca40e 100644 --- a/detox/e2e/test/notifications/in_app_notification.e2e.js +++ b/detox/e2e/test/notifications/in_app_notification.e2e.js @@ -7,12 +7,94 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {AddReactionScreen, ChannelScreen, NotificationScreen} from '@support/ui/screen'; -import {isAndroid, timeouts, wait} from '@support/utils'; -import {Setup} from '@support/server_api'; +import { + AddReactionScreen, + ChannelScreen, + NotificationScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, +} from '@support/server_api'; +import { + isAndroid, + timeouts, + wait, +} from '@support/utils'; const DetoxConstants = require('detox').DetoxConstants; +describe('in-app Notification', () => { + let testChannel1; + let testChannel2; + let testNotification; + + beforeAll(async () => { + const {channel, team, user} = await Setup.apiInit(); + testChannel1 = channel; + testNotification = getNotification(testChannel1, team, user); + + ({channel: testChannel2} = await Channel.apiGetChannelByName(team.name, 'town-square')); + + // # Open channel screen + await ChannelScreen.open(user); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T3440 should render an in-app notification', async () => { + const testMessage = Date.now().toString(); + const {postInput} = ChannelScreen; + + // # Type a message + await postInput.tap(); + await postInput.typeText(testMessage); + + // # Tap send button + await ChannelScreen.tapSendButton(); + + // # Open add reaction screen + const {post} = await Post.apiGetLastPostInChannel(testChannel2.id); + await ChannelScreen.openPostOptionsFor(post.id, testMessage); + await AddReactionScreen.open(); + + // # Close add reaction screen + await AddReactionScreen.close(); + + if (isAndroid()) { + // eslint-disable-next-line no-console + console.log('Skipping on Android until https://github.com/wix/Detox/issues/2141'); + return; + } + + // # When a push notification is received + await device.sendUserNotification(testNotification); + await wait(timeouts.HALF_SEC); + + // * Verify in-app notification is shown + const { + inAppNotificationScreen, + inAppNotificationIcon, + inAppNotificationTitle, + inAppNotificationMessage, + } = NotificationScreen; + + await NotificationScreen.toBeVisible(); + await expect(inAppNotificationIcon).toBeVisible(); + await expect(inAppNotificationTitle).toHaveText(testChannel1.name); + await expect(inAppNotificationMessage).toHaveText('This is an e2e test message'); + + // # Wait for some profiles to load + await wait(5 * timeouts.ONE_SEC); + + // * Verify in-app notification is hidden + await expect(inAppNotificationScreen).not.toBeVisible(); + }); +}); + function getNotification(channel, team, user) { if (isAndroid()) { return { @@ -52,66 +134,3 @@ function getNotification(channel, team, user) { }, }; } - -describe('in-app Notification', () => { - let testNotification; - let testChannel; - - beforeAll(async () => { - const {channel, team, user} = await Setup.apiInit(); - testChannel = channel; - testNotification = getNotification(channel, team, user); - await ChannelScreen.open(user); - }); - - afterAll(async () => { - await ChannelScreen.logout(); - }); - - it('MM-T3440 should render an in-app notification', async () => { - const message = Date.now().toString(); - - const {postInput, sendButton} = ChannelScreen; - - // # Type a message - await postInput.tap(); - await postInput.typeText(message); - - // # Tap the send button - await sendButton.tap(); - await wait(timeouts.HALF_SEC); - - // # Open Add reaction screen - await AddReactionScreen.open(message); - await AddReactionScreen.closeAddReactionButton.tap(); - - if (isAndroid()) { - // eslint-disable-next-line no-console - console.log('Skipping on Android until https://github.com/wix/Detox/issues/2141'); - return; - } - - // # When a push notification is received - await device.sendUserNotification(testNotification); - await wait(timeouts.HALF_SEC); - - // * Verify in-app notification is shown - const { - inAppNotification, - inAppNotificationIcon, - inAppNotificationTitle, - inAppNotificationMessage, - } = NotificationScreen; - - await NotificationScreen.toBeVisible(); - await expect(inAppNotificationIcon).toBeVisible(); - await expect(inAppNotificationTitle).toHaveText(testChannel.name); - await expect(inAppNotificationMessage).toHaveText('This is an e2e test message'); - - // # Wait for some profiles to load - await wait(5 * timeouts.ONE_SEC); - - // * Verify in-app notification is hidden - await expect(inAppNotification).not.toBeVisible(); - }); -}); diff --git a/detox/e2e/test/smoke_test/login_ldap.e2e.js b/detox/e2e/test/smoke_test/login_ldap.e2e.js index a99b03194..52144f99d 100644 --- a/detox/e2e/test/smoke_test/login_ldap.e2e.js +++ b/detox/e2e/test/smoke_test/login_ldap.e2e.js @@ -24,6 +24,7 @@ describe('Smoke Tests', () => { // * Check that LDAP server can connect and is synchronized with Mattermost server await Ldap.apiRequireLDAPServer(); + // # Ensure user has team await ensureUserHasTeam(testOne); }); diff --git a/detox/e2e/test/smoke_test/recent_mentions.e2e.js b/detox/e2e/test/smoke_test/recent_mentions.e2e.js new file mode 100644 index 000000000..1d0848ec0 --- /dev/null +++ b/detox/e2e/test/smoke_test/recent_mentions.e2e.js @@ -0,0 +1,79 @@ +// 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 {PostOptions} from '@support/ui/component'; +import { + ChannelScreen, + RecentMentionsScreen, +} from '@support/ui/screen'; +import { + Channel, + Post, + Setup, + Team, + User, +} from '@support/server_api'; + +describe('Recent Mentions', () => { + let testUser1; + let testUser2; + let testChannel; + + beforeAll(async () => { + const {team, user} = await Setup.apiInit(); + testUser1 = user; + + const {channel} = await Channel.apiGetChannelByName(team.name, 'town-square'); + testChannel = channel; + + ({user: testUser2} = await User.apiCreateUser()); + await Team.apiAddUserToTeam(testUser2.id, team.id); + await Channel.apiAddUserToChannel(testUser2.id, testChannel.id); + + // # Open channel screen + await ChannelScreen.open(testUser1); + }); + + afterAll(async () => { + await ChannelScreen.logout(); + }); + + it('MM-T3222 should display recent mentions and no option to add a reaction', async () => { + // # Post an at-mention message to user 1 by user 2 + const testMessage = `Mention @${testUser1.username}`; + await User.apiLogin(testUser2); + await Post.apiCreatePost({ + channelId: testChannel.id, + message: testMessage, + }); + + // * Verify most recent post has the message + const lastPost = await Post.apiGetLastPostInChannel(testChannel.id); + const postListPostItem = await ChannelScreen.getPostListPostItem(lastPost.post.id, testMessage); + await expect(postListPostItem).toBeVisible(); + + // # Open recent mentions screen + await ChannelScreen.openSettingsSidebar(); + await RecentMentionsScreen.open(); + + // * Verify most recent search result post has the at-mention message + const lastSearchResultPost = await Post.apiGetLastPostInChannel(testChannel.id); + const searchResultPostItem = await RecentMentionsScreen.getSearchResultPostItem(lastSearchResultPost.post.id, testMessage); + await expect(searchResultPostItem).toBeVisible(); + + // * Verify recent mentions post item does not have option to add a reaction + await searchResultPostItem.longPress(); + await PostOptions.toBeVisible(); + await expect(PostOptions.reactionPickerAction).not.toBeVisible(); + + // # Close recent mentions screen + await PostOptions.close(); + await RecentMentionsScreen.close(); + }); +}); diff --git a/detox/e2e/test/smoke_test/channels.e2e.js b/detox/e2e/test/smoke_test/unread_channels.e2e.js similarity index 86% rename from detox/e2e/test/smoke_test/channels.e2e.js rename to detox/e2e/test/smoke_test/unread_channels.e2e.js index f98d467b8..3c6a88679 100644 --- a/detox/e2e/test/smoke_test/channels.e2e.js +++ b/detox/e2e/test/smoke_test/unread_channels.e2e.js @@ -7,9 +7,12 @@ // - Use element testID when selecting an element. Create one if none. // ******************************************************************* -import {ChannelScreen} from '@support/ui/screen'; import {MainSidebar} from '@support/ui/component'; -import {Setup, Channel} from '@support/server_api'; +import {ChannelScreen} from '@support/ui/screen'; +import { + Channel, + Setup, +} from '@support/server_api'; describe('Unread channels', () => { let newChannel; @@ -26,6 +29,7 @@ describe('Unread channels', () => { ({channel: zChannel} = await Channel.apiCreateChannel({type: 'O', prefix: 'z-channel', teamId: team.id})); await Channel.apiAddUserToChannel(user.id, zChannel.id); + // # Open channel screen await ChannelScreen.open(user); }); @@ -34,13 +38,8 @@ describe('Unread channels', () => { }); it('MM-T3187 Unread channels sort at top', async () => { - const {mainSidebarDrawerButton} = ChannelScreen; - // # Open main sidebar (with at least one unread channel) - await mainSidebarDrawerButton.tap(); - - // * Channel should be visible - await MainSidebar.toBeVisible(); + await ChannelScreen.openMainSidebar(); // * Verify unread channel(s) display at top of channel list (with mentions first, if any), in alphabetical order, with title "Unreads" await expect(element(by.text('UNREADS'))).toBeVisible(); @@ -50,15 +49,13 @@ describe('Unread channels', () => { // # Tap an unread channel to view it await MainSidebar.getChannelByDisplayName(aChannel.display_name).tap(); - await mainSidebarDrawerButton.tap(); + await ChannelScreen.openMainSidebar(); await MainSidebar.getChannelByDisplayName(newChannel.display_name).tap(); - await mainSidebarDrawerButton.tap(); + await ChannelScreen.openMainSidebar(); await MainSidebar.getChannelByDisplayName(zChannel.display_name).tap(); - // # Open main sidebar again - await mainSidebarDrawerButton.tap(); - // * Channel you just read is no longer listed in Unreads + await ChannelScreen.openMainSidebar(); await expect(element(by.text('UNREADS'))).not.toBeVisible(); await expect(element(by.text('PUBLIC CHANNELS'))).toBeVisible(); await MainSidebar.hasChannelAtIndex(0, aChannel.display_name);