remove cypress tests of playbooks (#31128)

Этот коммит содержится в:
sabril
2025-05-21 16:37:01 +08:00
коммит произвёл GitHub
родитель 56c6d8a9ab
Коммит 1cb244e876
65 изменённых файлов: 0 добавлений и 18157 удалений

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

@@ -1,23 +0,0 @@
{
"checklists": [
{
"items": [
{
"title": "Untitled task"
}
],
"title": "Default checklist"
}
],
"create_channel_member_on_new_participant": true,
"create_channel_member_on_removed_participant": true,
"description": "Customize this playbook's description to give an overview of when and how this playbook is run.",
"message_on_join": "Welcome! This channel was automatically created as part of a playbook run.",
"metrics": [],
"reminder_timer_default_seconds": 604800,
"retrospective_enabled": true,
"retrospective_template": "### Summary\nThis should contain 2-3 sentences that give a reader an overview of what happened, what was the cause, and what was done. The briefer the better as this is what future teams will look at first for reference.\n\n### What was the impact?\nThis section describes the impact of this playbook run as experienced by internal and external customers as well as stakeholders.\n\n### What were the contributing factors?\nThis playbook may be a reactive protocol to a situation that is otherwise undesirable. If that's the case, this section explains the reasons that caused the situation in the first place. There may be multiple root causes - this helps stakeholders understand why.\n\n### What was done?\nThis section tells the story of how the team collaborated throughout the event to achieve the outcome. This will help future teams learn from this experience on what they could try.\n\n### What did we learn?\nThis section should include perspective from everyone that was involved to celebrate the victories and identify areas for improvement. For example: What went well? What didn't go well? What should be done differently next time?\n\n### Follow-up tasks\nThis section lists the action items to turn learnings into changes that help the team become more proficient with iterations. It could include tweaking the playbook, publishing the retrospective, or other improvements. The best follow-ups will have a clear owner as well as due date.\n\n### Timeline highlights\nThis section is a curated log that details the most important moments. It can contain key communications, screen shots, or other artifacts. Use the built-in timeline feature to help you retrace and replay the sequence of events.",
"status_update_enabled": true,
"title": "Example Playbook",
"version": 1
}

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

@@ -1,107 +0,0 @@
// 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('admin console', {testIsolation: true}, () => {
let testUser;
let testTeam;
let testPlaybook;
let testSysadmin;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
testSysadmin = sysadmin;
});
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Login as testSysddmin
cy.apiLogin(testSysadmin);
});
describe('site statistics', () => {
it('playbooks and runs counters are visible', () => {
// # Go to admin console > site statistics
cy.visit('/admin_console/reporting/system_analytics');
// * Check that the playbook and run counters are visible
cy.findByTestId('playbooks.playbook_count').should('exist');
cy.findByTestId('playbooks.playbook_run_count').should('exist');
});
it('playbook counter increases after creating a playbook', () => {
let counter;
// # Go to admin console > site statistics
cy.visit('/admin_console/reporting/system_analytics');
// # Capture current value of playbook counter
cy.findByTestId('playbooks.playbook_count').invoke('prop', 'innerText').then((pbCount) => {
counter = parseInt(pbCount, 10);
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
}).then(() => {
cy.apiLogin(testSysadmin);
// # Go to admin console > site statistics
cy.visit('/admin_console/reporting/system_analytics');
// * Verify that the Playbook Counter has been increased by 1
cy.findByTestId('playbooks.playbook_count').contains(String(counter + 1));
});
});
});
it('run counter increases after creating a run', () => {
let counter;
// # Go to admin console > site statistics
cy.visit('/admin_console/reporting/system_analytics');
// # Capture current value of run counter
cy.findByTestId('playbooks.playbook_run_count').invoke('prop', 'innerText').then((runCount) => {
counter = parseInt(runCount, 10);
cy.apiLogin(testUser);
// # create a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'My run for test',
ownerUserId: testUser.id,
}).then(() => {
cy.apiLogin(testSysadmin);
// # Go to admin console > site statistics
cy.visit('/admin_console/reporting/system_analytics');
// * Verify that the Run Counter has been increased by 1
cy.findByTestId('playbooks.playbook_run_count').contains(String(counter + 1));
});
});
});
});
});

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

@@ -1,189 +0,0 @@
// 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('api > runs', {testIsolation: true}, () => {
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: [],
createPublicPlaybookRun: true,
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
describe('creating a run', () => {
describe('in an existing, public channel', () => {
it('with no team_id specified', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel').then(({channel}) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
}, {expectedStatusCode: 201}).then((body) => {
expect(body).to.have.property('owner_user_id', testUser.id);
expect(body).to.have.property('reporter_user_id', testUser.id);
expect(body).to.have.property('team_id', testTeam.id);
expect(body).to.have.property('channel_id', channel.id);
expect(body).to.have.property('playbook_id', testPlaybook.id);
});
});
});
it('with correct team_id specified', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel').then(({channel}) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
teamId: testTeam.id,
}, {expectedStatusCode: 201}).then((body) => {
expect(body).to.have.property('owner_user_id', testUser.id);
expect(body).to.have.property('reporter_user_id', testUser.id);
expect(body).to.have.property('team_id', testTeam.id);
expect(body).to.have.property('channel_id', channel.id);
expect(body).to.have.property('playbook_id', testPlaybook.id);
});
});
});
it('with wrong team_id specified', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel').then(({channel}) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
teamId: 'other_team_id',
}, {expectedStatusCode: 400}).then((body) => {
expect(body).to.have.property('error', 'unable to create playbook run');
});
});
});
});
describe('in an existing, private channel', () => {
it('with no team_id specified', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel', 'P').then(({channel}) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
}, {expectedStatusCode: 201}).then((body) => {
expect(body).to.have.property('owner_user_id', testUser.id);
expect(body).to.have.property('reporter_user_id', testUser.id);
expect(body).to.have.property('team_id', testTeam.id);
expect(body).to.have.property('channel_id', channel.id);
expect(body).to.have.property('playbook_id', testPlaybook.id);
});
});
});
it('with correct team_id specified', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel', 'P').then(({channel}) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
teamId: testTeam.id,
}, {expectedStatusCode: 201}).then((body) => {
expect(body).to.have.property('owner_user_id', testUser.id);
expect(body).to.have.property('reporter_user_id', testUser.id);
expect(body).to.have.property('team_id', testTeam.id);
expect(body).to.have.property('channel_id', channel.id);
expect(body).to.have.property('playbook_id', testPlaybook.id);
});
});
});
it('with wrong team_id specified', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel', 'P').then(({channel}) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
teamId: 'other_team_id',
}, {expectedStatusCode: 400}).then((body) => {
expect(body).to.have.property('error', 'unable to create playbook run');
});
});
});
});
it('in an existing, private channel, of which the user is not a member', () => {
// # Create a test channel without a playbook run
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel', 'P').then(({channel}) => {
// # Leave the channel
cy.apiRemoveUserFromChannel(channel.id, testUser.id);
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
ownerUserId: testUser.id,
channelId: channel.id,
playbookId: testPlaybook.id,
teamId: testTeam.id,
}, {expectedStatusCode: 403}).then((body) => {
expect(body).to.have.property('error', 'unable to create playbook run');
});
});
});
it('in a channel with an existing playbook run', () => {
// # Run the playbook, creating a channel.
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'Playbook',
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # Run the testPlaybook in the previously created channel
cy.apiRunPlaybook({
owner_user_id: testUser.id,
channel_id: playbookRun.channel_id,
playbook_id: testPlaybook.id,
}, {expectedStatusCode: 400}).then((body) => {
expect(body).to.have.property('error', 'unable to create playbook run');
});
});
});
});
});

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

@@ -1,69 +0,0 @@
// 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 > App Bar', {testIsolation: true}, () => {
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
cy.apiAdminLogin();
});
describe('App Bar disabled', () => {
it('should not show the Playbook App Bar icon', () => {
cy.apiUpdateConfig({ExperimentalSettings: {DisableAppBar: true}});
// # Login as testUser
cy.apiLogin(testUser);
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify App Bar icon is not showing
cy.get('.app-bar').should('not.exist');
});
});
describe('App Bar enabled', () => {
beforeEach(() => {
cy.apiUpdateConfig({ExperimentalSettings: {DisableAppBar: false}});
// # Login as testUser
cy.apiLogin(testUser);
});
it('should show the Playbook App Bar icon', () => {
// # 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', () => {
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// # Hover over the channel header icon
cy.getPlaybooksAppBarIcon().trigger('mouseenter');
// * Verify tooltip text
cy.findByRole('tooltip').should('be.visible').and('contain', 'Playbooks');
});
});
});

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

@@ -1,390 +0,0 @@
// 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', {testIsolation: true}, () => {
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.findAllByTestId('postContent').should('have.length', 2);
// * The first should be announcement
cy.findAllByTestId('postContent').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.findAllByTestId('postContent').eq(0).parent().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('have.attr', 'role', 'menu').and('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 message?');
// * Check that confirmation dialog shows that the post has one comment on it
cy.get('#deletePostModal').should('contain', 'This message has 1 comment on it.');
// # Confirm deletion.
cy.get('#deletePostModalButton').click();
});
};

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

@@ -1,138 +0,0 @@
// 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 > channel header', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;
let testPlaybookRun;
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;
});
});
});
});
describe('App Bar enabled', () => {
it('webapp should hide the Playbook channel header button', () => {
cy.apiAdminLogin();
cy.apiUpdateConfig({ExperimentalSettings: {DisableAppBar: false}});
// # Login as testUser
cy.apiLogin(testUser);
// # 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', () => {
beforeEach(() => {
cy.apiAdminLogin();
cy.apiUpdateConfig({ExperimentalSettings: {DisableAppBar: true}});
// # Login as testUser
cy.apiLogin(testUser);
});
it.skip('webapp should show the Playbook channel header button', () => {
// # 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', () => {
// # 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('#playbooksChannelHeaderButton').contains('Playbooks');
});
it.skip('webapp should make the Playbook channel header button active when opened', () => {
// # Navigate directly to a non-playbook run channel
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.get('#channel-header').within(() => {
// # Click the channel header button
cy.get('#incidentIcon').click();
// * Verify channel header button is showing active className
cy.get('#incidentIcon').parent().
should('have.class', 'channel-header__icon--active-inverted');
});
});
});
describe('description text', () => {
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
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}`);
});
});
});
});

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

@@ -1,391 +0,0 @@
// 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', {testIsolation: true}, () => {
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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();
// * More actions menu option should be visible
cy.findByText('More actions').should('be.visible').trigger('mouseover');
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');
});
});
});

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

@@ -1,644 +0,0 @@
// 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', {testIsolation: true}, () => {
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');
});
});
});

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

@@ -1,173 +0,0 @@
// 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', {testIsolation: true}, () => {
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');
});
});
});
});
});

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

@@ -1,242 +0,0 @@
// 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', {testIsolation: true}, () => {
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();
});
};

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

@@ -1,132 +0,0 @@
// 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', {testIsolation: true}, () => {
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').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');
});
});
});

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

@@ -1,489 +0,0 @@
// 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', {testIsolation: true}, () => {
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();
};

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

@@ -1,225 +0,0 @@
// 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', {testIsolation: true}, () => {
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');
});
});
});

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

@@ -1,159 +0,0 @@
// 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', {testIsolation: true}, () => {
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');
});
});
});
});

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

@@ -1,263 +0,0 @@
// 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', {testIsolation: true}, () => {
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);
});
});
});
});

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

@@ -1,589 +0,0 @@
// 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)
// ***************************************************************
// Group: @playbooks
describe('channels rhs > start a run', {testIsolation: true}, () => {
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');
});
});
});
});
});
});

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

@@ -1,495 +0,0 @@
// 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)
// ***************************************************************
// Group: @playbooks
import * as TIMEOUTS from '../../../../fixtures/timeouts';
describe('channels > rhs > status update', {testIsolation: true}, () => {
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('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');
});
});
});
});

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

@@ -1,74 +0,0 @@
// 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', {testIsolation: true}, () => {
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`);
// # 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`);
// # 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');
});
});
});
});

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

@@ -1,94 +0,0 @@
// 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', {testIsolation: true}, () => {
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}`);
});
});

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

@@ -1,417 +0,0 @@
// 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', {testIsolation: true}, () => {
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 messages'}).
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 messages'}).
click({force: true});
// * Verify Saved Posts is open
cy.get('.sidebar--right__title').should('contain.text', 'Saved messages');
// # 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');
});
});
});

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

@@ -1,132 +0,0 @@
// 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', {testIsolation: true}, () => {
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;
});
});
});

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

@@ -1,120 +0,0 @@
// 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', {testIsolation: true}, () => {
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 channel header dropdown
cy.get('#channelHeaderDropdownButton').click();
// # Click on Channel Settings
cy.findByText('Channel Settings').should('be.visible').click();
// * Verify Channel Settings modal opens
cy.get('.ChannelSettingsModal').should('be.visible');
// * Verify the ability to edit the channel header exists
cy.get('#channel_settings_header_textbox').should('be.visible').and('not.be.disabled');
// # Close the modal
cy.get('.GenericModal .modal-header button[aria-label="Close"]').click();
});
});

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

@@ -1,557 +0,0 @@
// 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)
// ***************************************************************
// Group: @playbooks
import {switchToChannel} from '../../../channels/mark_as_unread/helpers';
describe('channels > slash command > owner', {testIsolation: true}, () => {
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__info').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__info').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__info').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__info').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__info').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__info').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__info').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');
});
});
});
});

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

@@ -1,120 +0,0 @@
// 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', {testIsolation: true}, () => {
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.');
});
});
});

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

@@ -1,222 +0,0 @@
// 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', {testIsolation: true}, () => {
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.');
});
});
});

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

@@ -1,257 +0,0 @@
// 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)
// ***************************************************************
// Group: @playbooks
describe('channels > slash command > test', {testIsolation: true}, () => {
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,
},
});
cy.apiReloadConfig();
});
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,
},
});
cy.apiReloadConfig();
});
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,
},
});
cy.apiReloadConfig();
});
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\'.');
});
});
});
});
});

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

@@ -1,321 +0,0 @@
// 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)
// ***************************************************************
// Group: @playbooks
describe('channels > slash command > todo', {testIsolation: true}, () => {
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);
});
});
});
});
});

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

@@ -1,199 +0,0 @@
// 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', {testIsolation: true}, () => {
let testTeam;
let testParticipant;
let testChannelMemberOnly;
let testPlaybookRun;
let testPlaybookRun2;
before(() => {
cy.apiUpdateConfig({
ServiceSettings: {
ThreadAutoFollow: true,
CollapsedThreads: 'default_on',
},
});
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}.`);
});
});
});
});
});

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

@@ -1,178 +0,0 @@
// 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('digest messages', {testIsolation: true}, () => {
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: [],
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'},
],
},
],
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Login as testUser
cy.apiLogin(testUser);
});
describe('digest message >', () => {
let testRun;
before(() => {
const runName = 'Playbook Run (' + Date.now() + ')';
// # Start a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: runName,
ownerUserId: testUser.id,
}).then((run) => {
testRun = run;
// # Set a timer that will expire.
cy.apiUpdateStatus({
playbookRunId: run.id,
message: 'no message 1',
reminder: 1,
});
cy.apiChangeChecklistItemAssignee(run.id, 0, 0, testUser.id);
});
});
it('has one run overdue and links to RDP', () => {
// # Switch to playbooks DM channel
cy.visit(`/${testTeam.name}/messages/@playbooks`);
// # Wait until the channel loads enough to show the post textbox.
cy.get('#post-create').should('exist');
// # Run a slash command to show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
cy.getLastPost().within(() => {
// # assert two blocks: inprogress+overdue
cy.get('ul').should('have.length', 3);
// * CLick the first link - overdue status
cy.get('ul a').eq(0).click();
});
// # assert url is RDP
cy.url().should('contain', '/playbooks/runs/' + testRun.id + '?from=digest_overduestatus');
// # assert telemetry tracks correctly the origin
cy.expectTelemetryToContain([
{
name: 'run_details',
type: 'page',
properties: {
from: 'digest_overduestatus',
},
},
]);
});
it('has one run in progress and links to RDP', () => {
// # Switch to playbooks DM channel
cy.visit(`/${testTeam.name}/messages/@playbooks`);
// # Wait until the channel loads enough to show the post textbox.
cy.get('#post-create').should('exist');
// # Run a slash command to show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
cy.getLastPost().within(() => {
// # assert two blocks: inprogress+overdue
cy.get('ul').should('have.length', 3);
// * CLick the second link - inprogress
cy.get('ul a').eq(1).click();
});
// # assert url is RDP
cy.url().should('contain', '/playbooks/runs/' + testRun.id + '?from=digest_runsinprogress');
// # assert telemetry tracks correctly the origin
cy.expectTelemetryToContain([
{
name: 'run_details',
type: 'page',
properties: {
from: 'digest_runsinprogress',
},
},
]);
});
it('has one run with one assigned task and links to RDP', () => {
// # Switch to playbooks DM channel
cy.visit(`/${testTeam.name}/messages/@playbooks`);
// # Wait until the channel loads enough to show the post textbox.
cy.get('#post-create').should('exist');
// # Run a slash command to show the to-do list.
cy.uiPostMessageQuickly('/playbook todo');
cy.getLastPost().within(() => {
// # assert two blocks: inprogress+overdue
cy.get('ul').should('have.length', 3);
// * CLick link - assigned task
cy.get('p a').click();
});
// # assert url is RDP
cy.url().should('contain', '/playbooks/runs/' + testRun.id + '?from=digest_assignedtask');
// # assert telemetry tracks correctly the origin
cy.expectTelemetryToContain([
{
name: 'run_details',
type: 'page',
properties: {
from: 'digest_assignedtask',
},
},
]);
});
});
});

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

@@ -1,394 +0,0 @@
// 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} from '../../fixtures/timeouts';
import {stubClipboard} from '../../utils';
describe('lhs', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPublicPlaybook;
let testPrivatePlaybook;
let playbookRun;
let testViewerUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
// # Create a private playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Private Playbook',
memberIDs: [],
public: false,
}).then((playbook) => {
testPrivatePlaybook = playbook;
});
});
});
beforeEach(() => {
// # Intercepts telemetry
cy.interceptTelemetry();
});
const getRunDropdownItemByText = (groupName, runName, itemName) => {
// # Click on run at LHS
cy.findByTestId(groupName).findByTestId(runName).click();
// # Click dot menu
cy.findByTestId(groupName).
findByTestId(runName).
findByTestId('menuButton').
click({force: true});
cy.findByTestId('dropdownmenu').should('be.visible');
return cy.findByTestId('dropdownmenu').findByText(itemName).should('be.visible');
};
describe('navigate', () => {
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name(' + Date.now() + ')',
ownerUserId: testUser.id,
}).then((run) => {
playbookRun = run;
// # Visit the playbook run
cy.visit('/playbooks/runs');
cy.findByTestId('lhs-navigation').findByText(playbookRun.name).should('be.visible');
});
cy.wait;
});
it('click run', () => {
// # Click on run at LHS
cy.findByTestId('Runs').findByTestId(playbookRun.name).click();
// * assert telemetry
cy.expectTelemetryToContain([
{
type: 'page',
name: 'run_details',
properties: {
from: 'playbooks_lhs',
role: 'participant',
playbookrun_id: playbookRun.id,
playbook_id: testPublicPlaybook.id,
},
},
]);
});
});
describe('run dot menu', () => {
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name(' + Date.now() + ')',
ownerUserId: testUser.id,
}).then((run) => {
playbookRun = run;
});
});
it('shows on click', () => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
// # Click dot menu
cy.findByTestId('Runs').
findByTestId(playbookRun.name).
findByTestId('menuButton').
click({force: true});
// * Assert context menu is opened
cy.findByTestId('dropdownmenu').should('be.visible');
});
it('can copy link', () => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
stubClipboard().as('clipboard');
// # Click on Copy link menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Copy link').click();
// * Verify clipboard content
cy.get('@clipboard').its('contents').should('contain', `/playbooks/runs/${playbookRun.id}`);
});
it('can favorite / unfavorite', () => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
// # Click on favorite menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Favorite').click();
// * Verify the run is added to favorites
cy.findByTestId('Favorite').findByTestId(playbookRun.name).should('exist');
// # Click on unfavorite menu item
getRunDropdownItemByText('Favorite', playbookRun.name, 'Unfavorite').click();
// * Verify the run is removed from favorites
cy.findByTestId('Favorite').should('not.exist');
});
it('lhs refresh on follow/unfollow', () => {
cy.apiLogin(testViewerUser);
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
// # The assertions here guard against the click() on 194
// # happening on a detached element.
cy.assertRunDetailsPageRenderComplete(testUser.username);
cy.findByTestId('runinfo-following').should('be.visible').within(() => {
// # Verify follower icon
cy.findAllByTestId('profile-option', {exact: false}).should('have.length', 1);
cy.findByText('Follow').should('be.visible').click();
// # Verify icons update
cy.findAllByTestId('profile-option', {exact: false}).should('have.length', 2);
});
// * Verify that the run was added to the lhs
cy.findByTestId('lhs-navigation').findByText(playbookRun.name).should('exist');
// # Click on unfollow menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Unfollow').click();
// * Verify that the run is removed lhs
cy.findByTestId('Runs').findByTestId(playbookRun.name).should('not.exist');
// # assert telemetry data
cy.expectTelemetryToContain([
{
type: 'track',
name: 'playbookrun_follow',
properties: {
from: 'run_details',
playbookrun_id: playbookRun.id,
},
},
{
type: 'track',
name: 'playbookrun_unfollow',
properties: {
from: 'playbooks_lhs',
playbookrun_id: playbookRun.id,
},
},
]);
});
it('leave run', () => {
// # Add viewer user to the channel
cy.apiAddUsersToRun(playbookRun.id, [testViewerUser.id]);
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
// # Click on leave menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Leave and unfollow run').click();
// * Verify that owner can't leave.
cy.get('#confirmModal').should('not.exist');
// # Change the owner to testViewerUser
cy.findByTestId('runinfo-owner').findByTestId('assignee-profile-selector').click();
cy.get('.playbook-react-select').findByText('@' + testViewerUser.username).click();
// # Wait for owner to change
cy.wait(HALF_SEC);
// # Click on leave menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Leave and unfollow run').click();
// * Click leave confirmation
cy.get('#confirmModalButton').click();
// # assert telemetry data
cy.expectTelemetryToContain([
{
type: 'track',
name: 'playbookrun_leave',
properties: {
from: 'playbooks_lhs',
playbookrun_id: playbookRun.id,
},
},
]);
});
});
describe('leave run - no permanent access', () => {
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPrivatePlaybook.id,
playbookRunName: 'the run name(' + Date.now() + ')',
ownerUserId: testUser.id,
}).then((run) => {
playbookRun = run;
cy.apiAddUsersToRun(playbookRun.id, [testViewerUser.id]);
cy.apiLogin(testViewerUser).then(() => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
});
it('leave run, when on rdp of the same run', () => {
// # Click on leave menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Leave and unfollow run').click();
// # confirm modal
cy.get('#confirmModal').should('be.visible').within(() => {
cy.get('#confirmModalButton').click();
});
// * Verify that user was redirected to the run list page
cy.url().should('include', 'playbooks/runs?sort=');
});
it('leave run, when not on rdp of the same run', () => {
// # Visit playbooks list page
cy.visit('/playbooks/playbooks');
// # Click on leave menu item
getRunDropdownItemByText('Runs', playbookRun.name, 'Leave and unfollow run').click();
// # confirm modal
cy.get('#confirmModal').should('be.visible').within(() => {
cy.get('#confirmModalButton').click();
});
// * Verify that user was not redirected to the run list page
cy.url().should('not.include', 'playbooks/runs?sort=');
});
});
describe('playbook dot menu', () => {
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'the run name(' + Date.now() + ')',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
// # Visit the playbooks page
cy.visit('/playbooks/playbooks');
});
});
it('shows on click', () => {
// # Click dot menu
cy.findByTestId('Playbooks').
findByTestId(testPublicPlaybook.title).
findByTestId('menuButton').
click({force: true});
// * Assert context menu is opened
cy.findByTestId('dropdownmenu').should('be.visible');
});
it('can copy link', () => {
stubClipboard().as('clipboard');
// # Click on Copy link menu item
getRunDropdownItemByText('Playbooks', testPublicPlaybook.title, 'Copy link').click();
// * Verify clipboard content
cy.get('@clipboard').
its('contents').
should('contain', `/playbooks/playbooks/${testPublicPlaybook.id}`);
});
it('can favorite / unfavorite', () => {
// # Click on favorite menu item
getRunDropdownItemByText('Playbooks', testPublicPlaybook.title, 'Favorite').click();
// * Verify the playbook is added to favorites
cy.findByTestId('Favorite').findByTestId(testPublicPlaybook.title).should('exist');
// # Click on unfavorite menu item
getRunDropdownItemByText('Favorite', testPublicPlaybook.title, 'Unfavorite').click();
// * Verify the playbook is removed from favorites
cy.findByTestId('Playbooks').findByTestId(testPublicPlaybook.title).should('exist');
});
it('can leave', () => {
stubClipboard().as('clipboard');
// # Click on Leave menu item
getRunDropdownItemByText('Playbooks', testPublicPlaybook.title, 'Leave').click();
// * Verify the playbook is removed from the list
cy.findByTestId('Playbooks').findByTestId(testPublicPlaybook.title).should('not.exist');
});
});
});

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

@@ -1,72 +0,0 @@
// 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('navigation', {testIsolation: true}, () => {
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as user-1
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
}).then((playbook) => {
cy.apiRunPlaybook({
teamId: team.id,
playbookId: playbook.id,
playbookRunName: 'Playbook Run',
ownerUserId: user.id,
});
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Navigate to the application
cy.visit(`/${testTeam.name}/`);
});
it('switches to playbooks list view via sidebar view all button', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// * Verify that playbooks are shown
cy.findByTestId('titlePlaybook').should('exist').contains('Playbooks');
});
it('switches to playbook runs list view via sidebar view all button', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Switch to playbook runs
cy.findByTestId('playbookRunsLHSButton').click();
// * Verify that playbook runs are shown
cy.findByTestId('titlePlaybookRun').should('exist').contains('Runs');
});
});

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

