Этот коммит содержится в:
Mario Vitale
2023-03-27 16:28:42 +02:00
родитель da7a6825ce
Коммит ba6b97fb62
1142 изменённых файлов: 44 добавлений и 44 удалений

Просмотреть файл

@@ -0,0 +1,97 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import {onlyOn} from '@cypress/skip-test';
describe('channels > App Bar', () => {
let testTeam;
let testUser;
let testPlaybook;
let appBarEnabled;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
// # Start a playbook run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'Playbook Run',
ownerUserId: testUser.id,
});
});
cy.apiGetConfig(true).then(({config}) => {
appBarEnabled = config.EnableAppBar === 'true';
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
describe('App Bar disabled', () => {
it('should not show the Playbook App Bar icon', () => {
onlyOn(!appBarEnabled);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify App Bar icon is not showing
cy.get('#channel_view').within(() => {
cy.getPlaybooksAppBarIcon().should('not.exist');
});
});
});
describe('App Bar enabled', () => {
it('should show the Playbook App Bar icon', () => {
onlyOn(appBarEnabled);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify App Bar icon is showing
cy.getPlaybooksAppBarIcon().should('exist');
});
it('should show "Playbooks" tooltip for Playbook App Bar icon', () => {
onlyOn(appBarEnabled);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Hover over the channel header icon
cy.getPlaybooksAppBarIcon().trigger('mouseover');
// * Verify tooltip text
cy.findByRole('tooltip', {name: 'Playbooks'}).should('be.visible');
});
});
});

Просмотреть файл

@@ -0,0 +1,390 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > broadcast', () => {
let testTeam;
let testUser;
let testAdmin;
let testPublicChannel1;
let testPublicChannel2;
let testPrivateChannel1;
let testPrivateChannel2;
let publicBroadcastPlaybook;
let privateBroadcastPlaybook;
let allBroadcastPlaybook;
let rootDeletePlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateCustomAdmin().then(({sysadmin: adminUser}) => {
testAdmin = adminUser;
cy.apiAddUserToTeam(testTeam.id, adminUser.id);
cy.apiSaveJoinLeaveMessagesPreference(adminUser.id, false);
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public channel
cy.apiCreateChannel(
testTeam.id,
'public-channel',
'Public Channel 1',
'O',
).then(({channel: publicChannel1}) => {
testPublicChannel1 = publicChannel1;
// # Create a public channel
cy.apiCreateChannel(
testTeam.id,
'public-channel',
'Public Channel 2',
'O',
).then(({channel: publicChannel2}) => {
testPublicChannel2 = publicChannel2;
// # Create a private channel
cy.apiCreateChannel(
testTeam.id,
'private-channel',
'Private Channel 1',
'P',
).then(({channel: privateChannel1}) => {
testPrivateChannel1 = privateChannel1;
// # Create a private channel
cy.apiCreateChannel(
testTeam.id,
'private-channel',
'Private Channel 2',
'P',
).then(({channel: privateChannel2}) => {
testPrivateChannel2 = privateChannel2;
// # Create a playbook that will broadcast to public channel1
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook - public broadcast',
userId: testUser.id,
broadcastChannelIds: [testPublicChannel1.id],
broadcastEnabled: true,
}).then((playbook) => {
publicBroadcastPlaybook = playbook;
});
// # Create a playbook that will broadcast to private channel1
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook - private broadcast',
userId: testUser.id,
broadcastChannelIds: [testPrivateChannel1.id],
broadcastEnabled: true,
}).then((playbook) => {
privateBroadcastPlaybook = playbook;
});
// # Create a playbook that will broadcast to all 4 channels
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook - public and private broadcast',
userId: testUser.id,
broadcastChannelIds: [testPublicChannel1.id, testPublicChannel2.id, testPrivateChannel1.id, testPrivateChannel2.id],
broadcastEnabled: true,
}).then((playbook) => {
allBroadcastPlaybook = playbook;
});
// # Create a playbook for testing deleting root posts
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook - test deleting root posts',
userId: testUser.id,
broadcastChannelIds: [testPublicChannel1.id, testPrivateChannel1.id],
broadcastEnabled: true,
otherMembers: [testAdmin.id],
invitedUserIds: [testAdmin.id],
}).then((playbook) => {
rootDeletePlaybook = playbook;
});
// # invite testAdmin to the channel they will need to be in to delete the post
cy.apiAddUserToChannel(testPublicChannel1.id, testAdmin.id);
cy.apiAddUserToChannel(testPrivateChannel1.id, testAdmin.id);
});
});
});
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Go to Town Square
cy.visit(`/${testTeam.name}/channels/town-square`);
});
it('to public channels', () => {
// # Create a new playbook run
const now = Date.now();
const playbookRunName = `Playbook Run (${now})`;
const playbookRunChannelName = `playbook-run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: publicBroadcastPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Update the playbook run's status
const updateMessage = 'Update - ' + now;
cy.updateStatus(updateMessage);
// * Verify the posts
const initialMessage = playbookRunName;
verifyInitialAndStatusPostInBroadcast(testTeam, testPublicChannel1.name, playbookRunName, initialMessage, updateMessage);
});
it('does not broadcast when broadcast is disabled, even if broadcastChannelIds contain data', () => {
// # Create a brand new channel
cy.apiCreateChannel(
testTeam.id,
'public-channel-do-not-broadcast',
'Public Channel 1 - do not broadcast',
'O',
).then(({channel}) => {
// # Create a playbook with broadcast disabled, but with broadcastChannelIds containing channel1
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook - disabled public broadcast',
userId: testUser.id,
broadcastChannelIds: [channel.id],
broadcastEnabled: false,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Playbook Run (${now})`;
const playbookRunChannelName = `playbook-run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Update the playbook run's status
const updateMessage = 'Update - ' + now;
cy.updateStatus(updateMessage);
// # Navigate to the broadcast channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Verify that the last post is the system post containing the join message,
// so no announcement nor update was posted
cy.getLastPostId().then((lastPostId) => {
cy.get(`#postMessageText_${lastPostId}`).contains('You joined the channel');
});
});
});
});
it('to private channels', () => {
// # Create a new playbook run
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: privateBroadcastPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Update the playbook run's status
const updateMessage = 'Update - ' + now;
cy.updateStatus(updateMessage);
// * Verify the posts
const initialMessage = playbookRunName;
verifyInitialAndStatusPostInBroadcast(testTeam, testPrivateChannel1.name, playbookRunName, initialMessage, updateMessage);
});
it('to 4 public and private channels', () => {
// # Create a new playbook run
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: allBroadcastPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Update the playbook run's status
const updateMessage = 'Update - ' + now;
cy.updateStatus(updateMessage, 0);
// * Verify the posts
const initialMessage = playbookRunName;
verifyInitialAndStatusPostInBroadcast(testTeam, testPublicChannel1.name, playbookRunName, initialMessage, updateMessage);
verifyInitialAndStatusPostInBroadcast(testTeam, testPrivateChannel1.name, playbookRunName, initialMessage, updateMessage);
verifyInitialAndStatusPostInBroadcast(testTeam, testPublicChannel2.name, playbookRunName, initialMessage, updateMessage);
verifyInitialAndStatusPostInBroadcast(testTeam, testPrivateChannel2.name, playbookRunName, initialMessage, updateMessage);
});
it('to 2 channels, delete the root post, update again', () => {
// # Create a new playbook run
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: rootDeletePlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Update the playbook run's status
const updateMessage = 'Update - ' + now;
cy.updateStatus(updateMessage, 0);
// * Verify the posts
const initialMessage = playbookRunName;
verifyInitialAndStatusPostInBroadcast(testTeam, testPublicChannel1.name, playbookRunName, initialMessage, updateMessage);
verifyInitialAndStatusPostInBroadcast(testTeam, testPrivateChannel1.name, playbookRunName, initialMessage, updateMessage);
// # need to be admin to delete the bot's posts
cy.apiLogin(testAdmin);
// # Delete both root posts
deleteLatestPostRoot(testTeam, testPublicChannel1.name);
deleteLatestPostRoot(testTeam, testPrivateChannel1.name);
// # Log back in as testUser
cy.apiLogin(testUser);
// # Make two more updates
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Update the playbook run's status twice
const updateMessage2 = updateMessage + ' - 2';
cy.updateStatus(updateMessage2, 0);
const updateMessage3 = updateMessage + ' - 3';
cy.updateStatus(updateMessage3, 0);
// * Verify the posts
verifyInitialAndStatusPostInBroadcast(testTeam, testPublicChannel1.name, playbookRunName, updateMessage2, updateMessage3);
verifyInitialAndStatusPostInBroadcast(testTeam, testPrivateChannel1.name, playbookRunName, updateMessage2, updateMessage3);
});
});
const verifyInitialAndStatusPostInBroadcast = (testTeam, channelName, runName, initialMessage, updateMessage) => {
cy.log(`Verifying initial and status post in broadcast (channel ${channelName}, run ${runName})`);
// # Navigate to the broadcast channel
cy.visit(`/${testTeam.name}/channels/${channelName}`);
// * Verify that the last post contains the expected header and the update message verbatim
cy.getLastPostId().then((lastPostId) => {
// # Open RHS comment menu
cy.clickPostCommentIcon(lastPostId);
cy.get('#rhsContainer').
should('exist').
within(() => {
// * Thread should have two posts
cy.findAllByRole('listitem').should('have.length', 2);
// * The first should be announcement
cy.findAllByRole('listitem').eq(0).contains(initialMessage);
// * Latest post should be update
cy.get(`#rhsPost_${lastPostId}`).contains(
`posted an update for ${runName}`,
);
cy.get(`#rhsPost_${lastPostId}`).contains('tasks checked');
cy.get(`#rhsPost_${lastPostId}`).contains('participant');
cy.get(`#rhsPost_${lastPostId}`).contains(updateMessage);
});
});
};
const deleteLatestPostRoot = (testTeam, channelName) => {
cy.log(`Deleting latest root post (channel ${channelName})`);
// # Navigate to the channel
cy.visit(`/${testTeam.name}/channels/${channelName}`);
cy.getLastPostId().then((lastPostId) => {
// # Open RHS comment menu
cy.clickPostCommentIcon(lastPostId);
cy.get('#rhsContainer').
should('exist').
within(() => {
cy.findAllByRole('listitem').eq(0).then((root) => {
const rootId = root.attr('id').slice(8);
// # Click root's post dot menu.
cy.clickPostDotMenu(rootId, 'RHS_ROOT');
// # Click delete button.
const id = `#delete_post_${rootId}`;
cy.wrap(id).as('deleteId');
});
});
// * Post extra options is visible
cy.findByLabelText('Post extra options').should('exist');
// # Click delete button.
cy.get('@deleteId').then((deleteId) => {
cy.get(deleteId).should('be.visible').click();
});
// * Check that confirmation dialog is open.
cy.get('#deletePostModal').should('be.visible');
// * Check that confirmation dialog contains correct text
cy.get('#deletePostModal').
should('contain', 'Are you sure you want to delete this Post?');
// * Check that confirmation dialog shows that the post has one comment on it
cy.get('#deletePostModal').should('contain', 'This post has 1 comment on it.');
// # Confirm deletion.
cy.get('#deletePostModalButton').click();
});
};

Просмотреть файл

@@ -0,0 +1,125 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import {onlyOn} from '@cypress/skip-test';
describe('channels > channel header', () => {
let testTeam;
let testUser;
let testPlaybook;
let testPlaybookRun;
let appBarEnabled;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
// # Start a playbook run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'Playbook Run',
ownerUserId: testUser.id,
}).then((run) => {
testPlaybookRun = run;
});
});
cy.apiGetConfig(true).then(({config}) => {
appBarEnabled = config.EnableAppBar === 'true';
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
describe('App Bar enabled', () => {
it('webapp should hide the Playbook channel header button', () => {
onlyOn(appBarEnabled);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify channel header button is not showing
cy.get('#channel-header').within(() => {
cy.get('#incidentIcon').should('not.exist');
});
});
});
describe('App Bar disabled', () => {
it('webapp should show the Playbook channel header button', () => {
onlyOn(!appBarEnabled);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify channel header button is showing
cy.get('#channel-header').within(() => {
cy.get('#incidentIcon').should('exist');
});
});
it('tooltip text should show "Playbooks" for Playbook channel header button', () => {
onlyOn(!appBarEnabled);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Hover over the channel header icon
cy.get('#channel-header').within(() => {
cy.get('#incidentIcon').trigger('mouseover');
});
// * Verify tooltip text
cy.get('#pluginTooltip').contains('Playbooks');
});
});
describe('description text', () => {
it('should contain a link to the playbook', () => {
// # Navigate directly to a playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// * Verify link to playbook
cy.get('.header-description__text').findByText('Playbook').should('have.attr', 'href').then((href) => {
expect(href).to.equals(`/playbooks/playbooks/${testPlaybook.id}`);
});
});
it('should contain a link to the overview page', () => {
// # Navigate directly to a playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// * Verify link to overview page
cy.get('.header-description__text').findByText('the overview page').should('have.attr', 'href').then((href) => {
expect(href).to.equals(`/playbooks/runs/${testPlaybookRun.id}`);
});
});
});
});

Просмотреть файл

