Skip to content

Commit

Permalink
[MM-15887] E2E: add test for interactive menus - basic options (matte…
Browse files Browse the repository at this point in the history
…rmost#3063)

* E2E: add test for interactive menus - basic options

* check status after initiating Cypress task both for postIncomingWebhook and postMessageAs
  • Loading branch information
saturninoabril authored and Bob Lubecker committed Jul 8, 2019
1 parent 09bab23 commit f4eb2bc
Show file tree
Hide file tree
Showing 13 changed files with 778 additions and 19 deletions.
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"func-names": 0,
"import/no-unresolved": 0,
"max-nested-callbacks": 0,
"no-process-env": 0,
"no-unused-expressions": 0
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ export default class MessageAttachmentList extends React.PureComponent {
});

return (
<div className='attachment__list'>
<div
id={`messageAttachmentList_${this.props.postId}`}
className='attachment__list'
>
{content}
</div>
);
Expand Down
26 changes: 26 additions & 0 deletions e2e/cypress/fixtures/hooks/message_menus.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"attachments": [{
"pretext": "This is the attachment pretext.",
"text": "This is the attachment text.",
"actions": [{
"name": "Select an option...",
"integration": {
"url": "http:https://localhost:3000/message_menus",
"context": {
"action": "do_something"
}
},
"type": "select",
"options": [{
"text": "Option 1",
"value": "option1"
}, {
"text": "Option 2",
"value": "option2"
}, {
"text": "Option 3",
"value": "option3"
}]
}]
}]
}
17 changes: 17 additions & 0 deletions e2e/cypress/fixtures/hooks/message_menus_with_datasource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"attachments": [{
"pretext": "This is the attachment pretext.",
"text": "This is the attachment text.",
"actions": [{
"name": "Select an option...",
"integration": {
"url": "http:https://localhost:3000/message_menus_datasource",
"context": {
"action": "do_something"
}
},
"type": "select",
"data_source": "channels"
}]
}]
}
165 changes: 165 additions & 0 deletions e2e/cypress/integration/interactive_menu/basic_options.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************

/**
* Note: This test requires webhook server running. Initiate `npm run start:webhook` to start.
*/

import * as TIMEOUTS from '../../fixtures/timeouts';
import users from '../../fixtures/users.json';
import {getMessageMenusPayload} from '../../utils';

const options = [
{text: 'Option 1', value: 'option1'},
{text: 'Option 2', value: 'option2'},
{text: 'Option 3', value: 'option3'},
];
const payload = getMessageMenusPayload({options});

let channelId;
let incomingWebhook;