@@ -1,114 +0,0 @@
// 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('playbooks > edit', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create a second test user in this team
cy.apiCreateUser().then((payload) => {
testUser2 = payload.user;
cy.apiAddUserToTeam(testTeam.id, payload.user.id);
});
// # Login as testUser
cy.apiLogin(testUser);
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
describe('rdp information refresh', () => {
let testPlaybook;
beforeEach(() => {
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
userId: testUser.id,
public: true,
}).then((playbook) => {
testPlaybook = playbook;
// Navigate to the playbook page
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
});
});
it('add / remove a member', () => {
// # Open playbook access modal
cy.findByTestId('playbook-members').click();
// # Add a new member
cy.findByTestId('add-people-input').type(testUser2.username);
cy.wait(500);
cy.findByTestId('profile-option-' + testUser2.username).click({force: true});
// * Verify that user was added
cy.findByTestId('members-list').findByText(testUser2.username).should('exist');
// # Close playbook access modal
cy.get('.close > [aria-hidden="true"]').click();
// * Verify members number
cy.findByTestId('playbook-members').findByText('2').should('exist');
// # Open playbook access modal
cy.findByTestId('playbook-members').click();
// # Open dropdown and remove user
cy.findByText('Playbook Member').click();
cy.findByTestId('dropdownmenu').findByText('Remove').click();
// * Verify that user was removed
cy.findByTestId('members-list').findByText(testUser2.username).should('not.exist');
// # Close playbook access modal
cy.get('.close > [aria-hidden="true"]').click();
// * Verify members number
cy.findByTestId('playbook-members').findByText('1').should('exist');
});
it('change to private', () => {
// # Open playbook access modal
cy.findByTestId('playbook-members').click();
// # Click on convert to private
cy.findByText('Convert to private playbook').click();
// * Check that confirm modal is open
cy.get('#confirmModal').should('be.visible');
// # Confirm convert to private
cy.get('#confirmModal').get('#confirmModalButton').click();
// * Verify that playbook is private
cy.findByText('Convert to private playbook').should('not.exist');
// # Close playbook access modal
cy.get('.close > [aria-hidden="true"]').click();
// * Verify lock icon is visible
cy.findByTestId('playbook-editor-header').get('.icon-lock-outline').should('be.visible');
});
});
});

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

@@ -1,201 +0,0 @@
// 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('playbooks > creation button', {testIsolation: true}, () => {
let testSysadmin;
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateCustomAdmin().then(({sysadmin}) => {
testSysadmin = sysadmin;
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
// # Creating this playbook ensures the list view
// # specifically is shown in the backstage content section.
// # Without it there is a brief flicker from the list view
// # to the no content view, which causes some flake
// # on clicking the 'Create playbook' button
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
});
});
});
beforeEach(() => {
// # Login as user-1
cy.apiLogin(testUser);
// # Size the viewport to show playbooks without weird scrolling issues
cy.viewport('macbook-13');
});
it('opens playbook creation page with New Playbook button', () => {
const playbookName = 'Untitled Playbook';
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click 'New Playbook' button
cy.findByTestId('titlePlaybook').findByText('Create playbook').click();
cy.get('#playbooks_create').findByText('Create playbook').click();
// * Verify playbook outline page opened
verifyPlaybookOutlineOpened(playbookName);
// * Verify playbook was added to the LHS
cy.findByTestId('lhs-navigation').findByText(playbookName).should('exist');
});
it('auto creates a playbook with "Blank" template option', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click 'Blank'
cy.findByText('Blank').click();
const playbookName = `@${testUser.username}'s Blank`;
// * Verify playbook outline opened
verifyPlaybookOutlineOpened(playbookName);
// * Verify playbook was added to the LHS
cy.findByTestId('lhs-navigation').findByText(playbookName).should('exist');
});
it('opens Service Outage Incident page from its template option (multiple teams)', () => {
cy.apiCreateTeam('second-team', 'Second Team').then(() => {
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click 'Incident Resolution'
cy.findByText('Incident Resolution').click();
const playbookName = `@${testUser.username}'s Incident Resolution`;
// * Verify playbook outline opened
verifyPlaybookOutlineOpened(playbookName);
// * Verify the playbook was added to the lhs of current team
cy.findByTestId('lhs-navigation').findByText(playbookName).should('exist');
});
});
let restrictedTestTeam;
let restrictedTestUser;
describe('user is lacking permissions to create playbooks', () => {
before(() => {
cy.apiLogin(testSysadmin);
cy.apiCreateUser().then(({user: createdUser}) => {
restrictedTestUser = createdUser;
});
cy.apiCreateTeam('restricted-team', 'Restricted Team').then(({team: createdTeam}) => {
restrictedTestTeam = createdTeam;
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);
});
it('create playbook entry in LHS dropdown should not exist', () => {
// # Open the product
cy.visit('/playbooks');
// # Open menu dropdown
cy.findByTestId('create-playbook-dropdown-toggle').click();
cy.get('#CreatePlaybookDropdown').within(() => {
// * Verify create playbook entry is missing
cy.findByText('Create New Playbook').should('not.exist');
});
});
it('permission notice should be shown if no playbooks exist', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// * Verify notice about missing permissions and no playbooks is shown
cy.findByText('There are no playbooks to view. You don\'t have permission to create playbooks in this workspace.').should('exist');
});
it('create playbook button should not exist if playbooks exist', () => {
// # Create a playbook for the team
cy.apiLogin(testSysadmin).then(() => {
cy.apiCreatePlaybook({
teamId: restrictedTestTeam.id,
title: 'Playbook',
memberIDs: [],
});
});
// # Login as user with restricted permissions
cy.apiLogin(restrictedTestUser);
// # Open the product
cy.visit('/playbooks');
// # Switch to playbooks
cy.findByTestId('playbooksLHSButton').click();
// * Verify create playbook button is missing
cy.findByTestId('titlePlaybook').findByText('Create playbook').should('not.exist');
});
});
});
function verifyPlaybookOutlineOpened(playbookName) {
// * Verify the page url contains 'playbooks/playbooks/new'
cy.url().should('contain', '/outline');
// * Verify the playbook name matches the one provided
cy.findByTestId('playbook-editor-title').within(() => {
cy.findByText(playbookName).should('be.visible');
});
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@@ -1,185 +0,0 @@
// 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('playbooks > edit', {testIsolation: true}, () => {
let testUser;
before(() => {
cy.apiInitSetup().then(({user}) => {
testUser = user;
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
describe('checklists', () => {
describe('pre-assignee', () => {
it('user gets pre-assigned, added to invite user list, and invitations become enabled', () => {
// # Open Playbooks
cy.visit('/playbooks/playbooks');
// # Start a blank playbook
cy.findByText('Blank').click();
cy.findByText('Outline').click();
cy.get('#actions').within(() => {
cy.get('#invite-users').within(() => {
// * Verify invitations are disabled and no invited user exists
cy.get('label input').should('not.be.checked');
cy.get('.invite-users-selector__control').
after('content').
should('eq', '');
});
});
// # Pre-assign the user
cy.get('#checklists').within(() => {
// # Trigger assignee select menu
cy.findByText('Untitled task').trigger('mouseover');
cy.findByTestId('hover-menu-edit-button').click();
cy.findByText('Assignee...').click();
// * Verify that the assignee input is focused now
cy.focused().
should('have.attr', 'type', 'text').
should('have.attr', 'id');
// * Verify that the root of the assignee select menu exists
cy.focused().parents('.playbook-react-select').
should('exist').
within(() => {
// # Select the test user
cy.findByText('@' + testUser.username).click();
});
});
cy.reload();
cy.get('#checklists').within(() => {
// # Trigger assignee select menu
cy.findByText('Untitled task').trigger('mouseover');
cy.findByTestId('hover-menu-edit-button').click();
cy.findByText('@' + testUser.username).click();
// * Verify that the assignee input is focused now
cy.focused().
should('have.attr', 'type', 'text').
should('have.attr', 'id');
// * Verify that the root of the assignee select menu exists
cy.focused().
parents('.playbook-react-select').
should('exist');
});
cy.get('#actions').within(() => {
cy.get('#invite-users').within(() => {
// * Verify invitations are enabled and a single user is invited
cy.get('label input').should('be.checked');
cy.get('.invite-users-selector__control').
after('content').
should('eq', '1 SELECTED');
});
});
});
});
describe('slash command', () => {
it('autocompletes after clicking Command...', () => {
// # Open Playbooks
cy.visit('/playbooks/playbooks');
// # Start a blank playbook
cy.findByText('Blank').click();
cy.findByText('Outline').click();
cy.get('#checklists').within(() => {
// # Open the slash command input on a step
cy.findByText('Untitled task').trigger('mouseover');
cy.findByTestId('hover-menu-edit-button').click();
cy.findByText('Command...').click();
// * Verify the slash command input field now has focus
// * and starts with a slash prefix.
cy.focused().
should('have.attr', 'placeholder', 'Slash Command').
should('have.value', '/');
});
// * Verify the autocomplete prompt is open
cy.get('#suggestionList').should('exist');
});
it('resets when saving with an empty slash command', () => {
// # Open Playbooks
cy.visit('/playbooks/playbooks');
// # Start a blank playbook
cy.findByText('Blank').click();
cy.findByText('Outline').click();
cy.get('#checklists').within(() => {
// # Open the slash command input on a step
cy.findByText('Untitled task').trigger('mouseover');
cy.findByTestId('hover-menu-edit-button').click();
cy.findByText('Command...').click();
});
cy.get('#floating-ui-root').within(() => {
// * Verify the slash command input field now has focus
// * and starts with a slash prefix.
cy.findByPlaceholderText('Slash Command').should('have.focus');
cy.findByPlaceholderText('Slash Command').should('have.value', '/');
cy.findByPlaceholderText('Slash Command').type('{backspace}');
// # Click the save button
cy.findByText('Save').click();
});
// * Verify no slash command was saved
cy.findByText('Command...').should('be.visible');
});
it('removes the input prompt when blurring with an invalid slash command', () => {
// # Open Playbooks
cy.visit('/playbooks/playbooks');
// # Start a blank playbook
cy.findByText('Blank').click();
cy.findByText('Outline').click();
cy.get('#checklists').within(() => {
// # Open the slash command input on a step
cy.findByText('Untitled task').trigger('mouseover');
cy.findByTestId('hover-menu-edit-button').click();
cy.findByText('Command...').click();
});
cy.get('#floating-ui-root').within(() => {
// * Verify the slash command input field now has focus
// * and starts with a slash prefix.
cy.findByPlaceholderText('Slash Command').should('have.focus');
cy.findByPlaceholderText('Slash Command').should('have.value', '/');
// # Click the save button
cy.findByText('Save').click();
});
// * Verify no slash command was saved
cy.findByText('Command...').should('be.visible');
});
});
});
});

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

@@ -1,99 +0,0 @@
// 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('playbooks > edit', {testIsolation: true}, () => {
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
describe('Edit playbook name', () => {
it('can be updated', () => {
// # Open Playbooks
cy.visit('/playbooks/playbooks');
// # Start a blank playbook
cy.findByText('Blank').click();
// # Open the title dropdown and Rename
cy.findByTestId('playbook-editor-title').click();
cy.findByText('Rename').click();
// # Change the name and save
cy.findByTestId('rendered-editable-text').type('{selectAll}{del}renamed playbook');
cy.findByRole('button', {name: /save/i}).click();
cy.reload();
// * Verify the modified name persists
cy.findByRole('button', {name: /renamed playbook/i}).should('exist');
});
});
describe('Edit playbook description', () => {
it('can be updated', () => {
// # Open Playbooks
cy.visit('/playbooks/playbooks');
// # Start a blank playbook
cy.findByText('Blank').click();
cy.findByText(/customize this playbook's description/i).dblclick();
cy.focused().type('{selectAll}{del}some new description');
cy.findByRole('button', {name: /save/i}).click();
cy.reload();
cy.findByText('some new description').should('exist');
});
});
describe('Duplicate', () => {
let testPlaybook;
beforeEach(() => {
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
});
it('can be duplicated', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Open the title dropdown and Duplicate
cy.findByTestId('playbook-editor-title').click();
cy.findByText('Duplicate').click();
// * Verify that playbook got duplicated
cy.findByTestId('playbook-editor-header').within(() => {
cy.findByText('Copy of ' + testPlaybook.title).should('exist');
});
// * Verify that the duplicated playbook is shown in the LHS
cy.findByTestId('Playbooks').within(() => {
cy.findByText('Copy of ' + testPlaybook.title).should('be.visible');
});
});
});
});

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

@@ -1,465 +0,0 @@
// 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('playbooks > edit > task actions', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
// # Add this new user to the team
cy.apiAddUserToTeam(team.id, testUser2.id);
});
});
});
let testPlaybook;
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
checklists: [{
title: 'Test Checklist',
items: [
{title: 'Test Task'},
],
}],
memberIDs: [
testUser.id,
],
}).then((playbook) => {
testPlaybook = playbook;
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
});
});
const editTask = () => {
cy.findByTestId('checkbox-item-container').within(() => {
cy.findByText('Test Task').trigger('mouseover');
cy.findByTestId('hover-menu-edit-button').click();
});
};
it('disallows no keywords', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify no actions are configured
cy.findByText('Task Actions').should('exist');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, []);
assert.deepEqual(trigger.user_ids, []);
assert.isFalse(actions.enabled);
});
});
it('allows a single keyword', () => {
// # intercepts telemetry
cy.interceptTelemetry();
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// Enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
// # assert telemetry data
cy.expectTelemetryToContain([
{
name: 'taskactions_updated',
type: 'track',
properties: {
playbook_id: testPlaybook.id,
},
},
]);
});
it('allows multiple keywords', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add multiple keywords
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
cy.get('input').eq(0).type('keyword2{enter}', {force: true});
});
// Enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1', 'keyword2']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
});
it('allows multi-word phrases', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a phrase
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('a phrase with multiple words{enter}', {force: true});
});
// Enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['a phrase with multiple words']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
});
it('allows removing previously configured keywords', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add multiple keywords
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
cy.get('input').eq(0).type('keyword2{enter}', {force: true});
});
// Enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Re-open the dialog
cy.findByText('1 action').click();
// Remove one trigger keyword
cy.get('.modal-body').within(() => {
cy.findByText('keyword1').next().click();
});
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword2']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
});
it('disables when all keywords removed', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add multiple keywords
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
cy.get('input').eq(0).type('keyword2{enter}', {force: true});
});
// Enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Re-open the dialog
cy.findByText('1 action').click();
// Remove all trigger keywords
cy.get('.modal-body').within(() => {
cy.findByText('keyword1').next().click();
cy.findByText('keyword2').next().click();
});
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('Task Actions');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, []);
assert.deepEqual(trigger.user_ids, []);
assert.isFalse(actions.enabled);
});
});
it('disallows a user without keywords', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a user
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify no actions are configured
cy.findByText('Task Actions').should('exist');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, []);
assert.deepEqual(trigger.user_ids, [testUser.id]);
assert.isFalse(actions.enabled);
});
});
it('allows a single user', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// Add a user
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions and user
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, [testUser.id]);
assert.isTrue(actions.enabled);
});
});
it('allows configuring multiple users', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// Add two users
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
cy.get('input').eq(1).
type('@' + testUser2.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions and user
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, [testUser.id, testUser2.id]);
assert.isTrue(actions.enabled);
});
});
it('rejects unknown user', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// Type an unknown user
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@unknown', {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// Click away
cy.get('.modal-body').click();
// Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions and user
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
});
it('allows removing previously configured users', () => {
// Open the task actions modal
editTask();
cy.findByText('Task Actions').click();
// Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// Add two users
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
cy.get('input').eq(1).
type('@' + testUser2.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Re-open the dialog
cy.findByText('1 action').click();
// Remove one user keyword
cy.get('.modal-body').within(() => {
cy.findByText(testUser.username).parent().parent().next().click();
});
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
const trigger = JSON.parse(playbook.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbook.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, [testUser2.id]);
assert.isTrue(actions.enabled);
});
});
});

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

@@ -1,568 +0,0 @@
// 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('playbooks > edit_metrics', {testIsolation: true}, () => {
let testTeam;
let testUser;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
describe('actions', () => {
let testPlaybook;
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
// # Set a bigger viewport so the action don't scroll out of view
cy.viewport('macbook-16');
cy.intercept('PUT', '/plugins/playbooks/api/v0/playbooks/**').as('addMetric');
});
describe('adding and editing metrics', () => {
it('can add 4, but not 5 metrics; can save and re-edit with metrics saved', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
// # Switch to Outline tab and focus retro section
cy.findByText('Outline').click();
cy.get('#retrospective').scrollIntoView();
// # Add and verify metric
addMetric('Duration', 'test duration', '0:0:1', 'test description');
verifyViewMetric(0, 'test duration', '1 minute per run', 'test description');
// # Add and verify metric
addMetric('Cost', 'test dollars', '2', 'test description 2');
verifyViewMetric(1, 'test dollars', '2 per run', 'test description 2');
// # Add and verify metric
addMetric('Integer', 'test integer', '4', 'test descr 3');
verifyViewMetric(2, 'test integer', '4 per run', 'test descr 3');
// # Add and verify metric
addMetric('Duration', 'test duration 2', '0:0:2', 'test description 4');
verifyViewMetric(3, 'test duration 2', '2 minutes per run', 'test description 4');
// * Verify Add Metric button is inactive
cy.findByRole('button', {name: 'Add Metric'}).should('be.disabled');
// * Verify we have four valid metrics and are editing none.
verifyViewsAndEdits(4, 0);
// Refresh the page
cy.reload();
// * Verify we saved the metrics
verifyViewMetric(0, 'test duration', '1 minute per run', 'test description');
verifyViewMetric(1, 'test dollars', '2 per run', 'test description 2');
verifyViewMetric(2, 'test integer', '4 per run', 'test descr 3');
verifyViewMetric(3, 'test duration 2', '2 minutes per run', 'test description 4');
// # Edit all 4 metrics and repeat the test
cy.findAllByTestId('edit-metric').eq(0).click();
cy.get('input[type=text]').eq(2).clear().type('12:8:97');
saveMetric();
cy.findAllByTestId('edit-metric').eq(1).click();
cy.get('textarea').eq(0).clear().type('a new description');
saveMetric();
cy.findAllByTestId('edit-metric').eq(2).click();
cy.get('input[type=text]').eq(2).clear().type('7777777');
saveMetric();
cy.findAllByTestId('edit-metric').eq(3).click();
cy.get('input[type=text]').eq(1).clear().type('test duration 2!!!');
saveMetric();
// # Refresh the page
cy.reload();
// * Verify we saved the metrics
verifyViewMetric(0, 'test duration', '12 days, 9 hours, 37 minutes per run', 'test description');
verifyViewMetric(1, 'test dollars', '2 per run', 'a new description');
verifyViewMetric(2, 'test integer', '7777777 per run', 'test descr 3');
verifyViewMetric(3, 'test duration 2!!!', '2 minutes per run', 'test description 4');
// # Now test: verifies when clicking "Add", for duration type
// # (using the previous state)
// # Edit the first metric
cy.findAllByTestId('edit-metric').eq(0).click();
// * Metrics need a title
cy.get('input[type=text]').eq(1).clear();
saveMetric();
cy.getStyledComponent('ErrorText').contains('Please add a title for your metric.');
// * Metrics need a unique title
cy.get('input[type=text]').eq(1).type('test dollars');
saveMetric();
cy.getStyledComponent('ErrorText').
contains('A metric with the same name already exists. Please add a unique name for each metric.');
// * A duration target needs to be in the correct format (no letters)
cy.get('input[type=text]').eq(1).clear().wait(100).type('test duration again');
cy.get('input[type=text]').eq(2).clear().type('a');
saveMetric();
cy.getStyledComponent('ErrorText').
contains('Please enter a duration in the format: dd:hh:mm (e.g., 12:00:00), or leave the target blank.');
// * A duration target needs to be in the correct format (mm:dd:ss)
cy.get('input[type=text]').eq(2).clear().type('0:123:0');
saveMetric();
cy.getStyledComponent('ErrorText').
contains('Please enter a duration in the format: dd:hh:mm (e.g., 12:00:00), or leave the target blank.');
// # A duration can have 1 or 2 numbers in each position
cy.get('input[type=text]').eq(2).clear().type('2:12:1');
saveMetric();
verifyViewMetric(0, 'test duration again', '2 days, 12 hours, 1 minute per run', 'test description');
// * Verify we have four valid metrics and are editing none.
verifyViewsAndEdits(4, 0);
// # Now test: on clicking edit, closes & saves current editing metric, and switches
// # (using the previous state)
// # Edit the second metric
cy.findAllByTestId('edit-metric').eq(1).click();
// * Verify editing correct metric, and only this metric
cy.getStyledComponent('EditContainer').should('have.length', 1).within(() => {
cy.get('input[type=text]').eq(0).should('have.value', 'test dollars');
});
cy.getStyledComponent('ViewContainer').should('have.length', 3);
// # Switch to editing third metric (second is in edit mode, so this is the third:)
cy.findAllByTestId('edit-metric').eq(1).click();
// * Verify editing correct metric, and only this metric
cy.getStyledComponent('EditContainer').should('have.length', 1).within(() => {
cy.get('input[type=text]').eq(0).should('have.value', 'test integer');
});
cy.getStyledComponent('ViewContainer').should('have.length', 3);
// # Edit third metric's title, switch to another metric
cy.getStyledComponent('EditContainer').should('have.length', 1).within(() => {
cy.get('input[type=text]').eq(0).clear().type('test integer222');
});
cy.findAllByTestId('edit-metric').eq(0).click();
// * Verify the title on the third metric (the second in view mode) was saved on switching
verifyViewMetric(1, 'test integer222', '7777777 per run', 'test descr 3');
// * Verify we have three valid metrics and are editing one.
verifyViewsAndEdits(3, 1);
});
});
describe('adding and editing metrics (new playbook)', () => {
it('verifies when clicking "Add Metric", for Currency type, and switches to new edit', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
// # Switch to Outline tab and focus retro section
cy.findByText('Outline').click();
cy.get('#retrospective').scrollIntoView();
// # Add and verify 1st metric
addMetric('Integer', 'test integer!', '12314123', 'test description');
verifyViewMetric(0, 'test integer!', '12314123 per run', 'test description');
// # Add metric
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Cost').click();
});
// # Don't fill in the metric's details
cy.get('input[type=text]').eq(1).clear();
// * Metrics need a title
cy.get('input[type=text]').eq(1).clear();
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Integer').click();
});
cy.getStyledComponent('ErrorText').contains('Please add a title for your metric.');
// * Metrics need a unique title
cy.get('input[type=text]').eq(1).type('test integer!');
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Integer').click();
});
cy.getStyledComponent('ErrorText').
contains('A metric with the same name already exists. Please add a unique name for each metric.');
// # Fill in title
cy.get('input[type=text]').eq(1).clear().type('test currency!');
// * A Currency target cannot be text
cy.get('input[type=text]').eq(2).clear().type('z');
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Integer').click();
});
cy.getStyledComponent('ErrorText').contains('Please enter a number, or leave the target blank.');
// * A Currency target /can/ be blank, so can the description, and Add next Integer metric
cy.get('input[type=text]').eq(2).clear();
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Integer').click();
});
cy.getStyledComponent('EditContainer').should('be.visible');
// * Verify metric was added without target or description.
verifyViewMetric(1, 'test currency!', '', '');
// * Verify we have two valid metrics and are editing next one.
verifyViewsAndEdits(2, 1);
// # Now test: verifies when clicking edit button, for Currency type, and switches to next edit
// # (using the previous state)
// # Don't fill in the metric's details
cy.get('input[type=text]').eq(1).clear();
// * Metrics need a title
cy.get('input[type=text]').eq(1).clear();
cy.findAllByTestId('edit-metric').eq(0).click();
cy.getStyledComponent('ErrorText').contains('Please add a title for your metric.');
// * Metrics need a unique title
cy.get('input[type=text]').eq(1).type('test currency!');
cy.findAllByTestId('edit-metric').eq(0).click();
cy.getStyledComponent('ErrorText').
contains('A metric with the same name already exists. Please add a unique name for each metric.');
// # Fill in title
cy.get('input[type=text]').eq(1).clear().type('test integer #2!!');
// * An Integer target cannot be text
cy.get('input[type=text]').eq(2).clear().type('arsoton');
cy.findAllByTestId('edit-metric').eq(0).click();
cy.getStyledComponent('ErrorText').contains('Please enter a number, or leave the target blank.');
// * An Integer target /can/ be blank, so can the description, and edit first metric
cy.get('input[type=text]').eq(2).clear();
cy.findAllByTestId('edit-metric').eq(0).click();
// * Verify we're editing the first metric, and only this metric
cy.getStyledComponent('EditContainer').should('have.length', 1).within(() => {
cy.get('input[type=text]').eq(0).should('have.value', 'test integer!');
});
cy.getStyledComponent('ViewContainer').should('have.length', 2);
// # Stop editing
saveMetric();
// * Verify metric was added without target or description.
verifyViewMetric(2, 'test integer #2!!', '', '');
// * Verify we have three valid metrics and are editing none.
verifyViewsAndEdits(3, 0);
});
});
describe('delete metric', () => {
it('verifies when clicking delete button; saved metrics have different confirmation text; deleted metrics are deleted', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
// # Switch to Outline tab and focus retro section
cy.findByText('Outline').click();
cy.get('#retrospective').scrollIntoView();
// # Add and verify 1st metric
addMetric('Integer', 'test integer!', '12314123', 'test description');
verifyViewMetric(0, 'test integer!', '12314123 per run', 'test description');
// # Add metric
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Cost').click();
});
// # Don't fill in the metric's details
cy.get('input[type=text]').eq(1).clear();
// * Metrics need a title
cy.get('input[type=text]').eq(1).clear();
cy.findAllByTestId('delete-metric').eq(0).click();
cy.getStyledComponent('ErrorText').contains('Please add a title for your metric.');
// * Metrics need a unique title
cy.get('input[type=text]').eq(1).type('test integer!');
cy.findAllByTestId('delete-metric').eq(0).click();
cy.getStyledComponent('ErrorText').
contains('A metric with the same name already exists. Please add a unique name for each metric.');
// # Fill in title
cy.get('input[type=text]').eq(1).clear().type('test currency!');
// * A Currency target cannot be text
cy.get('input[type=text]').eq(2).clear().type('z');
cy.findAllByTestId('delete-metric').eq(0).click();
cy.getStyledComponent('ErrorText').contains('Please enter a number, or leave the target blank.');
// # Remove error text and type another invalid entry
cy.get('input[type=text]').eq(2).clear().type('invalid');
// * Verify that we're allowed to delete a metric we are currently editing (even if it's invalid)
cy.findAllByTestId('delete-metric').eq(1).click();
cy.get('#confirm-modal-light').should('be.visible').contains('Are you sure you want to delete?');
// # Should see the confirmation /without/ extra text because we haven't saved this metric yet
cy.get('#confirm-modal-light').
should('not.contain.text', 'You will still be able to access historical data for this metric.');
// # Dismiss
cy.findByRole('button', {name: 'Cancel'}).click();
// * A Currency target /can/ be blank, so can the description, try to delete first metric
cy.get('input[type=text]').eq(2).clear();
cy.findAllByTestId('delete-metric').eq(0).click();
cy.get('#confirm-modal-light').
should('contain.text', 'If you delete this metric, the values for it will not be collected for any future runs.');
// # Delete first metric
cy.findByRole('button', {name: 'Delete metric'}).click();
// * Verify metric
verifyViewsAndEdits(1, 0);
verifyViewMetric(0, 'test currency!', '', '');
// # Make sure we can still edit and add a metric after deleting one (testing that the metrics
// component's state isn't broken)
addMetric('Integer', 'test integer 2!', '123', 'test description');
verifyViewMetric(1, 'test integer 2!', '123 per run', 'test description');
cy.findAllByTestId('delete-metric').eq(1).click();
cy.findByRole('button', {name: 'Delete metric'}).click();
cy.findAllByTestId('edit-metric').eq(0).click();
cy.get('input[type=text]').eq(1).clear().type('test currency 2!');
saveMetric();
verifyViewsAndEdits(1, 0);
verifyViewMetric(0, 'test currency 2!', '', '');
// # Make sure we can add a metric and then delete it, then can keep editing
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Cost').click();
});
cy.findAllByTestId('delete-metric').eq(1).click();
cy.findByRole('button', {name: 'Delete metric'}).click();
cy.findAllByTestId('edit-metric').eq(0).click();
cy.get('input[type=text]').eq(1).clear().type('test currency 3!');
saveMetric();
verifyViewsAndEdits(1, 0);
verifyViewMetric(0, 'test currency 3!', '', '');
// # Make sure we can add a metric and then delete it, then can keep adding
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Cost').click();
});
cy.findAllByTestId('delete-metric').eq(1).click();
cy.findByRole('button', {name: 'Delete metric'}).click();
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText('Cost').click();
});
cy.findAllByTestId('delete-metric').eq(1).click();
cy.findByRole('button', {name: 'Delete metric'}).click();
verifyViewsAndEdits(1, 0);
verifyViewMetric(0, 'test currency 3!', '', '');
// # Refresh and verify one is saved
cy.reload();
verifyViewsAndEdits(1, 0);
verifyViewMetric(0, 'test currency 3!', '', '');
// # Delete metric
cy.findAllByTestId('delete-metric').eq(0).click();
// # Should see the confirmation /with/ extra text
cy.get('#confirm-modal-light').
should('contain.text', 'If you delete this metric, the values for it will not be collected for any future runs. You will still be able to access historical data for this metric.');
// # Delete first metric
cy.findByRole('button', {name: 'Delete metric'}).click();
// * Verify
verifyViewsAndEdits(0, 0);
// # Refresh and verify deleted
cy.reload();
verifyViewsAndEdits(0, 0);
});
});
describe('nullable and 0-able targets', () => {
it('can add 0 targets and no (null) targets', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
// # Switch to Outline tab and focus retro section
cy.findByText('Outline').click();
cy.get('#retrospective').scrollIntoView();
// # Add and verify duration
addMetric('Duration', 'test duration', '0:0:0', 'test description');
verifyViewMetric(0, 'test duration', '0 seconds per run', 'test description');
// # Verify it shows 0:0:0, then turn it into null.
cy.findAllByTestId('edit-metric').eq(0).click();
cy.get('input[type=text]').eq(2).should('have.value', '00:00:00').
clear();
saveMetric();
// # Verify that the 'Target' section is gone
cy.getStyledComponent('ViewContainer').
getStyledComponent('DetailDiv').
should('have.length', 1);
verifyViewMetric(0, 'test duration', '', 'test description');
// * Verify it has null value when editing again.
cy.findAllByTestId('edit-metric').eq(0).click();
cy.get('input[type=text]').eq(2).should('have.value', '');
saveMetric();
// # Add and verify currency
addMetric('Cost', 'test money', '0', 'test description 2');
cy.wait('@addMetric');
verifyViewMetric(1, 'test money', '0', 'test description 2');
// # Verify it shows 0, then turn it into null.
cy.findAllByTestId('edit-metric').eq(1).click();
cy.get('input[type=text]').eq(2).should('have.value', '0').
clear();
saveMetric();
cy.getStyledComponent('ViewContainer').should('have.length', 2).eq(1).within(() => {
// # Verify that the 'Target' section is gone
cy.getStyledComponent('DetailDiv').
should('have.length', 1);
});
verifyViewMetric(1, 'test money', '', 'test description 2');
// * Verify it has null value when editing again.
cy.findAllByTestId('edit-metric').eq(1).click();
cy.get('input[type=text]').eq(2).should('have.value', '');
saveMetric();
// # Add and verify Integer
addMetric('Integer', 'test number', '0', 'test description 3');
cy.wait('@addMetric');
verifyViewMetric(2, 'test number', '0', 'test description 3');
// # Verify it shows 0, then turn it into null.
cy.findAllByTestId('edit-metric').eq(2).click();
cy.get('input[type=text]').eq(2).should('have.value', '0').
clear();
saveMetric();
cy.getStyledComponent('ViewContainer').should('have.length', 3).eq(2).within(() => {
// # Verify that the 'Target' section is gone
cy.getStyledComponent('DetailDiv').
should('have.length', 1);
});
verifyViewMetric(2, 'test number', '', 'test description 3');
// * Verify it has null value when editing again.
cy.findAllByTestId('edit-metric').eq(2).click();
cy.get('input[type=text]').eq(2).should('have.value', '');
saveMetric();
// * Verify we have three valid metrics and are editing none.
verifyViewsAndEdits(3, 0);
// # Refresh
cy.reload();
// * Verify we saved the metrics
verifyViewMetric(0, 'test duration', '', 'test description');
verifyViewMetric(1, 'test money', '', 'test description 2');
verifyViewMetric(2, 'test number', '', 'test description 3');
});
});
});
});
const addMetric = (type, title, target, description) => {
const fullType = type === 'Duration' ? 'Duration (in dd:hh:mm)' : type;
// # Add the requested metric
cy.findByRole('button', {name: 'Add Metric'}).click();
cy.findByTestId('dropdownmenu').within(() => {
cy.findByText(fullType).click();
});
// # Fill in the metric's details
cy.get('input[type=text]').eq(1).type(title).
tab().type(target).
tab().type(description);
// # Add the metric
saveMetric();
cy.wait('@addMetric');
};
const verifyViewMetric = (index, title, target, description) => {
cy.getStyledComponent('ViewContainer').should('have.length.of.at.least', index + 1).eq(index).within(() => {
cy.getStyledComponent('Title').should('have.text', title);
if (target) {
cy.getStyledComponent('DetailDiv').eq(0).contains(target);
}
if (description) {
const idx = target ? 1 : 0;
cy.getStyledComponent('DetailDiv').eq(idx).contains(description);
}
});
};
const verifyViewsAndEdits = (numViews, numEdits) => {
if (numViews === 0) {
cy.getStyledComponent('ViewContainer').should('not.exist');
} else {
cy.getStyledComponent('ViewContainer').should('have.length', numViews);
}
if (numEdits === 0) {
cy.getStyledComponent('EditContainer').should('not.exist');
} else {
cy.getStyledComponent('EditContainer').should('have.length', numEdits);
}
};
function saveMetric() {
cy.get('#retrospective-metrics').within(() => {
cy.findByRole('button', {name: 'Save'}).click();
});
}

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

