From 92eab3e8d1e359c98eb6936bc22a57971df2fcfb Mon Sep 17 00:00:00 2001 From: Shahzad Date: Wed, 1 Apr 2020 00:51:55 +0200 Subject: [PATCH 1/8] [Uptime] Added func test for obsv location map (#61518) * add functional test * update func test * refactor more changed * update test * update test * update type and test * fix the fix of fix, which din't get fix Co-authored-by: Elastic Machine --- .../location_map/embeddables/embedded_map.tsx | 6 +- .../legacy/plugins/uptime/public/routes.tsx | 12 +- .../test/functional/apps/uptime/locations.ts | 25 ++- x-pack/test/functional/apps/uptime/monitor.ts | 24 +- .../test/functional/apps/uptime/overview.ts | 66 +++--- .../test/functional/apps/uptime/settings.ts | 47 ++-- .../functional/page_objects/uptime_page.ts | 47 ++-- x-pack/test/functional/services/uptime.ts | 207 ------------------ .../test/functional/services/uptime/alerts.ts | 95 ++++++++ .../test/functional/services/uptime/common.ts | 80 +++++++ .../test/functional/services/uptime/index.ts | 7 + .../functional/services/uptime/monitor.ts | 31 +++ .../functional/services/uptime/navigation.ts | 45 ++++ .../functional/services/uptime/settings.ts | 42 ++++ .../test/functional/services/uptime/uptime.ts | 29 +++ 15 files changed, 449 insertions(+), 314 deletions(-) delete mode 100644 x-pack/test/functional/services/uptime.ts create mode 100644 x-pack/test/functional/services/uptime/alerts.ts create mode 100644 x-pack/test/functional/services/uptime/common.ts create mode 100644 x-pack/test/functional/services/uptime/index.ts create mode 100644 x-pack/test/functional/services/uptime/monitor.ts create mode 100644 x-pack/test/functional/services/uptime/navigation.ts create mode 100644 x-pack/test/functional/services/uptime/settings.ts create mode 100644 x-pack/test/functional/services/uptime/uptime.ts diff --git a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx index 168d71a31dd45..89227cdd56457 100644 --- a/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/functional/location_map/embeddables/embedded_map.tsx @@ -107,7 +107,11 @@ export const EmbeddedMap = React.memo(({ upPoints, downPoints }: EmbeddedMapProp return ( -
+
); }); diff --git a/x-pack/legacy/plugins/uptime/public/routes.tsx b/x-pack/legacy/plugins/uptime/public/routes.tsx index 590e00e92e1fb..bb0700287dbf1 100644 --- a/x-pack/legacy/plugins/uptime/public/routes.tsx +++ b/x-pack/legacy/plugins/uptime/public/routes.tsx @@ -18,13 +18,19 @@ interface RouterProps { export const PageRouter: FC = ({ autocomplete }) => ( - +
+ +
- +
+ +
- +
+ +
diff --git a/x-pack/test/functional/apps/uptime/locations.ts b/x-pack/test/functional/apps/uptime/locations.ts index 7f6932ab50319..bbf50344f3493 100644 --- a/x-pack/test/functional/apps/uptime/locations.ts +++ b/x-pack/test/functional/apps/uptime/locations.ts @@ -4,17 +4,24 @@ * you may not use this file except in compliance with the Elastic License. */ +import moment from 'moment'; import { makeChecksWithStatus } from '../../../api_integration/apis/uptime/graphql/helpers/make_checks'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const pageObjects = getPageObjects(['uptime']); + const { uptime: uptimePage } = getPageObjects(['uptime']); + const uptime = getService('uptime'); - describe('location', () => { - const start = new Date().toISOString(); - const end = new Date().toISOString(); + const monitor = () => uptime.monitor; + + describe('Observer location', () => { + const start = moment() + .subtract('15', 'm') + .toISOString(); + const end = moment().toISOString(); const MONITOR_ID = 'location-testing-id'; + beforeEach(async () => { /** * This mogrify function will strip the documents of their location @@ -38,11 +45,17 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { 'up', mogrifyNoLocation ); + await uptime.navigation.goToUptime(); + + await uptimePage.loadDataAndGoToMonitorPage(start, end, MONITOR_ID); + }); + + it('renders the location panel and canvas', async () => { + await monitor().locationMapIsRendered(); }); it('renders the location missing popover when monitor has location name, but no geo data', async () => { - await pageObjects.uptime.loadDataAndGoToMonitorPage(start, end, MONITOR_ID); - await pageObjects.uptime.locationMissingIsDisplayed(); + await monitor().locationMissingExists(); }); }); }; diff --git a/x-pack/test/functional/apps/uptime/monitor.ts b/x-pack/test/functional/apps/uptime/monitor.ts index 034ccad4815a1..e15750eb6157b 100644 --- a/x-pack/test/functional/apps/uptime/monitor.ts +++ b/x-pack/test/functional/apps/uptime/monitor.ts @@ -8,22 +8,28 @@ import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects, getService }: FtrProviderContext) => { const esArchiver = getService('esArchiver'); - const pageObjects = getPageObjects(['uptime']); + const uptimeService = getService('uptime'); + const { uptime } = getPageObjects(['uptime']); const archive = 'uptime/full_heartbeat'; describe('monitor page', function() { this.tags(['skipFirefox']); + const dateStart = 'Sep 10, 2019 @ 12:40:08.078'; + const dateEnd = 'Sep 11, 2019 @ 19:40:08.078'; + const monitorId = '0000-intermittent'; + const monitorName = '0000-intermittent'; + before(async () => { - await esArchiver.load(archive); + await esArchiver.loadIfNeeded(archive); + await uptimeService.navigation.goToUptime(); + }); + + after(async () => { + await esArchiver.unload(archive); }); - after(async () => await esArchiver.unload(archive)); + it('loads and displays uptime data based on date range', async () => { - await pageObjects.uptime.loadDataAndGoToMonitorPage( - 'Sep 10, 2019 @ 12:40:08.078', - 'Sep 11, 2019 @ 19:40:08.078', - '0000-intermittent', - '0000-intermittent' - ); + await uptime.loadDataAndGoToMonitorPage(dateStart, dateEnd, monitorId, monitorName); }); }); }; diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index f3b587b9bc1e2..8195e6bbb6035 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -8,14 +8,14 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const pageObjects = getPageObjects(['uptime']); + const { uptime } = getPageObjects(['uptime']); const retry = getService('retry'); describe('overview page', function() { const DEFAULT_DATE_START = 'Sep 10, 2019 @ 12:40:08.078'; const DEFAULT_DATE_END = 'Sep 11, 2019 @ 19:40:08.078'; it('loads and displays uptime data based on date range', async () => { - await pageObjects.uptime.goToUptimeOverviewAndLoadData( + await uptime.goToUptimeOverviewAndLoadData( DEFAULT_DATE_START, DEFAULT_DATE_END, 'monitor-page-link-0000-intermittent' @@ -23,20 +23,18 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('runs filter query without issues', async () => { - await pageObjects.uptime.inputFilterQuery( - 'monitor.status:up and monitor.id:"0000-intermittent"' - ); - await pageObjects.uptime.pageHasExpectedIds(['0000-intermittent']); + await uptime.inputFilterQuery('monitor.status:up and monitor.id:"0000-intermittent"'); + await uptime.pageHasExpectedIds(['0000-intermittent']); }); it('applies filters for multiple fields', async () => { - await pageObjects.uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); - await pageObjects.uptime.selectFilterItems({ + await uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await uptime.selectFilterItems({ location: ['mpls'], port: ['5678'], scheme: ['http'], }); - await pageObjects.uptime.pageHasExpectedIds([ + await uptime.pageHasExpectedIds([ '0000-intermittent', '0001-up', '0002-up', @@ -51,9 +49,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('pagination is cleared when filter criteria changes', async () => { - await pageObjects.uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); - await pageObjects.uptime.changePage('next'); - await pageObjects.uptime.pageHasExpectedIds([ + await uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await uptime.changePage('next'); + await uptime.pageHasExpectedIds([ '0010-down', '0011-up', '0012-up', @@ -66,9 +64,9 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '0019-up', ]); // there should now be pagination data in the URL - await pageObjects.uptime.pageUrlContains('pagination'); - await pageObjects.uptime.setStatusFilter('up'); - await pageObjects.uptime.pageHasExpectedIds([ + await uptime.pageUrlContains('pagination'); + await uptime.setStatusFilter('up'); + await uptime.pageHasExpectedIds([ '0000-intermittent', '0001-up', '0002-up', @@ -81,21 +79,21 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '0009-up', ]); // ensure that pagination is removed from the URL - await pageObjects.uptime.pageUrlContains('pagination', false); + await uptime.pageUrlContains('pagination', false); }); it('clears pagination parameters when size changes', async () => { - await pageObjects.uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); - await pageObjects.uptime.changePage('next'); - await pageObjects.uptime.pageUrlContains('pagination'); - await pageObjects.uptime.setMonitorListPageSize(50); + await uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await uptime.changePage('next'); + await uptime.pageUrlContains('pagination'); + await uptime.setMonitorListPageSize(50); // the pagination parameter should be cleared after a size change - await pageObjects.uptime.pageUrlContains('pagination', false); + await uptime.pageUrlContains('pagination', false); }); it('pagination size updates to reflect current selection', async () => { - await pageObjects.uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); - await pageObjects.uptime.pageHasExpectedIds([ + await uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await uptime.pageHasExpectedIds([ '0000-intermittent', '0001-up', '0002-up', @@ -107,8 +105,8 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { '0008-up', '0009-up', ]); - await pageObjects.uptime.setMonitorListPageSize(50); - await pageObjects.uptime.pageHasExpectedIds([ + await uptime.setMonitorListPageSize(50); + await uptime.pageHasExpectedIds([ '0000-intermittent', '0001-up', '0002-up', @@ -164,26 +162,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { describe('snapshot counts', () => { it('updates the snapshot count when status filter is set to down', async () => { - await pageObjects.uptime.goToUptimePageAndSetDateRange( - DEFAULT_DATE_START, - DEFAULT_DATE_END - ); - await pageObjects.uptime.setStatusFilter('down'); + await uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await uptime.setStatusFilter('down'); await retry.tryForTime(12000, async () => { - const counts = await pageObjects.uptime.getSnapshotCount(); + const counts = await uptime.getSnapshotCount(); expect(counts).to.eql({ up: '0', down: '7' }); }); }); it('updates the snapshot count when status filter is set to up', async () => { - await pageObjects.uptime.goToUptimePageAndSetDateRange( - DEFAULT_DATE_START, - DEFAULT_DATE_END - ); - await pageObjects.uptime.setStatusFilter('up'); + await uptime.goToUptimePageAndSetDateRange(DEFAULT_DATE_START, DEFAULT_DATE_END); + await uptime.setStatusFilter('up'); await retry.tryForTime(12000, async () => { - const counts = await pageObjects.uptime.getSnapshotCount(); + const counts = await uptime.getSnapshotCount(); expect(counts).to.eql({ up: '93', down: '0' }); }); }); diff --git a/x-pack/test/functional/apps/uptime/settings.ts b/x-pack/test/functional/apps/uptime/settings.ts index 477eeb84ae5c3..3294d928b61b3 100644 --- a/x-pack/test/functional/apps/uptime/settings.ts +++ b/x-pack/test/functional/apps/uptime/settings.ts @@ -9,64 +9,71 @@ import { FtrProviderContext } from '../../ftr_provider_context'; import { defaultDynamicSettings, DynamicSettings, -} from '../../../../legacy/plugins/uptime/common/runtime_types/dynamic_settings'; +} from '../../../../legacy/plugins/uptime/common/runtime_types'; import { makeChecks } from '../../../api_integration/apis/uptime/graphql/helpers/make_checks'; export default ({ getPageObjects, getService }: FtrProviderContext) => { - const pageObjects = getPageObjects(['uptime']); + const { uptime: uptimePage } = getPageObjects(['uptime']); + const uptimeService = getService('uptime'); + const es = getService('es'); // Flaky https://github.com/elastic/kibana/issues/60866 describe('uptime settings page', () => { - const settingsPage = () => pageObjects.uptime.settings; beforeEach('navigate to clean app root', async () => { // make 10 checks await makeChecks(es, 'myMonitor', 1, 1, 1); - await pageObjects.uptime.goToRoot(); + await uptimePage.goToRoot(); }); it('loads the default settings', async () => { - await pageObjects.uptime.settings.go(); + const settings = uptimeService.settings; + + await settings.go(); - const fields = await settingsPage().loadFields(); + const fields = await settings.loadFields(); expect(fields).to.eql(defaultDynamicSettings); }); it('should disable the apply button when invalid or unchanged', async () => { - await pageObjects.uptime.settings.go(); + const settings = uptimeService.settings; + + await settings.go(); // Disabled because it's the original value - expect(await settingsPage().applyButtonIsDisabled()).to.eql(true); + expect(await settings.applyButtonIsDisabled()).to.eql(true); // Enabled because it's a new, different, value - await settingsPage().changeHeartbeatIndicesInput('somethingNew'); - expect(await settingsPage().applyButtonIsDisabled()).to.eql(false); + await settings.changeHeartbeatIndicesInput('somethingNew'); + expect(await settings.applyButtonIsDisabled()).to.eql(false); // Disabled because it's blank - await settingsPage().changeHeartbeatIndicesInput(''); - expect(await settingsPage().applyButtonIsDisabled()).to.eql(true); + await settings.changeHeartbeatIndicesInput(''); + expect(await settings.applyButtonIsDisabled()).to.eql(true); }); // Failing: https://github.com/elastic/kibana/issues/60863 it('changing index pattern setting is reflected elsewhere in UI', async () => { - const originalCount = await pageObjects.uptime.getSnapshotCount(); + const settings = uptimeService.settings; + + const originalCount = await uptimePage.getSnapshotCount(); // We should find 1 monitor up with the default index pattern expect(originalCount.up).to.eql(1); - await pageObjects.uptime.settings.go(); + await settings.go(); const newFieldValues: DynamicSettings = { heartbeatIndices: 'new*' }; - await settingsPage().changeHeartbeatIndicesInput(newFieldValues.heartbeatIndices); - await settingsPage().apply(); + await settings.changeHeartbeatIndicesInput(newFieldValues.heartbeatIndices); + await settings.apply(); - await pageObjects.uptime.goToRoot(); + await uptimePage.goToRoot(); // We should no longer find any monitors since the new pattern matches nothing - await pageObjects.uptime.pageHasDataMissing(); + await uptimePage.pageHasDataMissing(); // Verify that the settings page shows the value we previously saved - await pageObjects.uptime.settings.go(); - const fields = await settingsPage().loadFields(); + await settings.go(); + const fields = await settings.loadFields(); expect(fields).to.eql(newFieldValues); }); }); diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts index 0b8e994ba8095..fcf2b77dbd624 100644 --- a/x-pack/test/functional/page_objects/uptime_page.ts +++ b/x-pack/test/functional/page_objects/uptime_page.ts @@ -9,14 +9,10 @@ import { FtrProviderContext } from '../ftr_provider_context'; export function UptimePageProvider({ getPageObjects, getService }: FtrProviderContext) { const pageObjects = getPageObjects(['common', 'timePicker']); - const uptimeService = getService('uptime'); + const { common: commonService, navigation, alerts } = getService('uptime'); const retry = getService('retry'); return new (class UptimePage { - public get settings() { - return uptimeService.settings; - } - public async goToRoot() { await pageObjects.common.navigateToApp('uptime'); } @@ -37,7 +33,7 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo await pageObjects.common.navigateToApp('uptime'); await pageObjects.timePicker.setAbsoluteRange(datePickerStartValue, datePickerEndValue); if (monitorIdToCheck) { - await uptimeService.monitorIdExists(monitorIdToCheck); + await commonService.monitorIdExists(monitorIdToCheck); } } @@ -47,50 +43,43 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo monitorId: string, monitorName?: string ) { - await pageObjects.common.navigateToApp('uptime'); await pageObjects.timePicker.setAbsoluteRange(datePickerStartValue, datePickerEndValue); - await uptimeService.navigateToMonitorWithId(monitorId); - if ( - monitorName && - (await uptimeService.getMonitorNameDisplayedOnPageTitle()) !== monitorName - ) { - throw new Error('Expected monitor name not found'); - } + await navigation.goToMonitor(monitorId, monitorName); } public async inputFilterQuery(filterQuery: string) { - await uptimeService.setFilterText(filterQuery); + await commonService.setFilterText(filterQuery); } public async pageHasDataMissing() { - return await uptimeService.pageHasDataMissing(); + return await commonService.pageHasDataMissing(); } public async pageHasExpectedIds(monitorIdsToCheck: string[]): Promise { return retry.tryForTime(15000, async () => { - await Promise.all(monitorIdsToCheck.map(id => uptimeService.monitorPageLinkExists(id))); + await Promise.all(monitorIdsToCheck.map(id => commonService.monitorPageLinkExists(id))); }); } public async pageUrlContains(value: string, expected: boolean = true): Promise { return retry.tryForTime(12000, async () => { - expect(await uptimeService.urlContains(value)).to.eql(expected); + expect(await commonService.urlContains(value)).to.eql(expected); }); } public async changePage(direction: 'next' | 'prev') { if (direction === 'next') { - await uptimeService.goToNextPage(); + await commonService.goToNextPage(); } else if (direction === 'prev') { - await uptimeService.goToPreviousPage(); + await commonService.goToPreviousPage(); } } public async setStatusFilter(value: 'up' | 'down') { if (value === 'up') { - await uptimeService.setStatusFilterUp(); + await commonService.setStatusFilterUp(); } else if (value === 'down') { - await uptimeService.setStatusFilterDown(); + await commonService.setStatusFilterDown(); } } @@ -99,18 +88,14 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo if (filters.hasOwnProperty(key)) { const values = filters[key]; for (let i = 0; i < values.length; i++) { - await uptimeService.selectFilterItem(key, values[i]); + await commonService.selectFilterItem(key, values[i]); } } } } public async getSnapshotCount() { - return await uptimeService.getSnapshotCount(); - } - - public locationMissingIsDisplayed() { - return uptimeService.locationMissingExists(); + return await commonService.getSnapshotCount(); } public async openAlertFlyoutAndCreateMonitorStatusAlert({ @@ -130,7 +115,7 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo alertTimerangeSelection: string; filters?: string; }) { - const { alerts, setKueryBarText } = uptimeService; + const { setKueryBarText } = commonService; await alerts.openFlyout(); await alerts.openMonitorStatusAlertType(); await alerts.setAlertName(alertName); @@ -148,8 +133,8 @@ export function UptimePageProvider({ getPageObjects, getService }: FtrProviderCo } public async setMonitorListPageSize(size: number): Promise { - await uptimeService.openPageSizeSelectPopover(); - return uptimeService.clickPageSizeSelectPopoverItem(size); + await commonService.openPageSizeSelectPopover(); + return commonService.clickPageSizeSelectPopoverItem(size); } })(); } diff --git a/x-pack/test/functional/services/uptime.ts b/x-pack/test/functional/services/uptime.ts deleted file mode 100644 index 5a24a51f967fd..0000000000000 --- a/x-pack/test/functional/services/uptime.ts +++ /dev/null @@ -1,207 +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 { FtrProviderContext } from '../ftr_provider_context'; - -export function UptimeProvider({ getService }: FtrProviderContext) { - const testSubjects = getService('testSubjects'); - const browser = getService('browser'); - const retry = getService('retry'); - - const settings = { - go: async () => { - await testSubjects.click('settings-page-link', 5000); - }, - changeHeartbeatIndicesInput: async (text: string) => { - const input = await testSubjects.find('heartbeat-indices-input-loaded', 5000); - await input.clearValueWithKeyboard(); - await input.type(text); - }, - loadFields: async () => { - const input = await testSubjects.find('heartbeat-indices-input-loaded', 5000); - const heartbeatIndices = await input.getAttribute('value'); - - return { heartbeatIndices }; - }, - applyButtonIsDisabled: async () => { - return !!(await (await testSubjects.find('apply-settings-button')).getAttribute('disabled')); - }, - apply: async () => { - await (await testSubjects.find('apply-settings-button')).click(); - await retry.waitFor('submit to succeed', async () => { - // When the form submit is complete the form will no longer be disabled - const disabled = await ( - await testSubjects.find('heartbeat-indices-input-loaded', 5000) - ).getAttribute('disabled'); - return disabled === null; - }); - }, - }; - - return { - settings, - alerts: { - async openFlyout() { - await testSubjects.click('xpack.uptime.alertsPopover.toggleButton', 5000); - await testSubjects.click('xpack.uptime.toggleAlertFlyout', 5000); - }, - async openMonitorStatusAlertType() { - return testSubjects.click('xpack.uptime.alerts.monitorStatus-SelectOption', 5000); - }, - async setAlertTags(tags: string[]) { - for (let i = 0; i < tags.length; i += 1) { - await testSubjects.click('comboBoxSearchInput', 5000); - await testSubjects.setValue('comboBoxInput', tags[i]); - await browser.pressKeys(browser.keys.ENTER); - } - }, - async setAlertName(name: string) { - return testSubjects.setValue('alertNameInput', name); - }, - async setAlertInterval(value: string) { - return testSubjects.setValue('intervalInput', value); - }, - async setAlertThrottleInterval(value: string) { - return testSubjects.setValue('throttleInput', value); - }, - async setAlertExpressionValue( - expressionAttribute: string, - fieldAttribute: string, - value: string - ) { - await testSubjects.click(expressionAttribute); - await testSubjects.setValue(fieldAttribute, value); - return browser.pressKeys(browser.keys.ESCAPE); - }, - async setAlertStatusNumTimes(value: string) { - return this.setAlertExpressionValue( - 'xpack.uptime.alerts.monitorStatus.numTimesExpression', - 'xpack.uptime.alerts.monitorStatus.numTimesField', - value - ); - }, - async setAlertTimerangeSelection(value: string) { - return this.setAlertExpressionValue( - 'xpack.uptime.alerts.monitorStatus.timerangeValueExpression', - 'xpack.uptime.alerts.monitorStatus.timerangeValueField', - value - ); - }, - async setAlertExpressionSelectable( - expressionAttribute: string, - selectableAttribute: string, - optionAttributes: string[] - ) { - await testSubjects.click(expressionAttribute, 5000); - await testSubjects.click(selectableAttribute, 5000); - for (let i = 0; i < optionAttributes.length; i += 1) { - await testSubjects.click(optionAttributes[i], 5000); - } - return browser.pressKeys(browser.keys.ESCAPE); - }, - async setMonitorStatusSelectableToHours() { - return this.setAlertExpressionSelectable( - 'xpack.uptime.alerts.monitorStatus.timerangeUnitExpression', - 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable', - ['xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable.hoursOption'] - ); - }, - async setLocationsSelectable() { - await testSubjects.click( - 'xpack.uptime.alerts.monitorStatus.locationsSelectionExpression', - 5000 - ); - await testSubjects.click( - 'xpack.uptime.alerts.monitorStatus.locationsSelectionSwitch', - 5000 - ); - await testSubjects.click( - 'xpack.uptime.alerts.monitorStatus.locationsSelectionSelectable', - 5000 - ); - return browser.pressKeys(browser.keys.ESCAPE); - }, - async clickSaveAlertButtion() { - return testSubjects.click('saveAlertButton'); - }, - }, - async assertExists(key: string) { - if (!(await testSubjects.exists(key))) { - throw new Error(`Couldn't find expected element with key "${key}".`); - } - }, - async monitorIdExists(key: string) { - await retry.tryForTime(10000, async () => { - await testSubjects.existOrFail(key); - }); - }, - async monitorPageLinkExists(monitorId: string) { - await testSubjects.existOrFail(`monitor-page-link-${monitorId}`); - }, - async urlContains(expected: string) { - const url = await browser.getCurrentUrl(); - return url.indexOf(expected) >= 0; - }, - async navigateToMonitorWithId(monitorId: string) { - await testSubjects.click(`monitor-page-link-${monitorId}`, 5000); - }, - async getMonitorNameDisplayedOnPageTitle() { - return await testSubjects.getVisibleText('monitor-page-title'); - }, - async pageHasDataMissing() { - return await testSubjects.find('data-missing', 5000); - }, - async setKueryBarText(attribute: string, value: string) { - await testSubjects.click(attribute); - await testSubjects.setValue(attribute, value); - await browser.pressKeys(browser.keys.ENTER); - }, - async setFilterText(filterQuery: string) { - await this.setKueryBarText('xpack.uptime.filterBar', filterQuery); - }, - async goToNextPage() { - await testSubjects.click('xpack.uptime.monitorList.nextButton', 5000); - }, - async goToPreviousPage() { - await testSubjects.click('xpack.uptime.monitorList.prevButton', 5000); - }, - async setStatusFilterUp() { - await testSubjects.click('xpack.uptime.filterBar.filterStatusUp'); - }, - async setStatusFilterDown() { - await testSubjects.click('xpack.uptime.filterBar.filterStatusDown'); - }, - async selectFilterItem(filterType: string, option: string) { - const popoverId = `filter-popover_${filterType}`; - const optionId = `filter-popover-item_${option}`; - await testSubjects.existOrFail(popoverId); - await testSubjects.click(popoverId); - await testSubjects.existOrFail(optionId); - await testSubjects.click(optionId); - await testSubjects.click(popoverId); - }, - async getSnapshotCount() { - return { - up: await testSubjects.getVisibleText('xpack.uptime.snapshot.donutChart.up'), - down: await testSubjects.getVisibleText('xpack.uptime.snapshot.donutChart.down'), - }; - }, - async locationMissingExists() { - return await testSubjects.existOrFail('xpack.uptime.locationMap.locationMissing', { - timeout: 3000, - }); - }, - async openPageSizeSelectPopover(): Promise { - return testSubjects.click('xpack.uptime.monitorList.pageSizeSelect.popoverOpen', 5000); - }, - async clickPageSizeSelectPopoverItem(size: number = 10): Promise { - return testSubjects.click( - `xpack.uptime.monitorList.pageSizeSelect.sizeSelectItem${size.toString()}`, - 5000 - ); - }, - }; -} diff --git a/x-pack/test/functional/services/uptime/alerts.ts b/x-pack/test/functional/services/uptime/alerts.ts new file mode 100644 index 0000000000000..5ee444adec82f --- /dev/null +++ b/x-pack/test/functional/services/uptime/alerts.ts @@ -0,0 +1,95 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function UptimeAlertsProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + + return { + async openFlyout() { + await testSubjects.click('xpack.uptime.alertsPopover.toggleButton', 5000); + await testSubjects.click('xpack.uptime.toggleAlertFlyout', 5000); + }, + async openMonitorStatusAlertType() { + return testSubjects.click('xpack.uptime.alerts.monitorStatus-SelectOption', 5000); + }, + async setAlertTags(tags: string[]) { + for (let i = 0; i < tags.length; i += 1) { + await testSubjects.click('comboBoxSearchInput', 5000); + await testSubjects.setValue('comboBoxInput', tags[i]); + await browser.pressKeys(browser.keys.ENTER); + } + }, + async setAlertName(name: string) { + return testSubjects.setValue('alertNameInput', name); + }, + async setAlertInterval(value: string) { + return testSubjects.setValue('intervalInput', value); + }, + async setAlertThrottleInterval(value: string) { + return testSubjects.setValue('throttleInput', value); + }, + async setAlertExpressionValue( + expressionAttribute: string, + fieldAttribute: string, + value: string + ) { + await testSubjects.click(expressionAttribute); + await testSubjects.setValue(fieldAttribute, value); + return browser.pressKeys(browser.keys.ESCAPE); + }, + async setAlertStatusNumTimes(value: string) { + return this.setAlertExpressionValue( + 'xpack.uptime.alerts.monitorStatus.numTimesExpression', + 'xpack.uptime.alerts.monitorStatus.numTimesField', + value + ); + }, + async setAlertTimerangeSelection(value: string) { + return this.setAlertExpressionValue( + 'xpack.uptime.alerts.monitorStatus.timerangeValueExpression', + 'xpack.uptime.alerts.monitorStatus.timerangeValueField', + value + ); + }, + async setAlertExpressionSelectable( + expressionAttribute: string, + selectableAttribute: string, + optionAttributes: string[] + ) { + await testSubjects.click(expressionAttribute, 5000); + await testSubjects.click(selectableAttribute, 5000); + for (let i = 0; i < optionAttributes.length; i += 1) { + await testSubjects.click(optionAttributes[i], 5000); + } + return browser.pressKeys(browser.keys.ESCAPE); + }, + async setMonitorStatusSelectableToHours() { + return this.setAlertExpressionSelectable( + 'xpack.uptime.alerts.monitorStatus.timerangeUnitExpression', + 'xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable', + ['xpack.uptime.alerts.monitorStatus.timerangeUnitSelectable.hoursOption'] + ); + }, + async setLocationsSelectable() { + await testSubjects.click( + 'xpack.uptime.alerts.monitorStatus.locationsSelectionExpression', + 5000 + ); + await testSubjects.click('xpack.uptime.alerts.monitorStatus.locationsSelectionSwitch', 5000); + await testSubjects.click( + 'xpack.uptime.alerts.monitorStatus.locationsSelectionSelectable', + 5000 + ); + return browser.pressKeys(browser.keys.ESCAPE); + }, + async clickSaveAlertButtion() { + return testSubjects.click('saveAlertButton'); + }, + }; +} diff --git a/x-pack/test/functional/services/uptime/common.ts b/x-pack/test/functional/services/uptime/common.ts new file mode 100644 index 0000000000000..ed465eee343f9 --- /dev/null +++ b/x-pack/test/functional/services/uptime/common.ts @@ -0,0 +1,80 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function UptimeCommonProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const browser = getService('browser'); + const retry = getService('retry'); + + return { + async assertExists(key: string) { + if (!(await testSubjects.exists(key))) { + throw new Error(`Couldn't find expected element with key "${key}".`); + } + }, + async monitorIdExists(key: string) { + await retry.tryForTime(10000, async () => { + await testSubjects.existOrFail(key); + }); + }, + async monitorPageLinkExists(monitorId: string) { + await testSubjects.existOrFail(`monitor-page-link-${monitorId}`); + }, + async urlContains(expected: string) { + const url = await browser.getCurrentUrl(); + return url.indexOf(expected) >= 0; + }, + async pageHasDataMissing() { + return await testSubjects.find('data-missing', 5000); + }, + async setKueryBarText(attribute: string, value: string) { + await testSubjects.click(attribute); + await testSubjects.setValue(attribute, value); + await browser.pressKeys(browser.keys.ENTER); + }, + async setFilterText(filterQuery: string) { + await this.setKueryBarText('xpack.uptime.filterBar', filterQuery); + }, + async goToNextPage() { + await testSubjects.click('xpack.uptime.monitorList.nextButton', 5000); + }, + async goToPreviousPage() { + await testSubjects.click('xpack.uptime.monitorList.prevButton', 5000); + }, + async setStatusFilterUp() { + await testSubjects.click('xpack.uptime.filterBar.filterStatusUp'); + }, + async setStatusFilterDown() { + await testSubjects.click('xpack.uptime.filterBar.filterStatusDown'); + }, + async selectFilterItem(filterType: string, option: string) { + const popoverId = `filter-popover_${filterType}`; + const optionId = `filter-popover-item_${option}`; + await testSubjects.existOrFail(popoverId); + await testSubjects.click(popoverId); + await testSubjects.existOrFail(optionId); + await testSubjects.click(optionId); + await testSubjects.click(popoverId); + }, + async getSnapshotCount() { + return { + up: await testSubjects.getVisibleText('xpack.uptime.snapshot.donutChart.up'), + down: await testSubjects.getVisibleText('xpack.uptime.snapshot.donutChart.down'), + }; + }, + async openPageSizeSelectPopover(): Promise { + return testSubjects.click('xpack.uptime.monitorList.pageSizeSelect.popoverOpen', 5000); + }, + async clickPageSizeSelectPopoverItem(size: number = 10): Promise { + return testSubjects.click( + `xpack.uptime.monitorList.pageSizeSelect.sizeSelectItem${size.toString()}`, + 5000 + ); + }, + }; +} diff --git a/x-pack/test/functional/services/uptime/index.ts b/x-pack/test/functional/services/uptime/index.ts new file mode 100644 index 0000000000000..57999066d038e --- /dev/null +++ b/x-pack/test/functional/services/uptime/index.ts @@ -0,0 +1,7 @@ +/* + * 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. + */ + +export { UptimeProvider } from './uptime'; diff --git a/x-pack/test/functional/services/uptime/monitor.ts b/x-pack/test/functional/services/uptime/monitor.ts new file mode 100644 index 0000000000000..3bdec4b6749d4 --- /dev/null +++ b/x-pack/test/functional/services/uptime/monitor.ts @@ -0,0 +1,31 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function UptimeMonitorProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const find = getService('find'); + + return { + async locationMissingExists() { + return await testSubjects.existOrFail('xpack.uptime.locationMap.locationMissing', { + timeout: 3000, + }); + }, + async locationMapIsRendered() { + return retry.tryForTime(15000, async () => { + await testSubjects.existOrFail('xpack.uptime.locationMap.embeddedPanel', { + timeout: 3000, + }); + const mapPanel = await testSubjects.find('xpack.uptime.locationMap.embeddedPanel'); + + await find.descendantExistsByCssSelector('canvas.mapboxgl-canvas', mapPanel); + }); + }, + }; +} diff --git a/x-pack/test/functional/services/uptime/navigation.ts b/x-pack/test/functional/services/uptime/navigation.ts new file mode 100644 index 0000000000000..c762ddf34be04 --- /dev/null +++ b/x-pack/test/functional/services/uptime/navigation.ts @@ -0,0 +1,45 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function UptimeNavigationProvider({ getService, getPageObjects }: FtrProviderContext) { + const retry = getService('retry'); + const testSubjects = getService('testSubjects'); + const PageObjects = getPageObjects(['common']); + + const goToUptimeRoot = async () => { + await retry.tryForTime(30 * 1000, async () => { + await PageObjects.common.navigateToApp('uptime'); + await testSubjects.existOrFail('uptimeOverviewPage', { timeout: 2000 }); + }); + }; + + return { + async goToUptime() { + await goToUptimeRoot(); + }, + + goToSettings: async () => { + await goToUptimeRoot(); + await testSubjects.click('settings-page-link', 5000); + await testSubjects.existOrFail('uptimeSettingsPage', { timeout: 2000 }); + }, + + goToMonitor: async (monitorId: string, monitorName?: string) => { + await testSubjects.click(`monitor-page-link-${monitorId}`, 5000); + if ( + monitorName && + (await testSubjects.getVisibleText('monitor-page-title')) !== monitorName + ) { + throw new Error('Expected monitor name not found'); + } + await testSubjects.existOrFail('uptimeMonitorPage', { + timeout: 30000, + }); + }, + }; +} diff --git a/x-pack/test/functional/services/uptime/settings.ts b/x-pack/test/functional/services/uptime/settings.ts new file mode 100644 index 0000000000000..a64d39cd62a6d --- /dev/null +++ b/x-pack/test/functional/services/uptime/settings.ts @@ -0,0 +1,42 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +export function UptimeSettingsProvider({ getService }: FtrProviderContext) { + const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + + return { + go: async () => { + await testSubjects.click('settings-page-link', 5000); + }, + changeHeartbeatIndicesInput: async (text: string) => { + const input = await testSubjects.find('heartbeat-indices-input-loaded', 5000); + await input.clearValueWithKeyboard(); + await input.type(text); + }, + loadFields: async () => { + const input = await testSubjects.find('heartbeat-indices-input-loaded', 5000); + const heartbeatIndices = await input.getAttribute('value'); + + return { heartbeatIndices }; + }, + applyButtonIsDisabled: async () => { + return !!(await (await testSubjects.find('apply-settings-button')).getAttribute('disabled')); + }, + apply: async () => { + await (await testSubjects.find('apply-settings-button')).click(); + await retry.waitFor('submit to succeed', async () => { + // When the form submit is complete the form will no longer be disabled + const disabled = await ( + await testSubjects.find('heartbeat-indices-input-loaded', 5000) + ).getAttribute('disabled'); + return disabled === null; + }); + }, + }; +} diff --git a/x-pack/test/functional/services/uptime/uptime.ts b/x-pack/test/functional/services/uptime/uptime.ts new file mode 100644 index 0000000000000..c96bd0e0c4675 --- /dev/null +++ b/x-pack/test/functional/services/uptime/uptime.ts @@ -0,0 +1,29 @@ +/* + * 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 { FtrProviderContext } from '../../ftr_provider_context'; + +import { UptimeSettingsProvider } from './settings'; +import { UptimeCommonProvider } from './common'; +import { UptimeMonitorProvider } from './monitor'; +import { UptimeNavigationProvider } from './navigation'; +import { UptimeAlertsProvider } from './alerts'; + +export function UptimeProvider(context: FtrProviderContext) { + const common = UptimeCommonProvider(context); + const settings = UptimeSettingsProvider(context); + const monitor = UptimeMonitorProvider(context); + const navigation = UptimeNavigationProvider(context); + const alerts = UptimeAlertsProvider(context); + + return { + common, + settings, + monitor, + navigation, + alerts, + }; +} From 5d8c65a10b7aa6eeab188fef1058dfbf7efd1587 Mon Sep 17 00:00:00 2001 From: spalger Date: Tue, 31 Mar 2020 16:30:15 -0700 Subject: [PATCH 2/8] skip flaky suite (#61714) --- test/functional/apps/discover/_field_visualize.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/discover/_field_visualize.ts b/test/functional/apps/discover/_field_visualize.ts index 46238bf143290..24f4ba592324c 100644 --- a/test/functional/apps/discover/_field_visualize.ts +++ b/test/functional/apps/discover/_field_visualize.ts @@ -32,7 +32,8 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { defaultIndex: 'logstash-*', }; - describe('discover field visualize button', () => { + // FLAKY: https://github.com/elastic/kibana/issues/61714 + describe.skip('discover field visualize button', () => { before(async function() { log.debug('load kibana index with default index pattern'); await esArchiver.load('discover'); From c98c2253f64d0897c5e18feb6de0f5127aaf642c Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Tue, 31 Mar 2020 17:40:38 -0700 Subject: [PATCH 3/8] [Search service] Shim total hits in async search response (#61565) * Shim total hits in async search response * Resolve types * Fix types * Fix tests Co-authored-by: Elastic Machine --- .../server/search/es_search_strategy.ts | 14 +++-- .../server/search/shim_hits_total.test.ts | 56 +++++++++++++++++++ .../server/search/shim_hits_total.ts | 18 ++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 x-pack/plugins/data_enhanced/server/search/shim_hits_total.test.ts create mode 100644 x-pack/plugins/data_enhanced/server/search/shim_hits_total.ts diff --git a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts index 57d31553382bf..301f184af7d81 100644 --- a/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/server/search/es_search_strategy.ts @@ -19,6 +19,7 @@ import { getTotalLoaded, } from '../../../../../src/plugins/data/server'; import { IEnhancedEsSearchRequest } from '../../common'; +import { shimHitsTotal } from './shim_hits_total'; export interface AsyncSearchResponse { id: string; @@ -56,22 +57,27 @@ async function asyncSearch( request: IEnhancedEsSearchRequest, options?: ISearchOptions ) { - const { body = undefined, index = undefined, ...params } = request.id ? {} : request.params; + const { timeout = undefined, restTotalHitsAsInt = undefined, ...params } = { + trackTotalHits: true, // Get the exact count of hits + ...request.params, + }; // If we have an ID, then just poll for that ID, otherwise send the entire request body + const { body = undefined, index = undefined, ...queryParams } = request.id ? {} : params; + const method = request.id ? 'GET' : 'POST'; const path = encodeURI(request.id ? `_async_search/${request.id}` : `${index}/_async_search`); // Wait up to 1s for the response to return - const query = toSnakeCase({ waitForCompletionTimeout: '1s', ...params }); + const query = toSnakeCase({ waitForCompletionTimeout: '1s', ...queryParams }); - const { response: rawResponse, id } = (await caller( + const { response, id } = (await caller( 'transport.request', { method, path, body, query }, options )) as AsyncSearchResponse; - return { id, rawResponse, ...getTotalLoaded(rawResponse._shards) }; + return { id, rawResponse: shimHitsTotal(response), ...getTotalLoaded(response._shards) }; } async function rollupSearch( diff --git a/x-pack/plugins/data_enhanced/server/search/shim_hits_total.test.ts b/x-pack/plugins/data_enhanced/server/search/shim_hits_total.test.ts new file mode 100644 index 0000000000000..61740b97299da --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/search/shim_hits_total.test.ts @@ -0,0 +1,56 @@ +/* + * 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 { shimHitsTotal } from './shim_hits_total'; + +describe('shimHitsTotal', () => { + test('returns the total if it is already numeric', () => { + const result = shimHitsTotal({ + hits: { + total: 5, + }, + } as any); + expect(result).toEqual({ + hits: { + total: 5, + }, + }); + }); + + test('returns the total if it is inside `value`', () => { + const result = shimHitsTotal({ + hits: { + total: { + value: 5, + }, + }, + } as any); + expect(result).toEqual({ + hits: { + total: 5, + }, + }); + }); + + test('returns other properties from the response', () => { + const result = shimHitsTotal({ + _shards: {}, + hits: { + hits: [], + total: { + value: 5, + }, + }, + } as any); + expect(result).toEqual({ + _shards: {}, + hits: { + hits: [], + total: 5, + }, + }); + }); +}); diff --git a/x-pack/plugins/data_enhanced/server/search/shim_hits_total.ts b/x-pack/plugins/data_enhanced/server/search/shim_hits_total.ts new file mode 100644 index 0000000000000..10d45be01563a --- /dev/null +++ b/x-pack/plugins/data_enhanced/server/search/shim_hits_total.ts @@ -0,0 +1,18 @@ +/* + * 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 { SearchResponse } from 'elasticsearch'; + +/** + * Temporary workaround until https://github.com/elastic/kibana/issues/26356 is addressed. + * Since we are setting `track_total_hits` in the request, `hits.total` will be an object + * containing the `value`. + */ +export function shimHitsTotal(response: SearchResponse) { + const total = (response.hits?.total as any)?.value ?? response.hits?.total; + const hits = { ...response.hits, total }; + return { ...response, hits }; +} From 433d06fd166e3bbb69c2f6ed156c197e6f36683f Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 31 Mar 2020 19:57:30 -0500 Subject: [PATCH 4/8] Don't fetch service map data if no license (#62071) Fixes #61994 --- .../apm/public/components/app/ServiceMap/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index 0abaa9d76fc07..351e039ca45df 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -33,7 +33,12 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { const license = useLicense(); const { urlParams } = useUrlParams(); - const { data } = useFetcher(() => { + const { data = { elements: [] } } = useFetcher(() => { + // When we don't have a license or a valid license, don't make the request. + if (!license || !isValidPlatinumLicense(license)) { + return; + } + const { start, end, environment } = urlParams; if (start && end) { return callApmApi({ @@ -48,7 +53,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { } }); } - }, [serviceName, urlParams]); + }, [license, serviceName, urlParams]); const { ref, height, width } = useRefDimensions(); From 4fe5c6346b448fe11748985a6fa6322c4b882822 Mon Sep 17 00:00:00 2001 From: Ryland Herrick Date: Tue, 31 Mar 2020 20:09:16 -0500 Subject: [PATCH 5/8] Fix race condition in integration tests (#62064) There's a race condition with our rule creation tests where if they're executed we'll get a failure message in the response, but if they haven't yet executed we won't. This ultimately seems like a bug with this removeServerGeneratedProperties helpers, which has been updated to remove those failure properties as well. --- .../security_and_spaces/tests/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts index f1404b79a07af..7b725a7830c56 100644 --- a/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts +++ b/x-pack/test/detection_engine_api_integration/security_and_spaces/tests/utils.ts @@ -18,6 +18,8 @@ export const removeServerGeneratedProperties = ( created_at, updated_at, id, + last_failure_at, + last_failure_message, last_success_at, last_success_message, status, From 6246393dcb1d65b3421c3149d75144cd583141d0 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 31 Mar 2020 19:02:01 -0700 Subject: [PATCH 6/8] [Maps] Updates tests to not rely on field order (#62092) Elasticsearch master is now returning a different order for these fields and is failing the promotion of our nightly builds. Signed-off-by: Tyler Smalley --- .../apps/maps/documents_source/docvalue_fields.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js index fdacd89722d3c..a313508e5d06e 100644 --- a/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js +++ b/x-pack/test/functional/apps/maps/documents_source/docvalue_fields.js @@ -29,26 +29,26 @@ export default function({ getPageObjects, getService }) { await PageObjects.maps.loadSavedMap('document example'); const response = await getResponse(); const firstHit = response.hits.hits[0]; - expect(Object.keys(firstHit).join(',')).to.equal('_index,_id,_score,fields'); - expect(Object.keys(firstHit.fields).join(',')).to.equal('geo.coordinates'); + expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']); + expect(firstHit.fields).to.only.have.keys(['geo.coordinates']); }); it('should only fetch geo_point field and data driven styling fields', async () => { await PageObjects.maps.loadSavedMap('document example with data driven styles'); const response = await getResponse(); const firstHit = response.hits.hits[0]; - expect(Object.keys(firstHit).join(',')).to.equal('_index,_id,_score,fields'); - expect(Object.keys(firstHit.fields).join(',')).to.equal('geo.coordinates,bytes,hour_of_day'); + expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']); + expect(firstHit.fields).to.only.have.keys(['bytes', 'geo.coordinates', 'hour_of_day']); }); it('should format date fields as epoch_millis when data driven styling is applied to a date field', async () => { await PageObjects.maps.loadSavedMap('document example with data driven styles on date field'); const response = await getResponse(); const firstHit = response.hits.hits[0]; - expect(Object.keys(firstHit).join(',')).to.equal('_index,_id,_score,fields'); - expect(Object.keys(firstHit.fields).join(',')).to.equal('geo.coordinates,bytes,@timestamp'); + expect(firstHit).to.only.have.keys(['_id', '_index', '_score', 'fields']); + expect(firstHit.fields).to.only.have.keys(['@timestamp', 'bytes', 'geo.coordinates']); expect(firstHit.fields['@timestamp']).to.be.an('array'); - expect(firstHit.fields['@timestamp'][0]).to.equal('1442709321445'); + expect(firstHit.fields['@timestamp'][0]).to.eql('1442709321445'); }); }); } From aab5a0ce6df4f2fc3c2531152e201a5c9d673905 Mon Sep 17 00:00:00 2001 From: spalger Date: Tue, 31 Mar 2020 19:49:32 -0700 Subject: [PATCH 7/8] skip flaky suite (#53308) --- test/functional/apps/context/_discover_navigation.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/apps/context/_discover_navigation.js b/test/functional/apps/context/_discover_navigation.js index b906296037888..a56a85546bbcd 100644 --- a/test/functional/apps/context/_discover_navigation.js +++ b/test/functional/apps/context/_discover_navigation.js @@ -31,7 +31,8 @@ export default function({ getService, getPageObjects }) { const filterBar = getService('filterBar'); const PageObjects = getPageObjects(['common', 'discover', 'timePicker']); - describe('context link in discover', function contextSize() { + // FLAKY: https://github.com/elastic/kibana/issues/53308 + describe.skip('context link in discover', function contextSize() { this.tags('smoke'); before(async function() { await PageObjects.common.navigateToApp('discover'); From 79757651a9a6073daf1287cea1b6fe84de00c6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Marcondes?= <55978943+cauemarcondes@users.noreply.github.com> Date: Wed, 1 Apr 2020 07:50:06 +0100 Subject: [PATCH 8/8] [APM] Filters are not prefilled when the custom link flyout is opened from a transaction page. (#61650) * open flyout with filters prefilled * addressing pr comments * addressing pr comments Co-authored-by: Elastic Machine --- .../CustomLinkFlyout/FiltersSection.tsx | 2 +- .../CustomLink/CustomLinkFlyout/index.tsx | 32 ++++++----- .../Settings/CustomizeUI/CustomLink/index.tsx | 3 +- .../TransactionActionMenu.tsx | 9 +-- .../__test__/TransactionActionMenu.test.tsx | 55 +++++++++++++++++++ 5 files changed, 78 insertions(+), 23 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx index fdef9e1f5b7e7..fb8ffe6722c87 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/CustomLinkFlyout/FiltersSection.tsx @@ -116,7 +116,7 @@ export const FiltersSection = ({ void; - customLinkSelected?: CustomLink; onSave: () => void; onDelete: () => void; + defaults?: { + url?: string; + label?: string; + filters?: Filter[]; + }; + customLinkId?: string; } +const filtersEmptyState: Filter[] = [{ key: '', value: '' }]; + export const CustomLinkFlyout = ({ onClose, - customLinkSelected, onSave, - onDelete + onDelete, + defaults, + customLinkId }: Props) => { const { toasts } = useApmPluginContext().core.notifications; const [isSaving, setIsSaving] = useState(false); - const [label, setLabel] = useState(customLinkSelected?.label || ''); - const [url, setUrl] = useState(customLinkSelected?.url || ''); - const selectedFilters = customLinkSelected?.filters; + const [label, setLabel] = useState(defaults?.label || ''); + const [url, setUrl] = useState(defaults?.url || ''); const [filters, setFilters] = useState( - selectedFilters?.length - ? selectedFilters - : ([{ key: '', value: '' }] as Filter[]) + defaults?.filters?.length ? defaults.filters : filtersEmptyState ); const isFormValid = !!label && !!url; @@ -61,7 +63,7 @@ export const CustomLinkFlyout = ({ event.preventDefault(); setIsSaving(true); await saveCustomLink({ - id: customLinkSelected?.id, + id: customLinkId, label, url, filters, @@ -131,7 +133,7 @@ export const CustomLinkFlyout = ({ onClose={onClose} isSaving={isSaving} onDelete={onDelete} - customLinkId={customLinkSelected?.id} + customLinkId={customLinkId} /> diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx index 47990bf9233f6..e9a915e0f59bc 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/CustomizeUI/CustomLink/index.tsx @@ -55,7 +55,8 @@ export const CustomLinkOverview = () => { {isFlyoutOpen && ( { onCloseFlyout(); refetch(); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx index 0c5359e446ab8..048ed662ec502 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/TransactionActionMenu.tsx @@ -7,10 +7,7 @@ import { EuiButtonEmpty } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { FunctionComponent, useMemo, useState } from 'react'; -import { - CustomLink as CustomLinkType, - Filter -} from '../../../../../../../plugins/apm/common/custom_link/custom_link_types'; +import { Filter } from '../../../../../../../plugins/apm/common/custom_link/custom_link_types'; import { Transaction } from '../../../../../../../plugins/apm/typings/es_schemas/ui/transaction'; import { ActionMenu, @@ -68,7 +65,7 @@ export const TransactionActionMenu: FunctionComponent = ({ { key: 'service.environment', value: transaction?.service.environment }, { key: 'transaction.name', value: transaction?.transaction.name }, { key: 'transaction.type', value: transaction?.transaction.type } - ] as Filter[], + ].filter((filter): filter is Filter => typeof filter.value === 'string'), [transaction] ); @@ -100,7 +97,7 @@ export const TransactionActionMenu: FunctionComponent = ({ <> {isCustomLinkFlyoutOpen && ( { toggleCustomLinkFlyout(); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx index 560884aec554a..ce42bd3e39ad1 100644 --- a/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/TransactionActionMenu/__test__/TransactionActionMenu.test.tsx @@ -17,6 +17,7 @@ import * as hooks from '../../../../hooks/useFetcher'; import { LicenseContext } from '../../../../context/LicenseContext'; import { License } from '../../../../../../../../plugins/licensing/common/license'; import { MockApmPluginContextWrapper } from '../../../../context/ApmPluginContext/MockApmPluginContext'; +import * as apmApi from '../../../../services/rest/createCallApmApi'; const renderTransaction = async (transaction: Record) => { const rendered = render( @@ -142,6 +143,12 @@ describe('TransactionActionMenu component', () => { }); describe('Custom links', () => { + beforeAll(() => { + spyOn(apmApi, 'callApmApi').and.returnValue({}); + }); + afterAll(() => { + jest.resetAllMocks(); + }); it('doesnt show custom links when license is not valid', () => { const license = new License({ signature: 'test signature', @@ -250,5 +257,53 @@ describe('TransactionActionMenu component', () => { }); expectTextsInDocument(component, ['Custom Links']); }); + it('opens flyout with filters prefilled', () => { + const license = new License({ + signature: 'test signature', + license: { + expiryDateInMillis: 0, + mode: 'gold', + status: 'active', + type: 'gold', + uid: '1' + } + }); + const component = render( + + + + + + ); + act(() => { + fireEvent.click(component.getByText('Actions')); + }); + expectTextsInDocument(component, ['Custom Links']); + act(() => { + fireEvent.click(component.getByText('Create custom link')); + }); + expectTextsInDocument(component, ['Create link']); + const getFilterKeyValue = (key: string) => { + return { + [(component.getAllByText(key)[0] as HTMLOptionElement) + .text]: (component.getAllByTestId( + `${key}.value` + )[0] as HTMLInputElement).value + }; + }; + expect(getFilterKeyValue('service.name')).toEqual({ + 'service.name': 'opbeans-go' + }); + expect(getFilterKeyValue('transaction.name')).toEqual({ + 'transaction.name': 'GET /api/products/:id/customers' + }); + expect(getFilterKeyValue('transaction.type')).toEqual({ + 'transaction.type': 'request' + }); + }); }); });