describe('MM-15887 Interactive menus - basic options', () => {
before(() => {
if (process.env.NODE_ENV !== 'qa') {
// Set AllowedUntrustedInternalConnections to localhost if running in development
const newSettings = {
ServiceSettings: {AllowedUntrustedInternalConnections: 'localhost'},
};
cy.apiUpdateConfig(newSettings);
}

// # Login as sysadmin and ensure that teammate name display setting us set to default 'username'
cy.apiLogin('sysadmin');
cy.apiSaveTeammateNameDisplayPreference('username');

// # Visit '/' and create incoming webhook
cy.visit('/');
cy.getCurrentChannelId().then((id) => {
channelId = id;

const newIncomingHook = {
channel_id: id,
channel_locked: true,
description: 'Incoming webhook interactive menu',
display_name: 'menuIn' + Date.now(),
};

cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
});
});

it('matches elements', () => {
// # Post an incoming webhook
cy.task('postIncomingWebhook', {url: incomingWebhook.url, data: payload}).
its('status').
should('be.equal', 200);

// # Get message attachment from the last post
cy.getLastPostId().then((postId) => {
cy.get(`#messageAttachmentList_${postId}`).as('messageAttachmentList');
});

// * Verify each element of message attachment list
cy.get('@messageAttachmentList').within(() => {
cy.get('.attachment__thumb-pretext').should('be.visible').and('have.text', 'This is attachment pretext with basic options');
cy.get('.post-message__text-container').should('be.visible').and('have.text', 'This is attachment text with basic options');
cy.get('.attachment-actions').should('be.visible');
cy.get('.select-suggestion-container').should('be.visible');
cy.get('.select-suggestion-container > input').should('be.visible').and('have.attr', 'placeholder', 'Select an option...');

cy.get('#suggestionList').should('not.be.visible');
cy.get('.select-suggestion-container > input').click();
cy.get('#suggestionList').should('be.visible').children().should('have.length', options.length);

cy.get('#suggestionList').children().each(($el, index) => {
cy.wrap($el).should('have.text', options[index].text);
});

// * Close suggestion list by clicking on other element
cy.get('.attachment__thumb-pretext').click();
});
});

it('displays selected option and posts ephemeral message', () => {
// # Post an incoming webhook
cy.task('postIncomingWebhook', {url: incomingWebhook.url, data: payload}).
its('status').
should('be.equal', 200);

// # Get message attachment from the last post
cy.getLastPostId().then((postId) => {
cy.get(`#messageAttachmentList_${postId}`).as('messageAttachmentList');
});

cy.get('@messageAttachmentList').within(() => {
// # Select option 1 by typing exact text and press enter
cy.get('.select-suggestion-container > input').click().clear().type(`${options[0].text}{enter}`);

// * Verify that the input is updated with the selected option
cy.get('.select-suggestion-container > input').should('be.visible').and('have.attr', 'value', options[0].text);
});

cy.wait(TIMEOUTS.SMALL);

cy.getLastPostId().then((postId) => {
// * Verify that ephemeral message is posted, visible to observer and contains an exact message
cy.get(`#${postId}_message`).should('be.visible').and('have.class', 'post--ephemeral');
cy.get('.post__visibility').should('be.visible').and('have.text', '(Only visible to you)');
cy.get(`#postMessageText_${postId}`).should('be.visible').and('have.text', 'Ephemeral | select option: option1');
});
});

it('displays reply in center channel with "commented on [user\'s] message: [text]"', () => {
const user1 = users['user-1'];

// # Post an incoming webhook
cy.task('postIncomingWebhook', {url: incomingWebhook.url, data: payload}).
its('status').
should('be.equal', 200);

// # Get last post
cy.getLastPostId().then((parentMessageId) => {
const baseUrl = Cypress.config('baseUrl');

// # Post another message
cy.task('postMessageAs', {sender: user1, message: 'Just another message', channelId, baseUrl}).
its('status').
should('be.equal', 201);

// # Click comment icon to open RHS
cy.clickPostCommentIcon(parentMessageId);

// * Check that the RHS is open
cy.get('#rhsContainer').should('be.visible');

// # Have another user reply to the webhook message
cy.task('postMessageAs', {sender: user1, message: 'Reply to webhook', channelId, rootId: parentMessageId, baseUrl}).
its('status').
should('be.equal', 201);

// # Get the latest post
cy.getLastPostId().then((replyMessageId) => {
// * Verify that the reply is in the channel view with matching text
cy.get(`#post_${replyMessageId}`).within(() => {
cy.get('.post__link').should('be.visible').and('have.text', 'Commented on sysadmin\'s message: This is attachment pretext with basic options');
cy.get(`#postMessageText_${replyMessageId}`).should('be.visible').and('have.text', 'Reply to webhook');
});

// * Verify that the reply is in the RHS with matching text
cy.get(`#rhsPost_${replyMessageId}`).within(() => {
cy.get('.post__link').should('not.be.visible');
cy.get(`#postMessageText_${replyMessageId}`).should('be.visible').and('have.text', 'Reply to webhook');
});
});
});
});
});
12 changes: 11 additions & 1 deletion e2e/cypress/plugins/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
const postMessageAs = require('./post_message_as');
const externalRequest = require('./external_request');
const getRecentEmail = require('./get_recent_email');
const postIncomingWebhook = require('./post_incoming_webhook');