@@ -1,75 +0,0 @@
// 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('playbooks > feedback', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as admin to create team and user below.
cy.apiAdminLogin();
// # Setup a team, user and playbook for each test.
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Test Playbook',
memberIDs: [],
}).then((playbook) => {
testPlaybook = playbook;
});
// # Login as the newly created testUser
cy.apiLogin(testUser);
});
});
it('playbooks shows prompt in global header', () => {
// # Visit the playbooks product
cy.visit('/playbooks');
// # Verify Give Feedback link is configured to open in a new tab.
cy.findByText('Give feedback').invoke('attr', 'target').should('eq', '_blank');
// # Verify Give Feedback link href
cy.findByText('Give feedback').invoke('attr', 'href').should('match', /playbooks-feedback/);
});
it('playbooks shows prompt in rhs header', () => {
// # 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 Give Feedback link is configured to open in a new tab.
cy.findByText('Give feedback').invoke('attr', 'target').should('eq', '_blank');
// # Verify Give Feedback link href
cy.findByText('Give feedback').invoke('attr', 'href').should('match', /playbooks-feedback/);
});
});

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

@@ -1,217 +0,0 @@
// 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('playbooks > list', {testIsolation: true}, () => {
const playbookTitle = 'The Playbook Name';
let testTeam;
let testUser;
let testUser2;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
// # Login as user-1
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: playbookTitle,
memberIDs: [],
});
// # Create an archived public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook archived',
memberIDs: [],
}).then(({id}) => cy.apiArchivePlaybook(id));
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
it('has "Playbooks" in heading', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// * Assert contents of heading.
cy.findByTestId('titlePlaybook').should('exist').contains('Playbooks');
});
it('join/leave playbook', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click on the dot menu
cy.findByTestId('menuButtonActions').click();
// # Click on leave
cy.findByText('Leave').click();
// * Verify it has disappeared from the LHS
cy.findByTestId('lhs-navigation').findByText(playbookTitle).should('not.exist');
// # Join a playbook
cy.findByTestId('join-playbook').click();
// * Verify it has appeared in LHS
cy.findByTestId('lhs-navigation').findByText(playbookTitle).should('exist');
});
it('can duplicate playbook', () => {
// # Login as testUser2
cy.apiLogin(testUser2);
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click on the dot menu
cy.findByTestId('menuButtonActions').click();
// # Click on duplicate
cy.findByText('Duplicate').click();
// * Verify that playbook got duplicated
cy.findByText('Copy of ' + playbookTitle).should('exist');
// * Verify that the current user is a member and can run the playbook.
cy.findByText('Copy of ' + playbookTitle).closest('[data-testid="playbook-item"]').within(() => {
cy.findByTestId('run-playbook').should('exist');
cy.findByTestId('join-playbook').should('not.exist');
});
// * Verify that the duplicated playbook is shown in the LHS
cy.findByTestId('Playbooks').within(() => {
cy.findByText('Copy of ' + playbookTitle).should('be.visible');
});
});
context('archived playbooks', () => {
it('does not show them by default', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// * Assert the archived playbook is not there.
cy.findAllByTestId('playbook-title').should((titles) => {
expect(titles).to.have.length(2);
});
});
it('shows them upon click on the filter', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click the With Archived button
cy.findByTestId('with-archived').click();
// * Assert the archived playbook is there.
cy.findAllByTestId('playbook-title').should((titles) => {
expect(titles).to.have.length(3);
});
});
});
describe('can import playbook', () => {
let validPlaybookExport;
let invalidTypePlaybookExport;
const bufferToCypressFile = (fileName, fileData, fileType) => ({
fileName,
contents: fileData,
mimeType: fileType,
});
before(() => {
// # Load fixtures and convert to File
cy.fixture('playbook-export.json', null).then((buffer) => {
validPlaybookExport = bufferToCypressFile('export.json', buffer, 'application/json');
});
cy.fixture('mp3-audio-file.mp3', null).then((buffer) => {
invalidTypePlaybookExport = bufferToCypressFile('audio.mp3', buffer, 'audio/mpeg');
});
});
it('triggered by drag and drop', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Drop loaded fixture onto playbook list
cy.findByTestId('playbook-list-scroll-container').selectFile(validPlaybookExport, {
action: 'drag-drop',
force: true,
});
// * Verify that a new playbook was created.
cy.findByTestId('playbook-editor-title').should('contain', 'Example Playbook');
});
it('triggered by using button/input', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
cy.findByTestId('titlePlaybook').within(() => {
// # Select loaded fixture for upload
cy.findByTestId('playbook-import-input').selectFile(validPlaybookExport, {force: true});
});
// * Verify that a new playbook was created.
cy.findByTestId('playbook-editor-title').should('contain', 'Example Playbook');
});
it('fails to import invalid file type', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
cy.findByTestId('titlePlaybook').within(() => {
// # Select loaded fixture for upload
cy.findByTestId('playbook-import-input').selectFile(invalidTypePlaybookExport, {force: true});
});
// * Verify that an error message is displayed.
cy.findByText('The file must be a valid JSON playbook template.').should('be.visible');
});
});
});

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

@@ -1,468 +0,0 @@
// 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 {stubClipboard} from '../../../utils';
describe('playbooks > overview', {testIsolation: true}, () => {
let testTeam;
let testOtherTeam;
let testUser;
let testUser2;
let testPublicPlaybook;
let testPlaybookOnTeamForSwitching;
let testPlaybookOnOtherTeamForSwitching;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
// # Create another team
cy.apiCreateTeam('second-team', 'Second Team').then(({team: createdTeam}) => {
testOtherTeam = createdTeam;
cy.apiAddUserToTeam(testOtherTeam.id, testUser.id);
// # Create a dedicated run follower
cy.apiCreateUser().then(({user: createdUser}) => {
cy.apiAddUserToTeam(testTeam.id, createdUser.id);
cy.apiAddUserToTeam(testOtherTeam.id, createdUser.id);
});
// # Create another user
cy.apiCreateUser().then(({user: anotherUser}) => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
retrospectiveTemplate: 'Retro template text',
retrospectiveReminderIntervalSeconds: 60 * 60 * 24 * 7, // 7 days
}).then((playbook) => {
testPublicPlaybook = playbook;
});
// # Create a private playbook with only the current user
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Private Only Mine Playbook',
memberIDs: [testUser.id],
});
// # Create a private playbook with multiple users
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Private Shared Playbook',
memberIDs: [testUser.id, anotherUser.id],
});
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Switch A',
memberIDs: [],
retrospectiveTemplate: 'Retro template text',
retrospectiveReminderIntervalSeconds: 60 * 60 * 24 * 7, // 7 days
}).then((playbook) => {
testPlaybookOnTeamForSwitching = playbook;
});
// # Create a public playbook on another team
cy.apiCreatePlaybook({
teamId: testOtherTeam.id,
title: 'Switch B',
memberIDs: [],
}).then((playbook) => {
testPlaybookOnOtherTeamForSwitching = playbook;
});
});
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
it('redirects to not found error if the playbook is unknown', () => {
// # Visit the URL of a non-existing playbook
cy.visit('/playbooks/playbooks/an_unknown_id');
// * Verify that the user has been redirected to the playbooks not found error page
cy.url().should('include', '/playbooks/error?type=playbooks');
});
describe('should switch to channels and prompt to run when clicking run', () => {
const openAndRunPlaybook = (team) => {
// # Navigate directly to town square on the team
cy.visit(`${team.name}/channels/town-square`);
// # Open Playbooks
cy.get('[aria-label="Product switch menu"]').click({force: true});
cy.get('a[href="/playbooks"]').click({force: true});
// Click through to open the playbook
cy.findByTestId('playbooksLHSButton').click({force: true});
cy.get('[placeholder="Search for a playbook"]').type(testPlaybookOnTeamForSwitching.title);
cy.findByTestId('playbook-title').click({force: true});
// # Click Run Playbook
cy.findByTestId('run-playbook').click({force: true});
// * Verify the playbook run creation dialog has opened
cy.get('#playbooks_run_playbook_dialog').should('exist').within(() => {
cy.findByText('Start run').should('exist');
});
};
it('for testPlaybookOnTeamForSwitching from its own team', () => {
openAndRunPlaybook(testTeam, testPlaybookOnTeamForSwitching);
});
it('for testPlaybookOnTeamForSwitching from another team', () => {
openAndRunPlaybook(testOtherTeam, testPlaybookOnTeamForSwitching);
});
it('for testPlaybookOnOtherTeamForSwitching from its own team', () => {
openAndRunPlaybook(testTeam, testPlaybookOnOtherTeamForSwitching);
});
it('for testPlaybookOnOtherTeamForSwitchingOnOtherTeam from another team', () => {
openAndRunPlaybook(testOtherTeam, testPlaybookOnOtherTeamForSwitching);
});
it('on direct navigation to a playbook', () => {
// # Navigate directly to the playbook
cy.visit(`/playbooks/playbooks/${testPlaybookOnTeamForSwitching.id}`);
// # Click Run Playbook
cy.findByTestId('run-playbook').click();
// * Verify the playbook run creation dialog has opened
cy.get('#playbooks_run_playbook_dialog').should('exist').within(() => {
cy.findByText('Start run').should('exist');
});
});
});
it('should copy playbook link', () => {
// # Navigate directly to the playbook
cy.visit(`/playbooks/playbooks/${testPublicPlaybook.id}`);
// # trigger the tooltip
cy.get('.icon-link-variant').trigger('mouseover', {force: true});
// * Verify tooltip text
cy.get('#copy-playbook-link-tooltip').should('contain', 'Copy link to');
stubClipboard().as('clipboard');
// # click on copy button
cy.get('.icon-link-variant').click({force: true}).then(() => {
// * Verify that tooltip text changed
cy.get('#copy-playbook-link-tooltip').should('contain', 'Copied!');
// * Verify clipboard content
cy.get('@clipboard').its('contents').should('contain', `/playbooks/playbooks/${testPublicPlaybook.id}`);
});
});
it('should duplicate playbook', () => {
// # Login as testUser2
cy.apiLogin(testUser2);
// # Navigate directly to the playbook
cy.visit(`/playbooks/playbooks/${testPublicPlaybook.id}`);
// # Click on playbook title
cy.findByTestId('playbook-editor-title').click();
// # Click on duplicate
cy.findByText('Duplicate').click();
// * Verify that playbook got duplicated
cy.findByTestId('playbook-editor-title').should('contain', `Copy of ${testPublicPlaybook.title}`);
// * Verify that the current user is a member and can run the playbook.
cy.findByTestId('run-playbook').should('exist');
cy.findByTestId('join-playbook').should('not.exist');
// * Verify that the current user is the only member.
cy.findByTestId('playbook-members').should('contain', '1');
});
describe('checklists', () => {
describe('header', () => {
beforeEach(() => {
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
description: 'Cypress Playbook',
memberIDs: [],
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
retrospectiveTemplate: 'Cypress test template',
}).then((playbook) => {
cy.visit(`/playbooks/playbooks/${playbook.id}/outline`);
});
});
it('has title', () => {
cy.get('#checklists').within(() => {
cy.findByText('Tasks').should('exist');
});
});
});
it('shows checklists', () => {
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
description: 'Cypress Playbook',
memberIDs: [],
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
retrospectiveTemplate: 'Cypress test template',
}).then((playbook) => {
cy.visit(`/playbooks/playbooks/${playbook.id}`);
});
// # Switch to Outline section
cy.findByText('Outline').click();
// * Verify checklist and associated steps
cy.get('#checklists').within(() => {
cy.findByText('Stage 1').should('exist');
cy.findByText('Step 1').should('exist');
cy.findByText('Step 2').should('exist');
});
});
});
it('shows correct retrospective timer and template text', () => {
cy.visit(`/playbooks/playbooks/${testPublicPlaybook.id}`);
cy.findByText('Outline').click();
cy.get('#retrospective').within(() => {
cy.findByText('7 days').should('exist');
cy.findByText('Retro template text').should('exist');
});
});
it('shows statistics in usage tab', () => {
// # Start playbook run.
const now = Date.now();
const playbookRunName = `Run (${now})`;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # Go to usage view
cy.visit(`/playbooks/playbooks/${testPublicPlaybook.id}`);
// * Verify basic information.
cy.findByText('Runs currently in progress').next().should('contain', '1');
cy.findByText('Participants currently active').next().should('contain', '1');
cy.findByText('Runs finished in the last 30 days').next().should('contain', '0');
// # End the run so those metrics change.
cy.apiFinishRun(playbookRun.id).then(() => {
cy.reload();
// * Verify changes.
cy.findByText('Runs currently in progress').next().should('contain', '0');
cy.findByText('Participants currently active').next().should('contain', '0');
cy.findByText('Runs finished in the last 30 days').next().should('contain', '1');
});
});
});
it('start a run', () => {
// # Visit playbook page
cy.visit(`/playbooks/playbooks/${testPublicPlaybook.id}`);
// # Click Run Playbook
cy.findByTestId('run-playbook').click();
// # Enter the run name
cy.findByTestId('run-name-input').clear().type('run1234567');
// # Click start run button
cy.get('button[data-testid=modal-confirm-button]').click();
// * Verify the run is added to lhs
cy.findByTestId('Runs').findByTestId('run1234567').should('exist');
});
describe('archiving', () => {
const playbookTitle = 'Playbook (' + Date.now() + ')';
let testPlaybook;
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: playbookTitle,
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
});
it('shows intended UI and disallows further updates', () => {
// # Programmatically archive it
cy.apiArchivePlaybook(testPlaybook.id);
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
// * Verify we're on the right playbook
cy.get('[class^="Title-"]').contains(playbookTitle);
// * Verify we can see the archived badge
cy.get('.icon-archive-outline').should('be.visible');
// * Verify the run button is disabled
cy.findByTestId('run-playbook').should('be.disabled');
// # Attempt to edit the playbook
cy.apiGetPlaybook(testPlaybook.id).then((playbook) => {
// # New title
playbook.title = 'new Title!!!';
// * Verify update fails
cy.apiUpdatePlaybook(playbook, 400);
});
});
});
describe('start a run', () => {
let testPlaybook;
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
after(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
beforeEach(() => {
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
});
it('start a run, create a new channel', () => {
// # Visit playbook page
cy.visit(`/playbooks/playbooks/${testPlaybook.id}`);
// # Click Run Playbook
cy.findByTestId('run-playbook').click();
// * Verify that channel configuration matches playbook config
cy.findByTestId('link-existing-channel-radio').should('not.be.checked');
cy.get('#link-existing-channel-selector').should('not.exist');
cy.findByTestId('create-channel-radio').should('be.checked');
cy.findByTestId('create-private-channel-radio').should('be.checked');
// # Enter the run name
const runName = 'run' + Date.now();
cy.findByTestId('run-name-input').clear().type(runName);
// # Click start run button
cy.get('button[data-testid=modal-confirm-button]').click();
// * Verify the run is added to lhs
cy.findByTestId('Runs').findByTestId(runName).should('exist');
// * Verify the channel is created
cy.findByTestId('runinfo-channel-link').contains(runName);
});
it('start a run in existing channel', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Select the action section.
cy.get('#actions #link-existing-channel').within(() => {
// # Enable link to existing channel
cy.get('input[type=radio]').click();
// * Verify that the toggle is checked and input is enabled
cy.get('input[type=radio]').should('be.checked');
cy.get('input[type=text]').should('not.be.disabled');
// # Select channel
cy.findByText('Select a channel').click().type('Town{enter}');
});
// # Click Run Playbook
cy.findByTestId('run-playbook').click({force: true});
// # Enter the run name
const runName = 'run' + Date.now();
cy.findByTestId('run-name-input').clear().type(runName);
// * Verify that channel configuration matches playbook config
cy.findByTestId('link-existing-channel-radio').should('be.checked');
cy.get('#link-existing-channel-selector').get('input[type=text]').should('be.enabled');
cy.findByTestId('create-channel-radio').should('not.be.checked');
cy.findByTestId('create-private-channel-radio').should('not.exist');
// # Click start run button
cy.get('button[data-testid=modal-confirm-button]').click();
// * Verify the run is added to lhs
cy.findByTestId('Runs').findByTestId(runName).should('exist');
// * Verify the channel is created
cy.findByTestId('runinfo-channel-link').contains('Town');
});
});
});

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

@@ -1,68 +0,0 @@
// 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('playbooks > list pagination', {testIsolation: true}, () => {
let testTeam;
let testUser;
const ExtraPlaybooks = 20;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as user-1
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
});
// # Populate the DB with more elements to force the pagination
for (let i = 0; i < ExtraPlaybooks; i++) {
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Elements before',
memberIDs: [],
});
}
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
});
it('reset page to 0 after search for an name with one value', () => {
// # Open the product
cy.visit('/playbooks');
// # Switch to Playbooks
cy.findByTestId('playbooksLHSButton').click();
// # Click on next page
cy.findByText('Next').click();
// # Click on Search input
cy.get('input[placeholder="Search for a playbook"]').type('Playbook');
// * Verify the page display the first page
cy.findByText('1–1 of 1 total');
// * Verify that previous isn't exist
cy.findByText('Previous').should('not.exist');
});
});

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