@@ -0,0 +1,367 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('channels > general actions', () => {
let testTeam;
let testSysadmin;
let testUser;
let testChannel;
beforeEach(() => {
cy.apiAdminLogin();
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, user}) => {
testTeam = team;
testSysadmin = user;
cy.apiCreateUser().then((resp) => {
testUser = resp.user;
cy.apiAddUserToTeam(team.id, resp.user.id);
cy.apiLogin(testUser);
// TODO: Make this work with CRT enabled.
cy.apiSaveCRTPreference(testUser.id, 'off');
});
cy.apiLogin(testSysadmin);
// TODO: Make this work with CRT enabled.
cy.apiSaveCRTPreference(testSysadmin.id, 'off');
cy.apiCreateChannel(
testTeam.id,
'action-channel',
'Action Channel',
'O',
).then(({channel}) => {
testChannel = channel;
});
});
});
describe('on join trigger', () => {
it('channel categorization can be enabled and works', () => {
// # Go to the test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// # Enable the categorization action and set the name
cy.contains('sidebar category').click();
cy.contains('Enter category name').click().type('example category{enter}');
cy.get('#channel-actions-modal').within(() => {
// # Save action
cy.findByRole('button', {name: /save/i}).click();
});
// # Switch to another user and reload
// # This drops them into the same channel
cy.apiLogin(testUser);
cy.reload();
cy.wait(TIMEOUTS.TEN_SEC);
// * Verify the channel category + channel exists
cy.contains('.SidebarChannelGroup', 'example category', {matchCase: false}).
should('exist').
within(() => {
cy.contains(testChannel.display_name).should('exist');
});
});
it('welcome message can be enabled and is shown to a joining user', () => {
// # Go to the test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// # Toggle on and set the welcome message
cy.contains('temporary welcome message').click();
cy.findByTestId('channel-actions-modal_welcome-msg').
type('test ephemeral welcome message');
cy.get('#channel-actions-modal').within(() => {
// # Save action
cy.findByRole('button', {name: /save/i}).click();
});
// # Switch to another user and reload
// # This drops them into the same channel
cy.apiLogin(testUser);
cy.reload();
cy.wait(TIMEOUTS.FIVE_SEC);
// * Verify the welcome message is shown
cy.verifyEphemeralMessage('test ephemeral welcome message');
});
});
describe('keyword trigger', () => {
it('prompt to run playbook can be enabled and works', () => {
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
});
// # Login as the non-sysadmin user first
// # to do the channel & action creation.
// # In the 'Select a playbook' dropdown later in this test,
// # sysadmin users could potentially see many other playbooks
// # besides the one created directly above. `testUser` will not.
cy.apiLogin(testUser);
cy.apiCreateChannel(
testTeam.id,
'action-channel',
'Action Channel',
'O',
).then(({channel}) => {
// # Go to the test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// # Set a keyword, enable the playbook trigger,
// # and select the Playbook to run
cy.contains('Type a keyword or phrase, then press Enter on your keyboard').click().type('red alert{enter}');
cy.contains('Prompt to run a playbook').click();
cy.contains('Select a playbook').click();
cy.findByText('Public Playbook').click();
cy.get('#channel-actions-modal').within(() => {
// # Save action
cy.findByRole('button', {name: /save/i}).click();
});
// # Post the trigger phrase
cy.uiPostMessageQuickly('error detected red alert!');
// * Verify that the bot posts the expected prompt
// # Open the playbook run modal
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.contains('trigger for the Public Playbook').should('exist');
cy.contains('Yes, run playbook').should('exist').click();
});
});
// # Enter a name and start the run
cy.findByTestId('playbookRunNameinput').type('run from trigger');
cy.findByRole('button', {name: /start run/i}).click();
// * Verify text from the run channel description
cy.contains('start of the run').should('exist');
});
});
it('deletes the post and ignores the thread when clicking on No, ignore thread', () => {
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
});
// # Login as the non-sysadmin user first
// # to do the channel & action creation.
// # In the 'Select a playbook' dropdown later in this test,
// # sysadmin users could potentially see many other playbooks
// # besides the one created directly above. `testUser` will not.
cy.apiLogin(testUser);
cy.apiCreateChannel(
testTeam.id,
'action-channel',
'Action Channel',
'O',
).then(({channel}) => {
// # Go to the test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// # Set a keyword, enable the playbook trigger,
// # and select the Playbook to run
cy.contains('Type a keyword or phrase, then press Enter on your keyboard').click().type('red alert{enter}');
cy.contains('Prompt to run a playbook').click();
cy.contains('Select a playbook').click();
cy.findByText('Public Playbook').click();
cy.get('#channel-actions-modal').within(() => {
// # Save action
cy.findByRole('button', {name: /save/i}).click();
});
// # Post the trigger phrase
cy.uiPostMessageQuickly('error detected red alert!');
// * Verify that the bot posts the expected prompt
// # Click on No, ignore thread
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.contains('trigger for the Public Playbook').should('exist');
cy.contains('No, ignore thread').should('exist').click();
});
});
// # Reload the channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// * Verify that the prompt post is no longer there
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.contains('No, ignore thread').should('not.exist');
});
});
// # Reply to the last thread with the trigger phrase
cy.getLastPostId().then((postId) => {
cy.clickPostCommentIcon(postId);
cy.postMessageReplyInRHS('error detected red alert!');
});
// * Verify that the bot did not post the prompt
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.contains('trigger for the Public Playbook').should('not.exist');
});
});
});
});
it('disabled triggers do not run even with a keyword set', () => {
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
});
// # Login as the non-sysadmin user first
// # to do the channel & action creation.
// # In the 'Select a playbook' dropdown later in this test,
// # sysadmin users could potentially see many other playbooks
// # besides the one created directly above. `testUser` will not.
cy.apiLogin(testUser);
cy.apiCreateChannel(
testTeam.id,
'action-channel',
'Action Channel',
'O',
).then(({channel}) => {
// # Go to the test channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// # Set a keyword, enable the playbook trigger,
// # and select the playbook to run. Turn the
// # trigger back off but leave the keyword set.
cy.contains('Type a keyword or phrase, then press Enter on your keyboard').click().type('red alert{enter}');
cy.contains('Prompt to run a playbook').click();
cy.contains('Select a playbook').click();
cy.findByText('Public Playbook').click();
cy.contains('Prompt to run a playbook').click();
cy.get('#channel-actions-modal').within(() => {
// # Save action
cy.findByRole('button', {name: /save/i}).click();
});
// # Post the trigger phrase
cy.uiPostMessageQuickly('error detected red alert!');
// * Verify that the bot _has not_ posted the expected prompt
cy.getLastPostId().then((postId) => {
cy.get(`#post_${postId}`).within(() => {
cy.contains('trigger for the Public Playbook').should('not.exist');
cy.contains('Yes, run playbook').should('not.exist');
});
});
});
});
});
it('action settings are disabled for non-channel admin', () => {
// # Login as non-channel admin
cy.apiLogin(testUser);
// # Go to the test channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// * Verify the toggles are disabled
cy.findByRole('dialog', {name: /channel actions/i}).within(() => {
cy.get('input').should('be.disabled');
});
});
it('action settings are reset to the default when switching to a channel with no actions configured', () => {
// # Create an additional channel
const name = 'New channel ' + Date.now();
cy.apiCreateChannel(
testTeam.id,
'new-channel',
name,
'O',
).then(({channel}) => {
// # Visit the first channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// # Enable the categorization action and set the name
const categoryName = 'example category ' + Date.now();
cy.contains('sidebar category').click();
cy.contains('Enter category name').click().type(categoryName + '{enter}');
cy.get('#channel-actions-modal').within(() => {
// # Save action
cy.findByRole('button', {name: /save/i}).click();
});
// # wait to avoid MM-45969
cy.wait(5000);
// # Switch to the additional channel
cy.get('#sidebarItem_' + channel.name).click();
// # Open Channel Header and the Channel Actions modal
cy.get('#channelHeaderTitle').click();
cy.findByText('Channel Actions').click();
// * Verify that the categorization action is disabled
cy.findByText('Add the channel to a sidebar category for the user').parent().within(() => {
cy.get('input').should('not.be.checked');
});
// * Verify that the category name is not there
cy.findByText(categoryName).should('not.exist');
});
});
});

Просмотреть файл

@@ -0,0 +1,644 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > actions', () => {
let testTeam;
let testSysadmin;
let testUser;
let testPublicChannel;
const testUsers = [];
before(() => {
cy.apiInitSetup({userPrefix: 'u'}).then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
testSysadmin = sysadmin;
});
// # Create extra test users in this team
cy.apiCreateUser({prefix: 'u'}).then((payload) => {
cy.apiAddUserToTeam(testTeam.id, payload.user.id);
testUsers.push(payload.user);
});
cy.apiCreateUser({prefix: 'u'}).then((payload) => {
cy.apiAddUserToTeam(testTeam.id, payload.user.id);
testUsers.push(payload.user);
});
// # Create a public channel
cy.apiCreateChannel(
testTeam.id,
'public-channel',
'Public Channel',
'O',
).then(({channel}) => {
testPublicChannel = channel;
cy.apiAddUserToChannel(channel.id, testUser.id);
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Go to Town Square
cy.visit(`/${testTeam.name}/channels/town-square`);
});
describe(('when a playbook run starts'), () => {
describe('invite members setting', () => {
it('with no invited users and setting disabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
let playbookId;
// # Create a playbook with the invite users disabled and no invited users
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
invitedUserIds: [],
inviteUsersEnabled: false,
}).then((playbook) => {
playbookId = playbook.id;
});
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that no users were invited
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).
contains('You were added to the channel by @playbooks.').
should('not.contain', 'joined the channel');
});
});
it('with invited users and setting enabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with a couple of invited users and the setting enabled, and a playbook run with it
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
invitedUserIds: [testUsers[0].id, testUsers[1].id],
inviteUsersEnabled: true,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the users were invited
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).within(() => {
cy.findByText('2 others').click();
});
cy.get(`#postMessageText_${id}`).contains(`@${testUsers[0].username}`);
cy.get(`#postMessageText_${id}`).contains(`@${testUsers[1].username}`);
cy.get(`#postMessageText_${id}`).contains('added to the channel by @playbooks.');
});
});
});
it('with invited users and setting disabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with a couple of invited users and the setting enabled, and a playbook run with it
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
invitedUserIds: [testUsers[0].id, testUsers[1].id],
inviteUsersEnabled: false,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that no users were invited
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).
contains('You were added to the channel by @playbooks.').
should('not.contain', 'joined the channel');
});
});
});
it('with non-existent users', () => {
let userToRemove;
let playbook;
// # Create a playbook with a user that is later removed from the team
cy.apiLogin(testSysadmin).then(() => {
cy.apiCreateUser().then((result) => {
userToRemove = result.user;
cy.apiAddUserToTeam(testTeam.id, userToRemove.id);
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with the user that will be removed from the team.
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id, testSysadmin.id],
invitedUserIds: [userToRemove.id],
inviteUsersEnabled: true,
}).then((res) => {
playbook = res;
});
// # Remove user from the team
cy.apiDeleteUserFromTeam(testTeam.id, userToRemove.id);
});
}).then(() => {
cy.apiLogin(testUser);
// # Create a new playbook run with the playbook.
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that there is an error message from the bot
cy.getNthPostId(1).then((id) => {
cy.get(`#postMessageText_${id}`).
contains(`Failed to invite the following users: @${userToRemove.username}`);
});
});
});
});
describe('default owner setting', () => {
it('defaults to the creator when no owner is specified', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
let playbookId;
// # Create a playbook with the default owner setting set to false
// and no owner specified
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
defaultOwnerId: '',
defaultOwnerEnabled: false,
}).then((playbook) => {
playbookId = playbook.id;
});
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the RHS shows the owner being the creator
cy.get('#rhsContainer').within(() => {
cy.findByText('Owner').parent().within(() => {
cy.findByText(`@${testUser.username}`);
});
});
});
it('defaults to the creator when no owner is specified, even if the setting is enabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
let playbookId;
// # Create a playbook with the default owner setting set to false
// and no owner specified
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
defaultOwnerId: '',
defaultOwnerEnabled: true,
}).then((playbook) => {
playbookId = playbook.id;
});
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the RHS shows the owner being the creator
cy.get('#rhsContainer').within(() => {
cy.findByText('Owner').parent().within(() => {
cy.findByText(`@${testUser.username}`);
});
});
});
it('assigns the owner when they are part of the invited members list', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with the owner being part of the invited users
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
invitedUserIds: [testUsers[0].id],
inviteUsersEnabled: true,
defaultOwnerId: testUsers[0].id,
defaultOwnerEnabled: true,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the RHS shows the owner being the invited user
cy.get('#rhsContainer').within(() => {
cy.findByText('Owner').parent().within(() => {
cy.findByText(`@${testUsers[0].username}`);
});
});
});
});
it('assigns the owner even if they are not invited', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with the owner being part of the invited users
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
invitedUserIds: [],
inviteUsersEnabled: false,
defaultOwnerId: testUsers[0].id,
defaultOwnerEnabled: true,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the RHS shows the owner being the invited user
cy.get('#rhsContainer').within(() => {
cy.findByText('Owner').parent().within(() => {
cy.findByText(`@${testUsers[0].username}`);
});
});
});
});
it('assigns the owner when they and the creator are the same', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
let playbookId;
// # Create a playbook with the default owner setting set to false
// and no owner specified
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
defaultOwnerId: testUser.id,
defaultOwnerEnabled: true,
}).then((playbook) => {
playbookId = playbook.id;
});
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the RHS shows the owner being the creator
cy.get('#rhsContainer').within(() => {
cy.findByText('Owner').parent().within(() => {
cy.findByText(`@${testUser.username}`);
});
});
});
});
describe('broadcast channel setting', () => {
it('with channel configured and setting enabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with a couple of invited users and the setting enabled, and a playbook run with it
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
broadcastChannelIds: [testPublicChannel.id],
broadcastEnabled: true,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel.
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the channel is created and that the first post exists.
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).
contains('You were added to the channel by @playbooks.').
should('not.contain', 'joined the channel');
});
// # Navigate to the broadcast channel
cy.visit(`/${testTeam.name}/channels/${testPublicChannel.name}`);
cy.getLastPostId().then((lastPostId) => {
cy.get(`#postMessageText_${lastPostId}`).contains(`${playbookRunName}`);
cy.get(`#postMessageText_${lastPostId}`).contains(`@${testUser.username} ran the ${playbookName} playbook.`);
});
});
});
it('with channel configured and setting disabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with a couple of invited users and the setting enabled, and a playbook run with it
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
broadcastChannelIds: [testPublicChannel.id],
broadcastEnabled: false,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the channel is created and that the first post exists.
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).
contains('You were added to the channel by @playbooks.').
should('not.contain', 'joined the channel');
});
// # Navigate to the broadcast channel
cy.visit(`/${testTeam.name}/channels/${testPublicChannel.name}`);
cy.getLastPostId().then((lastPostId) => {
cy.get(`#postMessageText_${lastPostId}`).should('not.contain', `New Run: ~${playbookRunName}`);
});
});
});
it('with non-existent channel', () => {
let playbookId;
// # Create a playbook with a channel that is later deleted
cy.apiLogin(testSysadmin).then(() => {
const channelDisplayName = String('Channel to delete ' + Date.now());
const channelName = channelDisplayName.replace(/ /g, '-').toLowerCase();
cy.apiCreateChannel(testTeam.id, channelName, channelDisplayName).then(({channel}) => {
// # Create a playbook with the channel to be deleted as the announcement channel
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
createPublicPlaybookRun: true,
memberIDs: [testUser.id, testSysadmin.id],
broadcastChannelIds: [channel.id],
broadcastEnabled: true,
}).then((playbook) => {
playbookId = playbook.id;
});
// # Delete channel
cy.apiDeleteChannel(channel.id);
});
}).then(() => {
cy.apiLogin(testUser);
// # Create a new playbook run with the playbook.
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that there is an error message from the bot
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).
contains('Failed to broadcast run creation to the configured channel.');
});
});
});
});
describe('creation webhook setting', () => {
it('with webhook correctly configured and setting enabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a playbook with a correct webhook and the setting enabled
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
webhookOnCreationURLs: ['https://httpbin.org/post'],
webhookOnCreationEnabled: true,
}).then((playbook) => {
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
description: 'Playbook run description.',
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the bot has not posted a message informing of the failure to send the webhook
cy.getLastPostId().then((lastPostId) => {
cy.get(`#postMessageText_${lastPostId}`).
should('not.contain', 'Playbook run creation announcement through the outgoing webhook failed. Contact your System Admin for more information.');
});
});
});
});
});
describe('when a playbook run is finished', () => {
it('retrospective is disabled', () => {
const playbookName = 'Playbook (' + Date.now() + ')';
// # Create a new playbook run with that playbook
const now = Date.now();
const playbookRunName = `Run (${now})`;
const playbookRunChannelName = `run-${now}`;
// # Create a playbook with the disabled retrospective functionality
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookName,
createPublicPlaybookRun: true,
memberIDs: [testUser.id],
retrospectiveEnabled: false,
}).then((playbook) => {
// # Run playbook
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName,
ownerUserId: testUser.id,
});
}).then((playbookRun) => {
// # End the playbook run
cy.apiFinishRun(playbookRun.id);
});
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that playbook run finished message was posted
cy.findAllByTestId('postView').contains(`marked ${playbookName} as finished`);
// * Verify that retrospective dialog was not posted
cy.findAllByTestId('retrospective-reminder').should('not.exist');
});
});
});

Просмотреть файл

@@ -0,0 +1,173 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > post type components', () => {
let testTeam;
let testUser;
let testChannel;
let testPlaybookRun;
beforeEach(() => {
cy.apiAdminLogin();
cy.apiInitSetup({loginAfter: true}).then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
}).then((playbook) => {
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: 'Test Run',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
});
});
cy.apiCreateChannel(
testTeam.id,
'other-channel',
'Other Channel',
'O',
).then(({channel}) => {
testChannel = channel;
});
});
});
describe('update post (custom_run_update)', () => {
it('displays in run channel', () => {
// # Go to the playbook run channel
cy.visit(`/${testTeam.name}/channels/test-run`);
// # intercepts telemetry
cy.interceptTelemetry();
// # Post a status update
cy.apiUpdateStatus({
playbookRunId: testPlaybookRun.id,
message: 'status update',
reminder: 60,
});
// Grab the post id
cy.getLastPostId().then((postId) => {
// * Assert telemetry data
cy.expectTelemetryToContain([
{
name: 'run_status_update',
type: 'page',
properties: {
post_id: postId,
playbook_run_id: testPlaybookRun.id,
channel_type: 'O',
},
},
]);
});
});
it('displays when permalinked in a different channel', () => {
// # Go to the playbook run channel
cy.visit(`/${testTeam.name}/channels/test-run`);
// # Post a status update
cy.apiUpdateStatus({
playbookRunId: testPlaybookRun.id,
message: 'status update',
reminder: 60,
});
// Grab the post id
cy.getLastPostId().then((postId) => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Go to the other channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Post a permalink to the status update
cy.uiPostMessageQuickly(`${Cypress.config('baseUrl')}/${testTeam.name}/pl/${postId}`);
// * Assert telemetry data
cy.expectTelemetryToContain([
{
name: 'run_status_update',
type: 'page',
properties: {
post_id: postId,
playbook_run_id: testPlaybookRun.id,
channel_type: 'O',
},
},
]);
cy.getLastPost().then((element) => {
// # Verify the expected message text
cy.get(element).contains(`${testUser.username} posted an update for ${testPlaybookRun.name}`);
cy.get(element).contains('status update');
});
});
});
it('displays when permalinked in a different channel, even if not a member of the original channel', () => {
// # Go to the playbook run channel
cy.visit(`/${testTeam.name}/channels/test-run`);
// # Post a status update
cy.apiUpdateStatus({
playbookRunId: testPlaybookRun.id,
message: 'status update',
reminder: 60,
});
cy.getLastPostId().then((postId) => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Leave the playbook run channel
cy.uiLeaveChannel();
// # Go to the other channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Post a permalink to the status update
cy.uiPostMessageQuickly(`${Cypress.config('baseUrl')}/${testTeam.name}/pl/${postId}`);
// * Assert telemetry data
cy.expectTelemetryToContain([
{
name: 'run_status_update',
type: 'page',
properties: {
post_id: postId,
playbook_run_id: testPlaybookRun.id,
channel_type: '',
},
},
]);
cy.getLastPost().then((element) => {
// # Verify the expected message text
cy.get(element).contains(`${testUser.username} posted an update for ${testPlaybookRun.name}`);
cy.get(element).contains('status update');
});
});
});
});
});

