diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts index 91eabb6c6472f..bb56b1a7b5269 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alert_create_flyout.ts @@ -4,13 +4,9 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; - -function generateUniqueKey() { - return uuid.v4().replace(/-/g, ''); -} +import { generateUniqueKey } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts index 4961f842628ba..e2c064169dc92 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts_list.ts @@ -4,67 +4,50 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; import { times } from 'lodash'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; - -function generateUniqueKey() { - return uuid.v4().replace(/-/g, ''); -} +import { ObjectRemover } from '../../lib/object_remover'; +import { generateUniqueKey, getTestAlertData, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const alerting = getService('alerting'); const testSubjects = getService('testSubjects'); const find = getService('find'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const supertest = getService('supertest'); const retry = getService('retry'); + const objectRemover = new ObjectRemover(supertest); - async function deleteAlerts(alertIds: string[]) { - alertIds.forEach(async (alertId: string) => { - await supertest.delete(`/api/alerts/alert/${alertId}`).set('kbn-xsrf', 'foo').expect(204, ''); - }); - } - - async function createAlert(overwrites: Record = {}) { + async function createAlertManualCleanup(overwrites: Record = {}) { const { body: createdAlert } = await supertest .post(`/api/alerts/alert`) .set('kbn-xsrf', 'foo') - .send({ - enabled: true, - name: generateUniqueKey(), - tags: ['foo', 'bar'], - alertTypeId: 'test.noop', - consumer: 'alerts', - schedule: { interval: '1m' }, - throttle: '1m', - actions: [], - params: {}, - ...overwrites, - }) + .send(getTestAlertData(overwrites)) .expect(200); return createdAlert; } - async function createFailingAlert(overwrites: Record = {}) { - const { body: createdAlert } = await supertest - .post(`/api/alerts/alert`) + async function createFailingAlert() { + return await createAlert({ + alertTypeId: 'test.failing', + schedule: { interval: '30s' }, + }); + } + + async function createAlert(overwrites: Record = {}) { + const createdAlert = await createAlertManualCleanup(overwrites); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; + } + + async function createAction(overwrites: Record = {}) { + const { body: createdAction } = await supertest + .post(`/api/actions/action`) .set('kbn-xsrf', 'foo') - .send({ - enabled: true, - name: generateUniqueKey(), - tags: ['foo', 'bar'], - alertTypeId: 'test.failing', - consumer: 'alerts', - schedule: { interval: '30s' }, - throttle: '1m', - actions: [], - params: {}, - ...overwrites, - }) + .send(getTestActionData(overwrites)) .expect(200); - return createdAlert; + objectRemover.add(createdAction.id, 'action', 'actions'); + return createdAction; } async function refreshAlertsList() { @@ -77,11 +60,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.click('alertsTab'); }); + afterEach(async () => { + await objectRemover.removeAll(); + }); + it('should display alerts in alphabetical order', async () => { const uniqueKey = generateUniqueKey(); - const a = await createAlert({ name: 'b', tags: [uniqueKey] }); - const b = await createAlert({ name: 'c', tags: [uniqueKey] }); - const c = await createAlert({ name: 'a', tags: [uniqueKey] }); + await createAlert({ name: 'b', tags: [uniqueKey] }); + await createAlert({ name: 'c', tags: [uniqueKey] }); + await createAlert({ name: 'a', tags: [uniqueKey] }); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(uniqueKey); @@ -90,8 +77,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(searchResults[0].name).to.eql('a'); expect(searchResults[1].name).to.eql('b'); expect(searchResults[2].name).to.eql('c'); - - await deleteAlerts([a.id, b.id, c.id]); }); it('should search for alert', async () => { @@ -108,7 +93,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { interval: '1m', }, ]); - await deleteAlerts([createdAlert.id]); }); it('should search for tags', async () => { @@ -125,16 +109,14 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { interval: '1m', }, ]); - await deleteAlerts([createdAlert.id]); }); it('should display an empty list when search did not return any alerts', async () => { - const createdAlert = await createAlert(); + await createAlert(); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(`An Alert That For Sure Doesn't Exist!`); expect(await pageObjects.triggersActionsUI.isAlertsListDisplayed()).to.eql(true); - await deleteAlerts([createdAlert.id]); }); it('should disable single alert', async () => { @@ -153,7 +135,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitchAfterDisable = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitchAfterDisable.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should re-enable single alert', async () => { @@ -178,7 +159,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitchAfterReEnable = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitchAfterReEnable.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should mute single alert', async () => { @@ -197,7 +177,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitchAfterMute = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitchAfterMute.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should unmute single alert', async () => { @@ -222,12 +201,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitchAfterUnmute = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitchAfterUnmute.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should delete single alert', async () => { - const firstAlert = await createAlert(); - const secondAlert = await createAlert(); + await createAlert(); + const secondAlert = await createAlertManualCleanup(); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(secondAlert.name); @@ -247,7 +225,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.searchAlerts(secondAlert.name); const searchResultsAfterDelete = await pageObjects.triggersActionsUI.getAlertsList(); expect(searchResultsAfterDelete.length).to.eql(0); - await deleteAlerts([firstAlert.id]); }); it('should mute all selection', async () => { @@ -271,7 +248,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitch = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should unmute all selection', async () => { @@ -297,7 +273,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const muteSwitch = await testSubjects.find('muteSwitch'); const isChecked = await muteSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it('should disable all selection', async () => { @@ -321,7 +296,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitch = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('true'); - await deleteAlerts([createdAlert.id]); }); it('should enable all selection', async () => { @@ -347,14 +321,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const disableSwitch = await testSubjects.find('disableSwitch'); const isChecked = await disableSwitch.getAttribute('aria-checked'); expect(isChecked).to.eql('false'); - await deleteAlerts([createdAlert.id]); }); it.skip('should delete all selection', async () => { const namePrefix = generateUniqueKey(); let count = 0; const createdAlertsFirstPage = await Promise.all( - times(2, () => createAlert({ name: `${namePrefix}-0${count++}` })) + times(2, () => createAlertManualCleanup({ name: `${namePrefix}-0${count++}` })) ); await refreshAlertsList(); await pageObjects.triggersActionsUI.searchAlerts(namePrefix); @@ -381,13 +354,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should filter alerts by the status', async () => { - const createdAlert = await createAlert(); - const failinfAlert = await createFailingAlert(); + await createAlert(); + const failingAlert = await createFailingAlert(); // initialy alert get Pending status, so we need to retry refresh list logic to get the post execution statuses await retry.try(async () => { await refreshAlertsList(); const refreshResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); - expect(refreshResults.map((item) => item.status).sort()).to.eql(['Error', 'Ok']); + expect(refreshResults.map((item: any) => item.status).sort()).to.eql(['Error', 'Ok']); }); await testSubjects.click('alertStatusFilterButton'); await testSubjects.click('alertStatuserrorFilerOption'); // select Error status filter @@ -395,7 +368,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const filterErrorOnlyResults = await pageObjects.triggersActionsUI.getAlertsListWithStatus(); expect(filterErrorOnlyResults).to.eql([ { - name: failinfAlert.name, + name: failingAlert.name, tagsText: 'foo, bar', alertType: 'Test: Failing', interval: '30s', @@ -403,8 +376,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }, ]); }); - - await deleteAlerts([createdAlert.id, failinfAlert.id]); }); it('should display total alerts by status and error banner only when exists alerts with status error', async () => { @@ -428,7 +399,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ); expect(alertsErrorBannerWhenNoErrors).to.have.length(0); - const failingAlert = await createFailingAlert(); + await createFailingAlert(); await retry.try(async () => { await refreshAlertsList(); const alertsErrorBannerExistErrors = await find.allByCssSelector( @@ -450,13 +421,11 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { expect(await testSubjects.getVisibleText('totalErrorAlertsCount')).to.be('Error: 1'); expect(await testSubjects.getVisibleText('totalPendingAlertsCount')).to.be('Pending: 0'); expect(await testSubjects.getVisibleText('totalUnknownAlertsCount')).to.be('Unknown: 0'); - - await deleteAlerts([createdAlert.id, failingAlert.id]); }); it('should filter alerts by the alert type', async () => { - const noopAlert = await createAlert(); - const failinfAlert = await createFailingAlert(); + await createAlert(); + const failingAlert = await createFailingAlert(); await refreshAlertsList(); await testSubjects.click('alertTypeFilterButton'); expect(await (await testSubjects.find('alertType0Group')).getVisibleText()).to.eql('Alerts'); @@ -466,27 +435,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const filterFailingAlertOnlyResults = await pageObjects.triggersActionsUI.getAlertsList(); expect(filterFailingAlertOnlyResults).to.eql([ { - name: failinfAlert.name, + name: failingAlert.name, tagsText: 'foo, bar', alertType: 'Test: Failing', interval: '30s', }, ]); }); - - await deleteAlerts([noopAlert.id, failinfAlert.id]); }); it('should filter alerts by the action type', async () => { - const noopAlert = await createAlert(); - const action = await alerting.actions.createAction({ - name: `slack-${Date.now()}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }); + await createAlert(); + const action = await createAction(); const noopAlertWithAction = await createAlert({ actions: [ { @@ -512,8 +472,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }, ]); }); - - await deleteAlerts([noopAlertWithAction.id, noopAlert.id]); }); }); }; diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts index f55114cf11d14..c28a5ed3794af 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/connectors.ts @@ -4,35 +4,35 @@ * you may not use this file except in compliance with the Elastic License. */ -import uuid from 'uuid'; import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; - -function generateUniqueKey() { - return uuid.v4().replace(/-/g, ''); -} +import { ObjectRemover } from '../../lib/object_remover'; +import { generateUniqueKey, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const alerting = getService('alerting'); const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const find = getService('find'); const retry = getService('retry'); const comboBox = getService('comboBox'); + const supertest = getService('supertest'); describe('Connectors', function () { - before(async () => { - await alerting.actions.createAction({ - name: `slack-${Date.now()}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }); + const objectRemover = new ObjectRemover(supertest); + before(async () => { + const { body: createdAction } = await supertest + .post(`/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send(getTestActionData()) + .expect(200); await pageObjects.common.navigateToApp('triggersActions'); await testSubjects.click('connectorsTab'); + objectRemover.add(createdAction.id, 'action', 'actions'); + }); + + after(async () => { + await objectRemover.removeAll(); }); it('should create a connector', async () => { diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts index fdcb456493dab..14bb35ee539fe 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/details.ts @@ -9,53 +9,103 @@ import uuid from 'uuid'; import { omit, mapValues, range, flatten } from 'lodash'; import moment from 'moment'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { ObjectRemover } from '../../lib/object_remover'; import { alwaysFiringAlertType } from '../../fixtures/plugins/alerts/server/plugin'; +import { getTestAlertData, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header', 'alertDetailsUI']); const browser = getService('browser'); const log = getService('log'); - const alerting = getService('alerting'); const retry = getService('retry'); const find = getService('find'); + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); + + async function createAction(overwrites: Record = {}) { + const { body: createdAction } = await supertest + .post(`/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send(getTestActionData(overwrites)) + .expect(200); + objectRemover.add(createdAction.id, 'action', 'actions'); + return createdAction; + } + + async function createAlert(overwrites: Record = {}) { + const { body: createdAlert } = await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData(overwrites)) + .expect(200); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; + } + + async function createAlwaysFiringAlert(overwrites: Record = {}) { + const { body: createdAlert } = await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send( + getTestAlertData({ + alertTypeId: 'test.always-firing', + ...overwrites, + }) + ) + .expect(200); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); + return createdAlert; + } + + async function createActions(testRunUuid: string) { + return await Promise.all([ + createAction({ name: `slack-${testRunUuid}-${0}` }), + createAction({ name: `slack-${testRunUuid}-${1}` }), + ]); + } + + async function createAlertWithActionsAndParams( + testRunUuid: string, + params: Record = {} + ) { + const actions = await createActions(testRunUuid); + return await createAlwaysFiringAlert({ + name: `test-alert-${testRunUuid}`, + actions: actions.map((action) => ({ + id: action.id, + group: 'default', + params: { + message: 'from alert 1s', + level: 'warn', + }, + })), + params, + }); + } + + async function getAlertInstanceSummary(alertId: string) { + const { body: summary } = await supertest + .get(`/api/alerts/alert/${alertId}/_instance_summary`) + .expect(200); + return summary; + } + + async function muteAlertInstance(alertId: string, alertInstanceId: string) { + const { body: response } = await supertest + .post(`/api/alerts/alert/${alertId}/alert_instance/${alertInstanceId}/_mute`) + .set('kbn-xsrf', 'foo') + .expect(204); + + return response; + } describe('Alert Details', function () { describe('Header', function () { const testRunUuid = uuid.v4(); before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - - const actions = await Promise.all([ - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${0}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${1}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - ]); - - const alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-${testRunUuid}`, - actions.map((action) => ({ - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - })) - ); + const alert = await createAlertWithActionsAndParams(testRunUuid); // refresh to see alert await browser.refresh(); @@ -69,6 +119,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('renders the alert details', async () => { const headingText = await pageObjects.alertDetailsUI.getHeadingText(); expect(headingText).to.be(`test-alert-${testRunUuid}`); @@ -154,33 +208,41 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); describe('Edit alert button', function () { - const testRunUuid = uuid.v4(); + const alertName = uuid.v4(); + const updatedAlertName = `Changed Alert Name ${alertName}`; - it('should open edit alert flyout', async () => { - await pageObjects.common.navigateToApp('triggersActions'); - const params = { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }; - const alert = await alerting.alerts.createAlertWithActions( - testRunUuid, - '.index-threshold', - params, - [ + before(async () => { + await createAlwaysFiringAlert({ + name: alertName, + alertTypeId: '.index-threshold', + params: { + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + groupBy: 'all', + threshold: [1000, 5000], + index: ['.kibana_1'], + timeField: 'alert', + }, + actions: [ { group: 'threshold met', id: 'my-server-log', params: { level: 'info', message: ' {{context.message}}' }, }, - ] - ); + ], + }); + }); + + after(async () => { + await objectRemover.removeAll(); + }); + + it('should open edit alert flyout', async () => { + await pageObjects.common.navigateToApp('triggersActions'); + // refresh to see alert await browser.refresh(); @@ -190,13 +252,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('alertsList'); // click on first alert - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alertName); const editButton = await testSubjects.find('openEditAlertFlyoutButton'); await editButton.click(); expect(await testSubjects.exists('hasActionsDisabled')).to.eql(false); - const updatedAlertName = `Changed Alert Name ${uuid.v4()}`; await testSubjects.setValue('alertNameInput', updatedAlertName, { clearWithKeyboard: true, }); @@ -212,22 +273,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('should reset alert when canceling an edit', async () => { await pageObjects.common.navigateToApp('triggersActions'); - const params = { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', - }; - const alert = await alerting.alerts.createAlertWithActions( - testRunUuid, - '.index-threshold', - params - ); + // refresh to see alert await browser.refresh(); @@ -237,13 +283,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('alertsList'); // click on first alert - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(updatedAlertName); const editButton = await testSubjects.find('openEditAlertFlyoutButton'); await editButton.click(); - const updatedAlertName = `Changed Alert Name ${uuid.v4()}`; - await testSubjects.setValue('alertNameInput', updatedAlertName, { + await testSubjects.setValue('alertNameInput', uuid.v4(), { clearWithKeyboard: true, }); @@ -254,23 +299,29 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const nameInputAfterCancel = await testSubjects.find('alertNameInput'); const textAfterCancel = await nameInputAfterCancel.getAttribute('value'); - expect(textAfterCancel).to.eql(alert.name); + expect(textAfterCancel).to.eql(updatedAlertName); }); }); describe('View In App', function () { - const testRunUuid = uuid.v4(); + const alertName = uuid.v4(); beforeEach(async () => { await pageObjects.common.navigateToApp('triggersActions'); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('renders the alert details view in app button', async () => { - const alert = await alerting.alerts.createNoOp(`test-alert-${testRunUuid}`); + const alert = await createAlert({ + name: alertName, + consumer: 'alerting_fixture', + }); // refresh to see alert await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); // Verify content @@ -287,14 +338,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('renders a disabled alert details view in app button', async () => { - const alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-disabled-nav`, - [] - ); + const alert = await createAlwaysFiringAlert({ + name: `test-alert-disabled-nav`, + }); // refresh to see alert await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); // Verify content @@ -314,44 +363,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - const actions = await Promise.all([ - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${0}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${1}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - ]); - const instances = [{ id: 'us-central' }, { id: 'us-east' }, { id: 'us-west' }]; - alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-${testRunUuid}`, - actions.map((action) => ({ - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - })), - { - instances, - } - ); + alert = await createAlertWithActionsAndParams(testRunUuid, { + instances, + }); // refresh to see alert await browser.refresh(); - await pageObjects.header.waitUntilLoadingHasFinished(); // Verify content @@ -362,13 +380,15 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // await first run to complete so we have an initial state await retry.try(async () => { - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); expect(Object.keys(alertInstances).length).to.eql(instances.length); }); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('renders the active alert instances', async () => { // refresh to ensure Api call and UI are looking at freshest output await browser.refresh(); @@ -384,8 +404,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { (actionGroup: { id: string; name: string }) => actionGroup.id === actionGroupId )?.name; - const summary = await alerting.alerts.getAlertInstanceSummary(alert.id); - const dateOnAllInstancesFromApiResponse = mapValues( + const summary = await getAlertInstanceSummary(alert.id); + const dateOnAllInstancesFromApiResponse: Record = mapValues( summary.instances, (instance) => instance.activeStartDate ); @@ -404,7 +424,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { .join(', ')}` ); - const instancesList = await pageObjects.alertDetailsUI.getAlertInstancesList(); + const instancesList: any[] = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect(instancesList.map((instance) => omit(instance, 'duration'))).to.eql([ { instance: 'us-central', @@ -474,12 +494,12 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { it('renders the muted inactive alert instances', async () => { // mute an alert instance that doesn't exist - await alerting.alerts.muteAlertInstance(alert.id, 'eu-east'); + await muteAlertInstance(alert.id, 'eu-east'); // refresh to see alert await browser.refresh(); - const instancesList = await pageObjects.alertDetailsUI.getAlertInstancesList(); + const instancesList: any[] = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect( instancesList.filter((alertInstance) => alertInstance.instance === 'eu-east') ).to.eql([ @@ -545,25 +565,6 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); - const actions = await Promise.all([ - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${0}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - alerting.actions.createAction({ - name: `slack-${testRunUuid}-${1}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }), - ]); - const instances = flatten( range(10).map((index) => [ { id: `us-central-${index}` }, @@ -571,26 +572,13 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { { id: `us-west-${index}` }, ]) ); - alert = await alerting.alerts.createAlwaysFiringWithActions( - `test-alert-${testRunUuid}`, - actions.map((action) => ({ - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - })), - { - instances, - } - ); + alert = await createAlertWithActionsAndParams(testRunUuid, { + instances, + }); // await first run to complete so we have an initial state await retry.try(async () => { - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); expect(Object.keys(alertInstances).length).to.eql(instances.length); }); @@ -606,14 +594,16 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); }); + after(async () => { + await objectRemover.removeAll(); + }); + const PAGE_SIZE = 10; it('renders the first page', async () => { // Verify content await testSubjects.existOrFail('alertInstancesList'); - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); const items = await pageObjects.alertDetailsUI.getAlertInstancesList(); expect(items.length).to.eql(PAGE_SIZE); @@ -626,9 +616,7 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { // Verify content await testSubjects.existOrFail('alertInstancesList'); - const { instances: alertInstances } = await alerting.alerts.getAlertInstanceSummary( - alert.id - ); + const { instances: alertInstances } = await getAlertInstanceSummary(alert.id); await pageObjects.alertDetailsUI.clickPaginationNextPage(); diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts index bd799947256d6..029209ec2024d 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/home_page.ts @@ -6,19 +6,26 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; +import { ObjectRemover } from '../../lib/object_remover'; +import { getTestAlertData, getTestActionData } from '../../lib/get_test_data'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const testSubjects = getService('testSubjects'); const pageObjects = getPageObjects(['common', 'triggersActionsUI', 'header']); const log = getService('log'); const browser = getService('browser'); - const alerting = getService('alerting'); + const supertest = getService('supertest'); + const objectRemover = new ObjectRemover(supertest); describe('Home page', function () { before(async () => { await pageObjects.common.navigateToApp('triggersActions'); }); + after(async () => { + await objectRemover.removeAll(); + }); + it('Loads the app', async () => { await log.debug('Checking for section heading to say Triggers and Actions.'); @@ -58,26 +65,19 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('navigates to an alert details page', async () => { - const action = await alerting.actions.createAction({ - name: `Slack-${Date.now()}`, - actionTypeId: '.slack', - config: {}, - secrets: { - webhookUrl: 'https://test', - }, - }); - - const alert = await alerting.alerts.createAlwaysFiringWithAction( - `test-alert-${Date.now()}`, - { - id: action.id, - group: 'default', - params: { - message: 'from alert 1s', - level: 'warn', - }, - } - ); + const { body: createdAction } = await supertest + .post(`/api/actions/action`) + .set('kbn-xsrf', 'foo') + .send(getTestActionData()) + .expect(200); + objectRemover.add(createdAction.id, 'action', 'actions'); + + const { body: createdAlert } = await supertest + .post(`/api/alerts/alert`) + .set('kbn-xsrf', 'foo') + .send(getTestAlertData()) + .expect(200); + objectRemover.add(createdAlert.id, 'alert', 'alerts'); // refresh to see alert await browser.refresh(); @@ -88,12 +88,10 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { await testSubjects.existOrFail('alertsList'); // click on first alert - await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(alert.name); + await pageObjects.triggersActionsUI.clickOnAlertInAlertsList(createdAlert.name); // Verify url - expect(await browser.getCurrentUrl()).to.contain(`/alert/${alert.id}`); - - await alerting.alerts.deleteAlert(alert.id); + expect(await browser.getCurrentUrl()).to.contain(`/alert/${createdAlert.id}`); }); }); }); diff --git a/x-pack/test/functional_with_es_ssl/config.ts b/x-pack/test/functional_with_es_ssl/config.ts index eedc39b09a8e4..08a44b5040a4f 100644 --- a/x-pack/test/functional_with_es_ssl/config.ts +++ b/x-pack/test/functional_with_es_ssl/config.ts @@ -7,7 +7,6 @@ import { resolve, join } from 'path'; import { CA_CERT_PATH } from '@kbn/dev-utils'; import { FtrConfigProviderContext } from '@kbn/test/types/ftr'; -import { services } from './services'; import { pageObjects } from './page_objects'; // .server-log is specifically not enabled @@ -39,7 +38,6 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) { const returnedObject = { ...xpackFunctionalConfig.getAll(), servers, - services, pageObjects, // list paths to the files that contain your plugins tests testFiles: [ diff --git a/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts b/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts index bb257cdcbfe1b..ca685f9495bb0 100644 --- a/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts +++ b/x-pack/test/functional_with_es_ssl/ftr_provider_context.d.ts @@ -5,8 +5,7 @@ */ import { GenericFtrProviderContext } from '@kbn/test/types/ftr'; - +import { services } from '../functional/services'; import { pageObjects } from './page_objects'; -import { services } from './services'; export type FtrProviderContext = GenericFtrProviderContext; diff --git a/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts b/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts new file mode 100644 index 0000000000000..79868fef53c4b --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/lib/get_test_data.ts @@ -0,0 +1,39 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import uuid from 'uuid'; + +export function generateUniqueKey() { + return uuid.v4().replace(/-/g, ''); +} + +export function getTestAlertData(overwrites = {}) { + return { + enabled: true, + name: generateUniqueKey(), + tags: ['foo', 'bar'], + alertTypeId: 'test.noop', + consumer: 'alerts', + schedule: { interval: '1m' }, + throttle: '1m', + notifyWhen: 'onThrottleInterval', + actions: [], + params: {}, + ...overwrites, + }; +} + +export function getTestActionData(overwrites = {}) { + return { + name: `slack-${Date.now()}`, + actionTypeId: '.slack', + config: {}, + secrets: { + webhookUrl: 'https://test', + }, + ...overwrites, + }; +} diff --git a/x-pack/test/functional_with_es_ssl/lib/object_remover.ts b/x-pack/test/functional_with_es_ssl/lib/object_remover.ts new file mode 100644 index 0000000000000..28ea197db4131 --- /dev/null +++ b/x-pack/test/functional_with_es_ssl/lib/object_remover.ts @@ -0,0 +1,36 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +interface ObjectToRemove { + id: string; + type: string; + plugin: string; +} + +export class ObjectRemover { + private readonly supertest: any; + private objectsToRemove: ObjectToRemove[] = []; + + constructor(supertest: any) { + this.supertest = supertest; + } + + add(id: ObjectToRemove['id'], type: ObjectToRemove['type'], plugin: ObjectToRemove['plugin']) { + this.objectsToRemove.push({ id, type, plugin }); + } + + async removeAll() { + await Promise.all( + this.objectsToRemove.map(({ id, type, plugin }) => { + return this.supertest + .delete(`/api/${plugin}/${type}/${id}`) + .set('kbn-xsrf', 'foo') + .expect(204); + }) + ); + this.objectsToRemove = []; + } +} diff --git a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts index a64ab06339c9b..5c512dd5a182a 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/alert_details.ts @@ -32,7 +32,7 @@ export function AlertDetailsPageProvider({ getService }: FtrProviderContext) { const $ = await table.parseDomContent(); return $.findTestSubjects('alert-instance-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { return { instance: $(row) .findTestSubject('alertInstancesTableCell-instance') @@ -86,7 +86,7 @@ export function AlertDetailsPageProvider({ getService }: FtrProviderContext) { $.findTestSubjects('alert-instance-row') .toArray() .filter( - (row) => + (row: CheerioElement) => $(row) .findTestSubject('alertInstancesTableCell-instance') .find('.euiTableCellContent') diff --git a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts index db6990d2b824e..f6f21f72d2307 100644 --- a/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts +++ b/x-pack/test/functional_with_es_ssl/page_objects/triggers_actions_ui_page.ts @@ -78,7 +78,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) const $ = await table.parseDomContent(); return $.findTestSubjects('connectors-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { return { name: $(row) .findTestSubject('connectorsTableCell-name') @@ -96,7 +96,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) const $ = await table.parseDomContent(); return $.findTestSubjects('alert-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { return getRowItemData(row, $); }); }, @@ -105,7 +105,7 @@ export function TriggersActionsPageProvider({ getService }: FtrProviderContext) const $ = await table.parseDomContent(); return $.findTestSubjects('alert-row') .toArray() - .map((row) => { + .map((row: CheerioElement) => { const rowItem = getRowItemData(row, $); return { ...rowItem, diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/actions.ts b/x-pack/test/functional_with_es_ssl/services/alerting/actions.ts deleted file mode 100644 index 060c4f13e70c7..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/alerting/actions.ts +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios, { AxiosInstance } from 'axios'; -import util from 'util'; -import { ToolingLog } from '@kbn/dev-utils'; - -export class Actions { - private log: ToolingLog; - private axios: AxiosInstance; - - constructor(url: string, log: ToolingLog) { - this.log = log; - this.axios = axios.create({ - headers: { 'kbn-xsrf': 'x-pack/ftr/services/alerting/actions' }, - baseURL: url, - maxRedirects: 0, - validateStatus: () => true, // we do our own validation below and throw better error messages - }); - } - - public async createAction(actionParams: { - name: string; - actionTypeId: string; - config: Record; - secrets: Record; - }) { - this.log.debug(`creating action ${actionParams.name}`); - - const { - data: action, - status: actionStatus, - statusText: actionStatusText, - } = await this.axios.post(`/api/actions/action`, actionParams); - if (actionStatus !== 200) { - throw new Error( - `Expected status code of 200, received ${actionStatus} ${actionStatusText}: ${util.inspect( - action - )}` - ); - } - - this.log.debug(`created action ${action.id}`); - return action; - } -} diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts b/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts deleted file mode 100644 index 5ab07aa00412b..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/alerting/alerts.ts +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import axios, { AxiosInstance } from 'axios'; -import util from 'util'; -import { ToolingLog } from '@kbn/dev-utils'; - -export interface AlertInstanceSummary { - status: string; - muted: boolean; - enabled: boolean; - lastRun?: string; - errorMessage?: string; - instances: Record; -} - -export interface AlertInstanceStatus { - status: string; - muted: boolean; - actionGroupId: string; - activeStartDate?: string; -} - -export class Alerts { - private log: ToolingLog; - private axios: AxiosInstance; - - constructor(url: string, log: ToolingLog) { - this.log = log; - this.axios = axios.create({ - headers: { 'kbn-xsrf': 'x-pack/ftr/services/alerting/alerts' }, - baseURL: url, - maxRedirects: 0, - validateStatus: () => true, // we do our own validation below and throw better error messages - }); - } - - public async createAlertWithActions( - name: string, - alertTypeId: string, - params?: Record, - actions?: Array<{ - id: string; - group: string; - params: Record; - }>, - tags?: string[], - consumer?: string, - schedule?: Record, - throttle?: string - ) { - this.log.debug(`creating alert ${name}`); - - const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { - enabled: true, - name, - tags, - alertTypeId, - consumer: consumer ?? 'alerts', - schedule: schedule ?? { interval: '1m' }, - throttle: throttle ?? '1m', - actions: actions ?? [], - params: params ?? {}, - }); - if (status !== 200) { - throw new Error( - `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - - this.log.debug(`created alert ${alert.id}`); - - return alert; - } - - public async createNoOp(name: string) { - this.log.debug(`creating alert ${name}`); - - const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { - enabled: true, - name, - tags: ['foo'], - alertTypeId: 'test.noop', - consumer: 'alerting_fixture', - schedule: { interval: '1m' }, - throttle: '1m', - actions: [], - params: {}, - }); - if (status !== 200) { - throw new Error( - `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - - this.log.debug(`created alert ${alert.id}`); - - return alert; - } - - public async createAlwaysFiringWithActions( - name: string, - actions: Array<{ - id: string; - group: string; - params: Record; - }>, - params: Record = {} - ) { - this.log.debug(`creating alert ${name}`); - - const { data: alert, status, statusText } = await this.axios.post(`/api/alerts/alert`, { - enabled: true, - name, - tags: ['foo'], - alertTypeId: 'test.always-firing', - consumer: 'alerts', - schedule: { interval: '1m' }, - throttle: '1m', - actions, - params, - }); - if (status !== 200) { - throw new Error( - `Expected status code of 200, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - - this.log.debug(`created alert ${alert.id}`); - - return alert; - } - - public async createAlwaysFiringWithAction( - name: string, - action: { - id: string; - group: string; - params: Record; - } - ) { - return this.createAlwaysFiringWithActions(name, [action]); - } - - public async deleteAlert(id: string) { - this.log.debug(`deleting alert ${id}`); - - const { data: alert, status, statusText } = await this.axios.delete(`/api/alerts/alert/${id}`); - if (status !== 204) { - throw new Error( - `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - this.log.debug(`deleted alert ${alert.id}`); - } - - public async getAlertInstanceSummary(id: string): Promise { - this.log.debug(`getting alert ${id} state`); - - const { data } = await this.axios.get(`/api/alerts/alert/${id}/_instance_summary`); - return data; - } - - public async muteAlertInstance(id: string, instanceId: string) { - this.log.debug(`muting instance ${instanceId} under alert ${id}`); - - const { data: alert, status, statusText } = await this.axios.post( - `/api/alerts/alert/${id}/alert_instance/${instanceId}/_mute` - ); - if (status !== 204) { - throw new Error( - `Expected status code of 204, received ${status} ${statusText}: ${util.inspect(alert)}` - ); - } - this.log.debug(`muted alert instance ${instanceId}`); - } -} diff --git a/x-pack/test/functional_with_es_ssl/services/alerting/index.ts b/x-pack/test/functional_with_es_ssl/services/alerting/index.ts deleted file mode 100644 index e0aa827316c01..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/alerting/index.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { format as formatUrl } from 'url'; - -import { Alerts } from './alerts'; -import { Actions } from './actions'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -export function AlertsServiceProvider({ getService }: FtrProviderContext) { - const log = getService('log'); - const config = getService('config'); - const url = formatUrl(config.get('servers.kibana')); - - return new (class AlertingService { - actions = new Actions(url, log); - alerts = new Alerts(url, log); - })(); -} diff --git a/x-pack/test/functional_with_es_ssl/services/index.ts b/x-pack/test/functional_with_es_ssl/services/index.ts deleted file mode 100644 index f04c2c980055d..0000000000000 --- a/x-pack/test/functional_with_es_ssl/services/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { services as xpackFunctionalServices } from '../../functional/services'; -import { AlertsServiceProvider } from './alerting'; - -export const services = { - ...xpackFunctionalServices, - alerting: AlertsServiceProvider, -};