@@ -1,578 +0,0 @@
// 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
const RUN_NAME_MAX_LENGTH = 64;
describe('playbooks > start a run', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
});
});
beforeEach(() => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
makePublic: true,
memberIDs: [testUser.id],
createPublicPlaybookRun: true,
}).then((playbook) => {
testPlaybook = playbook;
});
});
// This data is intentionally changed here instead of via api
const fillPBE = ({name, summary, channelMode, channelNameToLink, defaultOwnerEnabled}) => {
// # fill channel name temaplte
if (name) {
cy.get('#create-new-channel input[type="text"]').clear().type('Channel template');
}
// # fill summary template
if (summary) {
cy.contains('run summary template').dblclick();
cy.focused().type('run summary template');
cy.findByRole('button', {name: /save/i}).click();
}
if (channelMode === 'create_new_channel') {
cy.get('#create-new-channel input[type="radio"]').eq(0).click();
} else if (channelMode === 'link_to_existing_channel') {
cy.get('#link-existing-channel input[type="radio"]').click();
}
if (channelNameToLink) {
cy.get('#link-existing-channel').within(() => {
cy.findByText('Select a channel').click().type(`${channelNameToLink}{enter}`);
});
}
if (defaultOwnerEnabled) {
cy.get('#assign-owner').within(() => {
// * Verify that the toggle is unchecked
cy.get('label input').should('not.be.checked');
// # Click on the toggle to enable the setting
cy.get('label input').click({force: true});
// * Verify that the toggle is checked
cy.get('label input').should('be.checked');
});
}
};
describe('from playbook list', () => {
it('defaults', () => {
// # Visit playbook list
cy.visit('/playbooks/playbooks');
// # Click "Run" button on the first playbook
cy.findAllByTestId('playbook-item').first().within(() => {
cy.findByText('Run').click();
});
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert template name is filled
cy.findByTestId('run-name-input').clear().type('Run name');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([
{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'backstage_playbook_list',
playbookId: testPlaybook.id,
channelMode: 'create_new_channel',
hasPlaybookChanged: false,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Run name');
});
});
describe('from playbook editor', () => {
describe('pbe configured as create new channel', () => {
it('defaults', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// Fill default values
fillPBE({name: 'Channel template', summary: 'run summary template', channelMode: 'create_new_channel', defaultOwnerEnabled: true});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert template name is filled
cy.findByTestId('run-name-input').should('have.value', 'Channel template');
// * Assert template summary 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: 'backstage_playbook_editor',
playbookId: testPlaybook.id,
channelMode: 'create_new_channel',
hasPlaybookChanged: false,
hasNameChanged: false,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Channel template');
// * Verify run summary
cy.findByTestId('run-summary-section').contains('run summary template');
});
it('change title/summary', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Fill default values
fillPBE({name: 'Channel template', summary: 'run summary template', channelMode: 'create_new_channel'});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # 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');
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: 'backstage_playbook_editor',
playbookId: testPlaybook.id,
channelMode: 'create_new_channel',
hasPlaybookChanged: false,
hasNameChanged: true,
hasSummaryChanged: true,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Test Run Name');
// * Verify run summary
cy.findByTestId('run-summary-section').contains('Test Run Summary');
});
it('change to link to existing channel does not default to current channel', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Fill default values
fillPBE({name: 'Channel template', summary: 'run summary template', channelMode: 'create_new_channel', defaultOwnerEnabled: true});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # 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('Select a channel').should('be.visible');
});
});
it('change to link to existing channel', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Fill default values
fillPBE({name: 'Channel template', summary: 'run summary template', channelMode: 'create_new_channel', defaultOwnerEnabled: true});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # 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');
// * Assert cta is disabled
cy.findByTestId('modal-confirm-button').should('be.disabled');
// # Fill Town square as the channel to be linked
cy.findByText('Select a channel').click().type('Town{enter}');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([
{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'backstage_playbook_editor',
playbookId: testPlaybook.id,
channelMode: 'link_existing_channel',
hasPlaybookChanged: false,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: true,
hasChannelIdChanged: true,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Test Run Name');
// # Click channel link
cy.findByTestId('runinfo-channel-link').click();
// * Verify we are on town square
cy.url().should('include', `/${testTeam.name}/channels/town-square`);
});
});
describe('pbe configured as linked to existing channel', () => {
it('defaults', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Fill default values
fillPBE({summary: 'run summary template', channelMode: 'link_to_existing_channel', channelNameToLink: 'Town'});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # 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');
// * Assert button is still disabled
cy.findByTestId('modal-confirm-button').should('be.disabled');
// # 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: 'backstage_playbook_editor',
playbookId: testPlaybook.id,
channelMode: 'link_existing_channel',
hasPlaybookChanged: false,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: false,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Test Run Name');
// * Verify run summary
cy.findByTestId('run-summary-section').contains('run summary template');
// # Click channel link
cy.findByTestId('runinfo-channel-link').click();
// * Verify we are on town square
cy.url().should('include', `/${testTeam.name}/channels/town-square`);
});
it('fill initially empty channel', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Fill default values
fillPBE({summary: 'run summary template', channelMode: 'link_to_existing_channel'});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # 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');
// * Assert button is still disabled
cy.findByTestId('modal-confirm-button').should('be.disabled');
// # Fill Town square as the channel to be linked
cy.findByText('Select a channel').click().type('Town{enter}');
// # Click start button
cy.findByTestId('modal-confirm-button').click();
});
// * Assert telemetry data
cy.expectTelemetryToContain([
{
name: 'playbookrun_create',
type: 'track',
properties: {
place: 'backstage_playbook_editor',
playbookId: testPlaybook.id,
channelMode: 'link_existing_channel',
hasPlaybookChanged: false,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: false,
hasChannelIdChanged: true,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Test Run Name');
// * Verify run summary
cy.findByTestId('run-summary-section').contains('run summary template');
// # Click channel link
cy.findByTestId('runinfo-channel-link').click();
// * Verify we are on town square
cy.url().should('include', `/${testTeam.name}/channels/town-square`);
});
it('change to create new channel', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// Fill default values
fillPBE({name: 'Channel template', summary: 'run summary template', channelMode: 'link_to_existing_channel', channelNameToLink: 'Town'});
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # 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: 'backstage_playbook_editor',
playbookId: testPlaybook.id,
channelMode: 'create_new_channel',
hasPlaybookChanged: false,
hasNameChanged: true,
hasSummaryChanged: false,
hasChannelModeChanged: true,
hasChannelIdChanged: false,
hasPublicChanged: false,
},
},
]);
// * Verify we are on RDP
cy.url().should('include', '/playbooks/runs/');
cy.url().should('include', '?from=run_modal');
// * Verify run name
cy.get('h1').contains('Test Run Name');
// # Click channel link
cy.findByTestId('runinfo-channel-link').click();
// * Verify we are on channel Test Run Name
cy.url().should('include', `/${testTeam.name}/channels/test-run-name`);
});
});
});
describe('start run modal > invalid user input', () => {
it('submit button is disabled when run name is empty', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert template name is empty
cy.findByTestId('run-name-input').should('have.value', '');
// * Assert start button is disabled
cy.findByTestId('modal-confirm-button').should('have.attr', 'disabled');
});
});
it('error is shown when maximum length of run name is exceeded', () => {
// # Visit the selected playbook
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Click start a run button
cy.findByTestId('run-playbook').click();
cy.get('#root-portal.modal-open').within(() => {
// # Wait the modal to render
cy.wait(500);
// * Assert template name is empty
cy.findByTestId('run-name-input').should('have.value', '');
// # Type run name that exceeds maximum length
cy.findByTestId('run-name-input').type('a'.repeat(RUN_NAME_MAX_LENGTH + 1));
// * Assert error shown and contains maximum length in message
cy.findByTestId('run-name-error').should('contain', RUN_NAME_MAX_LENGTH);
// * Assert start button is disabled
cy.findByTestId('modal-confirm-button').should('have.attr', 'disabled');
// # Delete last character via backspace
cy.findByTestId('run-name-input').type('{backspace}');
// * Assert that error is not shown anymore
cy.findByTestId('run-name-error').should('not.exist');
});
});
});
});

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

@@ -1,196 +0,0 @@
// 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('playbooks > edit status update', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPlaybook;
let testChannel;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public channel
cy.apiCreateChannel(
testTeam.id,
'public-channel',
'Public Channel',
'O',
).then(({channel}) => {
testChannel = channel;
});
});
});
beforeEach(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a playbook
cy.apiCreateTestPlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
userId: testUser.id,
}).then((playbook) => {
testPlaybook = playbook;
});
// # Set a bigger viewport so the action don't scroll out of view
cy.viewport('macbook-16');
});
describe('status update enable/disable', () => {
it('can enable/disable status update', () => {
// # Visit the selected playbook outline tab
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// * Verify status update message
cy.findAllByTestId('status-update-section').should('exist').within(() => {
cy.contains('A status update is expected every');
cy.contains('1 day');
cy.contains('no channels');
cy.contains('no outgoing webhooks');
});
// # Disable status update
cy.findAllByTestId('status-update-toggle').eq(0).click();
// * Verify status update message
cy.findAllByTestId('status-update-section').should('exist').within(() => {
cy.contains('Status updates are not expected.');
cy.contains('A status update is expected every').should('not.exist');
});
});
});
describe('edit channels and webhooks', () => {
it('can enable/disable status update', () => {
// # Visit the selected playbook outline tab
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Select a channel
cy.findAllByTestId('status-update-broadcast-channels').click();
cy.get('#playbook-automation-broadcast').contains('Town Square').click({force: true});
cy.findAllByTestId('status-update-broadcast-channels').click();
// # Refresh the page
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// # Add webhooks
cy.findAllByTestId('status-update-webhooks').click();
cy.findAllByTestId('webhooks-input').type('http://hook1.com{enter}http://hook2.com{enter}http://hook3.com{enter}');
cy.findAllByTestId('checklist-item-save-button').click();
// # Refresh the page
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// * Verify status update message
cy.findAllByTestId('status-update-section').should('exist').within(() => {
cy.contains('1 channel');
cy.contains('3 outgoing webhooks');
});
// # Disable status update
cy.findAllByTestId('status-update-toggle').eq(0).click();
// * Verify status update message
cy.get('#status-updates').within(() => {
cy.findByText('Status updates are not expected.').should('exist');
});
// # Re-enable status update
cy.findAllByTestId('status-update-toggle').eq(0).click();
// # Refresh the page
cy.visit(`/playbooks/playbooks/${testPlaybook.id}/outline`);
// * Verify that channels and webhooks persist
cy.get('#status-updates').within(() => {
cy.contains('1 channel').should('exist');
cy.contains('3 outgoing webhooks').should('exist');
});
});
});
describe('status enabled, broadcasts disabled, but channels and webhooks specified', () => {
it('can enable/disable status update', () => {
const broadcastChannelIds = [testChannel.id];
const webhookOnStatusUpdateURLs = ['https://one.com', 'https://two.com'];
// # Create a playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook #### (' + Date.now() + ')',
userId: testUser.id,
broadcastChannelIds,
webhookOnStatusUpdateURLs,
}).then((playbook) => {
// # Visit the selected playbook outline tab
cy.visit(`/playbooks/playbooks/${playbook.id}/outline`);
// * Verify status update message. Status update should be enabled, but message should say `updates will be posted to no channels and no outgoing webhooks`
cy.findAllByTestId('status-update-section').should('exist').within(() => {
cy.contains('A status update is expected every');
cy.contains('no channels');
cy.contains('no outgoing webhooks');
});
// * Verify selected channels style
cy.findAllByTestId('status-update-broadcast-channels').click();
cy.get('.playbook-react-select__option').contains('Public Channel').
invoke('css', 'text-decoration').
should('equal', 'line-through solid rgba(63, 67, 80, 0.48)');
// # Close select options
cy.findAllByTestId('status-update-broadcast-channels').click();
// # Open webhooks text area
cy.findAllByTestId('status-update-webhooks').click();
// * Verify webhooks text style
cy.findAllByTestId('webhooks-input').
invoke('css', 'text-decoration').
should('equal', 'line-through solid rgba(63, 67, 80, 0.48)');
// # Edit webhooks
cy.findAllByTestId('webhooks-input').type('http://hook1.com{enter}http://hook2.com{enter}http://hook3.com{enter}');
cy.findAllByTestId('checklist-item-save-button').click();
// # Select a channel
cy.findAllByTestId('status-update-broadcast-channels').click();
cy.get('#playbook-automation-broadcast').contains('Town Square').click({force: true});
cy.findAllByTestId('status-update-broadcast-channels').click();
// * Verify status update message.
cy.findAllByTestId('status-update-section').should('exist').within(() => {
cy.contains('A status update is expected every');
cy.contains('2 channels');
cy.contains('4 outgoing webhooks');
});
// # Refresh the page
cy.visit(`/playbooks/playbooks/${playbook.id}/outline`);
// * Verify status update message.
cy.findAllByTestId('status-update-section').should('exist').within(() => {
cy.contains('A status update is expected every');
cy.contains('2 channels');
cy.contains('4 outgoing webhooks');
});
});
});
});
});

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

@@ -1,262 +0,0 @@
// 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 > list', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testAnotherUser;
let testPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
// # Create another user
cy.apiCreateUser().then(({user: anotherUser}) => {
testTeam = team;
testUser = user;
testAnotherUser = anotherUser;
cy.apiAddUserToTeam(testTeam.id, anotherUser.id);
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
makePublic: true,
memberIDs: [testUser.id, testAnotherUser.id],
createPublicPlaybookRun: true,
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
});
beforeEach(() => {
// # Size the viewport to show all
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
it('has "Runs" and team name in heading', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Open the product
cy.visit('/playbooks');
// # Switch to playbook runs
cy.findByTestId('playbookRunsLHSButton').click();
// * Assert contents of heading.
cy.findByTestId('titlePlaybookRun').should('exist').contains('Runs');
});
it('loads playbook run details page when clicking on a playbook run', () => {
// # Run the playbook
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
});
// # Open the product
cy.visit('/playbooks');
// # Switch to runs
cy.findByTestId('playbookRunsLHSButton').click();
// # Find the playbook run and click to open details view
cy.get('#playbookRunList').within(() => {
cy.findByText(playbookRunName).click();
});
// * Verify that the header contains the playbook run name
cy.findByTestId('run-header-section').get('h1').contains(playbookRunName);
});
describe('filters my runs only', () => {
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Run a playbook with testUser as a participant
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'testUsers Run',
ownerUserId: testUser.id,
});
// # Login as testAnotherUser
cy.apiLogin(testAnotherUser);
// # Run a playbook with testAnotherUser as a participant
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'testAnotherUsers Run',
// ownerUserId: testUser.id,
ownerUserId: testAnotherUser.id,
});
});
it('for testUser', () => {
// # Login as testUser
cy.apiLogin(testUser);
// # Open the product
cy.visit('/playbooks/runs');
cy.get('#playbookRunList').within(() => {
// # Make sure both runs are visible by default
cy.findByText('testUsers Run').should('be.visible');
cy.findByText('testAnotherUsers Run').should('be.visible');
// # Filter to only my runs
cy.findByTestId('my-runs-only').click();
// # Verify runs by testAnotherUser are not visible
cy.findByText('testAnotherUsers Run').should('not.exist');
// # Verify runs by testUser remain visible
cy.findByText('testUsers Run').should('be.visible');
});
});
it('for testAnotherUser', () => {
// # Login as testAnotherUser
cy.apiLogin(testAnotherUser);
// # Open the product
cy.visit('/playbooks');
cy.get('#playbookRunList').within(() => {
// Make sure both runs are visible by default
cy.findByText('testUsers Run').should('be.visible');
cy.findByText('testAnotherUsers Run').should('be.visible');
// # Filter to only my runs
cy.findByTestId('my-runs-only').click();
// # Verify runs by testUser are not visible
cy.findByText('testUsers Run').should('not.exist');
// # Verify runs by testAnotherUser remain visible
cy.findByText('testAnotherUsers Run').should('be.visible');
});
});
});
describe('filters Finished runs correctly', () => {
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Run a playbook with testUser as a participant
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'testUsers Run to be finished',
ownerUserId: testUser.id,
}).then((playbook) => {
cy.apiFinishRun(playbook.id);
});
});
it('shows finished runs', () => {
// # Login as testUser
cy.apiLogin(testUser);
// # Open the product
cy.visit('/playbooks');
cy.get('#playbookRunList').within(() => {
// # Make sure runs are visible by default, and finished is not
cy.findByText('testUsers Run').should('be.visible');
cy.findByText('testAnotherUsers Run').should('be.visible');
cy.findByText('testUsers Run to be finished').should('not.exist');
// # Filter to finished runs as well
cy.findByTestId('finished-runs').click();
// # Verify runs remain visible
cy.findByText('testUsers Run').should('be.visible');
cy.findByText('testAnotherUsers Run').should('be.visible');
// # Verify finished run is visible
cy.findByText('testUsers Run to be finished').should('be.visible');
});
});
});
describe('LHS run list', () => {
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
const runs = [
{
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'run-sort-check 0',
ownerUserId: testUser.id,
},
{
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'run-sort-check 1',
ownerUserId: testUser.id,
},
{
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'run-sort-check 2',
ownerUserId: testUser.id,
},
{
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: 'run-sort-check 3',
ownerUserId: testUser.id,
},
];
Promise.all(runs.map((run) => {
return new Promise((resolve) => cy.apiRunPlaybook(run).then(resolve));
})).then(() => {
cy.visit('/playbooks');
});
});
it('lhs run list sorted by name', () => {
cy.findByTestId('lhs-navigation').within(() => {
cy.get('li:contains(run-sort-check)').each((item, index) => {
// * Verify run list order
cy.wrap(item).should('have.text', 'run-sort-check ' + index);
});
});
});
});
});

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

@@ -1,498 +0,0 @@
// 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 {getRandomId} from '../../../utils';
describe('runs > permissions', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testOtherTeam;
let playbookMember;
let runParticipant;
let runFollower;
let teamMember;
let nonTeamMember;
let sysadminInTeam;
let sysadminNotInTeam;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create a dedicated playbook member
cy.apiCreateUser().then(({user: createdUser}) => {
playbookMember = createdUser;
cy.apiAddUserToTeam(testTeam.id, createdUser.id);
});
// # Create a dedicated run participant
cy.apiCreateUser().then(({user: createdUser}) => {
runParticipant = createdUser;
cy.apiAddUserToTeam(testTeam.id, createdUser.id);
});
// # Create a dedicated run follower
cy.apiCreateUser().then(({user: createdUser}) => {
runFollower = createdUser;
cy.apiAddUserToTeam(testTeam.id, createdUser.id);
});
// # Create a dedicated member in team 1
cy.apiCreateUser().then(({user: createdUser}) => {
teamMember = createdUser;
cy.apiAddUserToTeam(testTeam.id, createdUser.id);
});
// # Create a dedicated sysadmin in team 1
cy.apiCreateCustomAdmin().then(({sysadmin: createdUser}) => {
sysadminInTeam = createdUser;
cy.apiAddUserToTeam(testTeam.id, createdUser.id);
});
// # Create a public playbook and corresponding run with a public channel in
// team 1. This is to ensure the list isn't empty for users who can't access the
// run under test.
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook (Team 1)',
memberIDs: [],
createPublicPlaybookRun: true,
}).then((createdPlaybook) => {
// Create a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: createdPlaybook.id,
playbookRunName: getRandomId(),
ownerUserId: testUser.id,
});
});
// # Create another team
cy.apiCreateTeam('second-team', 'Second Team').then(({team: createdTeam}) => {
testOtherTeam = createdTeam;
// # Create a dedicated member not in team 1
cy.apiCreateUser().then(({user: createdUser}) => {
nonTeamMember = createdUser;
cy.apiAddUserToTeam(testOtherTeam.id, createdUser.id);
});
// # Create a dedicated sysadmin not in team 1
cy.apiCreateCustomAdmin().then(({sysadmin: createdUser}) => {
sysadminNotInTeam = createdUser;
cy.apiAddUserToTeam(testOtherTeam.id, createdUser.id);
});
// # Create a public playbook and corresponding run with a public channel in
// team 2. This is to ensure the list isn't empty for users who can't access the
// run under test.
cy.apiCreatePlaybook({
teamId: testOtherTeam.id,
title: 'Playbook (Team 2)',
memberIDs: [],
createPublicPlaybookRun: true,
}).then((createdPlaybook) => {
// Create a run
cy.apiRunPlaybook({
teamId: testOtherTeam.id,
playbookId: createdPlaybook.id,
playbookRunName: getRandomId(),
ownerUserId: nonTeamMember.id,
});
});
});
});
});
describe('run with private channel from a public playbook', () => {
let playbook;
let run;
before(() => {
// # Login as the user setup during initialization.
cy.apiLogin(testUser);
// # Create a public playbook, configured to create private channels for runs
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
createPublicPlaybookRun: false,
}).then((createdPlaybook) => {
playbook = createdPlaybook;
// Create a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: getRandomId(),
ownerUserId: runParticipant.id,
}).then((createdRun) => {
run = createdRun;
// Have the dedicated participant join the run
cy.apiAddUsersToRun(run.id, [runParticipant.id]);
// # Have the dedicated follower follow this playbook run
cy.apiLogin(runFollower);
cy.apiFollowPlaybookRun(run.id);
});
});
});
describe('should be visible', () => {
it('to playbook members', () => {
assertRunIsVisible(run, playbookMember);
});
it('to run participants', () => {
assertRunIsVisible(run, runParticipant);
});
it('to run followers', () => {
assertRunIsVisible(run, runFollower);
});
it('to team members', () => {
assertRunIsVisible(run, teamMember);
});
it('to admins in the team', () => {
assertRunIsVisible(run, sysadminInTeam);
});
// XXX: The following asserts that while sysadmins don't see runs from other teams in
// the list, they still have access to view the overview directly. Once we support
// sudo-admins, we should change this behaviour to be consistent with normal users.
it('to admins not in the team (overview only)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunOverviewIsVisible(run);
});
});
describe('should not be visible', () => {
it('to non-team members', () => {
assertRunIsNotVisible(run, nonTeamMember);
});
it('to admins not in the team (list only)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunIsNotVisibleInList(run);
});
});
});
describe('run with public channel from a public playbook', () => {
let playbook;
let run;
before(() => {
// # Login as the user setup during initialization.
cy.apiLogin(testUser);
// # Create a public playbook, configured to create public channels for runs
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
}).then((createdPlaybook) => {
playbook = createdPlaybook;
// Create a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: getRandomId(),
ownerUserId: runParticipant.id,
}).then((createdRun) => {
run = createdRun;
// Have the dedicated participant join the run
cy.apiAddUsersToRun(run.id, [runParticipant.id]);
// # Have the dedicated follower follow this playbook run
cy.apiLogin(runFollower);
cy.apiFollowPlaybookRun(run.id);
});
});
});
describe('should be visible', () => {
it('to playbook members', () => {
assertRunIsVisible(run, playbookMember);
});
it('to run participants', () => {
assertRunIsVisible(run, runParticipant);
});
it('to run followers', () => {
assertRunIsVisible(run, runFollower);
});
it('to team members', () => {
assertRunIsVisible(run, teamMember);
});
it('to admins in the team', () => {
assertRunIsVisible(run, sysadminInTeam);
});
// XXX: The following asserts that while sysadmins don't see runs from other teams in
// the list, they still have access to view the overview directly. Once we support
// sudo-admins, we should change this behaviour to be consistent with normal users.
it('to admins not in the team (overview only)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunOverviewIsVisible(run);
});
});
describe('should not be visible', () => {
it('to non-team members', () => {
assertRunIsNotVisible(run, nonTeamMember);
});
it('to admins not in the team (list only)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunIsNotVisibleInList(run);
});
});
});
describe('run with private channel from a private playbook', () => {
let playbook;
let run;
before(() => {
// # Login as the user setup during initialization.
cy.apiLogin(testUser);
// # Create private playbook, configured to create private channels for runs
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
makePublic: false,
memberIDs: [testUser.id, playbookMember.id],
createPublicPlaybookRun: false,
}).then((createdPlaybook) => {
playbook = createdPlaybook;
// Login as the playbook member authorized to start a run
cy.apiLogin(playbookMember);
// Create a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: getRandomId(),
ownerUserId: runParticipant.id,
}).then((createdRun) => {
run = createdRun;
// Have the dedicated participant join the run
cy.apiAddUsersToRun(run.id, [runParticipant.id]);
});
});
});
describe('should be visible', () => {
it('to playbook members', () => {
assertRunIsVisible(run, playbookMember);
});
it('to run participants', () => {
assertRunIsVisible(run, runParticipant);
});
// Followers cannot follow a run with a private channel from a private playbook
it('to run followers', () => {
assertRunIsNotVisible(run, runFollower);
});
it('to admins in the team', () => {
assertRunIsVisible(run, sysadminInTeam);
});
// XXX: The following asserts that while sysadmins don't see runs from other teams in
// the list, they still have access to view the run directly. Once we support
// sudo-admins, we should change this behaviour to be consistent with normal users.
it('to admins not in the team (run directly)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunOverviewIsVisible(run);
});
});
describe('should not be visible', () => {
it('to team members', () => {
assertRunIsNotVisible(run, teamMember);
});
it('to non-team members', () => {
assertRunIsNotVisible(run, nonTeamMember);
});
it('to admins not in the team (list only)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunIsNotVisibleInList(run);
});
});
});
describe('run with public channel from a private playbook', () => {
let playbook;
let run;
before(() => {
// # Login as the user setup during initialization.
cy.apiLogin(testUser);
// # Create private playbook, configured to create private channels for runs
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook',
memberIDs: [testUser.id, playbookMember.id],
makePublic: false,
createPublicPlaybookRun: true,
}).then((createdPlaybook) => {
playbook = createdPlaybook;
// Login as the playbook member authorized to start a run
cy.apiLogin(playbookMember);
// Create a run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: getRandomId(),
ownerUserId: runParticipant.id,
}).then((createdRun) => {
run = createdRun;
// Have the dedicated participant join the run
cy.apiAddUsersToRun(run.id, [runParticipant.id]);
});
});
});
describe('should be visible', () => {
it('to playbook members', () => {
assertRunIsVisible(run, playbookMember);
});
it('to run participants', () => {
assertRunIsVisible(run, runParticipant);
});
// Followers cannot follow a run with a private channel from a private playbook
it('to run followers', () => {
assertRunIsNotVisible(run, runFollower);
});
it('to admins in the team', () => {
assertRunIsVisible(run, sysadminInTeam);
});
// XXX: The following asserts that while sysadmins don't see runs from other teams in
// the list, they still have access to view the run directly. Once we support
// sudo-admins, we should change this behaviour to be consistent with normal users.
it('to admins not in the team (run directly)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunOverviewIsVisible(run);
});
});
describe('should not be visible', () => {
it('to team members', () => {
assertRunIsNotVisible(run, teamMember);
});
it('to non-team members', () => {
assertRunIsNotVisible(run, nonTeamMember);
});
it('to admins not in the team (list only)', () => {
cy.apiLogin(sysadminNotInTeam);
assertRunIsNotVisibleInList(run);
});
});
});
});
const assertRunIsVisible = (run, user) => {
// # Login as the user in question
cy.apiLogin(user);
// # Open Runs
cy.visit('/playbooks/runs');
// # Find the playbook run and click to open details view
cy.get('#playbookRunList').within(() => {
cy.findByText(run.name).click();
});
// * Verify that the details loaded
cy.findByTestId('run-header-section').get('h1').contains(run.name);
};
const assertRunOverviewIsVisible = (run) => {
// # Opening the playbook run directly
cy.visit(`/playbooks/runs/${run.id}`);
// * Verify that the details loaded
cy.findByTestId('run-header-section').get('h1').contains(run.name);
};
const assertRunIsNotVisible = (run, user) => {
// # Login as the user in question
cy.apiLogin(user);
assertRunIsNotVisibleInList(run, user);
assertRunOverviewIsNotVisible(run, user);
};
const assertRunIsNotVisibleInList = (run) => {
// # Open Runs
cy.visit('/playbooks/runs');
// * Verify the playbook run is not visible
cy.get('#playbookRunList').within(() => {
cy.findByText(run.name).should('not.exist');
});
};
const assertRunOverviewIsNotVisible = (run) => {
// # Opening the playbook run directly
cy.visit(`/playbooks/runs/${run.id}`);
// * Verify the not found error screen
cy.get('.error__container').within(() => {
cy.findByText('Run not found').should('be.visible');
cy.findByText('The run you\'re requesting is private or does not exist.').should('be.visible');
});
};

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