Просмотреть файл

@@ -0,0 +1,242 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('runs > retrospective', () => {
let testTeam;
let testUser;
let testPlaybookWithMetrics;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create playbook with metrics
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook with metrics',
memberIDs: [],
createPublicPlaybookRun: true,
metrics: [
{
title: 'Time to acknowledge',
description: 'some description text',
type: 'metric_duration',
target: 7200000,
},
{
title: 'Cost',
description: 'Cost of some events',
type: 'metric_currency',
target: 400,
},
{
title: 'Number of customers',
description: 'Number of customers who had issues',
type: 'metric_integer',
target: 30,
},
{
title: 'Duration',
description: 'Duration of incident',
type: 'metric_duration',
},
],
}).then((playbook) => {
testPlaybookWithMetrics = playbook;
});
});
describe('runs with metrics', () => {
let runId;
let runName;
let playbookRunChannelName;
beforeEach(() => {
// # Create a new playbook run
const now = Date.now();
runName = `Run (${now})`;
playbookRunChannelName = `run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybookWithMetrics.id,
playbookRunName: runName,
ownerUserId: testUser.id,
createPublicPlaybookRun: true,
}).then((run) => {
runId = run.id;
});
});
describe('publish retrospective', () => {
it('retrospective with 4 key metrics', () => {
// # Navigate directly to the retro tab
cy.visit(`/playbooks/runs/${runId}/retrospective`);
// * Verify metrics number
cy.getStyledComponent('InputContainer').should('have.length', 4);
// # Enter metrics values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('00:11:10').
tab().type('560').
tab().type('12').
tab().type('14:00:59');
// # Publish retrospective
publishRetro();
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify channel retro post content
cy.findAllByTestId('postView').last().contains(`Retrospective for ${runName} has been published by`);
cy.getStyledComponent('MetricInfo').should('have.length', 4);
cy.getStyledComponent('MetricInfo').eq(0).contains('11 hours, 10 minutes');
cy.getStyledComponent('MetricInfo').eq(1).contains('560');
cy.getStyledComponent('MetricInfo').eq(2).contains('12');
cy.getStyledComponent('MetricInfo').eq(3).contains('14 days, 59 minutes');
});
it('retrospective with 3 key metrics', () => {
// # Remove first metric, leave only 3
testPlaybookWithMetrics.metrics.splice(0, 1);
cy.apiUpdatePlaybook(testPlaybookWithMetrics).then(() => {
// # Navigate directly to the retro tab
cy.visit(`/playbooks/runs/${runId}/retrospective`);
// * Verify metrics number
cy.getStyledComponent('InputContainer').should('have.length', 3);
// # Enter metrics values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('43').
tab().type('121').
tab().type('11:00:02');
// # Publish retrospective
publishRetro();
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify channel retro post content
cy.findAllByTestId('postView').last().contains(`Retrospective for ${runName} has been published by`);
cy.getStyledComponent('MetricInfo').should('have.length', 3);
cy.getStyledComponent('MetricInfo').eq(0).contains('43');
cy.getStyledComponent('MetricInfo').eq(1).contains('121');
cy.getStyledComponent('MetricInfo').eq(2).contains('11 days, 2 minutes');
});
});
it('retrospective with 2 key metrics', () => {
// # Remove first two metrics, leave only 2
testPlaybookWithMetrics.metrics.splice(0, 2);
cy.apiUpdatePlaybook(testPlaybookWithMetrics).then(() => {
// # Navigate directly to the retro tab
cy.visit(`/playbooks/runs/${runId}/retrospective`);
// * Verify metrics number
cy.getStyledComponent('InputContainer').should('have.length', 2);
// # Enter metrics values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('0').
tab().type('00:04:02');
// # Publish retrospective
publishRetro();
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify channel retro post content
cy.findAllByTestId('postView').last().contains(`Retrospective for ${runName} has been published by`);
cy.getStyledComponent('MetricInfo').should('have.length', 2);
cy.getStyledComponent('MetricInfo').eq(0).contains('0');
cy.getStyledComponent('MetricInfo').eq(1).contains('4 hours, 2 minutes');
});
});
it('retrospective with 1 key metrics', () => {
// # Remove first 3 metrics, leave only 1
testPlaybookWithMetrics.metrics.splice(0, 3);
cy.apiUpdatePlaybook(testPlaybookWithMetrics).then(() => {
// # Navigate directly to the retro tab
cy.visit(`/playbooks/runs/${runId}/retrospective`);
// * Verify metrics number
cy.getStyledComponent('InputContainer').should('have.length', 1);
// # Enter metrics values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('00:00:00');
// # Publish retrospective
publishRetro();
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify channel retro post content
cy.findAllByTestId('postView').last().contains(`Retrospective for ${runName} has been published by`);
cy.getStyledComponent('MetricInfo').should('have.length', 1);
cy.getStyledComponent('MetricInfo').eq(0).contains('0 seconds');
});
});
it('retrospective with no metrics', () => {
// # Remove all metrics
testPlaybookWithMetrics.metrics.splice(0, 4);
cy.apiUpdatePlaybook(testPlaybookWithMetrics).then(() => {
// # Navigate directly to the retro tab
cy.visit(`/playbooks/runs/${runId}/retrospective`);
// * Verify there are no metrics inputs
cy.getStyledComponent('InputContainer').should('not.exist');
// # Publish retrospective
publishRetro();
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify channel retro post content
cy.findAllByTestId('postView').last().contains(`Retrospective for ${runName} has been published by`);
cy.getStyledComponent('MetricInfo').should('not.exist');
});
});
});
});
});
const publishRetro = () => {
// # Publish
cy.findByRole('button', {name: 'Publish'}).click();
cy.get('#confirm-modal-light').within(() => {
// * Verify we're showing the publish retro confirmation modal
cy.findByText('Are you sure you want to publish?');
// # Publish
cy.findByRole('button', {name: 'Publish'}).click();
});
};

Просмотреть файл

@@ -0,0 +1,132 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > header', () => {
let testTeam;
let testUser;
let testPlaybook;
let testPlaybookRun;
let playbookRunChannelName;
let playbookRunName;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Run the playbook
const now = Date.now();
playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((run) => {
testPlaybookRun = run;
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
});
describe('shows name', () => {
it('of active playbook run', () => {
// * Verify the title is displayed
cy.get('#rhsContainer').contains(playbookRunName);
});
it('of renamed playbook run', () => {
// * Verify the existing title is displayed
cy.get('#rhsContainer').contains(playbookRunName);
// # Rename the channel
cy.apiPatchChannel(testPlaybookRun.channel_id, {
id: testPlaybookRun.channel_id,
display_name: 'Updated',
});
// * Verify the updated title is displayed
cy.get('#rhsContainer').contains(playbookRunName);
});
});
describe('edit run name', () => {
it('by clicking on name', () => {
cy.get('#rhsContainer').findByTestId('rendered-run-name').should('be.visible').click();
// # type text in textarea
cy.get('#rhsContainer').findByTestId('textarea-run-name').should('be.visible').clear().type('new run name{ctrl+enter}');
// * make sure the updated name is here
cy.get('#rhsContainer').findByTestId('rendered-run-name').should('be.visible').contains('new run name');
// * make sure the channel name remains unchanged
cy.get('#channelHeaderInfo').findByRole('heading').contains(playbookRunName);
});
});
describe('edit summary', () => {
it('by clicking on placeholder', () => {
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').click();
// # type text in textarea
cy.get('#rhsContainer').findByTestId('textarea-description').should('be.visible').type('new summary{ctrl+enter}');
// * make sure the updated summary is here
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').contains('new summary');
});
it('by clicking on dot menu item', () => {
// # click on the field
cy.get('#rhsContainer').within(() => {
cy.findByTestId('buttons-row').invoke('show').within(() => {
cy.findAllByRole('button').eq(1).click();
});
});
cy.findByText('Edit run summary').click({force: true});
// # type text in textarea
cy.focused().should('be.visible').type('new summary{ctrl+enter}');
// * make sure the updated summary is here
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').contains('new summary');
});
});
describe('participate', () => {
it('icon is not visible if I am a participant', () => {
// * assert icon is not visible if I'm participant
cy.get('#rhsContainer').findByTestId('rhs-participate-icon').should('not.exist');
});
});
});

Просмотреть файл

@@ -0,0 +1,489 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import {HALF_SEC, ONE_SEC} from '../../../../fixtures/timeouts';
describe('channels > rhs > checklist', () => {
let testTeam;
let testUser;
let testPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreatePlaybook({
teamId: team.id,
title: 'Playbook',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1', command: '/invalid'},
{title: 'Step 2', command: '/echo VALID'},
{title: 'Step 3', command: '/playbook check 0 0'},
{title: 'Step 4'},
{title: 'Step 5'},
{title: 'Step 6'},
{title: 'Step 7'},
{title: 'Step 8'},
{title: 'Step 9'},
{title: 'Step 10'},
{title: 'Step 11'},
{title: 'Step 12'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1', command: '/invalid'},
{title: 'Step 2', command: '/echo VALID'},
{title: 'Step 3'},
{title: 'Step 4'},
{title: 'Step 5'},
{title: 'Step 6'},
{title: 'Step 7'},
{title: 'Step 8'},
{title: 'Step 9'},
{title: 'Step 10'},
{title: 'Step 11'},
{title: 'Step 12'},
],
},
{
title: 'Stage 3',
items: [
{title: 'Step 1', command: '/invalid'},
{title: 'Step 2', command: '/echo VALID'},
{title: 'Step 3'},
{title: 'Step 4'},
{title: 'Step 5'},
{title: 'Step 6'},
{title: 'Step 7'},
{title: 'Step 8'},
{title: 'Step 9'},
{title: 'Step 10'},
{title: 'Step 11'},
{title: 'Step 12'},
],
},
{
title: 'Stage 3',
items: [
{title: 'Step 1', command: '/invalid'},
{title: 'Step 2', command: '/echo VALID'},
{title: 'Step 3'},
{title: 'Step 4'},
{title: 'Step 5'},
{title: 'Step 6'},
{title: 'Step 7'},
{title: 'Step 8'},
{title: 'Step 9'},
{title: 'Step 10'},
{title: 'Step 11'},
{title: 'Step 12'},
],
},
],
memberIDs: [
user.id,
],
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
// // # Switch to clean display mode
// cy.apiSaveMessageDisplayPreference('clean');
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to task list without scrolling issues
cy.viewport('macbook-13');
});
describe('rhs stuff', () => {
let playbookRunName;
let playbookRunChannelName;
beforeEach(() => {
// # Run the playbook
const now = Date.now();
playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify the playbook run RHS is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText(playbookRunName).should('exist');
});
});
describe('header', () => {
it('has title', () => {
cy.get('#rhsContainer').within(() => {
cy.findByText('Tasks').should('exist');
});
});
});
it('shows an ephemeral error when running an invalid slash command', () => {
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify the command has not yet been run.
cy.findAllByTestId('run').eq(0).should('have.text', 'Run');
// * Run the /invalid slash command
cy.findAllByTestId('run').eq(0).click();
// * Verify the command still has not yet been run.
cy.findAllByTestId('run').eq(0).should('have.text', 'Run');
});
// * Verify the expected error message.
cy.verifyEphemeralMessage('Failed to execute slash command /invalid');
});
it('successfully runs a valid slash command', () => {
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify the command has not yet been run.
cy.findAllByTestId('run').eq(1).should('have.text', 'Run');
// * Run the /invalid slash command
cy.findAllByTestId('run').eq(1).click();
// * Verify the command has now been run.
cy.findAllByTestId('run').eq(1).should('have.text', 'Rerun');
});
// # Verify the expected output.
cy.verifyPostedMessage('VALID');
});
it('still shows slash commands as having been run after reload', () => {
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify the command has not yet been run.
cy.findAllByTestId('run').eq(1).should('have.text', 'Run');
// * Run the /invalid slash command
cy.findAllByTestId('run').eq(1).click();
// * Verify the command has now been run.
cy.findAllByTestId('run').eq(1).should('have.text', 'Rerun');
});
// # Verify the expected output.
cy.verifyPostedMessage('VALID');
// # Reload the page
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify the invalid command still has not yet been run.
cy.findAllByTestId('run').eq(0).should('have.text', 'Run');
// * Verify the valid command has been run.
cy.findAllByTestId('run').eq(1).should('have.text', 'Rerun');
});
});
it('runs /playbook slash commands', () => {
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify the `/playbook check 0 0` command has not yet been run.
cy.findAllByTestId('run').eq(2).should('have.text', 'Run');
// * Run the slash command
cy.findAllByTestId('run').eq(2).click();
// * Verify the command has now been run.
cy.findAllByTestId('run').eq(2).should('have.text', 'Rerun');
// * Verify the first checklist item is checked
cy.findAllByTestId('checkbox-item-container').eq(0).within(() => {
// # Check the overdue task
cy.get('input').should('be.checked');
});
});
// # Reload the page
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify the command has still been run.
cy.findAllByTestId('run').eq(2).should('have.text', 'Rerun');
// * Verify the first checklist item is still checked
cy.findAllByTestId('checkbox-item-container').eq(0).within(() => {
// # Check the overdue task
cy.get('input').should('be.checked');
});
});
});
it('can skip and restore task', () => {
// # Skip task and verify
skipTask(0);
// # Hover over the checklist item
cy.findAllByTestId('checkbox-item-container').eq(0).trigger('mouseover');
// # Click dot menu
cy.findAllByTestId('checkbox-item-container').eq(0).within(() => {
cy.findByTitle('More').click();
});
// # Click the restore button
cy.findByRole('button', {name: 'Restore task'}).click();
// * Verify the item has been restored
cy.findAllByTestId('checkbox-item-container').eq(0).within(() => {
cy.get('[data-cy=skipped]').should('not.exist');
});
});
it('add new task', () => {
const newTasktext = 'This is my new task' + Date.now();
cy.addNewTaskFromRHS(newTasktext);
// Check that it was created
cy.findByText(newTasktext).should('exist');
});
it('add new task slash command', () => {
const newTasktext = 'Task from slash command' + Date.now();
cy.uiPostMessageQuickly(`/playbook checkadd 0 ${newTasktext}`);
// Check that it was created
cy.findByText(newTasktext).should('exist');
});
it('creates a new checklist', () => {
// # Click on the button to add a checklist
cy.get('#rhsContainer').within(() => {
cy.findByTestId('add-a-checklist-button').click();
});
// # Type a title and click on the Add button
const title = 'Checklist - ' + Date.now();
cy.findByTestId('checklist-title-input').type(title);
cy.findByTestId('checklist-item-save-button').click();
// # Click on the button to add a checklist
cy.get('#rhsContainer').within(() => {
cy.findByText(title).should('exist');
});
});
it('renames a checklist', () => {
const oldTitle = 'Stage 1';
const newTitle = 'New title - ' + Date.now();
// # Open the dot menu and click on the rename button
cy.get('#rhsContainer').within(() => {
cy.findByText(oldTitle).trigger('mouseover');
cy.findAllByTestId('checklistHeader').eq(0).within(() => {
cy.findByTitle('More').click();
});
});
cy.findByTestId('dropdownmenu').findByText('Rename checklist').click();
// # Type the new title and click the confirm button
cy.findByTestId('checklist-title-input').type(newTitle);
cy.findByTestId('checklist-item-save-button').click();
// * Verify that the checklist changed its name
cy.get('#rhsContainer').within(() => {
cy.findByText(oldTitle).should('not.exist');
cy.findByText(oldTitle + newTitle).should('exist');
});
});
it('can set due date, from hover menu', () => {
// # Set due date and verify
setTaskDueDate(6, 'in 10 minutes');
});
it('can set due date, from edit mode', () => {
// # Hover over the checklist item
cy.findAllByTestId('checkbox-item-container').eq(6).trigger('mouseover');
// # Click the edit button
cy.findAllByTestId('hover-menu-edit-button').eq(0).click();
cy.findAllByTestId('due-date-info-button').eq(0).click();
// # Enter due date in 3 days
cy.get('.playbook-react-select__value-container').type('in 3 days').
wait(HALF_SEC).
trigger('keydown', {
key: 'Enter',
});
// * Verify if Due in 3 days info is added
cy.findAllByTestId('due-date-info-button').eq(0).should('exist').within(() => {
cy.findByText('in 3 days').should('exist');
cy.findByText('Due').should('exist');
});
});
it('filter overdue tasks', {retries: {runMode: 3}}, () => {
// # Set overdue date for several items
setTaskDueDate(2, '1 hour ago');
setTaskDueDate(3, '7 hours ago', 1);
setTaskDueDate(5, '3 hours ago', 2);
setTaskDueDate(6, '6 hours ago', 3);
// # Skip task
skipTask(3);
// # Mark a task as completed
cy.findAllByTestId('checkbox-item-container').eq(5).within(() => {
// # Check the overdue task
cy.get('input').click();
});
// * Verify if overdue tasks info was added. Should not include skipped / completed tasks.
cy.findAllByTestId('overdue-tasks-filter').eq(0).should('exist').within(() => {
cy.findByText('2 tasks overdue').should('exist');
});
// # Filter overdue tasks
cy.findAllByTestId('overdue-tasks-filter').eq(0).click();
// * Verify if filter works. Should not include skipped / completed tasks.
cy.findAllByTestId('checkbox-item-container').should('have.length', 2);
// # Cancel filter overdue tasks
cy.findAllByTestId('overdue-tasks-filter').eq(0).click();
// * Verify if filter was canceled
cy.findAllByTestId('checkbox-item-container').should('have.length', 48);
});
it('filter overdue automatically disappear if we check all overdue items', () => {
// # Set due date
setTaskDueDate(2, '1 minute ago');
// * Verify if overdue tasks info was added
cy.findAllByTestId('overdue-tasks-filter').eq(0).should('exist').within(() => {
cy.findByText('1 task overdue').should('exist');
});
// # Filter overdue tasks
cy.findAllByTestId('overdue-tasks-filter').eq(0).click();
// * Verify if filter works
cy.findAllByTestId('checkbox-item-container').should('have.length', 1);
// # Mark a task as completed
cy.findAllByTestId('checkbox-item-container').within(() => {
// # Check the overdue task
cy.get('input').click();
});
// * Verify there is no filter
cy.findAllByTestId('overdue-tasks-filter').should('not.exist');
// * Verify if filter was canceled
cy.findAllByTestId('checkbox-item-container').should('have.length', 48);
});
it('switching between runs with the same checklist', () => {
// # Create another run using the same playbook
const playbookRunName2 = 'RunWithSameChecklist';
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: playbookRunName2,
ownerUserId: testUser.id,
});
// # Set due date for the first channel's task
setTaskDueDate(2, 'in 2 hours');
// # Switch to the second run channel
cy.get('#sidebarItem_runwithsamechecklist').click();
// * Verify that tasks do not have due dates
cy.findAllByTestId('checkbox-item-container').eq(2).within(() => {
cy.findAllByTestId('due-date-info-button').should('not.exist');
});
});
it('scroll 2-3 pages and open due date selector- unexpected scroll issue', () => {
// # Hover over the checklist item that is ~3 pages down
cy.findAllByTestId('checkbox-item-container').eq(26).trigger('mouseover').within(() => {
// # Click the set due date button
cy.get('.icon-calendar-outline').click();
});
// * Verify if date selector is visible
cy.get('.playbook-react-select').should('be.visible');
});
});
});
const setTaskDueDate = (taskIndex, dateQuery, offset = 0) => {
// # Hover over the checklist item
cy.findAllByTestId('checkbox-item-container').eq(taskIndex).trigger('mouseover').within(() => {
// # Click the set due date button
cy.get('.icon-calendar-outline').click();
});
// # Wait for react select to finish rendering.
cy.wait(ONE_SEC);
// # Enter due date query
cy.get('.playbook-react-select').within(() => {
cy.get('input').type(dateQuery, {force: true}).
wait(HALF_SEC).
trigger('keydown', {
key: 'Enter',
});
});
// * Verify if Due date info is added
cy.findAllByTestId('due-date-info-button').eq(offset).should('exist').within(() => {
cy.findByText(dateQuery).should('exist');
cy.findByText('Due').should('exist');
});
};
const skipTask = (taskIndex) => {
// # Hover over the checklist item
cy.findAllByTestId('checkbox-item-container').eq(taskIndex).trigger('mouseover');
// # Click dot menu
cy.findAllByTestId('checkbox-item-container').eq(taskIndex).within(() => {
cy.findByTitle('More').click();
});
// # Click the skip button
cy.findByRole('button', {name: 'Skip task'}).click();
};

Просмотреть файл

@@ -0,0 +1,225 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > header', () => {
let testTeam;
let testUser;
let testPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
});
describe('shows name', () => {
it('of active playbook run', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify the title is displayed
cy.get('#rhsContainer').contains(playbookRunName);
});
it('of renamed playbook run', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify the existing title is displayed
cy.get('#rhsContainer').contains(playbookRunName);
// # Rename the channel
cy.apiPatchChannel(playbookRun.channel_id, {
id: playbookRun.channel_id,
display_name: 'Updated',
});
// * Verify the updated title is displayed
cy.get('#rhsContainer').contains(playbookRunName);
});
});
});
describe('edit summary', () => {
it('by clicking on placeholder', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # click on the field
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').click();
// # type text in textarea
cy.get('#rhsContainer').findByTestId('textarea-description').should('be.visible').type('new summary{ctrl+enter}');
// * make sure the updated summary is here
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').contains('new summary');
// * reload the page
cy.reload();
// * make sure the updated summary is still there
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').contains('new summary');
});
it('by clicking on dot menu item', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # click on the field
cy.get('#rhsContainer').within(() => {
cy.findByTestId('buttons-row').invoke('show').within(() => {
cy.findAllByRole('button').eq(1).click();
});
});
cy.findByTestId('dropdownmenu').within(() => {
cy.get('span').should('have.length', 3);
cy.findByText('Edit run summary').click();
});
// # type text in textarea
cy.focused().should('be.visible').type('new summary{ctrl+enter}');
// * make sure the updated summary is here
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').contains('new summary');
});
});
describe('edit summary of finished run', () => {
let playbookRunChannelName;
let finishedPlaybookRun;
beforeEach(() => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
finishedPlaybookRun = playbookRun;
});
});
it('by clicking on placeholder', () => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Wait for the RHS to open
cy.get('#rhsContainer').should('be.visible');
// # Mark the run as finished
cy.apiFinishRun(finishedPlaybookRun.id);
// # click on the field
cy.get('#rhsContainer').findByTestId('rendered-description').should('be.visible').click();
// * Verify textarea does not appear
cy.get('#rhsContainer').findByTestId('textarea-description').should('not.exist');
// * Verify no prompt to join appears (timeout ensures it fails right away before toast disappears)
cy.findByText('Become a participant to interact with this run', {timeout: 500}).should('not.exist');
});
it('by clicking on dot menu item', () => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Wait for the RHS to open
cy.get('#rhsContainer').should('be.visible');
// # Mark the run as finished
cy.apiFinishRun(finishedPlaybookRun.id);
// # click on the field
cy.get('#rhsContainer').within(() => {
cy.findByTestId('buttons-row').invoke('show').within(() => {
cy.findAllByRole('button').eq(1).click();
});
});
// * Verify the menu items
cy.findByTestId('dropdownmenu').within(() => {
cy.get('span').should('have.length', 2);
cy.findByText('Edit run summary').should('not.exist');
});
// * Verify no prompt to join appears (timeout ensures it fails right away before toast disappears)
cy.findByText('Become a participant to interact with this run', {timeout: 500}).should('not.exist');
});
});
});

Просмотреть файл

@@ -0,0 +1,159 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > home', () => {
let testSysadmin;
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
testSysadmin = sysadmin;
});
});
});
describe('default permission settings', () => {
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Navigate to the application, starting in a non-run channel.
cy.visit(`/${testTeam.name}/`);
// * Check time bar in the channel section
// * as an indicator of page stability / end of rendering
cy.findByText('Today').should('be.visible');
});
describe('telemetry', () => {
it('track page view', () => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// * Assert telemetry data
cy.expectTelemetryToContain([{name: 'channels_rhs_home', type: 'page'}]);
});
});
describe('shows available', () => {
it('starter templates', () => {
// templates are defined in webapp/src/components/templates/template_data.tsx
const templates = [
{name: 'Blank', checklists: '1 checklist', actions: '1 action'},
{name: 'Product Release', checklists: '4 checklists', actions: '3 actions'},
{name: 'Incident Resolution', checklists: '4 checklists', actions: '4 actions'},
{name: 'Customer Onboarding', checklists: '4 checklists', actions: '3 actions'},
{name: 'Employee Onboarding', checklists: '5 checklists', actions: '2 actions'},
{name: 'Feature Lifecycle', checklists: '5 checklists', actions: '3 actions'},
{name: 'Bug Bash', checklists: '5 checklists', actions: '3 actions'},
{name: 'Learn how to use playbooks', checklists: '2 checklists', actions: '2 actions'},
];
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// * Verify the templates are shown
cy.findByText('Playbook Templates').
parent().
next().
within(() => {
cy.findAllByTestId('template-details').each(($templateElement, index) => {
cy.wrap($templateElement).within(() => {
cy.findByText(templates[index].name).should('exist');
cy.findByText(templates[index].checklists).should('exist');
cy.findByText(templates[index].actions).should('exist');
});
});
});
});
});
describe('show zero case if there are playbooks', () => {
beforeEach(() => {
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Team Playbook',
memberIDs: [],
});
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
});
it('without pre-populated channel name template', () => {
// * Verify the templates are not shown
cy.findAllByTestId('template-details').should('not.exist');
// * Verify the zero case is shown
cy.get('#sidebar-right').findByText('There are no runs in progress linked to this channel').should('be.visible');
});
});
});
let restrictedTestTeam;
let restrictedTestUser;
describe('user is lacking permissions to create playbooks', () => {
before(() => {
cy.apiLogin(testSysadmin);
cy.apiCreateUser().then(({user}) => {
restrictedTestUser = user;
});
cy.apiCreateTeam('restricted-team', 'Restricted Team').then(({team}) => {
restrictedTestTeam = team;
cy.apiAddUserToTeam(restrictedTestTeam.id, restrictedTestUser.id);
});
cy.apiCreateScheme('Restricted Team Scheme', 'team').then(({scheme}) => {
cy.apiSetTeamScheme(restrictedTestTeam.id, scheme.id);
cy.apiGetRolesByNames([scheme.default_team_user_role]).then(({roles}) => {
const role = roles[0];
// Remove permissions to create playbooks
const permissions = role.permissions.filter((perm) => !(/playbook_(private|public)_create/).test(perm));
cy.apiPatchRole(role.id, {permissions});
});
});
});
beforeEach(() => {
// # Login as user with restricted permissions
cy.apiLogin(restrictedTestUser);
// # Navigate to the application, starting in a non-run channel.
cy.visit(`/${restrictedTestTeam.name}/`);
});
it('permission notice should be shown and no create button should exist', () => {
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
cy.get('#sidebar-right').within(() => {
// * Verify notice about missing permissions exists
cy.findByText('You don\'t have permission to create playbooks in this workspace.').should('be.visible');
// * Verify create playbook button does not exist
cy.findByText('Create playbook').should('not.exist');
});
});
});
});

Просмотреть файл

@@ -0,0 +1,263 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > runlist', () => {
let testTeam;
let testUser;
let testPlaybook;
let testChannel;
const numActiveRuns = 10;
const numFinishedRuns = 4;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'The playbook name',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
// # Create a test channel
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel').then(({channel}) => {
testChannel = channel;
// # Run the playbook a few times in the existing channel
for (let i = 0; i < numActiveRuns; i++) {
const runName = 'playbook-run-' + i;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
ownerUserId: testUser.id,
channelId: testChannel.id,
playbookRunName: runName,
});
}
// # Do it again but finished
for (let i = 0; i < numFinishedRuns; i++) {
const runName = 'playbook-run-' + i;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
ownerUserId: testUser.id,
channelId: testChannel.id,
playbookRunName: runName,
}).then((run) => {
cy.apiFinishRun(run.id);
});
}
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// # Wait the RHS to load
cy.findByText('Runs in progress').should('be.visible');
});
it('track page view', () => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${testChannel.name}`);
// * Assert telemetry data
cy.expectTelemetryToContain([{name: 'channels_rhs_runlist', type: 'page'}]);
});
it('can filter', () => {
// # Click the filter menu
cy.findByTestId('rhs-runs-filter-menu').click();
// * Verify displayed options
cy.get('[data-testid="dropdownmenu"] > :nth-child(1) > div').should('have.text', numActiveRuns);
cy.get('[data-testid="dropdownmenu"] > :nth-child(2) > div').should('have.text', numFinishedRuns);
// # Click the filter
cy.get('[data-testid="dropdownmenu"] > :nth-child(2)').click();
// * Verify displayed options
cy.get('[data-testid="rhs-runs-list"]').children().should('have.length', numFinishedRuns);
});
it('can show more (pagination)', () => {
// * Verify we have the first page
cy.get('[data-testid="rhs-runs-list"] > div').should('have.length', 8);
// # CLick in the show-more button
cy.get('[data-testid="rhs-runs-list"] > button').click();
// * Verify we have loaded the second page
cy.get('[data-testid="rhs-runs-list"] > div').should('have.length', 10);
});
it('card has the basic info', () => {
// # Click the first run
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1)').within(() => {
cy.findByText('playbook-run-9').should('be.visible');
cy.findByText('The playbook name').should('be.visible');
cy.findByText(testUser.username).should('be.visible');
});
});
it('can click though', () => {
// # Click the first run
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1)').click();
// * Verify we made it to the run details at Channels RHS
cy.get('#rhsContainer').contains('playbook-run-9');
cy.get('#rhsContainer').contains('Tasks');
});
it('can see give feedback button', () => {
// * Verify give feedback button exists and has the right URL
cy.get('#rhsContainer').findByText('Give feedback').
should('exist').
and('have.attr', 'href').
and('include', 'https://mattermost.com/pl/playbooks-feedback');
});
describe('dotmenu', () => {
it('can navigate to RDP', () => {
// # Click the first run's dotmenu
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1)').findByRole('button').click();
// # Click on go to run
cy.findByText('Go to run overview').click();
// * Assert we are in the run details page
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=channel_rhs_dotmenu');
});
it('can navigate to PBE', () => {
// # Click the first run's dotmenu
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1)').findByRole('button').click();
// # Click on go to polaybook
cy.findByText('Go to playbook').click();
// * Assert we are in the PBE page
cy.url().should('include', `/playbooks/${testPlaybook.id}`);
});
it('can change run name', () => {
// # Click on the kebab menu
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1) .icon-dots-vertical').click();
// # Click on the rename run option
cy.findByText('Rename run').click();
// # type new name
cy.findByTestId('run-name-input').clear().type('My cool new run name');
// # click save
cy.findByTestId('modal-confirm-button').click();
// * Verify the name has changed
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1)').contains('My cool new run name');
});
it('can change linked channel', () => {
// # Click on the kebab menu
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1) .icon-dots-vertical').click();
// # Click on the rename run option
cy.findByText('Link run to a different channel').click();
// # type new name
cy.get('.modal-body').within(() => {
// # select town square
cy.findByText(testChannel.display_name).click().type('Town Square{enter}');
});
// # click save
cy.findByTestId('modal-confirm-button').click();
// Let the listing refresh
cy.wait(1000);
// * Verify we have the first page
cy.get('[data-testid="rhs-runs-list"] > div').should('have.length', 8);
// # CLick in the show-more button
cy.get('[data-testid="rhs-runs-list"] > button').click();
// * Verify the channel has changed, now one run less
cy.get('[data-testid="rhs-runs-list"] > div').should('have.length', 9);
});
describe('navigation', () => {
let testChannelWith2Runs;
before(() => {
cy.apiLogin(testUser);
// # Create a test channel
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel').then(({channel}) => {
testChannelWith2Runs = channel;
// # Run the playbook a few times in the existing channel
for (let i = 0; i < 2; i++) {
const runName = 'playbook-run-' + i;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
ownerUserId: testUser.id,
channelId: testChannelWith2Runs.id,
playbookRunName: runName,
});
}
});
});
it('stays at list even if one only linked run after moving run', () => {
// # Visit channel with 2 runs
cy.visit(`/${testTeam.name}/channels/${testChannelWith2Runs.name}`);
// # Click on the kebab menu
cy.get('[data-testid="rhs-runs-list"] > :nth-child(1) .icon-dots-vertical').click();
// # Click on the rename run option
cy.findByText('Link run to a different channel').click();
// # type new name
cy.get('.modal-body').within(() => {
// # select town square
cy.findByText(testChannelWith2Runs.display_name).click().type('Town Square{enter}');
});
// # click save
cy.findByTestId('modal-confirm-button').click();
// * Verify the run is not there, but we are still in the list (not rhs details)
cy.get('[data-testid="rhs-runs-list"] > div').should('have.length', 1);
});
});
});
});

