From 36add48ac0b846d95392ca59cccb8b07e869438b Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Wed, 17 Jun 2020 08:41:16 +0200 Subject: [PATCH 1/2] Add validate function --- .../public/application/angular/discover.js | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 8ff5af1e3a767..6bf4b42540339 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -782,6 +782,10 @@ function discoverController( if (!init.complete) return; $scope.fetchCounter++; $scope.fetchError = undefined; + if (!validateTimeRange()) { + $scope.resultState = 'none'; + return; + } // Abort any in-progress requests before fetching again if (abortController) abortController.abort(); @@ -913,15 +917,39 @@ function discoverController( }); } + function validateTimeRange() { + const { from, to } = timefilter.getTime(); + if (!from || !to || !dateMath.parse(from) || !dateMath.parse(to)) { + toastNotifications.addDanger({ + title: i18n.translate('discover.notifications.invalidTimeRangeTitle', { + defaultMessage: `Invalid time range`, + }), + text: i18n.translate('discover.notifications.invalidTimeRangeText', { + defaultMessage: `The provided time range is invalid. (from: '{from}', to: '{to}')`, + values: { + from, + to, + }, + }), + }); + return false; + } + return true; + } + $scope.updateTime = function () { - //this is the timerange for the histogram, should be refactored + const { from, to } = timefilter.getTime(); + // this is the timerange for the histogram, should be refactored $scope.timeRange = { - from: dateMath.parse(timefilter.getTime().from), - to: dateMath.parse(timefilter.getTime().to, { roundUp: true }), + from: dateMath.parse(from), + to: dateMath.parse(to, { roundUp: true }), }; }; $scope.toMoment = function (datetime) { + if (!datetime) { + return; + } return moment(datetime).format(config.get('dateFormat')); }; From 5e03c8403f3aaa91fcf2da7fe0e64bba2f245611 Mon Sep 17 00:00:00 2001 From: Matthias Wilhelm Date: Tue, 23 Jun 2020 17:59:24 +0200 Subject: [PATCH 2/2] Improve tests --- .../public/application/angular/discover.js | 23 +-------- .../helpers/validate_time_range.test.ts | 47 ++++++++++++++++++ .../helpers/validate_time_range.ts | 49 +++++++++++++++++++ test/functional/apps/discover/_discover.js | 11 +++++ test/functional/page_objects/common_page.ts | 2 +- 5 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 src/plugins/discover/public/application/helpers/validate_time_range.test.ts create mode 100644 src/plugins/discover/public/application/helpers/validate_time_range.ts diff --git a/src/plugins/discover/public/application/angular/discover.js b/src/plugins/discover/public/application/angular/discover.js index 6bf4b42540339..5399a593cae74 100644 --- a/src/plugins/discover/public/application/angular/discover.js +++ b/src/plugins/discover/public/application/angular/discover.js @@ -64,6 +64,7 @@ const { } = getServices(); import { getRootBreadcrumbs, getSavedSearchBreadcrumbs } from '../helpers/breadcrumbs'; +import { validateTimeRange } from '../helpers/validate_time_range'; import { esFilters, fieldFormats, @@ -782,7 +783,7 @@ function discoverController( if (!init.complete) return; $scope.fetchCounter++; $scope.fetchError = undefined; - if (!validateTimeRange()) { + if (!validateTimeRange(timefilter.getTime(), toastNotifications)) { $scope.resultState = 'none'; return; } @@ -917,26 +918,6 @@ function discoverController( }); } - function validateTimeRange() { - const { from, to } = timefilter.getTime(); - if (!from || !to || !dateMath.parse(from) || !dateMath.parse(to)) { - toastNotifications.addDanger({ - title: i18n.translate('discover.notifications.invalidTimeRangeTitle', { - defaultMessage: `Invalid time range`, - }), - text: i18n.translate('discover.notifications.invalidTimeRangeText', { - defaultMessage: `The provided time range is invalid. (from: '{from}', to: '{to}')`, - values: { - from, - to, - }, - }), - }); - return false; - } - return true; - } - $scope.updateTime = function () { const { from, to } = timefilter.getTime(); // this is the timerange for the histogram, should be refactored diff --git a/src/plugins/discover/public/application/helpers/validate_time_range.test.ts b/src/plugins/discover/public/application/helpers/validate_time_range.test.ts new file mode 100644 index 0000000000000..a61a729caa22b --- /dev/null +++ b/src/plugins/discover/public/application/helpers/validate_time_range.test.ts @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { validateTimeRange } from './validate_time_range'; +import { notificationServiceMock } from '../../../../../core/public/mocks'; + +describe('Discover validateTimeRange', () => { + test('validates given time ranges correctly', async () => { + const { toasts } = notificationServiceMock.createStartContract(); + [ + { from: '', to: '', result: false }, + { from: 'now', to: 'now+1h', result: true }, + { from: 'now', to: 'lala+1h', result: false }, + { from: '', to: 'now', result: false }, + { from: 'now', to: '', result: false }, + { from: ' 2020-06-02T13:36:13.689Z', to: 'now', result: true }, + { from: ' 2020-06-02T13:36:13.689Z', to: '2020-06-02T13:36:13.690Z', result: true }, + ].map((test) => { + expect(validateTimeRange({ from: test.from, to: test.to }, toasts)).toEqual(test.result); + }); + }); + + test('displays a toast when invalid data is entered', async () => { + const { toasts } = notificationServiceMock.createStartContract(); + expect(validateTimeRange({ from: 'now', to: 'null' }, toasts)).toEqual(false); + expect(toasts.addDanger).toHaveBeenCalledWith({ + title: 'Invalid time range', + text: "The provided time range is invalid. (from: 'now', to: 'null')", + }); + }); +}); diff --git a/src/plugins/discover/public/application/helpers/validate_time_range.ts b/src/plugins/discover/public/application/helpers/validate_time_range.ts new file mode 100644 index 0000000000000..411147f827333 --- /dev/null +++ b/src/plugins/discover/public/application/helpers/validate_time_range.ts @@ -0,0 +1,49 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import dateMath from '@elastic/datemath'; +import { i18n } from '@kbn/i18n'; +import { ToastsStart } from 'kibana/public'; + +/** + * Validates a given time filter range, provided by URL or UI + * Unless valid, it returns false and displays a notification + */ +export function validateTimeRange( + { from, to }: { from: string; to: string }, + toastNotifications: ToastsStart +): boolean { + const fromMoment = dateMath.parse(from); + const toMoment = dateMath.parse(to); + if (!fromMoment || !toMoment || !fromMoment.isValid() || !toMoment.isValid()) { + toastNotifications.addDanger({ + title: i18n.translate('discover.notifications.invalidTimeRangeTitle', { + defaultMessage: `Invalid time range`, + }), + text: i18n.translate('discover.notifications.invalidTimeRangeText', { + defaultMessage: `The provided time range is invalid. (from: '{from}', to: '{to}')`, + values: { + from, + to, + }, + }), + }); + return false; + } + return true; +} diff --git a/test/functional/apps/discover/_discover.js b/test/functional/apps/discover/_discover.js index ecaa5aa2da97f..de9606f3d02ed 100644 --- a/test/functional/apps/discover/_discover.js +++ b/test/functional/apps/discover/_discover.js @@ -257,5 +257,16 @@ export default function ({ getService, getPageObjects }) { expect(refreshedTimeString).not.to.be(initialTimeString); }); }); + + describe('invalid time range in URL', function () { + it('should display a "Invalid time range toast"', async function () { + await PageObjects.common.navigateToUrl('discover', '#/?_g=(time:(from:now-15m,to:null))', { + useActualUrl: true, + }); + await PageObjects.header.awaitKibanaChrome(); + const toastMessage = await PageObjects.common.closeToast(); + expect(toastMessage).to.be('Invalid time range'); + }); + }); }); } diff --git a/test/functional/page_objects/common_page.ts b/test/functional/page_objects/common_page.ts index d08e88ecf47ea..439973aed23d4 100644 --- a/test/functional/page_objects/common_page.ts +++ b/test/functional/page_objects/common_page.ts @@ -401,7 +401,7 @@ export function CommonPageProvider({ getService, getPageObjects }: FtrProviderCo const toast = await find.byCssSelector('.euiToast', 2 * defaultFindTimeout); await toast.moveMouseTo(); const title = await (await find.byCssSelector('.euiToastHeader__title')).getVisibleText(); - log.debug(`Toast title: ${title}`); + await find.clickByCssSelector('.euiToast__closeButton'); return title; }