@@ -1,83 +0,0 @@
// 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 > run details page', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testPublicPlaybook;
let testPlaybookRun;
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: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
});
});
it('redirects to not found error if the playbook run is unknown', () => {
// # Visit the URL of a non-existing playbook run
cy.visit('/playbooks/runs/an_unknown_id');
// * Verify that the user has been redirected to the playbook runs not found error page
cy.url().should('include', '/playbooks/error?type=playbook_runs');
});
it('telemetry is triggered', () => {
// # Intercept all calls to telemetry
cy.interceptTelemetry();
// # Visit the URL of a non-existing playbook run
cy.visit(`/playbooks/runs/${testPlaybookRun.id}`);
// * assert telemetry pageview
cy.expectTelemetryToContain([
{
name: 'run_details',
type: 'page',
properties: {
from: '',
role: 'participant',
playbookrun_id: testPlaybookRun.id,
playbook_id: testPublicPlaybook.id,
},
},
]);
});
});

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

@@ -1,150 +0,0 @@
// 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
// Note that this test checks the basic behavior in Run details page as participant / viewer
// It relies on the Channel RHS Checklist test to cover the full behavior of the checklists
describe('runs > run details page > checklist', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
const taskIndex = 0;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
const getChecklist = () => cy.findByTestId('run-checklist-section');
const getChecklistTasks = () => getChecklist().findAllByTestId('checkbox-item-container');
const commonTests = () => {
it('is visible', () => {
// * Verify the tasks section is present
getChecklist().should('be.visible');
});
it('has title', () => {
// * Verify the task section has a title
getChecklist().find('h3').contains('Tasks');
});
it('can see the tasks', () => {
// * Verify tasks are shown
getChecklistTasks().should('have.length', 4);
});
};
describe('as participant', () => {
commonTests();
it('click marks task as done', () => {
// # Click first task
getChecklistTasks().eq(taskIndex).find('.checkbox').check({force: true});
// * Assert checkbox is checked
getChecklistTasks().eq(taskIndex).find('.checkbox').should('be.checked');
});
it('has hover menu', () => {
// # Hover over the checklist item
getChecklistTasks().eq(taskIndex).trigger('mouseover');
// # Click dot menu
getChecklistTasks().eq(taskIndex).findByTitle('More').click({force: true});
// * Assert actions are available
cy.findByRole('button', {name: 'Skip task'}).should('be.visible');
cy.findByRole('button', {name: 'Duplicate task'}).should('be.visible');
});
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
commonTests();
it('click does not work', () => {
// # Click first task
getChecklistTasks().eq(taskIndex).find('.checkbox').should('have.attr', 'readonly');
});
it('has not hover menu', () => {
// # Hover over the checklist item
getChecklistTasks().eq(taskIndex).trigger('mouseover');
// * Check that the hover menu is not rendered
getChecklistTasks().eq(taskIndex).findByTitle('More').should('not.exist');
});
});
});

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

@@ -1,130 +0,0 @@
// 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 > run details page > finish', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPlaybookRun;
let testPublicPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name(' + Date.now() + ')',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
it('is hidden as viewer', () => {
cy.apiLogin(testViewerUser).then(() => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${testPlaybookRun.id}`);
});
// * Assert that finish section does not exist
cy.findByTestId('run-finish-section').should('not.exist');
});
it('is visible', () => {
// * Verify the finish section is present
cy.findByTestId('run-finish-section').should('be.visible');
});
it('has a placeholder visible', () => {
// * Verify the placeholder is present
cy.findByTestId('run-finish-section').contains('Time to wrap up?');
});
describe('finish run', () => {
it('can be confirmed', () => {
// # Click finish run button
cy.findByTestId('run-finish-section').find('button').click();
// * Check that status badge is in-progress
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
// * Check that finish run modal is open and has the right title
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModal').find('h1').contains('Confirm finish run');
// # Click on confirm
cy.get('#confirmModal').get('#confirmModalButton').click();
// * Assert finish section is not visible anymore
cy.findByTestId('run-finish-section').should('not.exist');
// * Assert status badge is finished
cy.findByTestId('run-header-section').findByTestId('badge').contains('Finished');
// * Verify run has been removed from LHS
cy.findByTestId('lhs-navigation').findByText(testPlaybookRun.name).should('not.exist');
});
it('can be canceled', () => {
// # Click on finish run
cy.findByTestId('run-finish-section').find('button').click();
// * Check that status badge is in-progress
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
// * Check that finish run modal is open
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModal').find('h1').contains('Confirm finish run');
// # Click on cancel
cy.get('#confirmModal').get('#cancelModalButton').click();
// * Check that status badge is still in-progress
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
// * Check that section is still visible
cy.findByTestId('run-finish-section').should('be.visible');
});
});
});

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

@@ -1,869 +0,0 @@
// 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 {stubClipboard} from '../../../utils';
describe('runs > run details page > header', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testPublicPlaybookAndChannel;
let playbookRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// # Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
createPublicPlaybookRun: true,
memberIDs: [],
}).then((playbook) => {
testPublicPlaybookAndChannel = playbook;
});
});
});
const openRunActionsModal = () => {
// # Click on the run actions modal button
cy.findByRole('button', {name: /Run Actions/i}).click({force: true});
// * Verify that the modal is shown
cy.findByRole('dialog', {name: /Run Actions/i}).should('exist');
};
const saveRunActionsModal = () => {
// # Click on the Save button without changing anything
cy.findByRole('button', {name: /Save/i}).click();
// * Verify that the modal is no longer there
cy.findByRole('dialog', {name: /Run Actions/i}).should('not.exist');
};
const getHeader = () => {
return cy.findByTestId('run-header-section');
};
const getHeaderIcon = (selector) => {
return getHeader().find(selector);
};
const getDropdownItemByText = (text) => {
cy.findByTestId('run-header-section').find('h1').click();
return cy.findByTestId('dropdownmenu').findByText(text);
};
const commonHeaderTests = () => {
it('shows the title', () => {
// * Assert title is shown in h1 inside header
cy.findByTestId('run-header-section').find('h1').contains(playbookRun.name);
});
it('shows the in-progress status badge', () => {
// * Assert in progress status badge
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
});
it('has a copy-link icon', () => {
// # Mouseover on the icon
getHeaderIcon('.icon-link-variant').trigger('mouseover');
// * Assert tooltip is shown
cy.get('#copy-run-link-tooltip').should('contain', 'Copy link to run');
stubClipboard().as('clipboard');
getHeaderIcon('.icon-link-variant').click().then(() => {
// * Verify that tooltip text changed
cy.get('#copy-run-link-tooltip').should('contain', 'Copied!');
// * Verify clipboard content
cy.get('@clipboard').its('contents').should('contain', `/playbooks/runs/${playbookRun.id}`);
});
});
};
const commonContextDropdownTests = () => {
it('shows on click', () => {
// # Click title
cy.findByTestId('run-header-section').find('h1').click();
// * Assert context menu is opened
cy.findByTestId('dropdownmenu').should('be.visible');
});
it('can copy link', () => {
stubClipboard().as('clipboard');
getDropdownItemByText('Copy link').click().then(() => {
// * Verify clipboard content
cy.get('@clipboard').its('contents').should('contain', `/playbooks/runs/${playbookRun.id}`);
});
});
};
describe('as participant', () => {
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name(' + Date.now() + ')',
ownerUserId: testUser.id,
}).then((run) => {
playbookRun = run;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
});
describe('title, icons and buttons', () => {
commonHeaderTests();
it('has not participate button', () => {
// * Assert button is not showed
getHeader().findByText('Participate').should('not.exist');
});
describe('run actions', () => {
describe('modal behaviour', () => {
it('shows and hides as expected', () => {
// * Verify that the run actions modal is shown when clicking on the button
openRunActionsModal();
// # Click on the Cancel button
cy.findByRole('button', {name: /Cancel/i}).click();
// * Verify that the modal is no longer there
cy.findByRole('dialog', {name: /Run Actions/i}).should('not.exist');
// # Open the run actions modal
openRunActionsModal();
// Intercept all telemetry calls
cy.interceptTelemetry();
// * Verify that saving the modal hides it
saveRunActionsModal();
// * assert telemetry call
cy.expectTelemetryToContain([
{
name: 'playbookrun_update_actions',
type: 'track',
properties: {
playbookrun_id: playbookRun.id,
playbook_id: playbookRun.playbook_id,
},
},
]);
});
it('can not save an invalid form', () => {
// * Verify that the run actions modal is shown when clicking on the button
openRunActionsModal();
cy.findByRole('dialog', {name: /Run Actions/i}).within(() => {
// # click on webhooks toggle
cy.findByText('Send outgoing webhook').click();
// # Type an invalid webhook URL
cy.getStyledComponent('TextArea').clear().type('invalidurl');
// # Click outside textarea
cy.findByText('Run Actions').click();
// * Assert the error message is displayed
cy.findByText('Invalid webhook URLs').should('be.visible');
// # Click save
cy.findByTestId('modal-confirm-button').click();
// * Assert that modal is still open
cy.findByText('Run Actions').should('be.visible');
});
});
it('honours the settings from the playbook', () => {
cy.apiCreateChannel(
testTeam.id,
'action-channel',
'Action Channel',
'O',
).then(({channel}) => {
// # Create a different playbook with both settings enabled and populated with data,
// # and then start a run from it
const broadcastChannelIds = [channel.id];
const webhookOnStatusUpdateURLs = ['https://one.com', 'https://two.com'];
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook' + Date.now(),
broadcastEnabled: true,
broadcastChannelIds,
webhookOnStatusUpdateEnabled: true,
webhookOnStatusUpdateURLs,
}).then((playbook) => {
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: 'Run with actions preconfigured',
ownerUserId: testUser.id,
});
});
// # Navigate to the run page
cy.visit(`/${testTeam.name}/channels/run-with-actions-preconfigured`);
cy.findByRole('button', {name: /Run details/i}).click({force: true});
// # Open the run actions modal
openRunActionsModal();
// * Verify that the broadcast-to-channels toggle is checked
cy.findByText('Broadcast update to selected channels').parent().within(() => {
cy.get('input').should('be.checked');
});
// * Verify that the channel is in the selector
cy.findByText(channel.display_name);
// * Verify that the send-webhooks toggle is checked
cy.findByText('Send outgoing webhook').parent().within(() => {
cy.get('input').should('be.checked');
});
});
});
});
});
describe('trigger: when a status update is posted', () => {
describe('action: Broadcast update to selected channels', () => {
it('shows channel information on first load', () => {
// # Open the run actions modal
openRunActionsModal();
// # Enable broadcast to channels
cy.findByText('Broadcast update to selected channels').click();
// # Select a couple of channels
cy.findByText('Select channels').click().type('town square{enter}off-topic{enter}');
// # Save the changes
saveRunActionsModal();
// # Reload the page, so that the store is not pre-populated when visiting Channels
cy.visit(`/playbooks/runs/${playbookRun.id}/overview`);
// # Open the run actions modal
openRunActionsModal();
// * Check that the channels previously added are shown with their full name,
// * verifying that the store has been populated by the modal component.
cy.findByText('Town Square').should('exist');
cy.findByText('Off-Topic').should('exist');
});
it('broadcasts to two channels configured when it is enabled', () => {
// # Open the run actions modal
openRunActionsModal();
// # Enable broadcast to channels
cy.findByText('Broadcast update to selected channels').click();
// # Select a couple of channels
cy.findByText('Select channels').click().type('town square{enter}off-topic{enter}', {delay: 100});
// # Save the changes
saveRunActionsModal();
// # Post a status update, with a reminder in 1 second.
const message = 'Status update - ' + Date.now();
cy.apiUpdateStatus({
playbookRunId: playbookRun.id,
message,
});
// # Navigate to the town square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify that the last post contains the status update
cy.getLastPost().then((post) => {
cy.get(post).contains(message);
});
// # Navigate to the off-topic channel
cy.visit(`/${testTeam.name}/channels/off-topic`);
// * Verify that the last post contains the status update
cy.getLastPost().then((post) => {
cy.get(post).contains(message);
});
});
it('does not broadcast if it is disabled, even if there are channels configured', () => {
// # Open the run actions modal
openRunActionsModal();
// # Enable broadcast to channels
cy.findByText('Broadcast update to selected channels').click();
// # Select a couple of channels
cy.findByText('Select channels').click().type('town square{enter}off-topic{enter}', {delay: 100});
// # Disable broadcast to channels
cy.findByText('Broadcast update to selected channels').click();
// # Save the changes
saveRunActionsModal();
// # Post a status update, with a reminder in 1 second.
const message = 'Status update - ' + Date.now();
cy.apiUpdateStatus({
playbookRunId: playbookRun.id,
message,
});
// # Navigate to the town square channel
cy.visit(`/${testTeam.name}/channels/town-square`);
// * Verify that the last post does not contain the status update
cy.getLastPost().then((post) => {
cy.get(post).contains(message).should('not.exist');
});
// # Navigate to the off-topic channel
cy.visit(`/${testTeam.name}/channels/off-topic`);
// * Verify that the last post does not contain the status update
cy.getLastPost().then((post) => {
cy.get(post).contains(message).should('not.exist');
});
});
});
});
});
describe('context menu', () => {
commonContextDropdownTests();
it('can rename run', () => {
// # Click on rename run
getDropdownItemByText('Rename run').click();
cy.findByTestId('run-header-section').within(() => {
// # Type a new name
cy.findByTestId('rendered-editable-text').clear().type('The new fancy name');
// # Save
cy.findByTestId('checklist-item-save-button').click();
// * Assert name is updated
cy.get('h1').contains('The new fancy name');
});
cy.reload();
cy.findByTestId('run-header-section').within(() => {
// * Assert name is persisted
cy.get('h1').contains('The new fancy name');
});
});
describe('finish run', () => {
it('can be confirmed', () => {
// * Check that status badge is in-progress
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
// # Click on finish run
getDropdownItemByText('Finish run').click();
// # Check that finish run modal is open
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModal').find('h1').contains('Confirm finish run');
// # Click on confirm
cy.get('#confirmModal').get('#confirmModalButton').click();
// * Assert option is not anymore in context dropdown
getDropdownItemByText('Finish run').should('not.exist');
// * Assert status badge is finished
cy.findByTestId('run-header-section').findByTestId('badge').contains('Finished');
});
it('can be canceled', () => {
// * Check that status badge is in-progress
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
// # Click on finish run
getDropdownItemByText('Finish run').click();
// * Check that finish run modal is open
cy.get('#confirmModal').should('be.visible');
cy.get('#confirmModal').find('h1').contains('Confirm finish run');
// # Click on cancel
cy.get('#confirmModal').get('#cancelModalButton').click();
// * Assert option is not anymore in context dropdown
getDropdownItemByText('Finish run').should('be.visible');
// * Assert status badge is still in progress
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
});
});
describe('run actions', () => {
it('modal can be opened', () => {
// # Click on finish run
getDropdownItemByText('Run actions').click();
// * Assert modal pop up
cy.findByRole('dialog', {name: /Run Actions/i}).should('exist');
// # Click on cancel
cy.findByRole('dialog', {name: /Run Actions/i}).findByTestId('modal-cancel-button').click();
// * Assert modal disappeared
cy.findByRole('dialog', {name: /Run Actions/i}).should('not.exist');
});
});
describe('leave run', () => {
it('can leave run', () => {
// # Intercept all calls to telemetry
cy.interceptTelemetry();
// # Add viewer user to the channel
cy.apiAddUsersToRun(playbookRun.id, [testViewerUser.id]);
cy.findAllByTestId('timeline-item', {exact: false}).should('have.length', 3);
// # Change the owner to testViewerUser
cy.apiChangePlaybookRunOwner(playbookRun.id, testViewerUser.id);
cy.findByTestId('assignee-profile-selector').should('contain', testViewerUser.username);
// # Click on leave run
getDropdownItemByText('Leave and unfollow run').click();
// # confirm modal
cy.get('#confirmModal').get('#confirmModalButton').click();
// NOTE: this check fails because the front doesn't receive updated run object. Will deal in separate PR.
// * Assert that the Participate button is shown
getHeader().findByText('Participate').should('be.visible');
// * Verify run has been removed from LHS
cy.findByTestId('lhs-navigation').findByText(playbookRun.name).should('not.exist');
// # assert telemetry data
cy.expectTelemetryToContain([
{
name: 'playbookrun_leave',
type: 'track',
properties: {
from: 'run_details',
playbookrun_id: playbookRun.id,
},
},
]);
});
});
});
});
describe('as viewer', () => {
let playbookRunChannelName;
let playbookRunName;
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
const now = Date.now();
playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((run) => {
playbookRun = run;
cy.apiLogin(testViewerUser).then(() => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
});
describe('title, icons and buttons', () => {
commonHeaderTests();
describe('Favorite', () => {
it('add and remove from LHS', () => {
// # Click fav icon
getHeader().getStyledComponent('StarButton').click();
// * Assert run appears in LHS
cy.findByTestId('lhs-navigation').findByText(playbookRunName).should('exist');
// # Click fav icon again (unfav)
getHeader().getStyledComponent('StarButton').click();
// * Assert run disappeared from LHS
cy.findByTestId('lhs-navigation').findByText(playbookRunName).should('not.exist');
});
});
describe('Participate', () => {
it('shows button', () => {
// * Assert that the button is shown
getHeader().findByText('Participate').should('be.visible');
});
describe('Join action enabled', () => {
it('click button to show modal and cancel', () => {
// * Assert that component is rendered
getHeader().findByText('Participate').should('be.visible');
// # Click Participate button
getHeader().findByText('Participate').click();
// * Verify modal message is correct
cy.findByText('Youll also be added to the channel linked to this run.').should('exist');
// # cancel modal
cy.findByTestId('modal-cancel-button').click();
// * Assert modal is not shown
cy.get('#become-participant-modal').should('not.exist');
// # Login as testUser
cy.apiLogin(testUser).then(() => {
// # Visit the channel run
cy.visit(`${testTeam.name}/channels/${playbookRunChannelName}`);
// * Assert user has not been added to the channel
cy.getLastPost().should('not.contain', 'Someone');
cy.getLastPost().should('not.contain', testViewerUser.username);
});
});
it('click button to show modal and confirm when private channel', () => {
// # Intercept all calls to telemetry
cy.interceptTelemetry();
// * Assert component is rendered
getHeader().findByText('Participate').should('be.visible');
// # Click start-participating button
getHeader().findByText('Participate').click();
// * Verify modal message is correct
cy.findByText('Youll also be added to the channel linked to this run.').should('exist');
// # confirm modal
cy.findByTestId('modal-confirm-button').click();
// * Assert that modal is not shown
cy.get('#become-participant-modal').should('not.exist');
// * Verify run has been added to LHS
verifyRunHasBeenAddedToLHS(playbookRunName);
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Verify channel loads
cy.get('#channelHeaderTitle').should('be.visible').should('contain', playbookRunName);
// * assert telemetry data
cy.expectTelemetryToContain([
{
name: 'playbookrun_participate',
type: 'track',
properties: {
from: 'run_details',
playbookrun_id: playbookRun.id,
},
},
]);
});
it('click button and confirm to when public channel', () => {
// # Login as testUser
cy.apiLogin(testUser);
const now = Date.now();
playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
// # Create a run with public chanel
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybookAndChannel.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((run) => {
cy.apiLogin(testViewerUser);
// # Visit the playbook run
cy.visit(`/playbooks/runs/${run.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
// * Assert that component is rendered
getHeader().findByText('Participate').should('be.visible');
// # Click start-participating button
getHeader().findByText('Participate').click();
// * Verify modal message is correct
cy.findByText('Youll also be added to the channel linked to this run.').should('exist');
// # confirm modal
cy.findByTestId('modal-confirm-button').click();
// * Assert that modal is not shown
cy.get('#become-participant-modal').should('not.exist');
// * Verify run has been added to LHS
cy.findByTestId('lhs-navigation').findByText(playbookRunName).should('exist');
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Verify channel loads
cy.get('#channelHeaderTitle').should('be.visible').should('contain', playbookRunName);
});
});
});
describe('Join action disabled', () => {
beforeEach(() => {
cy.apiLogin(testUser);
// # Disable join action
cy.apiUpdateRun(playbookRun.id, {createChannelMemberOnNewParticipant: false});
cy.apiLogin(testViewerUser).then(() => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
it('join the run with private channel, request to join the channel', () => {
// # Click start-participating button
getHeader().findByText('Participate').click();
// * Verify modal message is correct
cy.findByText('Request access to the channel linked to this run').should('exist');
// # Select checkbox
cy.findByTestId('also-add-to-channel').click({force: true});
// # confirm modal
cy.findByTestId('modal-confirm-button').click();
// * Assert that modal is not shown
cy.get('#become-participant-modal').should('not.exist');
// * Verify run has been added to LHS
verifyRunHasBeenAddedToLHS(playbookRunName);
// # Login as testUser to check if join request was posted in the channel
cy.apiLogin(testUser);
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the request was sent to the channel
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).within(() => {
cy.contains(`@${testViewerUser.username} is a run participant and wants join this channel. Any member of the channel can invite them.`);
});
});
});
it('join the run with private channel, no request to join the channel', () => {
// # Click start-participating button
getHeader().findByText('Participate').click();
// * Verify modal message is correct
cy.findByText('Request access to the channel linked to this run').should('exist');
// # confirm modal
cy.findByTestId('modal-confirm-button').click();
// * Assert that modal is not shown
cy.get('#become-participant-modal').should('not.exist');
// * Verify run has been added to LHS
verifyRunHasBeenAddedToLHS(playbookRunName);
// # Login as testUser to check if join request was posted in the channel
cy.apiLogin(testUser);
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * Verify that the request was sent to the channel
cy.getLastPostId().then((id) => {
cy.get(`#postMessageText_${id}`).within(() => {
cy.contains(`@${testViewerUser.username} is a run participant and wants join this channel. Any member of the channel can invite them.`).should('not.exist');
});
});
});
it('join run with public channel, join the channel', () => {
// # Login as testUser
cy.apiLogin(testUser);
const now = Date.now();
playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
// Create a run with public chanel
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybookAndChannel.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((run) => {
cy.apiLogin(testViewerUser);
// # Visit the playbook run
cy.visit(`/playbooks/runs/${run.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
// * Assert that component is rendered
getHeader().findByText('Participate').should('be.visible');
// # Click start-participating button
getHeader().findByText('Participate').click();
// * Verify modal message is correct
cy.findByText('Youll also be added to the channel linked to this run.').should('exist');
// # confirm modal
cy.findByTestId('modal-confirm-button').click();
// * Assert that modal is not shown
cy.get('#become-participant-modal').should('not.exist');
// * Verify run has been added to LHS
cy.findByTestId('lhs-navigation').findByText(playbookRunName).should('exist');
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// # Verify channel loads
cy.get('#channelHeaderTitle').should('be.visible').should('contain', playbookRunName);
});
});
});
});
describe('run actions', () => {
describe('modal behaviour', () => {
it('modal can be opened read-only', () => {
// # Click on run actions
getDropdownItemByText('Run actions').click();
// * Assert modal pop up
cy.findByRole('dialog', {name: /Run Actions/i}).should('exist');
// * Assert there are no buttons
cy.findByRole('dialog', {name: /Run Actions/i}).findByTestId('modal-cancel-button').should('not.exist');
cy.findByRole('button', {name: /Save/i}).should('not.exist');
// # Close modal
cy.findByRole('dialog', {name: /Run Actions/i}).find('.close').click();
});
});
});
});
describe('context menu', () => {
commonContextDropdownTests();
it('can not rename run', () => {
// # There's no rename option
getDropdownItemByText('Rename run').should('not.exist');
});
it('can not finish run', () => {
// * There's no finish run item
getDropdownItemByText('Finish run').should('not.exist');
});
describe('run actions', () => {
it('modal can be opened read-only', () => {
// # Click on finish run
getDropdownItemByText('Run actions').click();
// * Assert modal pop up
cy.findByRole('dialog', {name: /Run Actions/i}).should('exist');
// * Assert there are no buttons
cy.findByRole('dialog', {name: /Run Actions/i}).findByTestId('modal-cancel-button').should('not.exist');
cy.findByRole('button', {name: /Save/i}).should('not.exist');
// # Close modal
cy.findByRole('dialog', {name: /Run Actions/i}).find('.close').click();
});
});
});
});
});
const verifyRunHasBeenAddedToLHS = (playbookRunName) => {
// * Verify run has been added to LHS
cy.findByTestId('lhs-navigation').
should('be.visible').
findByText(playbookRunName).
should('be.visible');
};

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

@@ -1,107 +0,0 @@
// 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 > run details page > restart run', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
// const taskIndex = 0;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
{
title: 'Stage 2',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
},
],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
describe('restart run', () => {
it('can be confirmed', () => {
cy.intercept('PUT', `/plugins/playbooks/api/v0/runs/${testRun.id}/finish`).as('routeFinish');
cy.intercept('PUT', `/plugins/playbooks/api/v0/runs/${testRun.id}/restore`).as('routeRestore');
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
// # Click finish run button
cy.findByTestId('run-finish-section').find('button').click();
cy.get('#confirmModal').get('#confirmModalButton').click();
cy.wait('@routeFinish');
cy.findByTestId('run-header-section').findByTestId('badge').contains('Finished');
cy.findByTestId('runDropdown').click();
cy.get('.restartRun').find('span').contains('Restart run');
cy.get('.restartRun').click();
cy.get('#confirmModal').get('#confirmModalButton').click();
cy.wait('@routeRestore');
cy.findByTestId('run-header-section').findByTestId('badge').contains('In Progress');
cy.findByTestId('lhs-navigation').findByText(testRun.name).should('exist');
},
);
});
});

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

@@ -1,500 +0,0 @@
// 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
const editAndPublishRetro = () => {
getRetro().within(() => {
// # Start editing
cy.findByTestId('retro-report-text').click();
// * Verify the provided template text is pre-filled
cy.focused().should('include.text', 'This is a retrospective template.');
// # Change the retro text
cy.focused().clear().type('Edited retrospective.');
// # Save it by clicking outside the text area
cy.findByText('Report').click();
// # 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();
});
// * Verify that retro got published
getRetro().get('.icon-check-all').should('be.visible');
};
const getMetricInput = (index) => getRetro().getStyledComponent('InputContainer').eq(index);
const verifyMetricInput = (index, title, target, description, placeholder) => {
getMetricInput(index).within(() => {
cy.getStyledComponent('Title').contains(title);
if (target) {
cy.getStyledComponent('TargetTitle').contains(target);
} else {
cy.getStyledComponent('TargetTitle').should('not.exist');
}
if (description) {
cy.getStyledComponent('HelpText').contains(description);
}
if (placeholder) {
cy.get('input').should('have.attr', 'placeholder', placeholder);
}
});
};
const getRetro = () => cy.findByTestId('run-retrospective-section');
describe('runs > run details page', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testPublicPlaybookWithMetrics;
let testRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
retrospectiveTemplate: 'This is a retrospective template.',
}).then((playbook) => {
testPublicPlaybook = playbook;
});
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
metrics: [
{
title: 'title1',
description: 'description1',
type: 'metric_duration',
target: 720000,
},
{
title: 'title2',
description: 'description2',
type: 'metric_currency',
target: 40,
},
{
title: 'title3',
description: 'description3',
type: 'metric_integer',
target: 30,
},
],
}).then((playbook) => {
testPublicPlaybookWithMetrics = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
});
describe('retrospective', () => {
const commonTests = () => {
it('is visible', () => {
// * Verify the retrospective section is present
getRetro().should('be.visible');
});
it('has title', () => {
// * Verify the retrospective section has a title
getRetro().find('h3').contains('Retrospective');
});
it('has template text', () => {
// * Verify the retrospective text is rendered
getRetro().findByTestId('retro-report-text').contains('This is a retrospective template.');
});
it('has no metrics', () => {
// * Verify there are no metric for this playbook
getRetro().getStyledComponent('InputContainer').should('not.exist');
});
};
describe('as participant', () => {
beforeEach(() => {
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
commonTests();
it('publishing posts to run channel', () => {
editAndPublishRetro();
// # Switch to the run channel
cy.findByTestId('runinfo-channel-link').click();
// * Verify the modified retro text is posted
cy.getStyledComponent('CustomPostContent').should('exist').contains('Edited retrospective.');
});
it('can be published once', () => {
editAndPublishRetro();
// * Verify the button is disabled
getRetro().findByText('Publish').should('not.be.enabled');
});
});
describe('as viewer', () => {
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create test playbook run
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
});
});
beforeEach(() => {
// Login as the test viewer
cy.apiLogin(testViewerUser);
// # Visit the playbook run
cy.visit(`/playbooks/runs/${testRun.id}`);
});
commonTests();
it('text is not clickable', () => {
getRetro().findByTestId('retro-report-text').click();
getRetro().find('textarea').should('not.exist');
});
it('there is no publish button', () => {
getRetro().findByText('Publish').should('not.exist');
});
});
});
describe('metrics', () => {
const commonTests = () => {
it('inputs info(title, target, description) and order', () => {
// * Verify the created metrics
verifyMetricInput(0, 'title1', '12 minutes', 'description1', 'Add value (in dd:hh:mm)');
verifyMetricInput(1, 'title2', '40', 'description2', 'Add value');
verifyMetricInput(2, 'title3', '30', 'description3', 'Add value');
});
};
describe('as participant', () => {
beforeEach(() => {
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybookWithMetrics.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
});
});
beforeEach(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
commonTests();
it('inputs, null and zero values', () => {
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
createPublicPlaybookRun: true,
metrics: [
{
title: 'title1',
description: 'description1',
type: 'metric_duration',
target: null,
},
{
title: 'title2',
description: 'description2',
type: 'metric_currency',
target: 0,
},
{
title: 'title3',
description: 'description3',
type: 'metric_integer',
target: 30,
},
],
}).then((playbook) => {
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
// # Navigate directly to the retro tab
cy.visit(`/playbooks/runs/${playbookRun.id}`);
// * Verify changes are reflected
verifyMetricInput(0, 'title1', null, 'description1', 'Add value (in dd:hh:mm)');
verifyMetricInput(1, 'title2', '0', 'description2', 'Add value');
verifyMetricInput(2, 'title3', '30', 'description3', 'Add value');
});
});
});
it('auto save', () => {
getRetro().within(() => {
// # Enter metric values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('12:11:10').
tab().type('56').
tab().type('123');
// # Click outside
cy.findByText('Retrospective').click({force: true});
cy.wait(2000);
// * Validate if values persist
cy.get('input[type=text]').eq(0).should('have.value', '12:11:10');
cy.get('input[type=text]').eq(1).should('have.value', '56');
cy.get('input[type=text]').eq(2).should('have.value', '123');
// # Enter new values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).clear().type('12:00:10').
tab().clear().type('20').
tab().clear().type('21');
});
// # Wait 2 sec to auto save
cy.wait(2000);
// # Reload page
cy.visit(`/playbooks/runs/${testRun.id}`);
getRetro().within(() => {
// * Validate if values are saved
cy.get('input[type=text]').eq(0).should('have.value', '12:00:10');
cy.get('input[type=text]').eq(1).should('have.value', '20');
cy.get('input[type=text]').eq(2).should('have.value', '21');
});
});
it('save empty and zero values', () => {
getRetro().within(() => {
// # Enter metric values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).clear().type('00:00:00').
tab().type('7').
tab().type('0');
// # Click outside
cy.findByText('Retrospective').click({force: true});
// * Validate if values persist
cy.get('input[type=text]').eq(0).should('have.value', '00:00:00');
cy.get('input[type=text]').eq(1).should('have.value', '7');
cy.get('input[type=text]').eq(2).should('have.value', '0');
// # Clear first two metrics values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).clear().
tab().clear();
// # Click outside
cy.findByText('Retrospective').click({force: true});
// * Validate if values persist
cy.get('input[type=text]').eq(0).should('have.value', '');
cy.get('input[type=text]').eq(1).should('have.value', '');
cy.get('input[type=text]').eq(2).should('have.value', '0');
});
});
it('only valid values are saved. check error messages', () => {
getRetro().within(() => {
// # Enter invalid metric values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('5').
tab().type('56d').
tab().type('125');
// * Validate error messages
cy.getStyledComponent('ErrorText').eq(0).contains('Please enter a duration in the format: dd:hh:mm (e.g., 12:00:00).');
cy.getStyledComponent('ErrorText').eq(1).contains('Please enter a number.');
// # Click outside
cy.findByText('Retrospective').click({force: true});
});
// # Reload page and navigate to the retro tab
cy.visit(`/playbooks/runs/${testRun.id}`);
getRetro().within(() => {
// * Validate that values are not saved
cy.get('input[type=text]').eq(0).should('have.value', '');
cy.get('input[type=text]').eq(1).should('have.value', '');
cy.get('input[type=text]').eq(2).should('have.value', '125');
// # Enter new metric values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('s').
tab().type('d').
tab().type('k');
});
});
it('publish retro', () => {
getRetro().within(() => {
// # Enter metric invalid values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('20:00:12d').
tab().type('56').
tab().type('125v');
// # Publish
cy.findByRole('button', {name: 'Publish'}).click();
});
// * Verify we're not showing the publish retro confirmation modal
cy.get('#confirm-modal-light').should('not.exist');
getRetro().within(() => {
//# Enter empty metric values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).clear().
tab().clear().
tab().clear().type(24);
// # Publish
cy.findByRole('button', {name: 'Publish'}).click();
// * Validate error messages
cy.getStyledComponent('ErrorText').eq(0).contains('Please fill in the metric value.');
cy.getStyledComponent('ErrorText').eq(1).contains('Please fill in the metric value.');
cy.getStyledComponent('ErrorText').should('have.length', 2);
});
// * Verify we're not showing the publish retro confirmation modal
cy.get('#confirm-modal-light').should('not.exist');
getRetro().within(() => {
//# Enter valid metric values
cy.get('input[type=text]').eq(0).click();
cy.get('input[type=text]').eq(0).type('09:87:12').
tab().type(123);
// # 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();
});
getRetro().within(() => {
// * Verify that retro got published
cy.get('.icon-check-all').should('be.visible');
// * Verify that metrics inputs are disabled
cy.get('input[type=text]').each(($el) => {
cy.wrap($el).should('not.be.enabled');
});
});
});
});
describe('as viewer', () => {
before(() => {
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybookWithMetrics.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
});
});
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
commonTests();
it('are not editable', () => {
// * Verify that inputs are disabled
getMetricInput(0).find('input').should('be.disabled');
getMetricInput(1).find('input').should('be.disabled');
getMetricInput(2).find('input').should('be.disabled');
});
});
});
});

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

@@ -1,304 +0,0 @@
// 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 > run details page > status update', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
let playbookRunChannelName;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
const now = Date.now();
const playbookRunName = 'Playbook Run (' + now + ')';
playbookRunChannelName = 'playbook-run-' + now;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName,
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
});
describe('as participant', () => {
it('is visible', () => {
// * Verify the status update section is present
cy.findByTestId('run-statusupdate-section').should('be.visible');
});
it('has no title', () => {
// * Verify the title
cy.findByTestId('run-statusupdate-section').find('h3').should('not.exist');
});
describe('post update', () => {
it('button disappears if we finish the run', () => {
// * Check that post update button is visible
cy.findByTestId('run-statusupdate-section').findByTestId('post-update-button').should('be.visible');
// # Click finish button and confirm modal
cy.findByTestId('run-finish-section').find('button').click();
cy.get('#confirmModal').get('#confirmModalButton').click();
// * Check that post update button does not exist anymore
cy.findByTestId('run-statusupdate-section').findByTestId('post-update-button').should('not.exist');
});
it('button triggers post update modal', () => {
// * Check due date
cy.findByTestId('update-due-date-text').contains('Update due');
cy.findByTestId('update-due-date-time').contains('in 24 hours');
// # Click post update
cy.findByTestId('run-statusupdate-section').findByTestId('post-update-button').click();
// * Assert modal is opened
cy.getStatusUpdateDialog().should('be.visible');
// # Write message
cy.findByTestId('update_run_status_textbox').clear().type('my nice update');
cy.get('#reminder_timer_datetime').within(() => {
cy.get('input').type('15 minutes', {delay: 200, force: true}).type('{enter}', {force: true});
});
// # Post update
cy.getStatusUpdateDialog().findByTestId('modal-confirm-button').click();
// * Check new due date
cy.findByTestId('update-due-date-text').contains('Update due');
cy.findByTestId('update-due-date-time').contains('in 15 minutes');
// # Intercept all calls to telemetry
cy.interceptTelemetry();
// # go to channel
cy.visit(`/${testTeam.name}/channels/${playbookRunChannelName}`);
// * check that post has been added
cy.getLastPost().contains('my nice update');
// * assert telemetry pageview
cy.expectTelemetryToContain([
{
name: 'run_status_update',
type: 'page',
properties: {
channel_type: 'P',
},
},
]);
});
});
describe('request an update', () => {
it('is disabled if the run is finished', () => {
cy.apiFinishRun(testRun.id).then(() => {
// # reload url
cy.visit(`/playbooks/runs/${testRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
// # Click on kebab menu
cy.findByTestId('run-statusupdate-section').getStyledComponent('Kebab').click();
// # click on request update option (force because is disabled)
cy.findByText('Request update...').click({force: true});
// * assert modal is not opened
cy.get('#confirmModalButton').should('not.exist');
});
});
it('requests and confirm', () => {
// # Click on kebab menu
cy.findByTestId('run-statusupdate-section').getStyledComponent('Kebab').click();
cy.findByTestId('dropdownmenu').within(($dropdown) => {
cy.wrap($dropdown).children().should('have.length', 2);
// # Click on request update
cy.findByText('Request update...').click();
});
// # Click on modal confirmation
cy.get('#confirmModalButton').click();
// # Go to channel
cy.visit(`${testTeam.name}/channels/${playbookRunChannelName}`);
// * Assert that message has been sent
cy.getLastPost().contains(`${testUser.username} requested a status update for ${testRun.name}.`);
});
it('requests and cancel', () => {
// # Click on kebab menu
cy.findByTestId('run-statusupdate-section').getStyledComponent('Kebab').click();
cy.findByTestId('dropdownmenu').within(($dropdown) => {
cy.wrap($dropdown).children().should('have.length', 2);
// # Click on request update
cy.findByText('Request update...').click();
});
// # Click on modal confirmation
cy.get('#cancelModalButton').click();
// # Go to channel
cy.visit(`${testTeam.name}/channels/${playbookRunChannelName}`);
// * Assert that message has not been sent
cy.getLastPost().should('not.contain', `${testUser.username} requested a status update for ${testRun.name}.`);
});
});
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
});
it('is visible', () => {
// * Verify the status update section is present
cy.findByTestId('run-statusupdate-section').should('be.visible');
});
it('has a title', () => {
// * Verify the title
cy.findByTestId('run-statusupdate-section').find('h3').contains('Recent status update');
});
it('has placeholder', () => {
// * Verify the placeholder
cy.findByTestId('run-statusupdate-section').find('i').contains('No updates have been posted yet');
});
it('has a due date', () => {
// * Verify the due date
cy.findByTestId('update-due-date-text').contains('Update due');
cy.findByTestId('update-due-date-time').contains('in 24 hours');
});
it('shows the most recent update', () => {
// # Login as participant
cy.apiLogin(testUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
// # Click post update
cy.findByTestId('run-statusupdate-section').
should('be.visible').
findByTestId('post-update-button').click();
// * Assert modal is opened
cy.getStatusUpdateDialog().should('be.visible');
// # Write message
cy.findByTestId('update_run_status_textbox').clear().type('my nice update');
cy.get('#reminder_timer_datetime').within(() => {
cy.get('input').type('15 minutes', {delay: 200, force: true}).type('{enter}', {force: true});
});
// # Post update
cy.getStatusUpdateDialog().findByTestId('modal-confirm-button').click();
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
// * Check new due date
cy.findByTestId('update-due-date-text').contains('Update due');
cy.findByTestId('update-due-date-time').contains('in 15 minutes');
// * Assert the recent updated text
cy.findByTestId('status-update-card').contains('my nice update');
});
});
it('requests an update and confirm', () => {
// # Click on request update
cy.findByTestId('run-statusupdate-section').
should('be.visible').
findByText('Request update...').click();
// # Click on modal confirmation
cy.get('#confirmModalButton').click();
cy.apiLogin(testUser).then(() => {
// # Go to channel
cy.visit(`${testTeam.name}/channels/${playbookRunChannelName}`);
// * Assert that message has been sent
cy.getLastPost().contains(`${testViewerUser.username} requested a status update for ${testRun.name}.`);
});
});
it('requests an update and cancel', () => {
// # Click request update
cy.findByTestId('run-statusupdate-section').
should('be.visible').
findByText('Request update...').click();
// # Click on modal confirmation
cy.get('#cancelModalButton').click();
cy.apiLogin(testUser).then(() => {
// # Go to channel
cy.visit(`${testTeam.name}/channels/${playbookRunChannelName}`);
// * Assert that message has been sent
cy.getLastPost().should('not.contain', `${testUser.username} requested a status update for ${testPublicPlaybook.name}].`);
});
});
});
});

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