Просмотреть файл

@@ -0,0 +1,590 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels rhs > start a run', () => {
let testTeam;
let testUser;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
cy.apiCreateChannel(testTeam.id, 'existing-channel', 'Existing Channel').then(({channel}) => {
testChannel = channel;
});
});
const createPlaybook = ({channelNameTemplate, runSummaryTemplate, channelId, channelMode, title}) => {
const runSummaryTemplateEnabled = Boolean(runSummaryTemplate);
// # Create a public playbook
return cy.apiCreatePlaybook({
title: title || 'Public Playbook',
channelNameTemplate,
runSummaryTemplate,
runSummaryTemplateEnabled,
channelMode,
channelId,
teamId: testTeam.id,
makePublic: true,
memberIDs: [testUser.id],
createPublicPlaybookRun: true,
}).then((playbook) => {
cy.wrap(playbook);
});
};
describe('From RHS run list > ', () => {
beforeEach(() => {
// # intercepts telemetry
cy.interceptTelemetry();
});
describe('playbook configured as create new channel', () => {
it('defaults', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'create_new_channel',
}).then((playbook) => {
// # Visit the selected playbook
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// * Assert template name is filled
cy.findByTestId('run-name-input').should('have.value', 'Channel template');
// * Assert summary template is filled
cy.findByTestId('run-summary-input').should('have.value', 'run summary template');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'channels_rhs_runlist',
playbookId: playbook.id,
channelMode: 'create_new_channel',
public: true,
hasPlaybookChanged: true,
hasNameChanged: false,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
}},
], {waitForCalls: 2});
// * Verify we are on the channel just created
cy.url().should('include', `/${testTeam.name}/channels/channel-template`);
// * Verify channel name
cy.get('h2').contains('Beginning of Channel template');
// * Verify run RHS
cy.get('#rhsContainer').should('exist').within(() => {
cy.contains('Channel template');
cy.contains('run summary template');
});
});
});
it('change title/summary', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'create_new_channel',
}).then((playbook) => {
// # Visit the selected playbook
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// * Assert template are filled (and force wait to them)
cy.findByTestId('run-name-input').should('have.value', 'Channel template');
// * Assert summary template is filled
cy.findByTestId('run-summary-input').should('have.value', 'run summary template');
// # Fill run name
cy.findByTestId('run-name-input').clear().type('Test Run Name');
// # Fill run summary
cy.findByTestId('run-summary-input').clear().type('Test Run Summary');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'channels_rhs_runlist',
playbookId: playbook.id,
channelMode: 'create_new_channel',
public: true,
hasPlaybookChanged: true,
hasNameChanged: true,
hasSummaryChanged: true,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
}},
]);
// * Verify we are on the channel just created
cy.url().should('include', `/${testTeam.name}/channels/test-run-name`);
// * Verify channel name
cy.get('h2').contains('Beginning of Test Run Name');
// * Verify run RHS
cy.get('#rhsContainer').should('exist').within(() => {
cy.contains('Test Run Name');
cy.contains('Test Run Summary');
});
});
});
it('change to link to existing channel defaults to current channel', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'create_new_channel',
}).then((playbook) => {
// # Visit the town square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// # Change to link to existing channel
cy.findByTestId('link-existing-channel-radio').click();
// * Assert current channel is selected
cy.findByText('Town Square').should('be.visible');
});
});
});
it('change to link to existing channel with already selected channel', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'create_new_channel',
channelId: testChannel.id,
}).then((playbook) => {
// # Visit the town square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// # Change to link to existing channel
cy.findByTestId('link-existing-channel-radio').click();
// * Assert selected channel is unchanged
cy.findByText(testChannel.display_name).should('be.visible');
});
});
});
it('change to link to existing channel', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'create_new_channel',
}).then((playbook) => {
// # Visit the selected playbook
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// # Change to link to existing channel
cy.findByTestId('link-existing-channel-radio').click();
// # Fill run name
cy.findByTestId('run-name-input').clear().type('Test Run Name');
// # Select test channel instead of current channel
cy.findByText('Town Square').click().type(`${testChannel.display_name}{enter}`);
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'channels_rhs_runlist',
playbookId: playbook.id,
channelMode: 'link_existing_channel',
hasPlaybookChanged: true,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: true,
hasChannelIdChanged: true,
hasPublicChanged: false,
}},
]);
// * Verify we are on the existing channel
cy.url().should('include', `/${testTeam.name}/channels/${testChannel.name}`);
// * Verify channel name
cy.get('h2').contains(`Beginning of ${testChannel.display_name}`);
// * Verify run RHS
cy.get('#rhsContainer').should('exist').within(() => {
cy.contains('Test Run Name');
cy.contains('run summary template');
});
});
});
});
describe('playbook configured as linked to existing channel', () => {
it('defaults', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'link_existing_channel',
channelId: testChannel.id,
}).then((playbook) => {
// # Visit the selected playbook
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// * Assert template name is empty
cy.findByTestId('run-name-input').should('be.empty');
// * Assert template summary is filled
cy.findByTestId('run-summary-input').should('have.value', 'run summary template');
// # Fill run name
cy.findByTestId('run-name-input').clear().type('Test Run Name');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'channels_rhs_runlist',
playbookId: playbook.id,
channelMode: 'link_existing_channel',
hasPlaybookChanged: true,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
}},
]);
// * Verify we are on the existing channel
cy.url().should('include', `/${testTeam.name}/channels/${testChannel.name}`);
// * Verify channel name
cy.get('h2').contains(`Beginning of ${testChannel.display_name}`);
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify run RHS
cy.contains('Test Run Name');
cy.contains('run summary template');
});
});
});
it('fill initially empty channel', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'link_existing_channel',
}).then((playbook) => {
// # Visit the selected playbook
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// * Assert template name is empty
cy.findByTestId('run-name-input').should('be.empty');
// * Assert template summary is filled
cy.findByTestId('run-summary-input').should('have.value', 'run summary template');
// # Fill run name
cy.findByTestId('run-name-input').clear().type('Test Run Name');
// # Fill Town square as the channel to be linked
cy.findByText('Select a channel').click().type(`${testChannel.display_name}{enter}`);
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'channels_rhs_runlist',
playbookId: playbook.id,
channelMode: 'link_existing_channel',
hasPlaybookChanged: true,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: true,
hasPublicChanged: false,
}},
]);
// * Verify we are on the existing channel
cy.url().should('include', `/${testTeam.name}/channels/${testChannel.name}`);
// * Verify channel name
cy.get('h2').contains(`Beginning of ${testChannel.display_name}`);
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify run RHS
cy.contains('Test Run Name');
cy.contains('run summary template');
});
});
});
it('change to create new channel', () => {
// # Fill default values
createPlaybook({
title: 'Playbook title' + Date.now(),
channelNameTemplate: 'Channel template',
runSummaryTemplate: 'run summary template',
channelMode: 'link_existing_channel',
channelId: testChannel.id,
}).then((playbook) => {
// # Visit the selected playbook
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Click start a run button
cy.findByTestId('rhs-runlist-start-run').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert we are at playbooks tab
cy.findByText('Select a playbook').should('be.visible');
// # Click on the playbook
cy.findAllByText(playbook.title).eq(0).click();
// # Wait the modal to render
cy.wait(500);
// # Change to create new channel
cy.findByTestId('create-channel-radio').click();
// # Fill run name
cy.findByTestId('run-name-input').clear().type('Test Run Name');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'channels_rhs_runlist',
playbookId: playbook.id,
channelMode: 'create_new_channel',
hasPlaybookChanged: true,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: true,
hasChannelIdChanged: false,
hasPublicChanged: false,
}},
]);
// * Verify we are on the channel just created
cy.url().should('include', `/${testTeam.name}/channels/test-run-name`);
// * Verify channel name
cy.get('h2').contains('Beginning of Test Run Name');
cy.get('#rhsContainer').should('exist').within(() => {
// * Verify run RHS
cy.contains('Test Run Name');
cy.contains('run summary template');
});
});
});
});
});
});