module.exports = (on) => {
module.exports = (on, config) => {
on('task', {
postMessageAs,
externalRequest,
getRecentEmail,
postIncomingWebhook,
});

on('before:browser:launch', (browser = {}, args) => {
Expand All @@ -19,4 +21,12 @@ module.exports = (on) => {

return args;
});

if (process.env.NODE_ENV === 'qa') { // eslint-disable-line no-process-env
config.webhookBaseUrl = 'https://cypress.test.mattermost.com/webhook';
} else {
config.webhookBaseUrl = 'http:https://localhost:3000';
}

return config;
};
18 changes: 18 additions & 0 deletions e2e/cypress/plugins/post_incoming_webhook.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

const axios = require('axios');

module.exports = async ({url, data}) => {
let response;

try {
response = await axios({method: 'post', url, data});
} catch (err) {
if (err.response) {
response = err.response;
}
}

return {status: response.status, data: response.data};
};
41 changes: 25 additions & 16 deletions e2e/cypress/plugins/post_message_as.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

const axios = require('axios');

module.exports = async ({sender, message, channelId, createAt = 0, baseUrl}) => {
module.exports = async ({sender, message, channelId, rootId, createAt = 0, baseUrl}) => {
const loginResponse = await axios({
url: `${baseUrl}/api/v4/users/login`,
headers: {'X-Requested-With': 'XMLHttpRequest'},
Expand All @@ -18,21 +18,30 @@ module.exports = async ({sender, message, channelId, createAt = 0, baseUrl}) =>
cookieString += nameAndValue + ';';
});

const response = await axios({
url: `${baseUrl}/api/v4/posts`,
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
Cookie: cookieString,
},
method: 'post',
data: {
channel_id: channelId,
message,
type: '',
create_at: createAt,
},
});
let response;
try {
response = await axios({
url: `${baseUrl}/api/v4/posts`,
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
Cookie: cookieString,
},
method: 'post',
data: {
channel_id: channelId,
message,
type: '',
create_at: createAt,
parent_id: rootId,
root_id: rootId,
},
});
} catch (err) {
if (err.response) {
response = err.response;
}
}

return {status: response.status, data: response.data, error: response.error};
};
20 changes: 20 additions & 0 deletions e2e/cypress/support/api_commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -492,3 +492,23 @@ Cypress.Commands.add('apiGetConfig', () => {
// # Get current settings
return cy.request('/api/v4/config');
});

// *****************************************************************************
// Webhooks
// https://api.mattermost.com/#tag/webhooks
// *****************************************************************************

Cypress.Commands.add('apiCreateWebhook', (hook = {}, isIncoming = true) => {
const hookUrl = isIncoming ? '/api/v4/hooks/incoming' : '/api/v4/hooks/outgoing';
const options = {
url: hookUrl,
headers: {'X-Requested-With': 'XMLHttpRequest'},
method: 'POST',
body: hook,
};

return cy.request(options).then((response) => {
const data = response.body;
return {...data, url: isIncoming ? `${Cypress.config().baseUrl}/hooks/${data.id}` : ''};
});
});
26 changes: 26 additions & 0 deletions e2e/cypress/utils/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import messageMenusData from '../fixtures/hooks/message_menus.json';
import messageMenusWithDatasourceData from '../fixtures/hooks/message_menus_with_datasource.json';

export function getRandomInt(max) {
return Math.floor(Math.random() * Math.floor(max));
}
Expand All @@ -21,5 +24,28 @@ export function getEmailMessageSeparator(baseUrl) {
return '\n';
}

export function getMessageMenusPayload({dataSource, options} = {}) {
let data;
if (dataSource) {
data = messageMenusWithDatasourceData;
data.attachments[0].actions[0].data_source = dataSource;
data.attachments[0].pretext = `This is attachment pretext with ${dataSource} options`;
data.attachments[0].text = `This is attachment text with ${dataSource} options`;
} else {
data = messageMenusData;
data.attachments[0].pretext = 'This is attachment pretext with basic options';
data.attachments[0].text = 'This is attachment text with basic options';

if (options) {
data.attachments[0].actions[0].options = options;
}
}

const callbackUrl = Cypress.config().webhookBaseUrl + '/message_menus';
data.attachments[0].actions[0].integration.url = callbackUrl;

return data;
}

export const reUrl = /(https?:\/\/[^ ]*)/;

Loading

0 comments on commit f4eb2bc

Please sign in to comment.