@@ -1,162 +0,0 @@
// 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 > run details page > summary', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testRun;
let testViewerUser;
let testPublicPlaybook;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
const commonTests = () => {
it('is visible', () => {
// * Verify the summary section is present
cy.findByTestId('run-summary-section').should('be.visible');
});
it('has title', () => {
// * Verify the summary section is present
cy.findByTestId('run-summary-section').find('h3').contains('Summary');
});
};
describe('as participant', () => {
commonTests();
it('has a placeholder', () => {
// * Assert the placeholder content
cy.findByTestId('run-summary-section').findByTestId('rendered-text').contains('Add a run summary');
});
it('can be edited', () => {
// # Mouseover the summary
cy.findByTestId('run-summary-section').trigger('mouseover');
cy.findByTestId('run-summary-section').within(() => {
// # Click the edit icon
cy.findByTestId('hover-menu-edit-button').click();
// # Write a summary
cy.findByTestId('editabletext-markdown-textbox2').clear().type('This is my new summary');
// # Save changes
cy.findByTestId('checklist-item-save-button').click();
// * Assert that data has changed
cy.findByTestId('rendered-text').contains('This is my new summary');
});
// * Assert last edition date is visible
cy.findByTestId('run-summary-section').contains('Last edited');
});
it('can be canceled', () => {
// # Mouseover the summary
cy.findByTestId('run-summary-section').trigger('mouseover');
cy.findByTestId('run-summary-section').within(() => {
// # Click the edit icon
cy.findByTestId('hover-menu-edit-button').click();
// # Write a summary
cy.findByTestId('editabletext-markdown-textbox2').clear().type('This is my new summary');
// # Cancel changes
cy.findByText('Cancel').click();
// * Assert that data has not changed
cy.findByTestId('rendered-text').contains('Add a run summary');
});
// * Assert last edition date is not visible
cy.findByTestId('run-summary-section').should('not.contain', 'Last edited');
});
it('can not be edited once run is finished', () => {
// # Finish the run
cy.apiFinishRun(testRun.id);
// # Mouseover the summary
cy.findByTestId('run-summary-section').trigger('mouseover');
// * Verify that the edit button is not rendered
cy.findByTestId('run-summary-section').findByTestId('hover-menu-edit-button').should('not.exist');
});
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
commonTests();
it('has a placeholder', () => {
// * Assert the placeholder content
cy.findByTestId('run-summary-section').findByTestId('rendered-text').contains('There\'s no summary');
});
it('can not be edited', () => {
// # Mouseover the summary
cy.findByTestId('run-summary-section').trigger('mouseover');
// * Verify that the edit button is not rendered
cy.findByTestId('run-summary-section').findByTestId('hover-menu-edit-button').should('not.exist');
});
});
});

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