Просмотреть файл

@@ -0,0 +1,498 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
/* eslint-disable no-only-tests/no-only-tests */
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('channels > rhs > status update', () => {
const defaultReminderMessage = '# Default reminder message';
let testTeam;
let testChannel;
let testUser;
let testPlaybook;
let testRun;
before(() => {
cy.apiInitSetup().then(({team, channel, user}) => {
testTeam = team;
testChannel = channel;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser,
broadcastChannelIds: [testChannel.id],
reminderTimerDefaultSeconds: 3600,
reminderMessageTemplate: defaultReminderMessage,
retrospectiveEnabled: false,
broadcastEnabled: true,
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
// # Create a new playbook run
const now = Date.now();
const name = 'Playbook Run (' + now + ')';
const channelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: name,
ownerUserId: testUser.id,
}).then((run) => {
testRun = run;
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${channelName}`);
});
describe('post update dialog', () => {
it('renders description correctly', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Check description
cy.findByTestId('update_run_status_description').contains(`This update for the run ${testRun.name} will be broadcasted to one channel and one direct message.`);
});
});
it.skip('description link navigates to run overview', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Click overview link
cy.findByTestId('run-overview-link').click();
});
// * Check that we are now in run overview page
cy.url().should('include', `/playbooks/runs/${testRun.id}`);
// * Check that the run actions modal is already opened
cy.findByRole('dialog', {name: /Run Actions/i}).should('exist');
});
it('prevents posting an update message with only whitespace', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Type the invalid data
cy.findByTestId('update_run_status_textbox').clear().type(' {enter} {enter} ');
// * Verify submit is disabled.
cy.get('button.confirm').should('be.disabled');
// # Enter valid data
cy.findByTestId('update_run_status_textbox').type('valid update');
// # Submit the dialog.
cy.get('button.confirm').click();
});
// * Verify that the Post update dialog has gone.
cy.getStatusUpdateDialog().should('not.exist');
});
it('lets users with no access to the playbook post an update', () => {
let channelName;
const updateMessage = 'status update ' + Date.now();
// # Login as sysadmin and create a private playbook and a run
cy.apiAdminLogin().then(({user: sysadmin}) => {
// # Create a private playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook - Private',
memberIDs: [sysadmin.id], // Make it accesible only to sysadmin
inviteUsersEnabled: true,
invitedUserIds: [testUser.id], // Invite the test user
}).then((playbook) => {
// # Create a new playbook run
const now = Date.now();
const name = 'Playbook Run (' + now + ')';
channelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: name,
ownerUserId: sysadmin.id,
}).then((run) => {
cy.apiAddUsersToRun(run.id, [testUser.id]);
});
});
}).then(() => {
// # Login as the test user
cy.apiLogin(testUser);
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${channelName}`);
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Enter valid data
cy.findByTestId('update_run_status_textbox').type(updateMessage);
// # Submit the dialog.
cy.get('button.confirm').click();
});
// * Verify that the Post update dialog has gone.
cy.getStatusUpdateDialog().should('not.exist');
// * Verify that the status update was posted.
cy.getLastPost().within(() => {
cy.findByText(updateMessage).should('exist');
});
});
});
it('confirms finishing the run, and remembers changes and reminder when canceled', () => {
const updateMessage = 'This is the update text to test with.';
const reminderTime = '1 day';
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the first message is there.
cy.findByTestId('update_run_status_textbox').within(() => {
cy.findByText(defaultReminderMessage).should('exist');
});
// # Type text to test for later
cy.findByTestId('update_run_status_textbox').clear().type(updateMessage);
// # Set a new reminder to test for later
cy.openReminderSelector();
cy.selectReminderTime(reminderTime);
// # Mark the run as finished
cy.findByTestId('mark-run-as-finished').click({force: true});
// # Submit the dialog.
cy.get('button.confirm').click();
});
// * Confirmation should appear
cy.get('.modal-header').should('be.visible').contains('Confirm finish run');
// # Cancel
cy.get('#cancelModalButton').click({force: true});
// * Verify post update has the same information
cy.getStatusUpdateDialog().within(() => {
// * Verify the message was remembered
cy.findByTestId('update_run_status_textbox').within(() => {
cy.findByText(updateMessage).should('exist');
});
// * Verify the reminder was remembered
cy.get('#reminder_timer_datetime').contains(reminderTime);
// * Marked run is still checked
cy.findByTestId('mark-run-as-finished').within(() => {
cy.get('[type="checkbox"]').should('be.checked');
});
// # Submit the dialog.
cy.get('button.confirm').click();
});
// * Confirmation should appear
cy.get('.modal-header').should('be.visible').contains('Confirm finish run');
// # Submit
cy.get('#confirmModalButton').click({force: true});
// * Verify the status update was posted.
cy.getStyledComponent('CustomPostContent').within(() => {
cy.findByText(updateMessage).should('exist');
});
// * Verify the run was finished.
cy.getLastPost().contains(`@${testUser.username} marked ${testRun.name} as finished.`);
});
describe('prevents user from losing changes', () => {
it('cancel, go back and save', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Type the invalid data
cy.findByTestId('update_run_status_textbox').clear().type('My valid and important changes that I don\'t want to lose');
// * Click cancel
cy.findByTestId('modal-cancel-button').click();
});
// * Go back from unsaved changes modal
cy.get('#confirm-modal-light').within(() => {
cy.findByTestId('modal-cancel-button').click();
});
// # Delay in between the modal switch to ensure the
// # animation has fully happened
cy.wait(TIMEOUTS.TWO_SEC);
// # Submit the dialog.
cy.get('button.confirm').click();
// * Verify that the Post update and unsaved changes modals have gone.
cy.getStatusUpdateDialog().should('not.exist');
cy.get('#confirm-modal-light').should('not.exist');
});
it('click overview link, go back and save', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Type the invalid data
cy.findByTestId('update_run_status_textbox').clear().type('My valid and important changes that I don\'t want to lose');
// # Click overview link
cy.findByTestId('run-overview-link').click();
});
// Verify that the confirmation modal is shown
cy.get('#confirm-modal-light').within(() => {
// * Go back from unsaved changes modal
cy.findByTestId('modal-cancel-button').click();
});
// # Delay in between the modal switch to ensure the
// # animation has fully happened
cy.wait(TIMEOUTS.TWO_SEC);
// # Submit the dialog.
cy.get('button.confirm').click();
// * Verify that the Post update and unsaved changes modals have gone.
cy.getStatusUpdateDialog().should('not.exist');
cy.get('#confirm-modal-light').should('not.exist');
});
it('cancel and discard explicitly', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Type the invalid data
cy.findByTestId('update_run_status_textbox').clear().type('My valid and important changes that I don\'t want to lose');
// * Click cancel
cy.findByTestId('modal-cancel-button').click();
});
// * Discard explicitly from unsaved changes
cy.get('#confirm-modal-light').within(() => {
cy.get('button.confirm').click();
});
// * Verify that the Post update and unsaved changes modals have gone.
cy.getStatusUpdateDialog().should('not.exist');
cy.get('#confirm-modal-light').should('not.exist');
});
it('click overview link and discard explicitly', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Type the invalid data
cy.findByTestId('update_run_status_textbox').clear().type('My valid and important changes that I don\'t want to lose');
// # Click overview link
cy.findByTestId('run-overview-link').click();
});
// * Discard explicitly from unsaved changes
cy.get('#confirm-modal-light').within(() => {
cy.get('button.confirm').click();
});
// * Assert that we are at run overview page.
cy.url().should('include', `/playbooks/runs/${testRun.id}`);
// * Verify that the Post update and unsaved changes modals have gone.
cy.getStatusUpdateDialog().should('not.exist');
cy.get('#confirm-modal-light').should('not.exist');
// * Verify that the run actions modal is opened.
cy.findByRole('dialog', {name: /Run Actions/i}).should('exist');
});
});
describe('shows the last update in update message', () => {
it('shows the default when we have not made an update before', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the first message is there.
cy.findByTestId('update_run_status_textbox').within(() => {
cy.findByText(defaultReminderMessage).should('exist');
});
});
});
it('when we have made a previous update', () => {
const now = Date.now();
const firstMessage = 'Update - ' + now;
// # Create a first status update
cy.updateStatus(firstMessage);
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the first message is there.
cy.findByTestId('update_run_status_textbox').within(() => {
cy.findByText(firstMessage).should('exist');
});
});
});
});
});
describe('the default reminder', () => {
it('shows the configured default when we have not made a previous update', () => {
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the default is as expected
cy.get('#reminder_timer_datetime').within(() => {
cy.get('[class$=singleValue]').should('have.text', '1 hour');
});
});
});
it('shows the last reminder we typed in: 15 minutes', () => {
const now = Date.now();
const firstMessage = 'Update - ' + now;
// # Create a first status update
cy.updateStatus(firstMessage, '15 minutes');
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the default is as expected
cy.get('#reminder_timer_datetime').within(() => {
cy.get('[class$=singleValue]').should('have.text', '15 minutes');
});
});
});
it('shows the last reminder we typed in: 90 minutes', () => {
const now = Date.now();
const firstMessage = 'Update - ' + now;
// # Create a first status update
cy.updateStatus(firstMessage, '90 minutes');
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the default is as expected
cy.get('#reminder_timer_datetime').within(() => {
cy.get('[class$=singleValue]').should('have.text', '1 hour, 30 minutes');
});
});
});
it('shows the last reminder we typed in: 7 days', () => {
const now = Date.now();
const firstMessage = 'Update - ' + now;
// # Create a first status update
cy.updateStatus(firstMessage, '7 days');
// # Run the `/playbook update` slash command.
cy.uiPostMessageQuickly('/playbook update');
// # Get the dialog modal.
cy.getStatusUpdateDialog().within(() => {
// * Verify the default is as expected
cy.get('#reminder_timer_datetime').within(() => {
cy.get('[class$=singleValue]').should('have.text', '7 days');
});
});
});
});
describe('playbook with disabled status updates', () => {
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser,
broadcastChannelId: testChannel.id,
statusUpdateEnabled: false,
}).then((playbook) => {
testPlaybook = playbook;
});
});
describe('omit status update dialog when status updates are disabled', () => {
it('shows the default when we have not made an update before', () => {
// * Check if RHS section is loaded
cy.get('#rhs-about').should('exist');
// * Check if Post Update section is omitted
cy.get('#rhs-post-update').should('not.exist');
});
});
});
});

