[Cases] Added serverless functional tests for list view (#164604)

## Summary

Serverless test for list view
https://github.com/elastic/kibana/issues/164552


### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Janki Salvi 2023-08-25 13:57:57 +02:00 committed by GitHub
parent 8da14b7c4c
commit b83585b844
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 575 additions and 4 deletions

View file

@ -524,6 +524,7 @@ export const getCase = async ({
`${getSpaceUrlPrefix(auth?.space)}${CASES_URL}/${caseId}?includeComments=${includeComments}`
)
.set('kbn-xsrf', 'true')
.set('x-elastic-internal-origin', 'foo')
.auth(auth.user.username, auth.user.password)
.expect(expectedHttpCode);

View file

@ -50,10 +50,10 @@ export function CasesAPIServiceProvider({ getService }: FtrProviderContext) {
return createCaseAPI(kbnSupertest, caseData);
},
async createNthRandomCases(amount: number = 3) {
async createNthRandomCases(amount: number = 3, owner?: string) {
const cases: CasePostRequest[] = Array.from(
{ length: amount },
() => generateRandomCaseWithoutConnector() as CasePostRequest
() => generateRandomCaseWithoutConnector(owner) as CasePostRequest
);
await pMap(cases, async (caseData) => createCaseAPI(kbnSupertest, caseData), {

View file

@ -9,7 +9,7 @@ import { CasePostRequest } from '@kbn/cases-plugin/common/types/api';
import { CaseConnector } from '@kbn/cases-plugin/common/types/domain';
import { v4 as uuidv4 } from 'uuid';
export function generateRandomCaseWithoutConnector(): CasePostRequest {
export function generateRandomCaseWithoutConnector(owner = 'cases'): CasePostRequest {
return {
title: 'random-' + uuidv4(),
tags: ['test', uuidv4()],
@ -23,6 +23,6 @@ export function generateRandomCaseWithoutConnector(): CasePostRequest {
settings: {
syncAlerts: false,
},
owner: 'cases',
owner,
};
}

View file

@ -0,0 +1,284 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { CaseSeverity, CaseStatuses } from '@kbn/cases-plugin/common/types/domain';
import { SeverityAll } from '@kbn/cases-plugin/common/ui';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default ({ getPageObject, getService }: FtrProviderContext) => {
const header = getPageObject('header');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
const svlCommonNavigation = getPageObject('svlCommonNavigation');
const svlObltNavigation = getService('svlObltNavigation');
describe('cases list', () => {
before(async () => {
await svlObltNavigation.navigateToLandingPage();
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'observability-overview:cases' });
});
after(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
describe('empty state', () => {
it('displays an empty list with an add button correctly', async () => {
await testSubjects.existOrFail('cases-table-add-case');
});
});
describe('listing', () => {
createNCasesBeforeDeleteAllAfter(2, getPageObject, getService);
it('lists cases correctly', async () => {
await cases.casesTable.validateCasesTableHasNthRows(2);
});
});
describe('bulk actions', () => {
describe('delete', () => {
createNCasesBeforeDeleteAllAfter(8, getPageObject, getService);
it('bulk delete cases from the list', async () => {
await cases.casesTable.selectAndDeleteAllCases();
await cases.casesTable.waitForTableToFinishLoading();
await cases.casesTable.validateCasesTableHasNthRows(0);
});
});
describe('status', () => {
createNCasesBeforeDeleteAllAfter(2, getPageObject, getService);
it('change the status of cases to in-progress correctly', async () => {
await cases.casesTable.selectAndChangeStatusOfAllCases(CaseStatuses['in-progress']);
await cases.casesTable.waitForTableToFinishLoading();
await testSubjects.missingOrFail('case-status-badge-open');
});
});
describe('severity', () => {
createNCasesBeforeDeleteAllAfter(2, getPageObject, getService);
it('change the severity of cases to medium correctly', async () => {
await cases.casesTable.selectAndChangeSeverityOfAllCases(CaseSeverity.MEDIUM);
await cases.casesTable.waitForTableToFinishLoading();
await testSubjects.missingOrFail('case-table-column-severity-low');
});
});
describe('tags', () => {
let caseIds: string[] = [];
beforeEach(async () => {
caseIds = [];
const case1 = await cases.api.createCase({
title: 'case 1',
tags: ['one', 'three'],
owner: 'observability',
});
const case2 = await cases.api.createCase({
title: 'case 2',
tags: ['two', 'four'],
owner: 'observability',
});
const case3 = await cases.api.createCase({
title: 'case 3',
tags: ['two', 'five'],
owner: 'observability',
});
caseIds.push(case1.id);
caseIds.push(case2.id);
caseIds.push(case3.id);
await header.waitUntilLoadingHasFinished();
await cases.casesTable.waitForCasesToBeListed();
});
afterEach(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
it('bulk edit tags', async () => {
/**
* Case 3 tags: two, five
* Case 2 tags: two, four
* Case 1 tags: one, three
* All tags: one, two, three, four, five.
*
* It selects Case 3 and Case 2 because the table orders
* the cases in descending order by creation date and clicks
* the one, three, and five tags
*/
await cases.casesTable.bulkEditTags([0, 1], ['two', 'three', 'five']);
await header.waitUntilLoadingHasFinished();
const case1 = await cases.api.getCase({ caseId: caseIds[0] });
const case2 = await cases.api.getCase({ caseId: caseIds[1] });
const case3 = await cases.api.getCase({ caseId: caseIds[2] });
expect(case3.tags).eql(['five', 'three']);
expect(case2.tags).eql(['four', 'five', 'three']);
expect(case1.tags).eql(['one', 'three']);
});
it('adds a new tag', async () => {
await cases.casesTable.bulkAddNewTag([0, 1], 'tw');
await header.waitUntilLoadingHasFinished();
const case1 = await cases.api.getCase({ caseId: caseIds[0] });
const case2 = await cases.api.getCase({ caseId: caseIds[1] });
const case3 = await cases.api.getCase({ caseId: caseIds[2] });
expect(case3.tags).eql(['two', 'five', 'tw']);
expect(case2.tags).eql(['two', 'four', 'tw']);
expect(case1.tags).eql(['one', 'three']);
});
});
});
describe('severity filtering', () => {
before(async () => {
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'observability-overview:cases' });
await cases.api.createCase({ severity: CaseSeverity.LOW, owner: 'observability' });
await cases.api.createCase({ severity: CaseSeverity.LOW, owner: 'observability' });
await cases.api.createCase({ severity: CaseSeverity.HIGH, owner: 'observability' });
await cases.api.createCase({ severity: CaseSeverity.HIGH, owner: 'observability' });
await cases.api.createCase({ severity: CaseSeverity.CRITICAL, owner: 'observability' });
await header.waitUntilLoadingHasFinished();
await cases.casesTable.waitForCasesToBeListed();
});
beforeEach(async () => {
/**
* There is no easy way to clear the filtering.
* Refreshing the page seems to be easier.
*/
await svlCommonNavigation.sidenav.clickLink({ deepLinkId: 'observability-overview:cases' });
});
after(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
it('filters cases by severity', async () => {
// by default filter by all
await cases.casesTable.validateCasesTableHasNthRows(5);
// low
await cases.casesTable.filterBySeverity(CaseSeverity.LOW);
await cases.casesTable.validateCasesTableHasNthRows(2);
// high
await cases.casesTable.filterBySeverity(CaseSeverity.HIGH);
await cases.casesTable.validateCasesTableHasNthRows(2);
// critical
await cases.casesTable.filterBySeverity(CaseSeverity.CRITICAL);
await cases.casesTable.validateCasesTableHasNthRows(1);
// back to all
await cases.casesTable.filterBySeverity(SeverityAll);
await cases.casesTable.validateCasesTableHasNthRows(5);
});
});
describe('pagination', () => {
createNCasesBeforeDeleteAllAfter(12, getPageObject, getService);
it('paginates cases correctly', async () => {
await testSubjects.click('tablePaginationPopoverButton');
await testSubjects.click('tablePagination-25-rows');
await testSubjects.missingOrFail('pagination-button-1');
await testSubjects.click('tablePaginationPopoverButton');
await testSubjects.click('tablePagination-10-rows');
await testSubjects.isEnabled('pagination-button-1');
await testSubjects.click('pagination-button-1');
await testSubjects.isEnabled('pagination-button-0');
});
});
describe('row actions', () => {
describe('Status', () => {
createNCasesBeforeDeleteAllAfter(1, getPageObject, getService);
it('to in progress', async () => {
await cases.casesTable.changeStatus(CaseStatuses['in-progress'], 0);
await testSubjects.existOrFail(`case-status-badge-${CaseStatuses['in-progress']}`);
});
it('to closed', async () => {
await cases.casesTable.changeStatus(CaseStatuses.closed, 0);
await testSubjects.existOrFail(`case-status-badge-${CaseStatuses.closed}`);
});
it('to open', async () => {
await cases.casesTable.changeStatus(CaseStatuses.open, 0);
await testSubjects.existOrFail(`case-status-badge-${CaseStatuses.open}`);
});
});
describe('Severity', () => {
createNCasesBeforeDeleteAllAfter(1, getPageObject, getService);
it('to medium', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.MEDIUM, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.MEDIUM}`);
});
it('to high', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.HIGH, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.HIGH}`);
});
it('to critical', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.CRITICAL, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.CRITICAL}`);
});
it('to low', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.LOW, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.LOW}`);
});
});
describe('Delete', () => {
createNCasesBeforeDeleteAllAfter(1, getPageObject, getService);
it('deletes a case correctly', async () => {
await cases.casesTable.deleteCase(0);
await cases.casesTable.waitForTableToFinishLoading();
await cases.casesTable.validateCasesTableHasNthRows(0);
});
});
});
});
};
const createNCasesBeforeDeleteAllAfter = (
numCasesToCreate: number,
getPageObject: FtrProviderContext['getPageObject'],
getService: FtrProviderContext['getService']
) => {
const cases = getService('cases');
const header = getPageObject('header');
before(async () => {
await cases.api.createNthRandomCases(numCasesToCreate, 'observability');
await header.waitUntilLoadingHasFinished();
await cases.casesTable.waitForCasesToBeListed();
});
after(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
};

View file

@ -14,5 +14,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./navigation'));
loadDiscoverLogExplorerSuite(loadTestFile);
loadTestFile(require.resolve('./cases/attachment_framework'));
loadTestFile(require.resolve('./cases/list_view'));
});
}

