From 39c04780c37a8441efd0f0126d1d387ee986beeb Mon Sep 17 00:00:00 2001 From: Justin Kambic Date: Wed, 13 Mar 2019 16:12:23 -0400 Subject: [PATCH] [Uptime] Add functional UI tests (#29667) * Refactor es queries and associated components/endpoints. * Add unit tests, repair broken tests. * [Uptime] Add API functional tests for uptime graphQL (#29128) * Add API functional tests for uptime graphQL. * Remove obsolete code. * Add CI group for UI functional tests. * Delete obsolete code, rename heartbeat es archive. * Refactor adapter methods. * Refactor adapter methods. * Attempt to fix ci-group tag error. * Skip functional app tests until later PR. * Remove unused code. * Optimize test runs. * Add uptime to api test index. * Fix formatting. * Add HB 7.0 data for API tests. * Configure first error_list test to work with 7.x data. * Configure error_list filtered by id to work with 7.x data. * Configure error_list functional tests to work with 7.x data. * Update snapshot test to work with 7.x data. * Update snapshot down filtered test to work with 7.x data. * Configure snapshot up test to work with 7.x data. * Configure ping list tests to work with 7.x data. * Configure monitor list tests to work with 7.x data. * Configure monitor status bar tests to work with 7.x data. * Configure filterBar tests to work with 7.x data. * Configure docCount tests to work with 7.x data. * Simplify code based on PR feedback. * Add loading spinner to monitor page title based on PR feedback. * Rename GQL type based on PR feedback. * Remove use of 'undefined' in ES query based on PR feedback. * Simplify code based on PR feedback. * Add definite size/shard_size for terms agg based on PR feedback. * Simplify ES query based on PR feedback. * Update x-pack/plugins/uptime/server/lib/adapters/monitors/elasticsearch_monitors_adapter.ts Implement PR feedback. Co-Authored-By: justinkambic * Increase size for ES errors query based on PR feedback. * Fix hardcoded field in terms filter based on PR feedback. * Simplify get code for monitors function. * Reduce unnecessarily large size for terms agg based on PR feedback. * Pluralize filter bar props. * Refactor filter bar query based on PR feedback. * Update test. * Fix busted GQL query. * Update functional test docs to use data without buggy values. * Update index name in HB functional api test docs. * Update snapshot base functional test. * Make snapshot filter tests pass, fix associated bug. * Configure remaining snapshot e2e tests to work with 7.x data. * Give better variable names and comments for ugly code. * Configure ping list query tests to work with updated 7.x data. * Rename graphql describe block. * Update monitor status bar query tests to work with updated 7.x data. * Update monitor list query tests to work with updated 7.x data. * Update filter bar query to work with updated 7.x data. * Update error list query to work with updated 7.x data. * Update doc count fixture to work with new 7.x data. * Create functional UI tests for Overview and Monitor pages. * Add additional waits to functional UI test to try to prevent flakiness. * [ftr/services/superDatePicker] make specialized service * Add data-test-subj value to monitor link. * Remove wait calls from UI tests. * Set default timezone for tests' kibana server. * Add @types for mocha and expect.js. * Implement PR feedback. * Remove added types. * Re-add mocha types. * Remove obsolete comment. * Disable timestamp on uptime app navigation for functional tests. * Undo previous change for default value of function parameter. * Add redirect hack to uptime client to change window location when expected router path is not satisfied. --- test/functional/page_objects/time_picker.js | 14 +++++++++ .../components/functional/monitor_list.tsx | 2 +- .../functional/monitor_page_title.tsx | 2 +- .../framework/kibana_framework_adapter.ts | 19 +++++++++++- x-pack/test/functional/apps/uptime/index.ts | 13 +++++++- x-pack/test/functional/apps/uptime/monitor.ts | 28 +++++++++++++++++ .../test/functional/apps/uptime/overview.ts | 21 +++++-------- .../functional/page_objects/uptime_page.ts | 31 ++++++++++++++----- x-pack/test/functional/services/uptime.ts | 10 +++++- x-pack/test/types/providers.ts | 2 ++ 10 files changed, 117 insertions(+), 25 deletions(-) create mode 100644 x-pack/test/functional/apps/uptime/monitor.ts diff --git a/test/functional/page_objects/time_picker.js b/test/functional/page_objects/time_picker.js index ef57503236600..679c73dbbff27 100644 --- a/test/functional/page_objects/time_picker.js +++ b/test/functional/page_objects/time_picker.js @@ -47,6 +47,18 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { await find.waitForElementStale(panelElement); } + async setAbsoluteStart(startTime) { + await this.showStartEndTimes(); + + await testSubjects.click('superDatePickerstartDatePopoverButton'); + const panel = await this.getTimePickerPanel(); + await testSubjects.click('superDatePickerAbsoluteTab'); + await testSubjects.setValue('superDatePickerAbsoluteDateInput', startTime); + await testSubjects.click('superDatePickerstartDatePopoverButton'); + await this.waitPanelIsGone(panel); + await PageObjects.header.awaitGlobalLoadingIndicatorHidden(); + } + /** * @param {String} fromTime YYYY-MM-DD HH:mm:ss.SSS * @param {String} fromTime YYYY-MM-DD HH:mm:ss.SSS @@ -110,6 +122,8 @@ export function TimePickerPageProvider({ getService, getPageObjects }) { } async showStartEndTimes() { + // This first await makes sure the superDatePicker has loaded before we check for the ShowDatesButton + await testSubjects.exists('superDatePickerToggleQuickMenuButton', { timeout: 20000 }); const isShowDatesButton = await testSubjects.exists('superDatePickerShowDatesButton'); if (isShowDatesButton) { await testSubjects.click('superDatePickerShowDatesButton'); diff --git a/x-pack/plugins/uptime/public/components/functional/monitor_list.tsx b/x-pack/plugins/uptime/public/components/functional/monitor_list.tsx index 4084d516a941e..c3238019b6ec9 100644 --- a/x-pack/plugins/uptime/public/components/functional/monitor_list.tsx +++ b/x-pack/plugins/uptime/public/components/functional/monitor_list.tsx @@ -83,7 +83,7 @@ export const MonitorList = ({ dangerColor, loading, monitors, primaryColor }: Mo defaultMessage: 'ID', }), render: (id: string, monitor: LatestMonitor) => ( - + {monitor.ping && monitor.ping.monitor && monitor.ping.monitor.name ? monitor.ping.monitor.name : id} diff --git a/x-pack/plugins/uptime/public/components/functional/monitor_page_title.tsx b/x-pack/plugins/uptime/public/components/functional/monitor_page_title.tsx index cd9d8e9fefc2b..b4d4a74a6cd03 100644 --- a/x-pack/plugins/uptime/public/components/functional/monitor_page_title.tsx +++ b/x-pack/plugins/uptime/public/components/functional/monitor_page_title.tsx @@ -16,7 +16,7 @@ export const MonitorPageTitle = ({ pageTitle: { name, url, id } }: MonitorPageTi -

{name ? name : url}

+

{name ? name : url}

diff --git a/x-pack/plugins/uptime/public/lib/adapters/framework/kibana_framework_adapter.ts b/x-pack/plugins/uptime/public/lib/adapters/framework/kibana_framework_adapter.ts index 169d5296ea9e3..2ac2e1044c2da 100644 --- a/x-pack/plugins/uptime/public/lib/adapters/framework/kibana_framework_adapter.ts +++ b/x-pack/plugins/uptime/public/lib/adapters/framework/kibana_framework_adapter.ts @@ -45,7 +45,7 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter { const route = { controllerAs: 'uptime', // @ts-ignore angular - controller: ($scope, $route, $http, config) => { + controller: ($scope, $route, config, $location, $window) => { const graphQLClient = createGraphQLClient(this.uriPath, this.xsrfHeader); $scope.$$postDigest(() => { const elem = document.getElementById('uptimeReactRoot'); @@ -57,6 +57,23 @@ export class UMKibanaFrameworkAdapter implements UMFrameworkAdapter { const routerBasename = basePath.endsWith('/') ? `${basePath}/${PLUGIN.ROUTER_BASE_NAME}` : basePath + PLUGIN.ROUTER_BASE_NAME; + + /** + * TODO: this is a redirect hack to deal with a problem that largely + * in testing but rarely occurs in the real world, where the specified + * URL contains `.../app/uptime{SOME_URL_PARAM_TEXT}#` instead of + * a path like `.../app/uptime#{SOME_URL_PARAM_TEXT}`. + * + * This redirect will almost never be triggered in practice, but it makes more + * sense to include it here rather than altering the existing testing + * infrastructure underlying the rest of Kibana. + * + * We welcome a more permanent solution that will result in the deletion of the + * block below. + */ + if ($location.absUrl().indexOf(PLUGIN.ROUTER_BASE_NAME) === -1) { + $window.location.replace(routerBasename); + } const persistedState = this.initializePersistedState(); const darkMode = config.get('theme:darkMode', false) || false; const { diff --git a/x-pack/test/functional/apps/uptime/index.ts b/x-pack/test/functional/apps/uptime/index.ts index ffe0d41d32aa8..3b4e0700de19d 100644 --- a/x-pack/test/functional/apps/uptime/index.ts +++ b/x-pack/test/functional/apps/uptime/index.ts @@ -6,11 +6,22 @@ import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; +const ARCHIVE = 'uptime/full_heartbeat'; + // tslint:disable-next-line:no-default-export -export default ({ loadTestFile }: KibanaFunctionalTestDefaultProviders) => { +export default ({ loadTestFile, getService }: KibanaFunctionalTestDefaultProviders) => { + const esArchiver = getService('esArchiver'); + const kibanaServer = getService('kibanaServer'); + describe('Uptime app', function() { + before(async () => { + await esArchiver.load(ARCHIVE); + await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'UTC' }); + }); + after(async () => await esArchiver.unload(ARCHIVE)); this.tags('ciGroup6'); loadTestFile(require.resolve('./overview')); + loadTestFile(require.resolve('./monitor')); }); }; diff --git a/x-pack/test/functional/apps/uptime/monitor.ts b/x-pack/test/functional/apps/uptime/monitor.ts new file mode 100644 index 0000000000000..7e1cd40d41796 --- /dev/null +++ b/x-pack/test/functional/apps/uptime/monitor.ts @@ -0,0 +1,28 @@ +/* + * 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 { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; + +// tslint:disable-next-line:no-default-export +export default ({ getPageObjects, getService }: KibanaFunctionalTestDefaultProviders) => { + const esArchiver = getService('esArchiver'); + const pageObjects = getPageObjects(['uptime']); + const archive = 'uptime/full_heartbeat'; + + describe('monitor page', () => { + before(async () => { + await esArchiver.load(archive); + }); + after(async () => await esArchiver.unload(archive)); + it('loads and displays uptime data based on date range', async () => { + await pageObjects.uptime.loadDataAndGoToMonitorPage( + '2019-01-28 12:40:08.078', + 'auto-http-0X131221E73F825974', + 'https://www.google.com/' + ); + }); + }); +}; diff --git a/x-pack/test/functional/apps/uptime/overview.ts b/x-pack/test/functional/apps/uptime/overview.ts index 793b55a3d8cee..5379dd9fd68da 100644 --- a/x-pack/test/functional/apps/uptime/overview.ts +++ b/x-pack/test/functional/apps/uptime/overview.ts @@ -7,20 +7,15 @@ import { KibanaFunctionalTestDefaultProviders } from '../../../types/providers'; // tslint:disable-next-line:no-default-export -export default ({ getPageObjects, getService }: KibanaFunctionalTestDefaultProviders) => { - const esArchiver = getService('esArchiver'); +export default ({ getPageObjects }: KibanaFunctionalTestDefaultProviders) => { // TODO: add UI functional tests - // const pageObjects = getPageObjects(['uptime']); - const archive = 'uptime/full_heartbeat'; - - describe('Overview page', () => { - describe('this is a simple test', () => { - beforeEach(async () => { - await esArchiver.load(archive); - }); - afterEach(async () => await esArchiver.unload(archive)); - - // TODO: add UI functional tests + const pageObjects = getPageObjects(['uptime']); + describe('overview page', () => { + it('loads and displays uptime data based on date range', async () => { + await pageObjects.uptime.goToUptimeOverviewAndLoadData( + '2019-01-28 12:40:08.078', + 'monitor-page-link-auto-http-0X131221E73F825974' + ); }); }); }; diff --git a/x-pack/test/functional/page_objects/uptime_page.ts b/x-pack/test/functional/page_objects/uptime_page.ts index de5470f60bc3a..d0ad3259247fd 100644 --- a/x-pack/test/functional/page_objects/uptime_page.ts +++ b/x-pack/test/functional/page_objects/uptime_page.ts @@ -10,13 +10,30 @@ export const UptimePageProvider = ({ getPageObjects, getService, }: KibanaFunctionalTestDefaultProviders) => { - const pageObject = getPageObjects(['common']); + const pageObjects = getPageObjects(['common', 'timePicker']); const uptimeService = getService('uptime'); - return { - async goToUptimeOverview() { - await pageObject.common.navigateToApp('uptime'); - await uptimeService.assertExists(); - }, - }; + return new class UptimePage { + public async goToUptimeOverviewAndLoadData( + datePickerStartValue: string, + monitorIdToCheck: string + ) { + await pageObjects.common.navigateToApp('uptime'); + await pageObjects.timePicker.setAbsoluteStart(datePickerStartValue); + await uptimeService.monitorIdExists(monitorIdToCheck); + } + + public async loadDataAndGoToMonitorPage( + datePickerStartValue: string, + monitorId: string, + monitorName: string + ) { + await pageObjects.common.navigateToApp('uptime'); + await pageObjects.timePicker.setAbsoluteStart(datePickerStartValue); + await uptimeService.navigateToMonitorWithId(monitorId); + if ((await uptimeService.getMonitorNameDisplayedOnPageTitle()) !== monitorName) { + throw new Error('Expected monitor name not found'); + } + } + }(); }; diff --git a/x-pack/test/functional/services/uptime.ts b/x-pack/test/functional/services/uptime.ts index ba959db070bb0..a97c43abb0127 100644 --- a/x-pack/test/functional/services/uptime.ts +++ b/x-pack/test/functional/services/uptime.ts @@ -8,12 +8,20 @@ import { KibanaFunctionalTestDefaultProviders } from '../../types/providers'; export const UptimeProvider = ({ getService }: KibanaFunctionalTestDefaultProviders) => { const testSubjects = getService('testSubjects'); - 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 testSubjects.existOrFail(key); + }, + async navigateToMonitorWithId(monitorId: string) { + await testSubjects.click(`monitor-page-link-${monitorId}`); + }, + async getMonitorNameDisplayedOnPageTitle() { + return await testSubjects.getVisibleText('monitor-page-title'); + }, }; }; diff --git a/x-pack/test/types/providers.ts b/x-pack/test/types/providers.ts index 9699775428a5c..2d655b8176807 100644 --- a/x-pack/test/types/providers.ts +++ b/x-pack/test/types/providers.ts @@ -5,9 +5,11 @@ */ import { EsArchiver } from '../../../src/es_archiver'; +import { UptimeProvider } from '../functional/services/uptime'; export interface KibanaFunctionalTestDefaultProviders { getService(serviceName: 'esArchiver'): EsArchiver; + getService(serviceName: 'uptime'): ReturnType; getService(serviceName: string): any; getPageObjects(pageObjectNames: string[]): any; loadTestFile(path: string): void;