Просмотреть файл

@@ -0,0 +1,82 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > template', () => {
let team1;
let testUser;
beforeEach(() => {
cy.apiAdminLogin().then(() => {
cy.apiInitSetup().then(({team, user}) => {
team1 = team;
testUser = user;
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
});
});
describe('create playbook', () => {
describe('open new playbook creation modal and navigates to playbooks', () => {
it('after clicking on Use', () => {
// # Switch to playbooks DM channel
cy.visit(`/${team1.name}/messages/@playbooks`);
// * Checking the bot badge as an indicator of page
// * stability / rendering finished
cy.findByText('BOT').should('be.visible');
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Return first template (Blank)
cy.contains('Blank').click();
// * Assert playbooks creation modal is shown.
cy.get('#playbooks_create').should('exist');
// # Click create playbook button.
cy.get('button[data-testid=modal-confirm-button]').click();
// * Assert expected playbook template title in outline.
cy.findByTestId('playbook-editor-title').contains('Blank');
});
it('after clicking on title', () => {
// # Switch to playbooks DM channel
cy.visit(`/${team1.name}/messages/@playbooks`);
// * Checking the bot badge as an indicator of page
// * stability / rendering finished
cy.findByText('BOT').should('be.visible');
// # Open playbooks RHS.
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// # Return first template (Blank)
cy.contains('Use').click();
// * Assert playbooks creation modal is shown.
cy.get('#playbooks_create').should('exist');
// # Click create playbook button.
cy.get('button[data-testid=modal-confirm-button]').click();
// * Assert expected playbook template title in outline.
cy.findByTestId('playbook-editor-title').contains('Blank');
});
});
});
});

Просмотреть файл

@@ -0,0 +1,94 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > rhs > title', () => {
let testTeam;
let testUser;
let testPlaybook;
let playbookRunChannelName;
let testPlaybookRun;
const getHeaderTitle = () => cy.get('#rhsContainer').find('.sidebar--right__title');
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((run) => {
testPlaybookRun = run;
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
});
it('has title', () => {
// * Verify the title is displayed
getHeaderTitle().contains('Run details');
});
it('has following button', () => {
// * Verify the following button is displayed
getHeaderTitle().find('button.unfollowButton').contains('Following');
// * Verify the follow button is not displayed
getHeaderTitle().find('button.followButton').should('not.exist');
});
it('can stop following', () => {
// # Click the following button
getHeaderTitle().find('button.unfollowButton').click();
// * Verify the following button is not displayed
getHeaderTitle().find('button.unfollowButton').should('not.exist');
// * Verify the follow button is displayed
getHeaderTitle().find('button.followButton').contains('Follow');
});
it('can navigate to RDP', () => {
// # Click the title
getHeaderTitle().findByTestId('rhs-title').click();
// * assert url is RDP
cy.url().should('include', `/playbooks/runs/${testPlaybookRun.id}`);
});
});

Просмотреть файл

@@ -0,0 +1,417 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('channels > rhs', () => {
let testTeam;
let testUser;
let testPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
describe('does not open', () => {
it('when navigating to a non-playbook run channel', () => {
// # Navigate to the application
cy.visit(`/${testTeam.name}/`);
// # Select a channel without a playbook run.
cy.get('#sidebarItem_off-topic').click({force: true});
// # Wait until the channel loads enough to show the post textbox.
cy.get('#post-create').should('exist');
// # Wait a bit longer to be confident.
cy.wait(TIMEOUTS.TWO_SEC);
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
});
it('when navigating to a playbook run channel with the RHS already open', () => {
// # Navigate to the application.
cy.visit(`/${testTeam.name}/`);
// # Select a channel without a playbook run.
cy.get('#sidebarItem_off-topic').click({force: true});
// # Run the playbook after loading the application
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Open the flagged posts RHS
cy.get('body').then(($body) => {
if ($body.find('#channelHeaderFlagButton').length > 0) {
cy.get('#channelHeaderFlagButton').click({force: true});
} else {
cy.findByRole('button', {name: 'Saved posts'}).
click({force: true});
}
});
// # Open the playbook run channel from the LHS.
cy.get(`#sidebarItem_${playbookRunChannelName}`).click({force: true});
// # Wait until the channel loads enough to show the post textbox.
cy.get('#post-create').should('exist');
// # Wait a bit longer to be confident.
cy.wait(TIMEOUTS.TWO_SEC);
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
});
it('when navigating directly to a finished playbook run channel', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # End the playbook run
cy.apiFinishRun(playbookRun.id);
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Wait a bit longer to be confident.
cy.wait(TIMEOUTS.TWO_SEC);
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
});
it('for an existing, finished playbook run channel opened from the lhs', () => {
// # Run the playbook before loading the application
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # End the playbook run
cy.apiFinishRun(playbookRun.id);
});
// # Navigate to a channel without a playbook run.
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Ensure the channel is loaded before continuing (allows redux to sync).
cy.findByTestId('post_textbox').should('exist');
// # Open the playbook run channel from the LHS.
cy.get(`#sidebarItem_${playbookRunChannelName}`).click({force: true});
// # Wait a bit longer to be confident.
cy.wait(TIMEOUTS.TWO_SEC);
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
});
it('for a new, finished playbook run channel opened from the lhs', () => {
// # Navigate to the application.
cy.visit(`/${testTeam.name}/`);
// # Ensure the channel is loaded before continuing (allows redux to sync).
cy.findByTestId('post_textbox').should('exist');
// # Select a channel without a playbook run.
cy.get('#sidebarItem_off-topic').click({force: true});
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
// # Run the playbook after loading the application
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # Wait a bit longer to avoid websocket events potentially being out-of-order.
cy.wait(TIMEOUTS.TWO_SEC);
// # End the playbook run
cy.apiFinishRun(playbookRun.id);
});
// # Wait because this test is flaky if we move too quickly
cy.wait(TIMEOUTS.FIVE_SEC);
// # Open the playbook run channel from the LHS.
cy.get(`#sidebarItem_${playbookRunChannelName}`).click({force: true});
// # Wait a bit longer to be confident.
cy.wait(TIMEOUTS.FIVE_SEC);
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
});
it('when starting a new run of a newly-created playbook created from RHS in a newly-created channel', () => {
// # Create a new channel
const channelName = 'playbook-test-' + Date.now();
cy.apiCreateChannel(testTeam.id, channelName, channelName, 'O').then(({channel}) => {
// # Navigate to the new channel
cy.visit(`/${testTeam.name}/channels/${channel.name}`);
// # Open RHS
cy.getPlaybooksAppBarIcon().click();
// # Wait a bit
cy.wait(TIMEOUTS.TWO_SEC);
// # open start run dialog
cy.findByTestId('rhs-runlist-start-run').click();
// # Create a new playbook
cy.findByText('Create new playbook').click();
// # confirm new playbook creation (with defaults)
cy.findByTestId('modal-confirm-button').click();
// * Verify we're in the playbook edit screen
cy.findByTestId('playbook-members');
// # Run the playbook
cy.findByTestId('run-playbook').click();
cy.findByTestId('run-name-input').type('Playbook Run');
// # Link to the new channel
cy.findByTestId('link-existing-channel-radio').click();
cy.get('#link-existing-channel-selector input').type(`${channel.name}{enter}`, {force: true});
cy.findByTestId('modal-confirm-button').click();
// # Wait a bit
cy.wait(TIMEOUTS.FIVE_SEC);
// * Verify the playbook run RHS is not open.
cy.get('#rhsContainer').should('not.exist');
});
});
});
describe('opens', () => {
it('when navigating directly to an ongoing playbook run channel', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify the playbook run RHS is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText(playbookRunName).should('exist');
});
});
it('for a new, ongoing playbook run channel opened from the lhs', () => {
// # Navigate to the application.
cy.visit(`/${testTeam.name}/`);
// # Ensure the channel is loaded before continuing (allows redux to sync).
cy.findByTestId('post_textbox').should('exist');
// # Select a channel without a playbook run.
cy.get('#sidebarItem_off-topic').click({force: true});
// # Run the playbook after loading the application
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Open the playbook run channel from the LHS.
cy.get(`#sidebarItem_${playbookRunChannelName}`).click({force: true});
// * Verify the playbook run RHS is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText(playbookRunName).should('exist');
});
});
it('for an existing, ongoing playbook run channel opened from the lhs', () => {
// # Run the playbook before loading the application
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Navigate to a channel without a playbook run.
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Ensure the channel is loaded before continuing (allows redux to sync).
cy.findByTestId('post_textbox').should('exist');
// # Open the playbook run channel from the LHS.
cy.get(`#sidebarItem_${playbookRunChannelName}`).click({force: true});
// * Verify the playbook run RHS is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText(playbookRunName).should('exist');
});
});
it('when starting a playbook run', () => {
// # Navigate to the application and a channel without a playbook run
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Start a playbook run with a slash command
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
cy.startPlaybookRunWithSlashCommand('Playbook', playbookRunName);
// * Verify the playbook run RHS is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText(playbookRunName).should('exist');
});
});
it('when starting a playbook run when rhs is already open', () => {
// # Navigate to the application and a channel without a playbook run
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Wait until the channel loads enough to show the post textbox.
cy.get('#post-create').should('exist');
// # Open the saved posts RHS
cy.findByRole('button', {name: 'Saved posts'}).
click({force: true});
// * Verify Saved Posts is open
cy.get('.sidebar--right__title').should('contain.text', 'Saved Posts');
// # Start a playbook run with a slash command
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
cy.startPlaybookRunWithSlashCommand('Playbook', playbookRunName);
// * Verify the playbook run RHS is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText(playbookRunName).should('exist');
});
});
it('when navigating directly to a finished playbook run channel and clicking on the button', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
const playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # End the playbook run
cy.apiFinishRun(playbookRun.id);
});
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// * Verify no active runs screen shows
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByTestId('no-active-runs').should('exist');
});
});
});
describe('is toggled', () => {
it('by icon in channel header', () => {
// # Size the viewport to show plugin icons even when RHS is open
cy.viewport('macbook-13');
// # Navigate to the application and a channel without a playbook run
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// * Verify RHS Home is open.
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText('Playbooks').should('exist');
});
// # Click the icon
cy.getPlaybooksAppBarIcon().should('be.visible').click();
// * Verify the playbook run RHS is no longer open.
cy.get('#rhsContainer').should('not.exist');
});
});
});

Просмотреть файл

@@ -0,0 +1,132 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > run dialog', () => {
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
});
// # Create a second playbook, so as to force dropdown.
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Second Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Navigate to the application
cy.visit(`${testTeam.name}`);
// # Trigger the playbook run creation dialog
cy.openPlaybookRunDialogFromSlashCommand();
// * Verify the playbook run creation dialog has opened
cy.get('#interactiveDialogModal').should('exist').within(() => {
cy.findByText('Start run').should('exist');
});
});
it('cannot create a playbook run without filling required fields', () => {
cy.get('#interactiveDialogModal').within(() => {
cy.findByText('Start run').should('exist');
// # Attempt to submit
cy.get('#interactiveDialogSubmit').click();
});
// * Verify it didn't submit
cy.get('#interactiveDialogModal').should('exist');
// * Verify required fields
cy.findByTestId('autoCompleteSelector').contains('Playbook');
cy.findByTestId('autoCompleteSelector').contains('This field is required.');
cy.findByTestId('playbookRunName').contains('This field is required.');
});
it('rejects invalid channel names', () => {
cy.selectPlaybookFromDropdown('Playbook');
const invalidPlaybookRunName = ' ';
cy.get('#interactiveDialogModal').within(() => {
cy.findByTestId('playbookRunNameinput').type(invalidPlaybookRunName, {force: true});
});
cy.get('#interactiveDialogModal').within(() => {
cy.findByText('Start run').should('exist');
// # Attempt to submit
cy.get('#interactiveDialogSubmit').click();
});
// * Verify it didn't submit
cy.get('#interactiveDialogModal').should('exist');
// * Verify error message
cy.get('#interactiveDialogModal').within(() => {
cy.get('div.error-text').contains('unable to create playbook run');
});
});
it('shows expected metadata', () => {
cy.get('#interactiveDialogModal').within(() => {
// * Shows current user as owner.
cy.findByText(`${testUser.first_name} ${testUser.last_name}`).should('exist');
// * Verify playbook dropdown prompt
cy.findByText('Playbook').should('exist');
// * Verify playbook run name prompt
cy.findByText('Run name').should('exist');
});
});
it('is canceled when cancel is clicked', () => {
// # Populate the interactive dialog
const playbookRunName = 'New Run' + Date.now();
cy.get('#interactiveDialogModal').within(() => {
cy.findByTestId('playbookRunNameinput').type('Playbook', {force: true});
});
// # Cancel the interactive dialog
cy.get('#interactiveDialogCancel').click();
// * Verify the modal is no longer displayed
cy.get('#interactiveDialogModal').should('not.exist');
// * Verify the playbook run did not get created
cy.apiGetAllPlaybookRuns(testTeam.id).then((response) => {
const allPlaybookRuns = response.body;
const playbookRun = allPlaybookRuns.items.find((inc) => inc.name === playbookRunName);
expect(playbookRun).to.be.undefined;
});
});
});

Просмотреть файл

@@ -0,0 +1,111 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > run', () => {
let testTeam;
let testUser;
let testPrivateChannel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreatePlaybook({
teamId: team.id,
title: 'Playbook',
memberIDs: [user.id],
});
// # Create a private channel
cy.apiCreateChannel(
testTeam.id,
'private-channel',
'Private Channel',
'P',
).then(({channel}) => {
testPrivateChannel = channel;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show plugin icons even when RHS is open
cy.viewport('macbook-13');
});
describe('via slash command', () => {
it('while viewing a public channel', () => {
// # Visit a public channel
cy.visit(`/${testTeam.name}/channels/off-topic`);
// * Verify that playbook run can be started with slash command
const playbookRunName = 'Public ' + Date.now();
cy.startPlaybookRunWithSlashCommand('Playbook', playbookRunName);
cy.verifyPlaybookRunActive(testTeam.id, playbookRunName);
});
it('while viewing a private channel', () => {
// # Visit a private channel
cy.visit(`/${testTeam.name}/channels/${testPrivateChannel.name}`);
// * Verify that playbook run can be started with slash command
const playbookRunName = 'Private ' + Date.now();
cy.startPlaybookRunWithSlashCommand('Playbook', playbookRunName);
cy.verifyPlaybookRunActive(testTeam.id, playbookRunName);
});
});
describe('via post menu', () => {
it('while viewing a public channel', () => {
// # Visit a public channel
cy.visit(`/${testTeam.name}/channels/off-topic`);
// * Verify that playbook run can be started from post menu
const playbookRunName = 'Public - ' + Date.now();
cy.startPlaybookRunFromPostMenu('Playbook', playbookRunName);
cy.verifyPlaybookRunActive(testTeam.id, playbookRunName);
});
it('while viewing a private channel', () => {
// # Visit a private channel
cy.visit(`/${testTeam.name}/channels/${testPrivateChannel.name}`);
// * Verify that playbook run can be started from post menu
const playbookRunName = 'Private - ' + Date.now();
cy.startPlaybookRunFromPostMenu('Playbook', playbookRunName);
cy.verifyPlaybookRunActive(testTeam.id, playbookRunName);
});
});
it('always as channel admin', () => {
// # Visit a public channel
cy.visit(`/${testTeam.name}/channels/off-topic`);
// # Start a playbook run with a slash command
const playbookRunName = 'Public ' + Date.now();
cy.startPlaybookRunWithSlashCommand('Playbook', playbookRunName);
cy.verifyPlaybookRunActive(testTeam.id, playbookRunName);
// # Open the channel header
cy.get('#channelHeaderTitle').click();
// * Verify the ability to edit the channel header exists
cy.get('#channelEditHeader').should('exist');
});
});