View file

@ -0,0 +1,284 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { CaseSeverity, CaseStatuses } from '@kbn/cases-plugin/common/types/domain';
import { SeverityAll } from '@kbn/cases-plugin/common/ui';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default ({ getPageObject, getService }: FtrProviderContext) => {
const header = getPageObject('header');
const testSubjects = getService('testSubjects');
const cases = getService('cases');
const svlSecNavigation = getService('svlSecNavigation');
describe('cases list', () => {
before(async () => {
await svlSecNavigation.navigateToLandingPage();
await testSubjects.click('solutionSideNavItemLink-cases');
});
after(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
describe('empty state', () => {
it('displays an empty list with an add button correctly', async () => {
await testSubjects.existOrFail('cases-table-add-case');
});
});
describe('listing', () => {
createNCasesBeforeDeleteAllAfter(2, getPageObject, getService);
it('lists cases correctly', async () => {
await cases.casesTable.validateCasesTableHasNthRows(2);
});
});
describe('bulk actions', () => {
describe('delete', () => {
createNCasesBeforeDeleteAllAfter(8, getPageObject, getService);
it('bulk delete cases from the list', async () => {
await cases.casesTable.selectAndDeleteAllCases();
await cases.casesTable.waitForTableToFinishLoading();
await cases.casesTable.validateCasesTableHasNthRows(0);
});
});
describe('status', () => {
createNCasesBeforeDeleteAllAfter(2, getPageObject, getService);
it('change the status of cases to in-progress correctly', async () => {
await cases.casesTable.selectAndChangeStatusOfAllCases(CaseStatuses['in-progress']);
await cases.casesTable.waitForTableToFinishLoading();
await testSubjects.missingOrFail('case-status-badge-open');
});
});
describe('severity', () => {
createNCasesBeforeDeleteAllAfter(2, getPageObject, getService);
it('change the severity of cases to medium correctly', async () => {
await cases.casesTable.selectAndChangeSeverityOfAllCases(CaseSeverity.MEDIUM);
await cases.casesTable.waitForTableToFinishLoading();
await testSubjects.missingOrFail('case-table-column-severity-low');
});
});
describe('tags', () => {
let caseIds: string[] = [];
beforeEach(async () => {
caseIds = [];
const case1 = await cases.api.createCase({
title: 'case 1',
tags: ['one', 'three'],
owner: 'securitySolution',
});
const case2 = await cases.api.createCase({
title: 'case 2',
tags: ['two', 'four'],
owner: 'securitySolution',
});
const case3 = await cases.api.createCase({
title: 'case 3',
tags: ['two', 'five'],
owner: 'securitySolution',
});
caseIds.push(case1.id);
caseIds.push(case2.id);
caseIds.push(case3.id);
await header.waitUntilLoadingHasFinished();
await cases.casesTable.waitForCasesToBeListed();
});
afterEach(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
it('bulk edit tags', async () => {
/**
* Case 3 tags: two, five
* Case 2 tags: two, four
* Case 1 tags: one, three
* All tags: one, two, three, four, five.
*
* It selects Case 3 and Case 2 because the table orders
* the cases in descending order by creation date and clicks
* the one, three, and five tags
*/
await cases.casesTable.bulkEditTags([0, 1], ['two', 'three', 'five']);
await header.waitUntilLoadingHasFinished();
const case1 = await cases.api.getCase({ caseId: caseIds[0] });
const case2 = await cases.api.getCase({ caseId: caseIds[1] });
const case3 = await cases.api.getCase({ caseId: caseIds[2] });
expect(case3.tags).eql(['five', 'three']);
expect(case2.tags).eql(['four', 'five', 'three']);
expect(case1.tags).eql(['one', 'three']);
});
it('adds a new tag', async () => {
await cases.casesTable.bulkAddNewTag([0, 1], 'tw');
await header.waitUntilLoadingHasFinished();
const case1 = await cases.api.getCase({ caseId: caseIds[0] });
const case2 = await cases.api.getCase({ caseId: caseIds[1] });
const case3 = await cases.api.getCase({ caseId: caseIds[2] });
expect(case3.tags).eql(['two', 'five', 'tw']);
expect(case2.tags).eql(['two', 'four', 'tw']);
expect(case1.tags).eql(['one', 'three']);
});
});
});
describe('severity filtering', () => {
before(async () => {
await testSubjects.click('solutionSideNavItemLink-cases');
await cases.api.createCase({ severity: CaseSeverity.LOW, owner: 'securitySolution' });
await cases.api.createCase({ severity: CaseSeverity.LOW, owner: 'securitySolution' });
await cases.api.createCase({ severity: CaseSeverity.HIGH, owner: 'securitySolution' });
await cases.api.createCase({ severity: CaseSeverity.HIGH, owner: 'securitySolution' });
await cases.api.createCase({ severity: CaseSeverity.CRITICAL, owner: 'securitySolution' });
await header.waitUntilLoadingHasFinished();
await cases.casesTable.waitForCasesToBeListed();
});
beforeEach(async () => {
/**
* There is no easy way to clear the filtering.
* Refreshing the page seems to be easier.
*/
await testSubjects.click('solutionSideNavItemLink-cases');
});
after(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
it('filters cases by severity', async () => {
// by default filter by all
await cases.casesTable.validateCasesTableHasNthRows(5);
// low
await cases.casesTable.filterBySeverity(CaseSeverity.LOW);
await cases.casesTable.validateCasesTableHasNthRows(2);
// high
await cases.casesTable.filterBySeverity(CaseSeverity.HIGH);
await cases.casesTable.validateCasesTableHasNthRows(2);
// critical
await cases.casesTable.filterBySeverity(CaseSeverity.CRITICAL);
await cases.casesTable.validateCasesTableHasNthRows(1);
// back to all
await cases.casesTable.filterBySeverity(SeverityAll);
await cases.casesTable.validateCasesTableHasNthRows(5);
});
});
describe('pagination', () => {
createNCasesBeforeDeleteAllAfter(12, getPageObject, getService);
it('paginates cases correctly', async () => {
await testSubjects.click('tablePaginationPopoverButton');
await testSubjects.click('tablePagination-25-rows');
await testSubjects.missingOrFail('pagination-button-1');
await testSubjects.click('tablePaginationPopoverButton');
await testSubjects.click('tablePagination-10-rows');
await testSubjects.isEnabled('pagination-button-1');
await testSubjects.click('pagination-button-1');
await testSubjects.isEnabled('pagination-button-0');
});
});
describe('row actions', () => {
describe('Status', () => {
createNCasesBeforeDeleteAllAfter(1, getPageObject, getService);
it('to in progress', async () => {
await cases.casesTable.changeStatus(CaseStatuses['in-progress'], 0);
await testSubjects.existOrFail(`case-status-badge-${CaseStatuses['in-progress']}`);
});
it('to closed', async () => {
await cases.casesTable.changeStatus(CaseStatuses.closed, 0);
await testSubjects.existOrFail(`case-status-badge-${CaseStatuses.closed}`);
});
it('to open', async () => {
await cases.casesTable.changeStatus(CaseStatuses.open, 0);
await testSubjects.existOrFail(`case-status-badge-${CaseStatuses.open}`);
});
});
describe('Severity', () => {
createNCasesBeforeDeleteAllAfter(1, getPageObject, getService);
it('to medium', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.MEDIUM, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.MEDIUM}`);
});
it('to high', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.HIGH, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.HIGH}`);
});
it('to critical', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.CRITICAL, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.CRITICAL}`);
});
it('to low', async () => {
await cases.casesTable.changeSeverity(CaseSeverity.LOW, 0);
await testSubjects.existOrFail(`case-table-column-severity-${CaseSeverity.LOW}`);
});
});
describe('Delete', () => {
createNCasesBeforeDeleteAllAfter(1, getPageObject, getService);
it('deletes a case correctly', async () => {
await cases.casesTable.deleteCase(0);
await cases.casesTable.waitForTableToFinishLoading();
await cases.casesTable.validateCasesTableHasNthRows(0);
});
});
});
});
};
const createNCasesBeforeDeleteAllAfter = (
numCasesToCreate: number,
getPageObject: FtrProviderContext['getPageObject'],
getService: FtrProviderContext['getService']
) => {
const cases = getService('cases');
const header = getPageObject('header');
before(async () => {
await cases.api.createNthRandomCases(numCasesToCreate, 'securitySolution');
await header.waitUntilLoadingHasFinished();
await cases.casesTable.waitForCasesToBeListed();
});
after(async () => {
await cases.api.deleteAllCases();
await cases.casesTable.waitForCasesToBeDeleted();
});
};

View file

@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./ftr/navigation'));
loadTestFile(require.resolve('./ftr/management'));
loadTestFile(require.resolve('./ftr/cases/attachment_framework'));
loadTestFile(require.resolve('./ftr/cases/list_view'));
});
}