@@ -1,712 +0,0 @@
// 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('runs > task actions', {testIsolation: true}, () => {
let testPlaybook;
let testTeam;
let testUser;
let testUser2;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateUser().then(({user: user2}) => {
testUser2 = user2;
// # Add this new user to the team
cy.apiAddUserToTeam(team.id, testUser2.id);
});
// # Create a playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Playbook (' + Date.now() + ')',
checklists: [{
title: 'Test Checklist',
items: [
{title: 'Test Task'},
],
}],
memberIDs: [
testUser.id,
],
}).then((playbook) => {
testPlaybook = playbook;
});
});
});
beforeEach(() => {
// # intercepts telemetry
cy.interceptTelemetry();
// # Login as testUser
cy.apiLogin(testUser);
});
describe('keywords trigger - mark task as done', () => {
let testPlaybookRun;
const getChecklist = () => cy.findByTestId('run-checklist-section');
const getChecklistTasks = () => getChecklist().findAllByTestId('checkbox-item-container');
beforeEach(() => {
// # Run a playbook
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: `the run name ${Date.now()}`,
ownerUserId: testUser.id,
}).then((playbookRun) => {
testPlaybookRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
it('disallows no keywords', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify no actions are configured
cy.findByText('Task Actions').should('exist');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, []);
assert.deepEqual(trigger.user_ids, []);
assert.isFalse(actions.enabled);
});
});
it('allows a single keyword', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// # Enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
// # assert telemetry data
cy.expectTelemetryToContain([
{
name: 'taskactions_updated',
type: 'track',
properties: {
playbookrun_id: testPlaybookRun.id,
},
},
]);
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser2.username} checked off checklist item "Test Task"`);
});
it('allows multiple keywords', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add multiple keywords
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
cy.get('input').eq(0).type('keyword2{enter}', {force: true});
});
// # Enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1', 'keyword2']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword2 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser2.username} checked off checklist item "Test Task"`);
});
it('allows multi-word phrases', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a phrase
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('a phrase with multiple words{enter}', {force: true});
});
// # Enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['a phrase with multiple words']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and a phrase with multiple words happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser2.username} checked off checklist item "Test Task"`);
});
it('allows removing previously configured keywords', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add multiple keywords
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
cy.get('input').eq(0).type('keyword2{enter}', {force: true});
});
// # Enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// # Re-open the dialog
cy.findByText('1 action').click();
// # Remove one trigger keyword
cy.get('.modal-body').within(() => {
cy.findByText('keyword1').next().click();
});
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword2']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
// # Post without activating trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action not activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('not.be.checked');
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword2 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser2.username} checked off checklist item "Test Task"`);
});
it('disables when all keywords removed', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add multiple keywords
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
cy.get('input').eq(0).type('keyword2{enter}', {force: true});
});
// # Enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// # Re-open the dialog
cy.findByText('1 action').click();
// # Remove all trigger keywords
cy.get('.modal-body').within(() => {
cy.findByText('keyword1').next().click();
cy.findByText('keyword2').next().click();
});
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions
cy.findByText('Task Actions');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, []);
assert.deepEqual(trigger.user_ids, []);
assert.isFalse(actions.enabled);
});
// # Post without activating trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh keyword1 keyword2 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action not activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('not.be.checked');
});
it('disallows a user without keywords', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a user
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// # Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify no actions are configured
cy.findByText('Task Actions');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, []);
assert.deepEqual(trigger.user_ids, [testUser.id]);
assert.isFalse(actions.enabled);
});
});
it('allows a single user', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// # Add a user
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// # Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions and user
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, [testUser.id]);
assert.isTrue(actions.enabled);
});
// # Post without activating trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action not activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('not.be.checked');
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser.id);
cy.postMessageAs({
sender: testUser,
message: `hello from ${testUser.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser.username} checked off checklist item "Test Task"`);
});
it('allows configuring multiple users', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// # Add two users
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
cy.get('input').eq(1).
type('@' + testUser2.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// # Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions and user
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, [testUser.id, testUser2.id]);
assert.isTrue(actions.enabled);
});
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser.id);
cy.postMessageAs({
sender: testUser,
message: `hello from ${testUser.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser.username} checked off checklist item "Test Task"`);
// # Reset-uncheck task
cy.apiSetChecklistItemState(testPlaybookRun.id, 0, 0, '');
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('not.be.checked');
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser2.username} checked off checklist item "Test Task"`);
});
it('rejects unknown user', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// # Type an unknown user
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@unknown', {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// # Click away
cy.get('.modal-body').click();
// # Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions and user
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser.id);
cy.postMessageAs({
sender: testUser,
message: `hello from ${testUser.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser.username} checked off checklist item "Test Task"`);
});
it('allows removing previously configured users', () => {
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// # Add two users
cy.get('.modal-body').within(() => {
cy.get('input').eq(1).
type('@' + testUser.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
cy.get('input').eq(1).
type('@' + testUser2.username, {force: true}).
wait(TIMEOUTS.ONE_SEC).
type('{enter}', {force: true});
});
// # Attempt to enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// # Re-open the dialog
cy.findByText('1 action').click();
// # Remove one user keyword
cy.get('.modal-body').within(() => {
cy.findByText(testUser.username).parent().parent().next().click();
});
// Save the dialog
cy.findByTestId('modal-confirm-button').click();
// Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybookRun(testPlaybookRun.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, [testUser2.id]);
assert.isTrue(actions.enabled);
});
// # Post without activating trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser.id);
cy.postMessageAs({
sender: testUser,
message: `hello from ${testUser.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action NOT activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('not.be.checked');
// # Attempt to activate trigger
cy.apiAddUserToChannel(testPlaybookRun.channel_id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testPlaybookRun.channel_id,
});
// * Verify action activated
getChecklistTasks().eq(0).find('input[type="checkbox"]').should('be.checked');
cy.findAllByTestId('timeline-item task_state_modified').findByText(`${testUser2.username} checked off checklist item "Test Task"`);
});
});
describe('keywords trigger - mark task as done, multiple runs in a channel', () => {
let testChannel;
let testPlaybookRun1;
let testPlaybookRun2;
const configureTaskAction = (run) => {
// # Visit the playbook run
cy.visit(`/playbooks/runs/${run.id}`);
// # Open the task actions modal
cy.findByText('Task Actions').click();
// # Add a keyword
cy.get('.modal-body').within(() => {
cy.get('input').eq(0).type('keyword1{enter}', {force: true});
});
// # Enable the trigger
cy.findByText('Mark the task as done').click();
// # Save the dialog
cy.findByTestId('modal-confirm-button').click();
// * Verify configured actions
cy.findByText('1 action');
cy.apiGetPlaybookRun(run.id).then(({body: playbookRun}) => {
const trigger = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].trigger.payload);
const actions = JSON.parse(playbookRun.checklists[0].items[0].task_actions[0].actions[0].payload);
assert.deepEqual(trigger.keywords, ['keyword1']);
assert.deepEqual(trigger.user_ids, []);
assert.isTrue(actions.enabled);
});
};
beforeEach(() => {
cy.apiCreateChannel(testTeam.id, 'channel', 'Channel').then(({channel}) => {
testChannel = channel;
// # Run #1
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: `the run name ${Date.now()}`,
ownerUserId: testUser.id,
channelId: testChannel.id,
}).then((playbookRun) => {
testPlaybookRun1 = playbookRun;
configureTaskAction(testPlaybookRun1);
});
// # Run #2
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPlaybook.id,
playbookRunName: `the run name ${Date.now()}`,
ownerUserId: testUser.id,
channelId: testChannel.id,
}).then((playbookRun) => {
testPlaybookRun2 = playbookRun;
configureTaskAction(testPlaybookRun2);
});
});
});
it('triggers', () => {
// # Attempt to activate trigger
cy.apiAddUserToChannel(testChannel.id, testUser2.id);
cy.postMessageAs({
sender: testUser2,
message: `hello from ${testUser2.username}: ${Date.now()}, oh and keyword1 happened`,
channelId: testChannel.id,
});
// Give the system a chance to effect the task actions.
cy.wait(TIMEOUTS.HALF_SEC);
// * Verify action activated ion testPlaybookRun1
cy.apiGetPlaybookRun(testPlaybookRun1.id).then(({body: playbookRun}) => {
assert.equal(playbookRun.checklists[0].items[0].state, 'closed');
});
// * Verify action activated in testPlaybookRun2
cy.apiGetPlaybookRun(testPlaybookRun2.id).then(({body: playbookRun}) => {
assert.equal(playbookRun.checklists[0].items[0].state, 'closed');
});
});
});
});

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

@@ -1,266 +0,0 @@
// 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 > run details page > rhs > participants', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testUser2;
let testViewerUser;
let testPublicPlaybook;
let testRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testUser2 = viewer;
cy.apiAddUserToTeam(testTeam.id, testUser2.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the-run-name' + Date.now(),
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Add viewer user to the channel
cy.apiAddUsersToRun(testRun.id, [testUser2.id]);
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
describe('as participant', () => {
it('switching between manage modes', () => {
navigateToParticipantsList();
// # Switch to manage mode
cy.findByRole('button', {name: 'Manage'}).click();
// * Verify that we are in manage mode
cy.findByRole('button', {name: 'Manage'}).should('not.exist');
// # Switch to normal mode
cy.findByRole('button', {name: 'Done'}).click();
// * Verify that we are in normal mode
cy.findByRole('button', {name: 'Manage'}).should('exist');
});
it('change owner', () => {
navigateToParticipantsList();
// * Verify run owner
cy.findByTestId('run-owner').contains(testUser.username);
// # Switch to manage mode
cy.findByRole('button', {name: 'Manage'}).click();
// # Change owner
cy.findByTestId(testUser2.id).findByTestId('menuButton').click();
cy.findByTestId('dropdownmenu').findByText('Make run owner').click();
// # Wait for changes to apply
cy.wait(2000);
// * Verify the owner has changed
cy.findByTestId('run-owner').contains(testUser2.username);
});
it('remove participant', () => {
navigateToParticipantsList();
// * Verify run owner
cy.findByTestId('run-owner').contains(testUser.username);
// # Switch to manage mode
cy.findByRole('button', {name: 'Manage'}).click();
// # remove participant
cy.findByTestId(testUser2.id).findByTestId('menuButton').click();
cy.findByTestId('dropdownmenu').findByText('Remove from run').click();
// * Verify the user has been removed
cy.findByTestId(testUser2.id).should('not.exist');
});
describe('add participant', () => {
it('join action enabled', () => {
navigateToParticipantsList();
// * Verify run owner
cy.findByTestId('run-owner').contains(testUser.username);
// # show add participant modal
cy.findByRole('button', {name: 'Add'}).click();
// # Select two new participants
cy.get('#profile-autocomplete').click().type(testUser2.username + '{enter}', {delay: 400});
cy.get('#profile-autocomplete').click().type(testViewerUser.username + '{enter}', {delay: 400});
// # Intercept all calls to telemetry
cy.interceptTelemetry();
// * Verify modal message is correct
cy.findByText('Participants will also be added to the channel linked to this run').should('exist');
// # Add selected participant
cy.findByTestId('modal-confirm-button').click();
// * Verify telemetry
cy.expectTelemetryToContain([
{
name: 'playbookrun_participate',
type: 'track',
properties: {
from: 'run_details',
trigger: 'add_participant',
count: '2',
},
},
]);
// * Verify the users have been added
cy.findByTestId(testUser2.id).should('exist');
cy.findByTestId(testViewerUser.id).should('exist');
});
it('join action disabled', () => {
cy.apiUpdateRun(testRun.id, {createChannelMemberOnNewParticipant: false});
navigateToParticipantsList();
// * Verify run owner
cy.findByTestId('run-owner').contains(testUser.username);
// # show add participant modal
cy.findByRole('button', {name: 'Add'}).click();
// # Select two new participants
cy.get('#profile-autocomplete').click().type(testViewerUser.username + '{enter}', {delay: 400});
// * Verify modal message is correct
cy.findByText('Also add people to the channel linked to this run').should('exist');
// # Add selected participant
cy.findByTestId('modal-confirm-button').click();
// * Verify the user has been added to the run
cy.findByTestId(testViewerUser.id).should('exist');
// # Intercept fetching channel members
cy.intercept('channels/members/me/view').as('members');
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${testRun.name}`);
// * Verify that no users were invited
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).within(() => {
// just to wait until the username is fetched
cy.contains('Someone').should('not.exist');
cy.contains('You were added to the channel by @playbooks.');
cy.contains(`@${testViewerUser.username}`).should('not.exist');
});
});
});
it('join action disabled, checkbox selected', () => {
cy.apiUpdateRun(testRun.id, {createChannelMemberOnNewParticipant: false});
navigateToParticipantsList();
// * Verify run owner
cy.findByTestId('run-owner').contains(testUser.username);
// # show add participant modal
cy.findByRole('button', {name: 'Add'}).click();
// # Select two new participants
cy.get('#profile-autocomplete').click().type(testViewerUser.username + '{enter}', {delay: 400});
// * Verify modal message is correct
cy.findByText('Also add people to the channel linked to this run').should('exist');
// # Select checkbox
cy.findByTestId('also-add-to-channel').click({force: true});
// # Add selected participant
cy.findByTestId('modal-confirm-button').click();
// * Verify the user has been added to the run
cy.findByTestId(testViewerUser.id).should('exist');
// # Navigate to the playbook run channel
cy.visit(`/${testTeam.name}/channels/${testRun.name}`);
// * Verify that the user was added to the channel
cy.getFirstPostId().then((id) => {
cy.get(`#postMessageText_${id}`).within(() => {
cy.contains('Someone').should('not.exist');
cy.contains(`@${testViewerUser.username}`);
});
});
});
});
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
it('no manage button', () => {
navigateToParticipantsList();
// * Verify that there is no manage button
cy.findByRole('button', {name: 'Manage'}).should('not.exist');
});
});
});
const navigateToParticipantsList = () => {
// # Click on participants row
cy.findByTestId('runinfo-participants').click();
};

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

@@ -1,549 +0,0 @@
// 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 > run details page > run info', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
const getHeader = () => {
return cy.findByTestId('run-header-section');
};
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
const getRHSSection = (title) => cy.findByRole('complementary').contains('section', title);
describe('> overview', () => {
const getOverviewEntry = (entryName) => (
getRHSSection('Overview').findByTestId(`runinfo-${entryName}`)
);
const commonTests = () => {
it('Playbook entry links to the playbook', () => {
// # Click on the Playbook entry
getOverviewEntry('playbook').within(() => cy.getStyledComponent('ItemLink').click());
// * Verify the we're in the right playbook page
cy.url().should('include', '/playbooks/playbooks');
cy.findByTestId('playbook-editor-title').contains(testPublicPlaybook.title);
});
it('Owner entry shows the owner', () => {
// * Verify that the owner is shown
getOverviewEntry('owner').contains(testUser.username);
});
it('Participants entry shows the participants', () => {
// * Verify that the participants are rendered
getOverviewEntry('participants').within(() => {
cy.getStyledComponent('Participants').within(() => {
cy.getStyledComponent('UserPic').should('exist');
});
});
});
it('clicking on Participants show the full list of participants', () => {
// * Click on the Participants entry
getOverviewEntry('participants').click();
cy.findByRole('complementary').within(() => {
// * Verify that the Participants RHS is shown
cy.findByTestId('rhs-title').contains('Participants');
// * Verify that the back button is shown
cy.findByTestId('rhs-back-button').should('exist');
// * Verify that the participants list shows the number of participants
cy.findByText('1 Participant');
// * Verify that the participants list contains the test user
cy.findByText(`@${testUser.username}`);
// # Click on the back button
cy.findByTestId('rhs-back-button').click();
// * Verify that the RHS is back to Run info
cy.findByTestId('rhs-title').contains('Run info');
});
});
};
describe('as participant', () => {
commonTests();
it('Following button can be toggled', () => {
// # Intercept all calls to telemetry
cy.interceptTelemetry();
getOverviewEntry('following').within(() => {
// * Verify that the user shows in the following list
cy.getStyledComponent('UserRow').within(() => {
cy.getStyledComponent('UserPic').should('have.length', 1);
});
// # Click the Following button
cy.findByRole('button', {name: /Following/}).click({force: true});
// * Verify that it now says (exactly) Follow
cy.findByRole('button', {name: /^Follow$/}).should('exist');
// * Verify that the user no longer shows in the following list
cy.getStyledComponent('UserRow').should('not.exist');
// # Click the Follow button
cy.findByRole('button', {name: /^Follow$/}).click({force: true});
// * Verify that it now says Following
cy.findByRole('button', {name: /Following/}).should('exist');
});
cy.expectTelemetryToContain([
{
name: 'playbookrun_unfollow',
type: 'track',
from: 'run_details',
playbookrun_id: testRun.id,
},
{
name: 'playbookrun_follow',
type: 'track',
from: 'run_details',
playbookrun_id: testRun.id,
},
], {waitForCalls: 2});
});
it('click channel link navigates to run\'s channel', () => {
// * Assert channel name
getOverviewEntry('channel').contains('the run name');
// # Click on channel item
getOverviewEntry('channel').within(() => cy.getStyledComponent('ItemLink').click());
// * Assert we navigated correctly
cy.url().should('include', `${testTeam.name}/channels/the-run-name`);
});
it('channel is still there when the run is finished', () => {
cy.apiFinishRun(testRun.id).then(() => {
// # Reload page
cy.reload();
// * Assert channel name
getOverviewEntry('channel').contains('the run name');
// # Click on channel item
getOverviewEntry('channel').within(() => cy.getStyledComponent('ItemLink').click());
// * Assert we navigated correctly
cy.url().should('include', `${testTeam.name}/channels/the-run-name`);
});
});
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
commonTests();
it('Following button can be toggled', () => {
getOverviewEntry('following').within(() => {
// * Verify that the user is not in the following list
cy.getStyledComponent('UserRow').within(() => {
cy.getStyledComponent('UserPic').should('have.length', 1);
});
// # Click the Follow button
cy.findByRole('button', {name: /^Follow$/}).click({force: true});
// * Verify that it now says Following
cy.findByRole('button', {name: /Following/}).should('exist');
// * Verify that the user is now in the following list
cy.getStyledComponent('UserRow').within(() => {
cy.getStyledComponent('UserPic').should('have.length', 2);
});
// # Click the Follow button
cy.findByRole('button', {name: /Following/}).click({force: true});
// * Verify that it now says (exactly) Follow
cy.findByRole('button', {name: /^Follow$/}).should('exist');
});
});
it('there is no channel link but can request to join', () => {
// * Assert that the section exists with label Private
getOverviewEntry('channel').contains('Private');
// * Assert that link does not exist
getOverviewEntry('channel').within(() => {
cy.get('a').should('not.exist');
});
// * Assert that request-join button does not exist
getOverviewEntry('channel').within(() => {
cy.get('button').should('not.exist');
});
cy.wait(500);
// # Click Participate button
getHeader().findByText('Participate').click();
// * Assert that modal is shown
cy.get('#become-participant-modal').should('exist');
// # Confirm modal
cy.findByTestId('modal-confirm-button').click();
// Assert that request-join button doesn't exist
getOverviewEntry('channel').within(() => {
cy.get('button').should('not.exist');
});
});
});
});
describe('> key metrics', () => {
describe('playbook without metrics', () => {
describe('it should not render', () => {
it('as participant', () => {
// * assert metrics does not exist
getRHSSection('Key Metrics').should('not.exist');
});
it('as viewer', () => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
// * assert metrics does not exist
getRHSSection('Key Metrics').should('not.exist');
});
});
});
describe('playbook with metrics (enabled retro)', () => {
let playbookWithMetrics;
let runWithMetrics;
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook with metrics
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook with metrics',
memberIDs: [],
metrics: [
{
title: 'Duration',
description: 'duration',
type: 'metric_duration',
target: 6000,
},
{
title: 'Currency',
description: 'currency',
type: 'metric_currency',
target: 100,
},
{
title: 'Integer',
description: 'integer',
type: 'metric_integer',
target: 1,
},
],
}).then((playbook) => {
playbookWithMetrics = playbook;
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbookWithMetrics.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
runWithMetrics = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
const commonTests = () => {
it('key metrics is present', () => {
getRHSSection('Key Metrics').should('exist');
});
it('link scrolls to retrospective', () => {
// # click in view retro link
cy.findByRole('link', {name: /View Retrospective/}).click({force: true});
// * verify that URL has been changed
cy.url().should('contain', '#playbook-run-retrospective');
});
it('metric items scroll to corresponding metric', () => {
getRHSSection('Key Metrics').within(() => {
playbookWithMetrics.metrics.forEach((metric) => {
// # Click on metric
cy.findByText(metric.title).click({force: true});
// * Verify that url changed (and therefore we scrolled)
cy.url().should('contain', `#playbook-run-retrospective${metric.id}`);
});
});
});
};
describe('as participant', () => {
commonTests();
it('metric items show Add value if empty', () => {
getRHSSection('Key Metrics').within(() => {
playbookWithMetrics.metrics.forEach((metric) => {
// * Verify that we show a placeholder when empty
cy.findByText(metric.title).parent().contains('Add value...');
});
});
});
it('click on metric items, type and see the result in the RHS', () => {
const testData = {
metric_duration: {
input: '12:06:03',
expected: '12d, 6h, 3m',
},
metric_currency: {
input: '5000',
expected: '5000',
},
metric_integer: {
input: '42',
expected: '42',
},
};
// # Type the values for the metrics
getRHSSection('Key Metrics').within(() => {
playbookWithMetrics.metrics.forEach((metric) => {
// # Click on the metric row
cy.findByText(metric.title).click();
// # Seems there's a re-render between clicking the title and
// # typing that occasionally leads to dropped keystrokes in
// # .type(). Wait for it to avoid.
cy.wait(1000);
// # Type a value for the metric
cy.focused().type(testData[metric.type].input);
});
});
// * Verify that the RHS is updated with those values
getRHSSection('Key Metrics').within(() => {
playbookWithMetrics.metrics.forEach((metric) => {
// * Verify that the metric was updated in the RHS
cy.findByText(metric.title).parent().contains(testData[metric.type].expected);
});
});
});
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${runWithMetrics.id}`);
});
});
commonTests();
it('metric items show - if empty', () => {
getRHSSection('Key Metrics').within(() => {
playbookWithMetrics.metrics.forEach((metric) => {
// * verify that values are shown as - when empty
cy.findByText(metric.title).parent().contains('-');
});
});
});
});
});
describe('playbook with metrics (disabled retro)', () => {
let playbookWithMetrics;
let runWithMetrics;
before(() => {
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook with metrics
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook with metrics',
memberIDs: [],
metrics: [
{
title: 'Integer',
description: 'integer',
type: 'metric_integer',
target: 1,
},
],
retrospectiveEnabled: false,
}).then((playbook) => {
playbookWithMetrics = playbook;
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: playbookWithMetrics.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
runWithMetrics = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
const commonTests = () => {
it('key metrics is hidden', () => {
getRHSSection('Key Metrics').should('not.exist');
});
};
describe('as participant', () => {
commonTests();
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${runWithMetrics.id}`);
});
});
commonTests();
});
});
});
describe('> recent activity', () => {
const commonTests = () => {
it('recent activity is present and it contains a timeline', () => {
getRHSSection('Recent Activity').within(() => {
// * assert that section is shown
cy.findByTestId('rhs-timeline').should('exist');
});
});
it('link switches the RHS to Timeline', () => {
getRHSSection('Recent Activity').within(() => {
// * click link to see all timeline
cy.findByText('View all').click({force: true});
});
cy.findByRole('complementary').within(() => {
// * verify we changed to RHS-timeline
cy.findByTestId('rhs-title').contains('Timeline');
cy.findByTestId('rhs-back-button').should('exist');
});
});
};
describe('as participant', () => {
commonTests();
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
commonTests();
});
});
});

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

@@ -1,129 +0,0 @@
// 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 > run details page > RHS', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
const getRHS = () => cy.findByRole('complementary');
const getHeaderButton = (name) => cy.findByTestId(`rhs-header-button-${name}`);
const checkRHSTitle = (expectedTitle) => {
getRHS().within(() => {
cy.findByTestId('rhs-title').contains(expectedTitle);
});
};
const commonTests = () => {
it('timeline button toggles timeline in the RHS', () => {
// * Verify that the run info RHS is open
checkRHSTitle('Run info');
// # Click on the header timeline button
getHeaderButton('timeline').click();
// * Verify that the run info RHS changed to Timeline
checkRHSTitle('Timeline');
// # Wait so we don't double-click
cy.wait(500);
// # Click again on the header timeline button
getHeaderButton('timeline').click();
// * Verify that the RHS is closed
getRHS().should('not.exist');
});
it('info button toggles info in the RHS', () => {
// * Verify that the run info RHS is open
checkRHSTitle('Run info');
// # Click on the header info button
getHeaderButton('info').click();
// * Verify that the RHS is now closed
getRHS().should('not.exist');
// # Wait so we don't double-click
cy.wait(500);
// # Click again on the header info button
getHeaderButton('info').click();
// * Verify that the run info RHS is open again
checkRHSTitle('Run info');
});
};
describe('as participant', () => {
commonTests();
});
describe('as viewer', () => {
beforeEach(() => {
cy.apiLogin(testViewerUser).then(() => {
cy.visit(`/playbooks/runs/${testRun.id}`);
});
});
commonTests();
});
});

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

@@ -1,150 +0,0 @@
// 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 > run details page > status update', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
const getRHS = () => cy.findByRole('complementary');
const getStatusUpdates = () => getRHS().findAllByTestId('status-update-card');
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
// # Visit the playbook run
cy.visit(`/playbooks/runs/${playbookRun.id}`);
});
});
describe('as participant', () => {
it('rhs can not be open when there is no updates', () => {
// * Assert that the link is not present
cy.findByTestId('run-statusupdate-section').findByText('View all updates').should('not.exist');
});
it('link opens the RHS when there are updates', () => {
cy.apiUpdateStatus({
playbookRunId: testRun.id,
message: 'message 1',
reminder: 300,
});
cy.apiUpdateStatus({
playbookRunId: testRun.id,
message: 'message 2',
reminder: 300,
});
// # Click View all updates link
cy.findByTestId('run-statusupdate-section').findByText('View all updates').click();
// * Assert RHS is open and have the correct title/subtitle
getRHS().should('be.visible');
getRHS().findByTestId('rhs-title').contains('Status updates');
getRHS().findByTestId('rhs-subtitle').contains(testRun.name);
// * Assert that we have both updates in reverse order
getStatusUpdates().should('have.length', 2);
getStatusUpdates().eq(0).contains('message 2');
getStatusUpdates().eq(0).contains(testUser.username);
getStatusUpdates().eq(1).contains('message 1');
getStatusUpdates().eq(1).contains(testUser.username);
});
});
describe('as viewer', () => {
it('rhs can not be open when there is no updates', () => {
// * Log in as viewer user
cy.apiLogin(testViewerUser);
// * Browse to test run
cy.visit(`/playbooks/runs/${testRun.id}`);
// * Assert that the link is not present
cy.findByTestId('run-statusupdate-section').findByText('View all updates').should('not.exist');
});
it('link opens the RHS when there are updates', () => {
cy.apiLogin(testUser).then(() => {
cy.apiUpdateStatus({
playbookRunId: testRun.id,
message: 'message 1',
reminder: 300,
});
cy.apiUpdateStatus({
playbookRunId: testRun.id,
message: 'message 2',
reminder: 300,
});
});
// * Log in as viewer user
cy.apiLogin(testViewerUser);
// * Browse to test run
cy.visit(`/playbooks/runs/${testRun.id}`);
// # Click View all updates link
cy.findByTestId('run-statusupdate-section').findByText('View all updates').click();
// * Assert RHS is open and have the correct title/subtitle
getRHS().should('be.visible');
getRHS().findByTestId('rhs-title').contains('Status updates');
getRHS().findByTestId('rhs-subtitle').contains(testRun.name);
// * Assert that we have both updates in reverse order
getStatusUpdates().should('have.length', 2);
getStatusUpdates().eq(0).contains('message 2');
getStatusUpdates().eq(0).contains(testUser.username);
getStatusUpdates().eq(1).contains('message 1');
getStatusUpdates().eq(1).contains(testUser.username);
});
});
});

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

@@ -1,189 +0,0 @@
// 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('Task Inbox >', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testViewerUser;
let testPublicPlaybook;
let testRun;
before(() => {
cy.apiInitSetup().then(({team, user}) => {
testTeam = team;
testUser = user;
cy.apiCreateCustomAdmin().then(({sysadmin: adminUser}) => {
cy.apiAddUserToTeam(testTeam.id, adminUser.id);
});
// Create another user in the same team
cy.apiCreateUser().then(({user: viewer}) => {
testViewerUser = viewer;
cy.apiAddUserToTeam(testTeam.id, testViewerUser.id);
});
// # Login as testUser
cy.apiLogin(testUser);
// # Create a public playbook
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Public Playbook',
checklists: [
{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
{title: 'Step 3'},
{title: 'Step 4'},
],
},
],
memberIDs: [],
}).then((playbook) => {
testPublicPlaybook = playbook;
cy.apiRunPlaybook({
teamId: testTeam.id,
playbookId: testPublicPlaybook.id,
playbookRunName: 'the run name',
ownerUserId: testUser.id,
}).then((playbookRun) => {
testRun = playbookRun;
cy.apiChangeChecklistItemAssignee(testRun.id, 0, 0, testUser.id);
});
});
});
});
beforeEach(() => {
// # Size the viewport to show the RHS without covering posts.
cy.viewport('macbook-13');
// # Login as testUser
cy.apiLogin(testUser);
cy.visit(`/playbooks/runs/${testRun.id}`);
cy.assertRunDetailsPageRenderComplete(testUser.username);
});
const getRHS = () => cy.get('#playbooks-backstage-sidebar-right');
it('icon in global header', () => {
// # Visit the playbooks product
cy.visit('/playbooks');
// # Verify icon present in global header icon to open
cy.findByTestId('header-task-inbox-icon').click();
});
it('icon toggles taskinbox view', () => {
// # Intercept all calls to telemetry
cy.interceptTelemetry();
// # Click on global header icon to open
cy.findByTestId('header-task-inbox-icon').click();
// * assert RHS is shown
getRHS().should('be.visible');
// * assert telemetry pageview
cy.expectTelemetryToContain([
{
name: 'task_inbox',
type: 'page',
},
]);
// * assert zero case
getRHS().within(() => {
cy.getStyledComponent('HeaderTitle').contains('Your tasks');
cy.getStyledComponent('Body').contains('1 assigned');
});
// # Click on global header icon to close
cy.findByTestId('header-task-inbox-icon').click();
// * assert RHS is not shown
getRHS().should('not.exist');
});
it('show unassigned tasks from runs I own', () => {
// # Click on global header icon to open
cy.findByTestId('header-task-inbox-icon').click();
// * assert 4 tasks are shown (all tasks from runs I own enabled by default)
getRHS().within(() => {
cy.getStyledComponent('TaskList').within(() => {
cy.getStyledComponent('Container').should('have.length', 4);
});
});
});
it('show only assigned tasks', () => {
// # Click on global header icon to open
cy.findByTestId('header-task-inbox-icon').click();
getRHS().within(() => {
cy.getStyledComponent('TaskList').within(() => {
// * assert 4 tasks are shown
cy.getStyledComponent('Container').should('have.length', 4);
});
// # Click on filters
cy.findByText('Filters').click();
});
// # Deactivate show alltasks
cy.findByText('Show all tasks from runs I own').click();
cy.getStyledComponent('TaskList').within(() => {
// * assert 1 tasks are shown
cy.getStyledComponent('Container').should('have.length', 1);
});
});
it('tasks can be checked', () => {
// # Click on global header icon to open
cy.findByTestId('header-task-inbox-icon').click();
getRHS().within(() => {
cy.getStyledComponent('TaskList').within(() => {
// * assert 4 tasks are shown
cy.getStyledComponent('Container').should('have.length', 4);
// # Check the first task
cy.getStyledComponent('Container').eq(0).within(() => {
cy.get('input').click();
});
// * assert 3 tasks are shown
cy.getStyledComponent('Container').should('have.length', 3);
});
// # Click on filters
cy.findByText('Filters').click();
});
// # Activate checked task visibility in filters
cy.findByText('Show checked tasks').click();
getRHS().within(() => {
cy.getStyledComponent('TaskList').within(() => {
// * assert 4 tasks are shown
cy.getStyledComponent('Container').should('have.length', 4);
});
});
});
});

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