Просмотреть файл

@@ -0,0 +1,558 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import {switchToChannel} from '../../../channels/mark_as_unread/helpers';
describe('channels > slash command > owner', () => {
let testTeam;
let testUser;
let testUser2;
let testPlaybook;
let playbookRunName;
let playbookRunChannelName;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
cy.apiLogin(testUser);
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
memberIDs: [testUser.id],
}).then((playbook) => {
testPlaybook = playbook;
const now = Date.now();
playbookRunName = `Playbook Run (${now})`;
playbookRunChannelName = `playbook-run-${now}`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
});
describe('single run channel', () => {
it('check', () => {
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook check ');
// * Verify suggestions number: a single run with 4 tasks + 1 title
cy.get('.slash-command').should('have.length', 5);
// # Clear input
cy.findByTestId('post_textbox').clear();
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook check 1 1');
// * Verify the task is checked
cy.get('[data-rbd-droppable-id="1"]').find('.checkbox').eq(1).should('be.checked');
});
it('check add', () => {
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook checkadd 1 new-task');
// * Verify the task was added
cy.get('[data-rbd-droppable-id="1"]').contains('new-task');
});
it('check remove', () => {
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook checkremove 1 1');
// * Verify the task was added
cy.get('[data-rbd-droppable-id="1"]').contains('Step 2').should('not.exist');
});
it('owner', () => {
// # Run a slash command
cy.uiPostMessageQuickly('/playbook owner');
// * Verify the message.
cy.verifyEphemeralMessage(`@${testUser.username} is the current owner for this playbook run.`);
// # Run a slash command
cy.uiPostMessageQuickly(`/playbook owner @${testUser2.username}`);
// * Verify that the owner was set.
cy.uiPostMessageQuickly('/playbook owner');
cy.verifyEphemeralMessage(`@${testUser2.username} is the current owner for this playbook run.`);
});
it('timeline', () => {
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook timeline');
// * Verify the message.
cy.verifyEphemeralMessage(`Timeline for ${playbookRunName}`);
});
it('finish', () => {
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook finish');
// * Verify confirm modal is visible.
cy.get('#interactiveDialogModalLabel').should('exist');
// # Confirm finish
cy.get('#interactiveDialogSubmit').click();
// * Verify that the run is finished.
cy.get('#rhsContainer').findByTestId('badge').contains('Finished');
});
});
describe('multiple runs in the channel', () => {
let playbookRuns;
let testPrivatePlaybook;
let testPublicPlaybook;
let testPublicChannel;
let channelName;
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create private playbook, channel mode set to link existing channel
cy.apiCreatePlaybook({
makePublic: false,
createPublicPlaybookRun: false,
teamId: testTeam.id,
title: 'Playbook private',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
memberIDs: [testUser.id],
channelMode: 'link_existing_channel',
}).then((playbook) => {
testPrivatePlaybook = playbook;
});
// # Create public playbook, channel mode set to link existing channel
cy.apiCreatePlaybook({
makePublic: true,
teamId: testTeam.id,
title: 'Playbook public',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
memberIDs: [testUser.id],
channelMode: 'link_existing_channel',
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
beforeEach(() => {
playbookRuns = [];
const now = Date.now();
channelName = 'public-channel-' + now;
// # Create channel for runs
cy.apiCreateChannel(
testTeam.id,
channelName,
'public channel',
'O',
).then(({channel: publicChannel}) => {
testPublicChannel = publicChannel;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPrivatePlaybook.id,
playbookRunName: 'run write access ' + now,
ownerUserId: testUser.id,
channelId: testPublicChannel.id,
}).then((playbookRun) => {
cy.apiAddUsersToRun(playbookRun.id, [testUser2.id]);// add test user to participants list
playbookRuns.push(playbookRun);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'run view access' + now,
ownerUserId: testUser.id,
channelId: testPublicChannel.id,
}).then((playbookRun2) => {
playbookRuns.push(playbookRun2);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPrivatePlaybook.id,
playbookRunName: 'run no access' + now,
ownerUserId: testUser.id,
channelId: testPublicChannel.id,
}).then((playbookRun3) => {
playbookRuns.push(playbookRun3);
// # Add testUser2 to the channel
cy.apiAddUserToChannel(testPublicChannel.id, testUser2.id);
// # Login as testUser2
cy.apiLogin(testUser2);
// # Navigate directly to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${testPublicChannel.name}`);
switchToChannel(testPublicChannel);
});
});
});
});
});
it('check', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook check 1 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Command expects three arguments: the run number, the checklist number and the item number.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook check 2 1 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook check 0 1 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Become a participant to interact with this run');
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook check ');
// * Verify suggestions number: 2 runs * 4 tasks + 1 title
cy.get('.slash-command').should('have.length', 9);
// # Clear input
cy.findByTestId('post_textbox').clear();
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook check 1 1 1');
cy.get('#rhsContainer').within(() => {
// * Verify number of runs
cy.get('[data-testid="run-list-card"]').should('have.length', 2);
// # Open run details view
cy.findByText(playbookRuns[0].name).click({force: true});
});
// * Verify the task is checked
cy.get('[data-rbd-droppable-id="1"]').find('.checkbox').eq(1).should('be.checked');
});
it('check add', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook checkadd 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Command expects two arguments: the run number and the checklist number.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook checkadd 2 1 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook checkadd 0 1 new-task');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Become a participant to interact with this run');
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook checkadd ');
// * Verify suggestions number: 2 runs * 2 checklists + 1 title
cy.get('.slash-command').should('have.length', 5);
// # Clear input
cy.findByTestId('post_textbox').clear();
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook checkadd 1 1 new-task');
cy.get('#rhsContainer').within(() => {
// * Verify number of runs
cy.get('[data-testid="run-list-card"]').should('have.length', 2);
// # Open run details view
cy.findByText(playbookRuns[0].name).click({force: true});
});
// * Verify the task was added
cy.get('[data-rbd-droppable-id="1"]').contains('new-task');
});
it('check remove', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook checkremove 1 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Command expects three arguments: the run number, the checklist number and the item number.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook checkremove 2 0 1');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook checkremove 0 1 0');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Become a participant to interact with this run');
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook checkremove ');
// * Verify suggestions number: 2 runs * 4 tasks + 1 title
cy.get('.slash-command').should('have.length', 9);
// # Clear input
cy.findByTestId('post_textbox').clear();
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook checkremove 1 1 1');
cy.get('#rhsContainer').within(() => {
// * Verify number of runs
cy.get('[data-testid="run-list-card"]').should('have.length', 2);
// # Open run details view
cy.findByText(playbookRuns[0].name).click({force: true});
});
// * Verify the task was added
cy.get('[data-rbd-droppable-id="1"]').contains('Step 2').should('not.exist');
});
it('owner', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook owner');
// * Verify the expected error message.
cy.verifyEphemeralMessage('/playbook owner expects at most one argument.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook owner 2');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook owner 0');
// * Verify the message.
cy.verifyEphemeralMessage(`@${testUser.username} is the current owner for this playbook run.`);
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook owner ');
// * Verify suggestions number: 2 runs + 1 title
cy.get('.slash-command').should('have.length', 3);
// # Clear input
cy.findByTestId('post_textbox').clear();
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly(`/playbook owner 0 @${testUser2.username}`);
// * Verify the expected error message.
cy.verifyEphemeralMessage('Become a participant to interact with this run');
// # Run a slash command on a run with write access
cy.uiPostMessageQuickly(`/playbook owner 1 @${testUser2.username}`);
// * Verify that the owner was set.
cy.uiPostMessageQuickly('/playbook owner 1');
cy.verifyEphemeralMessage(`@${testUser2.username} is the current owner for this playbook run.`);
});
it('finish', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook finish');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Command expects one argument: the run number.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook finish 2');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook finish 0');
// * Verify the message.
cy.verifyEphemeralMessage(`userID ${testUser2.id} is not an admin or channel member`);
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook finish ');
// * Verify suggestions number: 2 runs + 1 title
cy.get('.slash-command').should('have.length', 3);
// # Clear input
cy.findByTestId('post_textbox').clear();
cy.get('#rhsContainer').within(() => {
// * Verify number of runs
cy.get('[data-testid="run-list-card"]').should('have.length', 2);
// # Open run details view
cy.findByText(playbookRuns[0].name).click({force: true});
});
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook finish 1');
// * Verify confirm modal is visible.
cy.get('#interactiveDialogModalLabel').should('exist');
// # Confirm finish
cy.get('#interactiveDialogSubmit').click();
// * Verify that the run is finished.
cy.get('#rhsContainer').findByTestId('badge').contains('Finished');
});
it('timeline', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook timeline');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Command expects one argument: the run number.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook timeline 2');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Run a slash command on a run with view access
cy.uiPostMessageQuickly('/playbook timeline 0');
// * Verify the message.
cy.verifyEphemeralMessage(`Timeline for ${playbookRuns[1].name}`);
});
it('update', () => {
// # Run a slash command with not enough parameters
cy.uiPostMessageQuickly('/playbook update');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Command expects one argument: the run number.');
// # Run a slash command wrong run number
cy.uiPostMessageQuickly('/playbook update 2');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Invalid run number');
// # Type a command
cy.findByTestId('post_textbox').clear().type('/playbook update ');
// * Verify suggestions number: 2 runs + 1 title
cy.get('.slash-command').should('have.length', 3);
// # Clear input
cy.findByTestId('post_textbox').clear();
// # Run a slash command with correct parameters
cy.uiPostMessageQuickly('/playbook update 1');
// # Get dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # Enter valid data
cy.findByTestId('update_run_status_textbox').type('valid update');
// # Submit the dialog.
cy.get('button.confirm').click();
});
// * Verify that the Post update dialog has gone.
cy.getStatusUpdateDialog().should('not.exist');
// * Verify that the status update was posted.
cy.getLastPost().within(() => {
cy.findByText('posted an update for').should('exist');
});
});
});
});

Просмотреть файл

@@ -0,0 +1,120 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > info', () => {
let testTeam;
let testUser;
let testUser2;
let testPlaybook;
let testPlaybookRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
cy.apiLogin(testUser);
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
memberIDs: [testUser.id],
}).then((playbook) => {
testPlaybook = playbook;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'Playbook Run',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Reset the owner back to testUser as necessary.
cy.apiChangePlaybookRunOwner(testPlaybookRun.id, testUser.id);
});
describe('/playbook info', () => {
it('should show an error when not in a playbook run channel', () => {
// # Navigate to a non-playbook run channel.
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Run a slash command to show the playbook run's info.
cy.uiPostMessageQuickly('/playbook info');
// * Verify the expected error message.
cy.verifyEphemeralMessage('This command only works when run from a playbook run channel.');
});
it('should open the RHS when it is not open', () => {
// # Navigate directly to the application and the playbook run channel.
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// # Close the RHS, which is opened by default when navigating to a playbook run channel.
cy.get('#searchResultsCloseButton').click();
// * Verify that the RHS is indeed closed.
cy.get('#rhsContainer').should('not.exist');
// # Run a slash command to show the playbook run's info.
cy.uiPostMessageQuickly('/playbook info');
// * Verify that the RHS is now open.
cy.get('#rhsContainer').should('be.visible');
});
it('should show an ephemeral post when the RHS is already open', () => {
// # Navigate directly to the application and the playbook run channel.
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// * Verify that the RHS is open.
cy.get('#rhsContainer').should('be.visible');
// # Run a slash command to show the playbook run's info.
cy.uiPostMessageQuickly('/playbook info');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Your playbook run details are already open in the right hand side of the channel.');
});
});
});

Просмотреть файл

@@ -0,0 +1,222 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > owner', () => {
let testTeam;
let testUser;
let testUser2;
let testPlaybook;
let testPlaybookRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
cy.apiLogin(testUser);
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
memberIDs: [testUser.id],
}).then((playbook) => {
testPlaybook = playbook;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'Playbook Run',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Reset the owner back to testUser as necessary.
cy.apiChangePlaybookRunOwner(testPlaybookRun.id, testUser.id);
});
describe('/playbook owner', () => {
it('should show an error when not in a playbook run channel', () => {
// # Navigate to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Run a slash command to show the current owner
cy.uiPostMessageQuickly('/playbook owner');
// * Verify the expected error message.
cy.verifyEphemeralMessage('This command only works when run from a playbook run channel.');
});
it('should show the current owner', () => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// # Run a slash command to show the current owner
cy.uiPostMessageQuickly('/playbook owner');
// * Verify the expected owner.
cy.verifyEphemeralMessage(`@${testUser.username} is the current owner for this playbook run.`);
});
});
describe('/playbook owner @username', () => {
it('should show an error when not in a playbook run channel', () => {
// # Navigate to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner ${testUser2.username}`);
// * Verify the expected error message.
cy.verifyEphemeralMessage('This command only works when run from a playbook run channel.');
});
describe('should show an error when the user is not found', () => {
beforeEach(() => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
});
it('when the username has no @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly('/playbook owner unknown');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Unable to find user @unknown');
});
it('when the username has an @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly('/playbook owner @unknown');
// * Verify the expected error message.
cy.verifyEphemeralMessage('Unable to find user @unknown');
});
});
describe('should not show an error when the user is not in the channel', () => {
beforeEach(() => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// # Ensure the user3 is not part of the channel.
cy.uiPostMessageQuickly(`/kick ${testUser2.username}`);
});
it('when the username has no @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner ${testUser2.username}`);
// * Verify the owner has changed.
cy.findByTestId('owner-profile-selector').contains(testUser2.username);
});
it('when the username has an @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner @${testUser2.username}`);
// * Verify the owner has changed.
cy.findByTestId('owner-profile-selector').contains(testUser2.username);
});
});
describe('should show a message when the user is already the owner', () => {
beforeEach(() => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
});
it('when the username has no @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner ${testUser.username}`);
// * Verify the expected error message.
cy.verifyEphemeralMessage(`User @${testUser.username} is already owner of this playbook run.`);
});
it('when the username has an @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner @${testUser.username}`);
// * Verify the expected error message.
cy.verifyEphemeralMessage(`User @${testUser.username} is already owner of this playbook run.`);
});
});
describe('should change the current owner', () => {
beforeEach(() => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// # Ensure the testUser2 is part of the channel.
cy.uiPostMessageQuickly(`/invite ${testUser2.username}`);
});
it('when the username has no @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner ${testUser2.username}`);
// # Verify the owner has changed.
cy.findByTestId('owner-profile-selector').contains(testUser2.username);
});
it('when the username has an @-prefix', () => {
// # Run a slash command to change the current owner
cy.uiPostMessageQuickly(`/playbook owner @${testUser2.username}`);
// # Verify the owner has changed.
cy.findByTestId('owner-profile-selector').contains(testUser2.username);
});
});
it('should show an error when specifying more than one username', () => {
// # Navigate directly to the application and the playbook run channel
cy.visit(`/${testTeam.name}/channels/playbook-run`);
// # Run a slash command with too many parameters
cy.uiPostMessageQuickly(`/playbook owner ${testUser.username} ${testUser2.username}`);
// * Verify the expected error message.
cy.verifyEphemeralMessage('/playbook owner expects at most one argument.');
});
});
});

Просмотреть файл

