Skip to content

Commit

Permalink
[ResponseOps][Maintenance Window] Add and improve E2E tests for the m…
Browse files Browse the repository at this point in the history
…aintenance window table (#156611)

## Summary

Adds functional tests for the maintenance windows table.

Should test:
-  Cancel a maintenance window
- Archiving/Unarchiving a maintenance window
- Cancelling and archiving a maintenance window
- Searching/filtering


Issue linked - #155902
  • Loading branch information
doakalexi committed May 5, 2023
1 parent 88cbc0f commit 3fef5e5
Show file tree
Hide file tree
Showing 11 changed files with 345 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
2 changes: 2 additions & 0 deletions x-pack/test/functional/page_objects/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -88,4 +89,5 @@ export const pageObjects = {
banners: BannersPageObject,
detections: DetectionsPageObject,
observability: ObservabilityPageProvider,
maintenanceWindows: MaintenanceWindowsPageProvider,
};
43 changes: 43 additions & 0 deletions x-pack/test/functional/page_objects/maintenance_windows_page.ts
Original file line number Diff line number Diff line change
@@ -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)'
);
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
});
};
Original file line number Diff line number Diff line change
@@ -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'));
});
};
Original file line number Diff line number Diff line change
@@ -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');
});
});
});
};
Original file line number Diff line number Diff line change
@@ -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;
};
3 changes: 3 additions & 0 deletions x-pack/test/functional_with_es_ssl/config.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
Loading

0 comments on commit 3fef5e5

Please sign in to comment.