[8.8] [ResponseOps][Maintenance Window] Add and improve E2E tests for the maintenance window table (#156611) (#156853)

# Backport

This will backport the following commits from `main` to `8.8`:
- [[ResponseOps][Maintenance Window] Add and improve E2E tests for the
maintenance window table
(#156611)](https://github.com/elastic/kibana/pull/156611)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Alexi
Doak","email":"109488926+doakalexi@users.noreply.github.com"},"sourceCommit":{"committedDate":"2023-05-05T12:47:40Z","message":"[ResponseOps][Maintenance
Window] Add and improve E2E tests for the maintenance window table
(#156611)\n\n## Summary\r\n\r\nAdds functional tests for the maintenance
windows table.\r\n\r\nShould test:\r\n- Cancel a maintenance window\r\n-
Archiving/Unarchiving a maintenance window\r\n- Cancelling and archiving
a maintenance window\r\n- Searching/filtering\r\n\r\n\r\nIssue linked -
https://github.com/elastic/kibana/issues/155902","sha":"3fef5e5cda748c11786cfe66f69644b56841d656","branchLabelMapping":{"^v8.9.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","v8.8.0","v8.9.0"],"number":156611,"url":"https://github.com/elastic/kibana/pull/156611","mergeCommit":{"message":"[ResponseOps][Maintenance
Window] Add and improve E2E tests for the maintenance window table
(#156611)\n\n## Summary\r\n\r\nAdds functional tests for the maintenance
windows table.\r\n\r\nShould test:\r\n- Cancel a maintenance window\r\n-
Archiving/Unarchiving a maintenance window\r\n- Cancelling and archiving
a maintenance window\r\n- Searching/filtering\r\n\r\n\r\nIssue linked -
https://github.com/elastic/kibana/issues/155902","sha":"3fef5e5cda748c11786cfe66f69644b56841d656"}},"sourceBranch":"main","suggestedTargetBranches":["8.8"],"targetPullRequestStates":[{"branch":"8.8","label":"v8.8.0","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.9.0","labelRegex":"^v8.9.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/156611","number":156611,"mergeCommit":{"message":"[ResponseOps][Maintenance
Window] Add and improve E2E tests for the maintenance window table
(#156611)\n\n## Summary\r\n\r\nAdds functional tests for the maintenance
windows table.\r\n\r\nShould test:\r\n- Cancel a maintenance window\r\n-
Archiving/Unarchiving a maintenance window\r\n- Cancelling and archiving
a maintenance window\r\n- Searching/filtering\r\n\r\n\r\nIssue linked -
https://github.com/elastic/kibana/issues/155902","sha":"3fef5e5cda748c11786cfe66f69644b56841d656"}}]}]
BACKPORT-->

Co-authored-by: Alexi Doak <109488926+doakalexi@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2023-05-05 10:52:38 -04:00 committed by GitHub
parent b7f0eadd86
commit 772c00c87e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 345 additions and 6 deletions

View file

@ -45,6 +45,7 @@ const COLUMNS: Array<EuiBasicTableColumn<MaintenanceWindowFindResponse>> = [
{
field: 'status',
name: i18n.TABLE_STATUS,
'data-test-subj': 'maintenance-windows-column-status',
render: (status: MaintenanceWindowStatus) => {
return (
<EuiBadge color={STATUS_DISPLAY[status].color}>{STATUS_DISPLAY[status].label}</EuiBadge>
@ -168,7 +169,7 @@ export const MaintenanceWindowsList = React.memo<MaintenanceWindowsListProps>(
return (
<EuiInMemoryTable
data-test-subj="mw-table"
data-test-subj="maintenance-windows-table"
css={tableCss}
itemId="id"
loading={loading || isLoadingFinish || isLoadingArchive || isLoadingFinishAndArchive}

View file

@ -217,6 +217,7 @@ export const TableActionsPopover: React.FC<TableActionsPopoverProps> = React.mem
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downCenter"
data-test-subj="table-actions-popover"
>
<EuiContextMenuPanel items={items} />
</EuiPopover>

View file

@ -78,7 +78,7 @@ describe('Maintenance windows page', () => {
};
appMockRenderer = createAppMockRenderer({ capabilities, license });
const result = appMockRenderer.render(<MaintenanceWindowsPage />);
expect(result.queryByTestId('mw-table')).toBeInTheDocument();
expect(result.queryByTestId('maintenance-windows-table')).toBeInTheDocument();
expect(appMockRenderer.mocked.setBadge).toBeCalledTimes(1);
});
});

View file

@ -45,6 +45,7 @@ import { SearchSessionsPageProvider } from './search_sessions_management_page';
import { DetectionsPageObject } from '../../security_solution_ftr/page_objects/detections';
import { BannersPageObject } from './banners_page';
import { InfraHostsViewProvider } from './infra_hosts_view';
import { MaintenanceWindowsPageProvider } from './maintenance_windows_page';
// just like services, PageObjects are defined as a map of
// names to Providers. Merge in Kibana's or pick specific ones
@ -88,4 +89,5 @@ export const pageObjects = {
banners: BannersPageObject,
detections: DetectionsPageObject,
observability: ObservabilityPageProvider,
maintenanceWindows: MaintenanceWindowsPageProvider,
};

View file

@ -0,0 +1,43 @@
/*
* 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 { FtrProviderContext } from '../ftr_provider_context';
const ENTER_KEY = '\uE007';
export function MaintenanceWindowsPageProvider({ getService }: FtrProviderContext) {
const find = getService('find');
return {
async getMaintenanceWindowsList() {
const table = await find.byCssSelector('[data-test-subj="maintenance-windows-table"] table');
const $ = await table.parseDomContent();
return $.findTestSubjects('list-item')
.toArray()
.map((row) => {
return {
status: $(row)
.findTestSubject('maintenance-windows-column-status')
.find('.euiTableCellContent')
.text(),
};
});
},
async searchMaintenanceWindows(searchText: string) {
const searchBox = await find.byCssSelector(
'.euiFieldSearch:not(.euiSelectableTemplateSitewide__search)'
);
await searchBox.click();
await searchBox.clearValue();
await searchBox.type(searchText);
await searchBox.pressKeys(ENTER_KEY);
await find.byCssSelector(
'.euiBasicTable[data-test-subj="maintenance-windows-table"]:not(.euiBasicTable-loading)'
);
},
};
}

View file

@ -16,5 +16,6 @@ export default ({ loadTestFile, getService }: FtrProviderContext) => {
loadTestFile(require.resolve('./connectors'));
loadTestFile(require.resolve('./logs_list'));
loadTestFile(require.resolve('./rules_settings'));
loadTestFile(require.resolve('./maintenance_windows'));
});
};

View file

@ -0,0 +1,14 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';
export default ({ loadTestFile }: FtrProviderContext) => {
describe('Maintenance Windows', function () {
loadTestFile(require.resolve('./maintenance_windows_table'));
});
};

View file

@ -0,0 +1,216 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';
import { ObjectRemover } from '../../../lib/object_remover';
import { generateUniqueKey } from '../../../lib/get_test_data';
import { createMaintenanceWindow, createObjectRemover } from './utils';
export default ({ getPageObjects, getService }: FtrProviderContext) => {
const testSubjects = getService('testSubjects');
const pageObjects = getPageObjects(['common', 'maintenanceWindows', 'header']);
const retry = getService('retry');
let objectRemover: ObjectRemover;
const browser = getService('browser');
describe('Maintenance windows table', function () {
before(async () => {
objectRemover = await createObjectRemover({ getService });
});
beforeEach(async () => {
await pageObjects.common.navigateToApp('maintenanceWindows');
});
after(async () => {
await objectRemover.removeAll();
});
it('should should cancel a running maintenance window', async () => {
const name = generateUniqueKey();
const createdMaintenanceWindow = await createMaintenanceWindow({
name,
getService,
});
objectRemover.add(createdMaintenanceWindow.id, 'rules/maintenance_window', 'alerting', true);
await browser.refresh();
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
let list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Running');
await testSubjects.click('table-actions-popover');
await testSubjects.click('table-actions-cancel');
await testSubjects.click('confirmModalConfirmButton');
await retry.try(async () => {
const toastTitle = await pageObjects.common.closeToast();
expect(toastTitle).to.eql(`Cancelled running maintenance window '${name}'`);
});
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.not.eql('Running');
});
it('should should archive finished maintenance window', async () => {
const name = generateUniqueKey();
const createdMaintenanceWindow = await createMaintenanceWindow({
name,
startDate: new Date('05-01-2023'),
notRecurring: true,
getService,
});
objectRemover.add(createdMaintenanceWindow.id, 'rules/maintenance_window', 'alerting', true);
await browser.refresh();
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
let list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Finished');
await testSubjects.click('table-actions-popover');
await testSubjects.click('table-actions-archive');
await testSubjects.click('confirmModalConfirmButton');
await retry.try(async () => {
const toastTitle = await pageObjects.common.closeToast();
expect(toastTitle).to.eql(`Archived maintenance window '${name}'`);
});
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Archived');
});
it('should should cancel and archive a running maintenance window', async () => {
const name = generateUniqueKey();
const createdMaintenanceWindow = await createMaintenanceWindow({
name,
getService,
});
objectRemover.add(createdMaintenanceWindow.id, 'rules/maintenance_window', 'alerting', true);
await browser.refresh();
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
let list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Running');
await testSubjects.click('table-actions-popover');
await testSubjects.click('table-actions-cancel-and-archive');
await testSubjects.click('confirmModalConfirmButton');
await retry.try(async () => {
const toastTitle = await pageObjects.common.closeToast();
expect(toastTitle).to.eql(`Cancelled and archived running maintenance window '${name}'`);
});
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Archived');
});
it('should should unarchive a maintenance window', async () => {
const name = generateUniqueKey();
const createdMaintenanceWindow = await createMaintenanceWindow({
name,
startDate: new Date('05-01-2023'),
notRecurring: true,
getService,
});
objectRemover.add(createdMaintenanceWindow.id, 'rules/maintenance_window', 'alerting', true);
await browser.refresh();
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
let list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Finished');
await testSubjects.click('table-actions-popover');
await testSubjects.click('table-actions-archive');
await testSubjects.click('confirmModalConfirmButton');
await retry.try(async () => {
const toastTitle = await pageObjects.common.closeToast();
expect(toastTitle).to.eql(`Archived maintenance window '${name}'`);
});
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Archived');
await testSubjects.click('table-actions-popover');
await testSubjects.click('table-actions-unarchive');
await testSubjects.click('confirmModalConfirmButton');
await retry.try(async () => {
const toastTitle = await pageObjects.common.closeToast();
expect(toastTitle).to.eql(`Unarchived maintenance window '${name}'`);
});
await pageObjects.maintenanceWindows.searchMaintenanceWindows(name);
list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(1);
expect(list[0].status).to.eql('Finished');
});
it('should filter maintenance windows by the status', async () => {
const running = await createMaintenanceWindow({
name: 'running-mw',
getService,
});
objectRemover.add(running.id, 'rules/maintenance_window', 'alerting', true);
const finished = await createMaintenanceWindow({
name: 'finished-mw',
startDate: new Date('05-01-2023'),
notRecurring: true,
getService,
});
objectRemover.add(finished.id, 'rules/maintenance_window', 'alerting', true);
const date = new Date();
date.setDate(date.getDate() + 1);
const upcoming = await createMaintenanceWindow({
name: 'upcoming-mw',
startDate: date,
getService,
});
objectRemover.add(upcoming.id, 'rules/maintenance_window', 'alerting', true);
await browser.refresh();
await pageObjects.maintenanceWindows.searchMaintenanceWindows('mw');
const list = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(list.length).to.eql(3);
await testSubjects.click('status-filter-button');
await testSubjects.click('status-filter-upcoming'); // select Upcoming status filter
await retry.try(async () => {
const upcomingList = await pageObjects.maintenanceWindows.getMaintenanceWindowsList();
expect(upcomingList.length).to.equal(1);
expect(upcomingList[0].status).to.equal('Upcoming');
});
});
});
};

View file

@ -0,0 +1,52 @@
/*
* 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 { ObjectRemover } from '../../../lib/object_remover';
import { FtrProviderContext } from '../../../ftr_provider_context';
export const createObjectRemover = async ({
getService,
}: {
getService: FtrProviderContext['getService'];
}) => {
const supertest = getService('supertest');
const objectRemover = new ObjectRemover(supertest);
return objectRemover;
};
export const createMaintenanceWindow = async ({
name,
startDate,
notRecurring,
getService,
}: {
name: string;
startDate?: Date;
notRecurring?: boolean;
getService: FtrProviderContext['getService'];
}) => {
const supertest = getService('supertest');
const dtstart = startDate ? startDate : new Date();
const createParams = {
title: name,
duration: 60 * 60 * 1000,
r_rule: {
dtstart: dtstart.toISOString(),
tzid: 'UTC',
...(notRecurring ? { freq: 1, count: 1 } : { freq: 2 }),
},
};
const { body } = await supertest
.post(`/internal/alerting/rules/maintenance_window`)
.set('kbn-xsrf', 'foo')
.send(createParams)
.expect(200);
return body;
};

View file

@ -61,6 +61,9 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
triggersActionsConnectors: {
pathname: '/app/management/insightsAndAlerting/triggersActionsConnectors',
},
maintenanceWindows: {
pathname: '/app/management/insightsAndAlerting/maintenanceWindows',
},
},
esTestCluster: {
...xpackFunctionalConfig.get('esTestCluster'),

View file

@ -9,6 +9,7 @@ interface ObjectToRemove {
id: string;
type: string;
plugin: string;
isInternal?: boolean;
}
export class ObjectRemover {
@ -19,15 +20,20 @@ export class ObjectRemover {
this.supertest = supertest;
}
add(id: ObjectToRemove['id'], type: ObjectToRemove['type'], plugin: ObjectToRemove['plugin']) {
this.objectsToRemove.push({ id, type, plugin });
add(
id: ObjectToRemove['id'],
type: ObjectToRemove['type'],
plugin: ObjectToRemove['plugin'],
isInternal?: ObjectToRemove['isInternal']
) {
this.objectsToRemove.push({ id, type, plugin, isInternal });
}
async removeAll() {
await Promise.all(
this.objectsToRemove.map(({ id, type, plugin }) => {
this.objectsToRemove.map(({ id, type, plugin, isInternal }) => {
return this.supertest
.delete(`/api/${plugin}/${type}/${id}`)
.delete(`/${isInternal ? 'internal' : 'api'}/${plugin}/${type}/${id}`)
.set('kbn-xsrf', 'foo')
.expect(204);
})