@@ -1,121 +0,0 @@
// 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('playbook tour points', {testIsolation: true}, () => {
let testTeam;
let testUser;
let testSysadmin;
beforeEach(() => {
cy.apiInitSetup({promoteNewUserAsAdmin: true}).then(({team, user: sysadmin}) => {
testTeam = team;
testSysadmin = sysadmin;
// # Create a user with tutorials enabled
cy.apiCreateUser({bypassTutorial: false}).then(({user: userWithTours}) => {
testUser = userWithTours;
cy.apiAddUserToTeam(team.id, testUser.id);
cy.apiLogin(userWithTours);
});
});
});
afterEach(() => {
// # Ensure apiInitSetup() can run again
cy.apiLogin(testSysadmin);
});
it('creation tour', () => {
// # Open creation view from RHS
cy.visit(`/${testTeam.name}/channels/town-square`);
cy.get('#incidentIcon').click({force: true});
cy.findByRole('button', {name: /create playbook/i}).click();
cy.url().should('contain', '/playbooks/playbooks/new');
// * Verify the tutorial steps
cy.contains('Create and assign tasks').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('Set up assumptions').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('Keep stakeholders updated').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('Learn AND reflect').should('be.visible');
cy.findByRole('button', {name: /done/i}).click();
});
it('preview tour', () => {
// # Make a playbook to preview
cy.apiCreatePlaybook({
teamId: testTeam.id,
title: 'Preview Tour Test Playbook',
memberIDs: [],
}).then(() => {
// # Open the playbook
cy.visit('/playbooks/playbooks');
cy.findByText('Preview Tour Test Playbook').click();
// * Verify the tutorial steps
cy.contains('Welcome to the playbook preview page!').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('different sections of the playbook').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('Ready to run your playbook?').should('be.visible');
cy.findByRole('button', {name: /done/i}).click();
});
});
describe('run tour', () => {
beforeEach(() => {
// # Disable the preview tour which we would otherwise see
cy.apiSaveUserPreference([{
user_id: testUser.id,
category: 'playbook_preview',
name: testUser.id,
value: '999',
}], testUser.id);
// # Start a run from the tutorial template
cy.visit('/playbooks/playbooks');
cy.findByText('Learn how to use playbooks').click();
cy.findByRole('button', {name: /run playbook/i}).click({force: true});
// * Verify the tour confirmation modal is shown (other tours don't have one)
cy.contains('auto-created your run').should('be.visible');
});
it('follows the tour when chosen from modal', () => {
// # Accept the tour
cy.contains('quick tour').click();
// * Verify the tutorial steps
cy.contains('See who is involved').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('Post status updates').should('be.visible');
cy.findByRole('button', {name: /next/i}).click();
cy.contains('Track progress and ownership').should('be.visible');
cy.findByRole('button', {name: /done/i}).click();
});
it('does not follow the tour when dismissed from modal', () => {
// # Dismiss the tour
cy.findByRole('button', {name: /let me explore/i}).click();
// * Verify the first step is _not_ shown
cy.contains('See who is involved').should('not.exist');
});
});
});

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

@@ -11,7 +11,6 @@ import './data_retention';
import './group';
import './keycloak';
import './ldap';
import './playbooks';
import './preference';
import './plugin';
import './role';

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

@@ -1,483 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
const playbookRunsEndpoint = '/plugins/playbooks/api/v0/runs';
const StatusOK = 200;
const StatusCreated = 201;
/**
* Get all playbook runs directly via API
*/
Cypress.Commands.add('apiGetAllPlaybookRuns', (teamId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/plugins/playbooks/api/v0/runs',
qs: {team_id: teamId, per_page: 10000},
method: 'GET',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
/**
* Get all InProgress playbook runs directly via API
*/
Cypress.Commands.add('apiGetAllInProgressPlaybookRuns', (teamId, userId = '') => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/plugins/playbooks/api/v0/runs',
qs: {team_id: teamId, status: 'InProgress', participant_id: userId},
method: 'GET',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
/**
* Get playbook run by name directly via API
*/
Cypress.Commands.add('apiGetPlaybookRunByName', (teamId, name) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/plugins/playbooks/api/v0/runs',
qs: {team_id: teamId, search_term: name},
method: 'GET',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
/**
* Get a playbook run directly via API
* @param {String} playbookRunId
* All parameters required
*/
Cypress.Commands.add('apiGetPlaybookRun', (playbookRunId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `${playbookRunsEndpoint}/${playbookRunId}`,
method: 'GET',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
/**
* Start a playbook run directly via API.
*/
Cypress.Commands.add('apiRunPlaybook', (
{
teamId,
playbookId,
playbookRunName,
ownerUserId,
channelId,
description,
}, options) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: playbookRunsEndpoint,
method: 'POST',
body: {
name: playbookRunName,
owner_user_id: ownerUserId,
team_id: teamId,
playbook_id: playbookId,
channel_id: channelId,
description,
},
failOnStatusCode: !(options?.expectedStatusCode),
}).then((response) => {
const statusCode = options?.expectedStatusCode || StatusCreated;
expect(response.status).to.equal(statusCode);
cy.wrap(response.body);
});
});
// Finish a playbook's run programmaticially. Uses currently logged in user, so that user must
// have edit permissions on the run
Cypress.Commands.add('apiFinishRun', (playbookRunId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `${playbookRunsEndpoint}/${playbookRunId}/finish`,
method: 'PUT',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
// Update a playbook run's status programmatically.
Cypress.Commands.add('apiUpdateStatus', (
{
playbookRunId,
message,
reminder = 300,
}) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `${playbookRunsEndpoint}/${playbookRunId}/status`,
method: 'POST',
body: {
message,
reminder,
},
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
/**
* Change the owner of a playbook run directly via API
* @param {String} playbookRunId
* @param {String} userId
* All parameters required
*/
Cypress.Commands.add('apiChangePlaybookRunOwner', (playbookRunId, userId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: playbookRunsEndpoint + '/' + playbookRunId + '/owner',
method: 'POST',
body: {
owner_id: userId,
},
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
/**
* Change the assignee of a checklist item directly via API
* @param {String} playbookRunId
* @param {String} checklistId
* @param {String} itemId
* @param {String} userId
* All parameters required
*/
Cypress.Commands.add('apiChangeChecklistItemAssignee', (playbookRunId, checklistId, itemId, userId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: playbookRunsEndpoint + `/${playbookRunId}/checklists/${checklistId}/item/${itemId}/assignee`,
method: 'PUT',
body: {
assignee_id: userId,
},
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
/**
* Check a checklist item directly via API
* @param {String} playbookRunId
* @param {String} checklistId
* @param {String} itemId
* @param {String} state ('' or 'closed')
*/
Cypress.Commands.add('apiSetChecklistItemState', (playbookRunId, checklistId, itemId, state) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: playbookRunsEndpoint + `/${playbookRunId}/checklists/${checklistId}/item/${itemId}/state`,
method: 'PUT',
body: {
new_state: state,
},
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response);
});
});
// Verify playbook run is created
Cypress.Commands.add('verifyPlaybookRunActive', (teamId, playbookRunName, playbookRunDescription) => {
cy.apiGetPlaybookRunByName(teamId, playbookRunName).then((response) => {
const returnedPlaybookRuns = response.body;
const playbookRun = returnedPlaybookRuns.items.find((inc) => inc.name === playbookRunName);
assert.isDefined(playbookRun);
assert.equal(playbookRun.end_at, 0);
assert.equal(playbookRun.name, playbookRunName);
cy.log('test 1');
// Only check the description if provided. The server may supply a default depending
// on how the playbook run was started.
if (playbookRunDescription) {
assert.equal(playbookRun.description, playbookRunDescription);
}
});
});
// Verify playbook run exists but is not active
Cypress.Commands.add('verifyPlaybookRunEnded', (teamId, playbookRunName) => {
cy.apiGetPlaybookRunByName(teamId, playbookRunName).then((response) => {
const returnedPlaybookRuns = response.body;
const playbookRun = returnedPlaybookRuns.items.find((inc) => inc.name === playbookRunName);
assert.isDefined(playbookRun);
assert.notEqual(playbookRun.end_at, 0);
});
});
// Create a playbook programmatically.
Cypress.Commands.add('apiCreatePlaybook', (
{
teamId,
title,
description,
createPublicPlaybookRun,
createChannelMemberOnNewParticipant = true,
checklists,
memberIDs,
makePublic = true,
broadcastEnabled,
broadcastChannelIds,
reminderMessageTemplate,
reminderTimerDefaultSeconds = 24 * 60 * 60, // 24 hours
statusUpdateEnabled = true,
retrospectiveReminderIntervalSeconds,
retrospectiveTemplate,
retrospectiveEnabled = true,
invitedUserIds,
inviteUsersEnabled,
defaultOwnerId,
defaultOwnerEnabled,
announcementChannelId,
announcementChannelEnabled,
webhookOnCreationURLs,
webhookOnCreationEnabled,
webhookOnStatusUpdateURLs,
webhookOnStatusUpdateEnabled,
messageOnJoin,
messageOnJoinEnabled,
signalAnyKeywords,
signalAnyKeywordsEnabled,
channelNameTemplate,
runSummaryTemplate,
runSummaryTemplateEnabled,
channelMode = 'create_new_channel',
channelId = '',
metrics,
}) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/plugins/playbooks/api/v0/playbooks',
method: 'POST',
body: {
title,
description,
team_id: teamId,
create_public_playbook_run: createPublicPlaybookRun,
create_channel_member_on_new_participant: createChannelMemberOnNewParticipant,
checklists,
public: makePublic,
members: memberIDs?.map((val) => ({user_id: val, roles: ['playbook_member', 'playbook_admin']})),
broadcast_enabled: broadcastEnabled,
broadcast_channel_ids: broadcastChannelIds,
reminder_message_template: reminderMessageTemplate,
reminder_timer_default_seconds: reminderTimerDefaultSeconds,
status_update_enabled: statusUpdateEnabled,
retrospective_reminder_interval_seconds: retrospectiveReminderIntervalSeconds,
retrospective_template: retrospectiveTemplate,
retrospective_enabled: retrospectiveEnabled,
invited_user_ids: invitedUserIds,
invite_users_enabled: inviteUsersEnabled,
default_owner_id: defaultOwnerId,
default_owner_enabled: defaultOwnerEnabled,
announcement_channel_id: announcementChannelId,
announcement_channel_enabled: announcementChannelEnabled,
webhook_on_creation_urls: webhookOnCreationURLs,
webhook_on_creation_enabled: webhookOnCreationEnabled,
webhook_on_status_update_urls: webhookOnStatusUpdateURLs,
webhook_on_status_update_enabled: webhookOnStatusUpdateEnabled,
message_on_join: messageOnJoin,
message_on_join_enabled: messageOnJoinEnabled,
signal_any_keywords: signalAnyKeywords,
signal_any_keywords_enabled: signalAnyKeywordsEnabled,
channel_name_template: channelNameTemplate,
run_summary_template: runSummaryTemplate,
run_summary_template_enabled: runSummaryTemplateEnabled,
channel_mode: channelMode,
channel_id: channelId,
metrics,
},
}).then((response) => {
expect(response.status).to.equal(201);
cy.wrap(response.headers.location);
}).then((location) => {
cy.request({
url: location,
method: 'GET',
}).then((response) => {
cy.wrap(response.body);
});
});
});
// Create a test playbook programmatically.
Cypress.Commands.add('apiCreateTestPlaybook', (
{
teamId,
title,
userId,
broadcastEnabled,
broadcastChannelIds,
reminderMessageTemplate,
checklists,
inviteUsersEnabled,
reminderTimerDefaultSeconds = 24 * 60 * 60, // 24 hours
otherMembers = [],
invitedUserIds = [],
channelNameTemplate = '',
}) => (
cy.apiCreatePlaybook({
teamId,
title,
checklists: checklists || [{
title: 'Stage 1',
items: [
{title: 'Step 1'},
{title: 'Step 2'},
],
}],
memberIDs: [
userId,
...otherMembers,
],
broadcastEnabled,
broadcastChannelIds,
reminderMessageTemplate,
reminderTimerDefaultSeconds,
invitedUserIds,
inviteUsersEnabled,
channelNameTemplate,
createChannelMemberOnNewParticipant: true,
removeChannelMemberOnRemovedParticipant: true,
})
));
// Verify that the playbook was created
Cypress.Commands.add('verifyPlaybookCreated', (teamId, playbookTitle) => (
cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/plugins/playbooks/api/v0/playbooks',
qs: {team_id: teamId, sort: 'title', direction: 'asc'},
method: 'GET',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
const playbookResults = response.body;
const playbook = playbookResults.items.find((p) => p.title === playbookTitle);
assert.isDefined(playbook);
})
));
// Get a playbook
Cypress.Commands.add('apiGetPlaybook', (playbookId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `/plugins/playbooks/api/v0/playbooks/${playbookId}`,
method: 'GET',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
// Update a playbook
Cypress.Commands.add('apiUpdatePlaybook', (playbook, expectedHttpCode = StatusOK) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `/plugins/playbooks/api/v0/playbooks/${playbook.id}`,
method: 'PUT',
body: JSON.stringify(playbook),
failOnStatusCode: false,
}).then((response) => {
expect(response.status).to.equal(expectedHttpCode);
cy.wrap(response.body);
});
});
// Archive a playbook
Cypress.Commands.add('apiArchivePlaybook', (playbookId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `/plugins/playbooks/api/v0/playbooks/${playbookId}`,
method: 'DELETE',
}).then((response) => {
expect(response.status).to.equal(204);
});
});
// Follow a playbook run
Cypress.Commands.add('apiFollowPlaybookRun', (playbookRunId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `/plugins/playbooks/api/v0/runs/${playbookRunId}/followers`,
method: 'PUT',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
// Unfollow a playbook run
Cypress.Commands.add('apiUnfollowPlaybookRun', (playbookRunId) => {
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: `/plugins/playbooks/api/v0/runs/${playbookRunId}/followers`,
method: 'DELETE',
}).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
//addUsersToRun
Cypress.Commands.add('apiAddUsersToRun', (playbookRunId, usersIds) => {
const query = `
mutation AddRunParticipants($runID: String!, $userIDs: [String!]!) {
addRunParticipants(runID: $runID, userIDs: $userIDs)
}
`;
const vars = {
runID: playbookRunId,
userIDs: usersIds,
};
return doGraphqlQuery(query, 'AddRunParticipants', vars).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
//updateRun
Cypress.Commands.add('apiUpdateRun', (playbookRunId, updates) => {
const query = `
mutation UpdateRun($id: String!, $updates: RunUpdates!) {
updateRun(id: $id, updates: $updates)
}
`;
const vars = {
id: playbookRunId,
updates,
};
return doGraphqlQuery(query, 'UpdateRun', vars).then((response) => {
expect(response.status).to.equal(StatusOK);
cy.wrap(response.body);
});
});
const doGraphqlQuery = (query, operationName, variables) => {
const payload = {query, operationName, variables};
return cy.request({
headers: {'X-Requested-With': 'XMLHttpRequest'},
url: '/plugins/playbooks/api/v0/query',
body: JSON.stringify(payload),
method: 'POST',
});
};

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

@@ -19,7 +19,6 @@ import './login';
import './menu';
import './mfa';
import './modal';
import './playbooks';
import './post';
import './post_dropdown_menu';
import './search';

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

@@ -1,324 +0,0 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import * as TIMEOUTS from '../../fixtures/timeouts';
const playbookRunStartCommand = '/playbook run ';
Cypress.Commands.add('startPlaybookRun', (playbookName, playbookRunName) => {
cy.get('#interactiveDialogModal').should('exist').within(() => {
// # Select playbook
cy.selectPlaybookFromDropdown(playbookName);
// # Type playbook run name
cy.findByTestId('playbookRunNameinput').type(playbookRunName, {force: true});
// # Submit
cy.get('#interactiveDialogSubmit').click();
});
cy.get('#interactiveDialogModal').should('not.exist');
});
// Opens playbook run dialog using the `/playbook run` slash command
Cypress.Commands.add('openPlaybookRunDialogFromSlashCommand', () => {
cy.uiPostMessageQuickly(playbookRunStartCommand);
});
// Starts playbook run with the `/playbook run` slash command
Cypress.Commands.add('startPlaybookRunWithSlashCommand', (playbookName, playbookRunName) => {
cy.openPlaybookRunDialogFromSlashCommand();
cy.startPlaybookRun(playbookName, playbookRunName);
});
// Selects Playbooks icon in the App Bar
Cypress.Commands.add('getPlaybooksAppBarIcon', () => {
cy.get('#channel_view').should('be.visible');
return cy.get('.app-bar').find('#app-bar-icon-playbooks .app-bar__icon-inner');
});
// Starts playbook run from the playbook run RHS
Cypress.Commands.add('startPlaybookRunFromRHS', (playbookName, playbookRunName) => {
cy.get('#channel-header').within(() => {
// open flagged posts to ensure playbook run RHS is closed
cy.get('#channelHeaderFlagButton').click();
// open the playbook run RHS
cy.getPlaybooksAppBarIcon().should('exist').click();
});
cy.get('#rhsContainer').should('exist').within(() => {
cy.findByText('Run playbook').click();
});
cy.startPlaybookRun(playbookName, playbookRunName);
});
// Create a new task from the RHS
Cypress.Commands.add('addNewTaskFromRHS', (taskname) => {
// Click add new task
cy.findByTestId('add-new-task-0').click();
// Type a name
cy.findByTestId('checklist-item-textarea-title').type(taskname);
// Save task
cy.findByTestId('checklist-item-save-button').click();
});
// Starts playbook run from the post menu
Cypress.Commands.add('startPlaybookRunFromPostMenu', (playbookName, playbookRunName) => {
// post a message as user to avoid system message
cy.findByTestId('post_textbox').clear().type('new message here{enter}');
// post a second message because cypress has trouble finding latest post when there's only one message
cy.findByTestId('post_textbox').clear().type('another new message here{enter}');
cy.clickPostActionsMenu();
cy.findByTestId('playbookRunPostMenuIcon').click();
cy.startPlaybookRun(playbookName, playbookRunName);
});
// Create playbook
Cypress.Commands.add('createPlaybook', (teamName, playbookName) => {
cy.visit('/playbooks/playbooks/new');
cy.findByTestId('save_playbook', {timeout: TIMEOUTS.HALF_MIN}).should('exist');
// # Type playbook name
cy.get('#playbook-name .editable-trigger').click();
cy.get('#playbook-name .editable-input').type(playbookName);
cy.get('#playbook-name .editable-input').type('{enter}');
// # Save playbook
cy.findByTestId('save_playbook', {timeout: TIMEOUTS.HALF_MIN}).should('not.be.disabled').click();
cy.wait(TIMEOUTS.TWO_SEC);
cy.findByTestId('save_playbook', {timeout: TIMEOUTS.HALF_MIN}).should('not.be.disabled').click();
});
// Select the playbook from the dropdown menu
Cypress.Commands.add('selectPlaybookFromDropdown', (playbookName) => {
cy.findByTestId('autoCompleteSelector').should('exist').within(() => {
cy.get('input').click().type(playbookName.toLowerCase(), {force: true});
cy.get('#suggestionList').contains(playbookName).click({force: true});
});
});
Cypress.Commands.add('createPost', (message) => {
// post a message as user to avoid system message
cy.findByTestId('post_textbox').clear().type(`${message}{enter}`);
});
Cypress.Commands.add('addPostToTimelineUsingPostMenu', (playbookRunName, summary, postId) => {
cy.clickPostDotMenu(postId);
cy.findByTestId('playbookRunAddToTimeline').click();
cy.get('#interactiveDialogModal').should('exist').within(() => {
// # Select playbook run
cy.findByTestId('autoCompleteSelector').should('exist').within(() => {
cy.get('input').click().type(playbookRunName);
cy.get('#suggestionList').contains(playbookRunName).click({force: true});
});
// # Type playbook run name
cy.findByTestId('summaryinput').clear().type(summary, {force: true});
// # Submit
cy.get('#interactiveDialogSubmit').click();
});
cy.get('#interactiveDialogModal').should('not.exist');
});
Cypress.Commands.add('openSelector', () => {
cy.findByText('Search for people').click({force: true});
});
Cypress.Commands.add('addInvitedUser', (userName) => {
cy.get('.invite-users-selector__menu').within(() => {
cy.findByText(userName).click({force: true});
});
});
Cypress.Commands.add('selectOwner', (userName) => {
cy.get('.assign-owner-selector__menu').within(() => {
cy.findByText(userName).click({force: true});
});
});
Cypress.Commands.add('selectChannel', (channelName) => {
cy.get('#playbook-automation-broadcast .playbooks-rselect__menu').within(() => {
cy.findByText(channelName).click({force: true});
});
});
Cypress.Commands.add('openReminderSelector', () => {
cy.get('#reminder_timer_datetime input').click({force: true});
});
Cypress.Commands.add('selectReminderTime', (timeText) => {
cy.get('#reminder_timer_datetime .playbooks-rselect__menu').within(() => {
cy.findByText(timeText).click({force: true});
});
});
/**
* Update the status of the current playbook run through the slash command.
*/
Cypress.Commands.add('updateStatus', (message, reminderQuery) => {
// # Run the slash command to update status.
cy.uiPostMessageQuickly('/playbook update ');
// # Get the interactive dialog modal.
cy.getStatusUpdateDialog().within(() => {
// # remove what's there if applicable, and type the new update in the textbox.
cy.findByTestId('update_run_status_textbox').clear().type(message);
if (reminderQuery) {
cy.get('#reminder_timer_datetime').within(() => {
cy.get('input').type(reminderQuery, {delay: TIMEOUTS.TWO_HUNDRED_MILLIS, force: true}).wait(TIMEOUTS.ONE_SEC).type('{enter}');
});
}
// # Submit the dialog.
cy.get('button.confirm').click();
});
// * Verify that the interactive dialog has gone.
cy.getStatusUpdateDialog().should('not.exist');
// # Return the post ID of the status update.
return cy.getLastPostId();
});
/**
* Edit a post through the post dot menu.
* @param {String} postId - ID of the post to delete.
* @param {String} newMessage - New content of the post.
*/
Cypress.Commands.add('editPost', (postId, newMessage) => {
// # Open the post dot menu.
cy.clickPostDotMenu(postId);
// # Click on the Edit menu option.
cy.get(`#edit_post_${postId}`).click();
// # Overwrite the post content with the new message provided.
cy.get('#edit_textbox').clear().type(newMessage);
// # Confirm the edit in the dialog.
cy.get('#editButton').click();
});
Cypress.Commands.add('getStatusUpdateDialog', () => {
return cy.findByRole('dialog', {name: /post update/i});
});
Cypress.Commands.add('getStyledComponent', (className) => {
cy.get(`[class^="${className}-"]`);
});
/**
* Get the provided pseudo-class from the previous element and return the property passed as argument
* @param {String} pseudoClass - CSS pseudo class to get.
* @param {String} property - Property that will be returned.
*
* Stolen from https://stackoverflow.com/questions/55516990/cypress-testing-pseudo-css-class-before
*/
Cypress.Commands.add('cssPseudoClass', {prevSubject: 'element'}, (el, pseudoClass, property) => {
const win = el[0].ownerDocument.defaultView;
const pseudoElem = win.getComputedStyle(el[0], pseudoClass);
return pseudoElem.getPropertyValue(property).replace(/(^")|("$)/g, '');
});
/**
* Get the :before pseudo-class from the previous element and return the property passed as argument
* @param {String} property - Property that will be returned.
*/
Cypress.Commands.add('before', {prevSubject: 'element'}, (el, property) => {
return cy.wrap(el).cssPseudoClass('before', property);
});
/**
* Get the :after pseudo-class from the previous element and return the property passed as argument
* @param {String} property - Property that will be returned.
*/
Cypress.Commands.add('after', {prevSubject: 'element'}, (el, property) => {
return cy.wrap(el).cssPseudoClass('after', property);
});
function waitUntilPermanentPost() {
cy.get('#postListContent').should('exist');
cy.waitUntil(() => cy.findAllByTestId('postView').last().then((el) => !(el[0].id.includes(':'))));
}
Cypress.Commands.add('getFirstPostId', () => {
waitUntilPermanentPost();
cy.findAllByTestId('postView').first().should('have.attr', 'id').and('not.include', ':').
invoke('replace', 'post_', '');
});
Cypress.Commands.add('assertRunDetailsPageRenderComplete', (expectedRunOwner) => {
cy.findByTestId('lhs-navigation').should('be.visible').within(() => {
cy.contains('Playbooks').should('be.visible');
cy.contains('Runs').should('be.visible');
});
cy.get('#playbooks-sidebar-right').should('be.visible').within(() => {
cy.findByTestId('assignee-profile-selector').should('contain', expectedRunOwner);
cy.findAllByTestId('timeline-item', {exact: false}).should('have.length.of.at.least', 1);
cy.findAllByTestId('profile-option', {exact: false}).should('have.length.of.at.least', 1);
});
});
Cypress.Commands.add('interceptTelemetry', () => {
cy.intercept('/plugins/playbooks/api/v0/telemetry').as('telemetry');
});
const defaultExpectTelemetryToContainOptions = {
waitForCalls: 'auto',
};
// cy.expectTelemetryToContain expects to find the given telemetry events in the order given among the
// recorded telemetry. It doesn't fail if other telemetry events happen to occur in between.
Cypress.Commands.add('expectTelemetryToContain', (items, opts) => {
const options = {...defaultExpectTelemetryToContainOptions, ...opts};
// Wait for at least as many telemetry events as requested if auto, or explicit number if passed.
if (options.waitForCalls === 'auto') {
items.forEach(() => cy.wait('@telemetry'));
} else {
for (let i = 0; i < options.waitForCalls; i++) {
cy.wait('@telemetry');
}
}
// When additional telemetry events are emitted than what is expected, the ones we want may
// still be be pending, so wait a bit more to try to let requests settle.
cy.wait(TIMEOUTS.HALF_SEC);
cy.get('@telemetry.all').then((xhrs) => {
let xhrIndex = 0;
items.forEach((item) => {
while (xhrIndex < xhrs.length) {
const xhr = xhrs[xhrIndex];
// Advance to the next xhr element regardless of whether or not we find a match.
xhrIndex++;
if (xhr.request.body.name === item.name && xhr.request.body.type === item.type) {
// Validate only passed properties
if (item.properties) {
for (const [key, value] of Object.entries(item.properties)) {
expect(xhr.request.body.properties[key]).to.eq(value, `Property ${key} does not match for event ${item.name}`);
}
}
return;
}
}
throw new Error(`failed to find telemetry event '${item.type}' '${item.name}'`);
});
});
});