Move /e2e -> /e2e-tests
Этот коммит содержится в:
262
e2e-tests/cypress/tests/integration/playbooks/runs/list_spec.js
Обычный файл
262
e2e-tests/cypress/tests/integration/playbooks/runs/list_spec.js
Обычный файл
@@ -0,0 +1,262 @@
|
||||
// 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', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,506 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @playbooks
|
||||
|
||||
/* eslint-disable no-only-tests/no-only-tests */
|
||||
|
||||
import {getRandomId} from '../../../utils';
|
||||
|
||||
describe('runs > permissions', () => {
|
||||
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', () => {
|
||||
// XXX: Skipping this test, since public playbooks currently have no members. This will
|
||||
// likely change in the future, so keeping the skeleton.
|
||||
it.skip('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', () => {
|
||||
// XXX: Skipping this test, since public playbooks currently have no members. This will
|
||||
// likely change in the future.
|
||||
it.skip('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);
|
||||
});
|
||||
|
||||
// Skipping this test, since followers cannot follow a run with a private channel from
|
||||
// a private playbook. (But leaving it for clarity in the code.)
|
||||
it.skip('to run followers', () => {
|
||||
assertRunIsVisible(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);
|
||||
});
|
||||
|
||||
// Skipping this test, since followers cannot follow a run with a private channel from
|
||||
// a private playbook. (But leaving it for clarity in the code.)
|
||||
it.skip('to run followers', () => {
|
||||
assertRunIsVisible(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');
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
// 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', () => {
|
||||
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,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,150 @@
|
||||
// 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', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,886 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @playbooks
|
||||
|
||||
/* eslint-disable no-only-tests/no-only-tests */
|
||||
|
||||
import {stubClipboard} from '../../../utils';
|
||||
|
||||
describe('runs > run details page > header', () => {
|
||||
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('You’ll 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('You’ll 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 the user was added to the channel
|
||||
cy.getFirstPostId().then((id) => {
|
||||
cy.get(`#postMessageText_${id}`).within(() => {
|
||||
cy.contains('You and');
|
||||
cy.contains('joined the channel');
|
||||
});
|
||||
});
|
||||
|
||||
// * 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('You’ll 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 that the user was added to the channel
|
||||
cy.getFirstPostId().then((id) => {
|
||||
cy.get(`#postMessageText_${id}`).within(() => {
|
||||
cy.contains('You and');
|
||||
cy.contains('joined the channel');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('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('You’ll 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 that the user was added to the channel
|
||||
cy.getFirstPostId().then((id) => {
|
||||
cy.get(`#postMessageText_${id}`).within(() => {
|
||||
cy.contains('You and');
|
||||
cy.contains('joined the channel');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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');
|
||||
};
|
||||
@@ -0,0 +1,107 @@
|
||||
// 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', () => {
|
||||
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');
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,500 @@
|
||||
// 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', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,306 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
// ***************************************************************
|
||||
// - [#] indicates a test step (e.g. # Go to a page)
|
||||
// - [*] indicates an assertion (e.g. * Check the title)
|
||||
// ***************************************************************
|
||||
|
||||
// Stage: @prod
|
||||
// Group: @playbooks
|
||||
|
||||
/* eslint-disable no-only-tests/no-only-tests */
|
||||
|
||||
describe('runs > run details page > status update', () => {
|
||||
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.skip('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(`${testUser.username} requested a status update for ${testPublicPlaybook.name}.`);
|
||||
});
|
||||
});
|
||||
|
||||
it.skip('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}].`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,162 @@
|
||||
// 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', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,712 @@
|
||||
// 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', () => {
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,266 @@
|
||||
// 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', () => {
|
||||
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();
|
||||
};
|
||||
@@ -0,0 +1,549 @@
|
||||
// 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', () => {
|
||||
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: 3});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
129
e2e-tests/cypress/tests/integration/playbooks/runs/rdp_rhs_spec.js
Обычный файл
129
e2e-tests/cypress/tests/integration/playbooks/runs/rdp_rhs_spec.js
Обычный файл
@@ -0,0 +1,129 @@
|
||||
// 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', () => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
// 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', () => {
|
||||
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', () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin(testViewerUser).then(() => {
|
||||
cy.visit(`/playbooks/runs/${testRun.id}`);
|
||||
});
|
||||
});
|
||||
|
||||
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.apiLogin(testUser).then(() => {
|
||||
cy.apiUpdateStatus({
|
||||
playbookRunId: testRun.id,
|
||||
message: 'message 1',
|
||||
reminder: 300,
|
||||
});
|
||||
cy.apiUpdateStatus({
|
||||
playbookRunId: testRun.id,
|
||||
message: 'message 2',
|
||||
reminder: 300,
|
||||
});
|
||||
});
|
||||
|
||||
cy.apiLogin(testViewerUser).then(() => {
|
||||
// # 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
213
e2e-tests/cypress/tests/integration/playbooks/runs/taskinbox_spec.js
Обычный файл
213
e2e-tests/cypress/tests/integration/playbooks/runs/taskinbox_spec.js
Обычный файл
@@ -0,0 +1,213 @@
|
||||
// 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 >', () => {
|
||||
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, with experimental feature flag', () => {
|
||||
// # Enable experimental feature flag
|
||||
cy.apiAdminLogin().then(() => {
|
||||
cy.apiEnsureFeatureFlag('enableexperimentalfeatures', true);
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
});
|
||||
|
||||
// # 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 in global header, without experimental feature flag', () => {
|
||||
// # Disable experimental feature flag
|
||||
cy.apiAdminLogin().then(() => {
|
||||
cy.apiEnsureFeatureFlag('enableexperimentalfeatures', false);
|
||||
|
||||
// # Login as testUser
|
||||
cy.apiLogin(testUser);
|
||||
});
|
||||
|
||||
// # 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Ссылка в новой задаче
Block a user