@@ -0,0 +1,255 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > test', () => {
let testTeam;
let testUser;
let testUser2;
let testPlaybook;
let testPlaybookRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
cy.apiLogin(testUser);
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
memberIDs: [testUser.id],
}).then((playbook) => {
testPlaybook = playbook;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'Playbook Run',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Reset the owner back to testUser as necessary.
cy.apiChangePlaybookRunOwner(testPlaybookRun.id, testUser.id);
});
describe('as a regular user', () => {
before(() => {
// # Login as sysadmin.
cy.apiAdminLogin();
// # Set EnableTesting to true.
cy.apiUpdateConfig({
ServiceSettings: {
EnableTesting: true,
},
});
});
beforeEach(() => {
// # Login as user-1
cy.apiLogin(testUser);
// # Navigate to a channel.
cy.visit(`/${testTeam.name}/channels/town-square`);
});
it('fails to run subcommand bulk-data', () => {
// # Execute the bulk-data command.
cy.uiPostMessageQuickly('/playbook test bulk-data');
// * Verify the ephemeral message warns that the user is not admin.
cy.verifyEphemeralMessage('Running the test command is restricted to system administrators.');
});
it('fails to run subcommand create-playbook-run', () => {
// # Execute the create-playbook-run command.
cy.uiPostMessageQuickly('/playbook test create-playbook-run');
// * Verify the ephemeral message warns that the user is not admin.
cy.verifyEphemeralMessage('Running the test command is restricted to system administrators.');
});
it('fails to run subcommand self', () => {
// # Execute the self command.
cy.uiPostMessageQuickly('/playbook test self');
// * Verify the ephemeral message warns that the user is not admin.
cy.verifyEphemeralMessage('Running the test command is restricted to system administrators.');
});
});
describe('as an admin', () => {
describe('with EnableTesting set to false', () => {
before(() => {
// # Login as sysadmin.
cy.apiAdminLogin();
// # Set EnableTesting to false.
cy.apiUpdateConfig({
ServiceSettings: {
EnableTesting: false,
},
});
});
beforeEach(() => {
// # Login as sysadmin.
cy.apiAdminLogin();
// # Navigate to a channel.
cy.visit(`/${testTeam.name}/channels/town-square`);
});
it('fails to run subcommand bulk-data', () => {
// # Execute the bulk-data command.
cy.uiPostMessageQuickly('/playbook test bulk-data');
// * Verify the ephemeral message warns that the user is not admin.
cy.verifyEphemeralMessage('Setting EnableTesting must be set to true to run the test command.');
});
it('fails to run subcommand create-playbook-run', () => {
// # Execute the create-playbook-run command.
cy.uiPostMessageQuickly('/playbook test create-playbook-run');
// * Verify the ephemeral message warns that the user is not admin.
cy.verifyEphemeralMessage('Setting EnableTesting must be set to true to run the test command.');
});
it('fails to run subcommand self', () => {
// # Execute the self command.
cy.uiPostMessageQuickly('/playbook test self');
// * Verify the ephemeral message warns that the user is not admin.
cy.verifyEphemeralMessage('Setting EnableTesting must be set to true to run the test command.');
});
});
describe('with EnableTesting set to true', () => {
before(() => {
// # Login as sysadmin.
cy.apiAdminLogin();
// # Set EnableTesting to true.
cy.apiUpdateConfig({
ServiceSettings: {
EnableTesting: true,
},
});
});
beforeEach(() => {
// # Login as sysadmin.
cy.apiAdminLogin();
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Navigate to a channel.
cy.visit(`/${testTeam.name}/channels/town-square`);
});
describe('with subcommand self', () => {
it('asks for confirmation', () => {
// # Execute the self command.
cy.uiPostMessageQuickly('/playbook test self');
// * Verify the ephemeral message asks for the confirmation keywords.
cy.verifyEphemeralMessage('Are you sure you want to self-test (which will nuke the database and delete all data -- instances, configuration)? All data will be lost. To self-test, type /playbook test self CONFIRM TEST SELF');
});
});
describe('with subcommand create', () => {
it('fails to run with no arguments', () => {
// # Execute the create-playbook-run command with no arguments.
cy.uiPostMessageQuickly('/playbook test create-playbook-run');
// * Verify the ephemeral message warns about the parameters.
cy.verifyEphemeralMessage('The command expects three parameters: <playbook_id> <timestamp> <name>');
});
it('fails to run with one argument', () => {
// # Execute the create-playbook-run command with one argument.
cy.uiPostMessageQuickly(`/playbook test create-playbook-run ${testPlaybook.id}`);
// * Verify the ephemeral message warns about the parameters.
cy.verifyEphemeralMessage('The command expects three parameters: <playbook_id> <timestamp> <name>');
});
it('fails to run with two arguments', () => {
// # Execute the create-playbook-run command with two arguments.
cy.uiPostMessageQuickly(`/playbook test create-playbook-run ${testPlaybook.id} 2020-01-01`);
// * Verify the ephemeral message warns about the parameters.
cy.verifyEphemeralMessage('The command expects three parameters: <playbook_id> <timestamp> <name>');
});
it('fails to run with a malformed playbook ID', () => {
// # Execute the create-playbook-run command with all arguments, but a malformed plabook ID.
cy.uiPostMessageQuickly('/playbook test create-playbook-run unknownID 2020-01-01 The playbook run name');
// * Verify the ephemeral message warns about the ID.
cy.verifyEphemeralMessage('The first parameter, <playbook_id>, must be a valid ID.');
});
it('fails to run with a valid, but unknown playbook ID', () => {
// # Execute the create-playbook-run command with all arguments, but an unknown plabook ID.
cy.uiPostMessageQuickly('/playbook test create-playbook-run abcdefghijklmnopqrstuvwxyz 2020-01-01 The playbook run name');
// * Verify the ephemeral message warns about the parameter.
cy.verifyEphemeralMessage('The playbook with ID \'abcdefghijklmnopqrstuvwxyz\' does not exist.');
});
it('fails to run with a malformed date', () => {
// # Execute the create-playbook-run command with all arguments, but a malformed creation timestamp.
cy.uiPostMessageQuickly(`/playbook test create-playbook-run ${testPlaybook.id} 2020-1-1 The playbook run name`);
// * Verify the ephemeral message warns about the parameter.
cy.verifyEphemeralMessage('Timestamp \'2020-1-1\' could not be parsed as a date. If you want the playbook run to start on January 2, 2006, the timestamp should be \'2006-01-02\'.');
});
});
});
});
});

Просмотреть файл

@@ -0,0 +1,322 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
describe('channels > slash command > todo', () => {
let team1;
let team2;
let testUser;
let testOtherUser;
let run1;
let run2;
let run3;
let run4;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
team1 = team;
testUser = user;
cy.apiCreateUser().then(({user: otherUser}) => {
testOtherUser = otherUser;
// # Add this new user to the team
cy.apiAddUserToTeam(team1.id, testOtherUser.id);
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: team1.id,
title: 'Playbook One',
memberIDs: [],
createPublicPlaybookRun: true,
checklists: [
{
title: 'Playbook One - Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Playbook One - Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
}).then(({id: playbookId}) => {
// # Create two runs in team 1.
const now = Date.now();
cy.apiRunPlaybook({
teamId: team1.id,
playbookId,
playbookRunName: 'Playbook Run (' + now + ')',
ownerUserId: testUser.id,
}).then((run) => {
run1 = run;
});
const now2 = Date.now() + 100;
cy.apiRunPlaybook({
teamId: team1.id,
playbookId,
playbookRunName: 'Playbook Run (' + now2 + ')',
ownerUserId: testUser.id,
}).then((run) => {
run2 = run;
});
});
// # Create a second team to test cross-team notifications
cy.apiCreateTeam('team2', 'Team 2').then(({team: secondTeam}) => {
team2 = secondTeam;
cy.apiAdminLogin();
cy.apiAddUserToTeam(team2.id, testUser.id);
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: team2.id,
title: 'Playbook Two',
memberIDs: [],
createPublicPlaybookRun: true,
checklists: [
{
title: 'Playbook Two - Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Playbook Two - Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
}).then(({id: playbookId}) => {
// # Create one run in team 2.
const now = Date.now() + 200;
cy.apiRunPlaybook({
teamId: team2.id,
playbookId,
playbookRunName: 'Playbook Run (' + now + ')',
ownerUserId: testUser.id,
}).then((run) => {
run3 = run;
});
});
});
// # Create another playbook with runs owned by another user
cy.apiCreatePlaybook({
teamId: team1.id,
title: 'Playbook Other',
memberIDs: [],
createPublicPlaybookRun: true,
checklists: [
{
title: 'Playbook Other - Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Playbook Other - Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
}).then(({id: playbookId}) => {
// # Login as testOtherUser
cy.apiLogin(testOtherUser);
// # Create a run in team 1, with testOtherUser as owner and inviting testUser
const now = Date.now();
cy.apiRunPlaybook({
teamId: team1.id,
playbookId,
playbookRunName: 'Other Playbook Run (' + now + ')',
ownerUserId: testOtherUser.id,
}).then((run) => {
run4 = run;
// # Invite testUser to the channel
// cy.apiAddUserToChannel(run.channel_id, testUser.id);
cy.apiAddUsersToRun(run.id, [testUser.id]);
// # Force this run to be overdue
cy.apiUpdateStatus({
playbookRunId: run4.id,
message: 'no message 4',
reminder: 1,
});
});
// # Create a run in team 1, with testOtherUser as owner but not inviting testUser
const now2 = Date.now() + 100;
cy.apiRunPlaybook({
teamId: team1.id,
playbookId,
playbookRunName: 'Other Playbook Run (' + now2 + ')',
ownerUserId: testOtherUser.id,
}).then((run) => {
// # Force this run to be overdue
cy.apiUpdateStatus({
playbookRunId: run.id,
message: 'no message 5',
reminder: 1,
});
});
});
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
describe('/playbook todo should show', () => {
it('three runs', () => {
// # Navigate to a non-playbook run channel.
cy.visit(`/${team2.name}/channels/town-square`);
// # Run a slash command to show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
cy.getLastPost().within((post) => {
// * Should show titles
cy.wrap(post).contains('You have 0 runs overdue.');
cy.wrap(post).contains('You have 0 assigned tasks.');
cy.wrap(post).contains('You have 4 runs currently in progress:');
// * Should show four active runs
cy.get('li').then((liItems) => {
expect(liItems[0]).to.contain.text(run4.name);
expect(liItems[1]).to.contain.text(run1.name);
expect(liItems[2]).to.contain.text(run2.name);
expect(liItems[3]).to.contain.text(run3.name);
});
});
});
it('four assigned tasks', () => {
// # assign self four tasks
cy.apiChangeChecklistItemAssignee(run1.id, 0, 0, testUser.id);
cy.apiChangeChecklistItemAssignee(run1.id, 1, 1, testUser.id);
cy.apiChangeChecklistItemAssignee(run2.id, 0, 1, testUser.id);
cy.apiChangeChecklistItemAssignee(run3.id, 1, 0, testUser.id);
// # Navigate to a non-playbook run channel.
cy.visit(`/${team2.name}/channels/town-square`);
// # Run a slash command to show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
cy.getLastPost().within((post) => {
// * Should show titles
cy.wrap(post).contains('You have 0 runs overdue.');
cy.wrap(post).contains('You have 4 total assigned tasks:');
// * Should show 3 runs w/ tasks
cy.get('.post__body a').then((links) => {
expect(links[0]).to.contain.text(run1.name);
expect(links[1]).to.contain.text(run2.name);
expect(links[2]).to.contain.text(run3.name);
});
cy.get('.post__body li').then((items) => {
// * first run
expect(items[0]).to.contain.text('Playbook One - Stage 1: Step 1');
expect(items[1]).to.contain.text('Playbook One - Stage 2: Step 2');
// * second run
expect(items[2]).to.contain.text('Playbook One - Stage 1: Step 2');
// * third run
expect(items[3]).to.contain.text('Playbook Two - Stage 2: Step 1');
});
});
// # check two of the items via API
cy.apiSetChecklistItemState(run1.id, 0, 0, 'closed');
cy.apiSetChecklistItemState(run3.id, 1, 0, 'closed');
// # Show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
// * Should show 2 tasks
cy.getLastPost().within((post) => {
// * Should show titles
cy.wrap(post).contains('You have 0 runs overdue.');
cy.wrap(post).contains('You have 2 total assigned tasks:');
// * Should show 2 runs w/ tasks
cy.get('.post__body a').then((links) => {
expect(links[0]).to.contain.text(run1.name);
expect(links[1]).to.contain.text(run2.name);
});
cy.get('.post__body li').then((items) => {
// * first run
expect(items[0]).to.contain.text('Playbook One - Stage 2: Step 2');
// * second run
expect(items[1]).to.contain.text('Playbook One - Stage 1: Step 2');
});
});
});
it('two overdue status updates', () => {
// # set two updates with short timers
cy.apiUpdateStatus({
playbookRunId: run1.id,
message: 'no message 1',
reminder: 1,
});
cy.apiUpdateStatus({
playbookRunId: run3.id,
message: 'no message 3',
reminder: 1,
});
cy.wait(1100);
// # Switch to playbooks DM channel
cy.visit(`/${team2.name}/messages/@playbooks`);
// # Run a slash command to show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
// # Should show two runs overdue -- ignoring the rest
cy.getLastPost().within(() => {
cy.get('.post__body li').then((liItems) => {
expect(liItems[0]).to.contain.text(run1.name);
expect(liItems[1]).to.contain.text(run3.name);
});
});
});
});
});

Просмотреть файл

@@ -0,0 +1,192 @@
// 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)
// ***************************************************************
// Stage: @prod
// Group: @playbooks
import * as TIMEOUTS from '../../../fixtures/timeouts';
describe('channels > update request post', () => {
let testTeam;
let testParticipant;
let testChannelMemberOnly;
let testPlaybookRun;
let testPlaybookRun2;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testParticipant = user;
cy.apiCreateUser().then(({user: channelMemberOnly}) => {
testChannelMemberOnly = channelMemberOnly;
// # Add testChannelMemberOnly to the testTeam
cy.apiAddUserToTeam(testTeam.id, testChannelMemberOnly.id);
// # Login as testChannelMemberOnly
cy.apiLogin(testChannelMemberOnly);
// # Enable threads view
cy.apiSaveCRTPreference(testChannelMemberOnly.id, 'on');
});
// # Login as testParticipant
cy.apiLogin(testParticipant);
// # Enable threads view
cy.apiSaveCRTPreference(testParticipant.id, 'on');
// # Create a public playbook with 2 runs in the same channel
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
}).then((playbook) => {
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: 'Test Run',
ownerUserId: testParticipant.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
// # Add testChannelMemberOnly to the channel, but not the run.
cy.apiAddUserToChannel(playbookRun.channel_id, testChannelMemberOnly.id);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: 'Test Run 2',
ownerUserId: testParticipant.id,
channelId: testPlaybookRun.channel_id,
}).then((playbookRun2) => {
testPlaybookRun2 = playbookRun2;
});
});
});
});
});
describe('displays interactive post', () => {
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testParticipant);
// # Post a status update, with a reminder in 1 second.
cy.apiUpdateStatus({
playbookRunId: testPlaybookRun2.id,
message: 'status update 2',
reminder: 1,
});
// # Post a status update, with a reminder in 2 second.
cy.apiUpdateStatus({
playbookRunId: testPlaybookRun.id,
message: 'status update',
reminder: 2,
});
// Ensure the status update reminder gets posted
cy.wait(TIMEOUTS.TWO_SEC);
});
describe('as a participant', () => {
beforeEach(() => {
// # Navigate to the application
cy.visit(`${testTeam.name}/channels/test-run`);
});
it('in the run channel', () => {
cy.getLastPost().then((element) => {
// # Verify the expected message text
cy.get(element).contains(`@${testParticipant.username}, please provide a status update for ${testPlaybookRun.name}.`);
// # Verify interactive message button to post an update
cy.get(element).find('button').contains('Post update');
});
});
it('reset reminder', () => {
cy.getLastPost().within(() => {
// * Snooze reminder
cy.getStyledComponent('StyledSelect').click().type('{downArrow}{downArrow}{enter}');
// # Verify interactive message button to post an update has dissapeared
cy.findByText('(message deleted)').should('be.visible');
});
});
it('in threads view', () => {
// # Find the update request post and post a reply to make it show up in threads view
cy.getLastPostId().then((lastPostId) => {
// Open RHS
cy.clickPostCommentIcon(lastPostId);
// Post a reply message
cy.postMessageReplyInRHS('test reply');
// # Navigate to the threads view
cy.get('#sidebarItem_threads').click();
// # Verify the expected text in the list view
cy.get('.ThreadItem').first().contains(`@${testParticipant.username}, please provide a status update for ${testPlaybookRun.name}.`);
// # Click to open details
cy.get('.ThreadItem').first().click();
// # Verify post still rendered
cy.get(`#rhsPost_${lastPostId}`).contains(`@${testParticipant.username}, please provide a status update for ${testPlaybookRun.name}.`);
// # Verify interactive message button to post an update
cy.get(`#rhsPost_${lastPostId}`).find('button').contains('Post update');
});
});
});
describe('as a channel member only', () => {
beforeEach(() => {
// # Login as testChannelMemberOnly
cy.apiLogin(testChannelMemberOnly);
// # Navigate to the application
cy.visit(`${testTeam.name}/channels/test-run`);
});
it('in the run channel', () => {
cy.getLastPost().then((element) => {
// # Verify the expected message text
cy.get(element).contains(`@${testParticipant.username}, please provide a status update for ${testPlaybookRun.name}.`);
});
});
it('in threads view', () => {
// # Find the update request post and post a reply to make it show up in threads view
cy.getLastPostId().then((lastPostId) => {
// Open RHS
cy.clickPostCommentIcon(lastPostId);
// Post a reply message
cy.postMessageReplyInRHS('test reply');
// # Navigate to the threads view
cy.get('#sidebarItem_threads').click();
// # Verify the expected text in the list view
cy.get('.ThreadItem').first().contains(`@${testParticipant.username}, please provide a status update for ${testPlaybookRun.name}.`);
// # Click to open details
cy.get('.ThreadItem').first().click();
// # Verify post still rendered
cy.get(`#rhsPost_${lastPostId}`).contains(`@${testParticipant.username}, please provide a status update for ${testPlaybookRun.name}.`);
});
});
});
});
});