diff --git a/detox/e2e/support/server_api/channel.js b/detox/e2e/support/server_api/channel.js index 22292aad2..b55b2a3ce 100644 --- a/detox/e2e/support/server_api/channel.js +++ b/detox/e2e/support/server_api/channel.js @@ -40,6 +40,23 @@ export const apiCreateChannel = async ({teamId = null, type = 'O', prefix = 'cha } }; +/** + * Get a channel by name and team name. + * See https://api.mattermost.com/#tag/channels/paths/~1teams~1name~1{team_name}~1channels~1name~1{channel_name}/get + * @param {string} teamName - team name + * @param {string} channelName - channel name + * @return {Object} returns {channel} on success or {error, status} on error + */ +export const apiGetChannelByName = async (teamName, channelName) => { + try { + const response = await client.get(`/api/v4/teams/name/${teamName}/channels/name/${channelName}`); + + return {channel: response.data}; + } catch (err) { + return getResponseFromError(err); + } +}; + /** * Add user to channel. * See https://api.mattermost.com/#tag/channels/paths/~1channels~1{channel_id}~1members/post @@ -76,6 +93,7 @@ function generateRandomChannel(teamId, type, prefix) { export const Channel = { apiAddUserToChannel, apiCreateChannel, + apiGetChannelByName, }; export default Channel; diff --git a/detox/e2e/support/server_api/index.js b/detox/e2e/support/server_api/index.js index 12e9ec633..64779a08d 100644 --- a/detox/e2e/support/server_api/index.js +++ b/detox/e2e/support/server_api/index.js @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import Channel from './channel'; +import Post from './post'; import Setup from './setup'; import System from './system'; import Team from './team'; @@ -9,6 +10,7 @@ import User from './user'; export { Channel, + Post, Setup, System, Team, diff --git a/detox/e2e/support/server_api/post.js b/detox/e2e/support/server_api/post.js new file mode 100644 index 000000000..99bf5a9f7 --- /dev/null +++ b/detox/e2e/support/server_api/post.js @@ -0,0 +1,54 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import client from './client'; +import {getResponseFromError} from './common'; + +// **************************************************************** +// Channels +// See https://api.mattermost.com/#tag/channels +// +// Exported API function should have the following: +// - documented using JSDoc +// - meaningful description +// - match the referenced API endpoints +// - parameter/s defined by `@param` +// - return value defined by `@return` +// **************************************************************** + +/** + * Get posts for a channel. + * See https://api.mattermost.com/#tag/posts/paths/~1channels~1{channel_id}~1posts/get + * @param {string} channelId - The channel ID to get the posts for + * @return {Object} returns {posts} on success or {error, status} on error + */ +export const apiGetPostsInChannel = async (channelId) => { + try { + const response = await client.get(`/api/v4/channels/${channelId}/posts`); + + const {order, posts} = response.data; + + const orderedPosts = order.map((postId) => posts[postId]); + + return {posts: orderedPosts}; + } catch (err) { + return getResponseFromError(err); + } +}; + +/** + * Get last post in a channel. + * @param {string} channelId - The channel ID to get the the last post + * @return {Object} returns {post} on success or {error, status} on error + */ +export const apiGetLastPostInChannel = async (channelId) => { + const {posts} = await apiGetPostsInChannel(channelId); + return {post: posts[0]}; +}; + +export const Post = { + apiGetLastPostInChannel, + apiGetPostsInChannel, +}; + +export default Post; diff --git a/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js b/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js new file mode 100644 index 000000000..ce7a3bbd5 --- /dev/null +++ b/detox/e2e/test/messaging/tap_send_button_repeatedly.e2e.js @@ -0,0 +1,50 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import nodeExpect from 'expect'; + +import {toChannelScreen} from '@support/ui/screen'; +import {Channel, Post, Setup} from '@support/server_api'; + +describe('Messaging', () => { + let team; + let user; + + beforeAll(async () => { + ({team, user} = await Setup.apiInit()); + + await toChannelScreen(user); + }); + + it('MM-T109 User can\'t send the same message repeatedly', async () => { + const message = Date.now().toString(); + + // # Type a message + const postInput = await element(by.id('post_input')); + await postInput.tap(); + await postInput.typeText(message); + + // # Tap the send button + const sendButton = await element(by.id('send_button')); + await expect(sendButton).toBeVisible(); + await sendButton.tap(); + await expect(sendButton).not.toExist(); + + // # Then tap send button repeatedly + const disabledSendButton = await element(by.id('disabled_send_button')); + await expect(disabledSendButton).toBeVisible(); + await expect(disabledSendButton).toExist(); + await disabledSendButton.multiTap(3); + + // # Check that message is successfully posted + await expect(element(by.text(message))).toExist(); + + // # Check that no duplicate message is saved. + const {channel} = await Channel.apiGetChannelByName(team.name, 'town-square'); + const {posts} = await Post.apiGetPostsInChannel(channel.id); + nodeExpect(posts.length).toEqual(3); + nodeExpect(posts[0].message).toEqual(message); + nodeExpect(posts[1].message).toEqual(`${user.username} joined the team.`); + nodeExpect(posts[2].message).toEqual('sysadmin joined the team.'); + }); +});