diff --git a/CHANGELOG.md b/CHANGELOG.md index 624a3a1d4da..871ae0137ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * **Templating**: Make $__interval and $__interval_ms global built in variables that can be used in by any datasource (in panel queries), closes [#7190](https://github.com/grafana/grafana/issues/7190), closes [#6582](https://github.com/grafana/grafana/issues/6582) * **S3 Image Store**: External s3 image store (used in alert notifications) now support AWS IAM Roles, closes [#6985](https://github.com/grafana/grafana/issues/6985), [#7058](https://github.com/grafana/grafana/issues/7058) thx [@mtanda](https://github.com/mtanda) * **Optimzation**: Never issue refresh event when Grafana tab is not visible [#7218](https://github.com/grafana/grafana/issues/7218), thx [@mtanda](https://github.com/mtanda) +* **Browser History**: Browser back/forward now works time ranges / zoom, [#7259](https://github.com/grafana/grafana/issues/7259) # 4.1.1 (2017-01-11) diff --git a/public/app/core/services/timer.js b/public/app/core/services/timer.js index a41c7d1376f..67ad02367c0 100644 --- a/public/app/core/services/timer.js +++ b/public/app/core/services/timer.js @@ -22,7 +22,7 @@ function (angular, _, coreModule) { $timeout.cancel(promise); }; - this.cancel_all = function() { + this.cancelAll = function() { _.each(timers, function(t) { $timeout.cancel(t); }); diff --git a/public/app/features/dashboard/all.js b/public/app/features/dashboard/all.js index 976bff07058..c362f9cd032 100644 --- a/public/app/features/dashboard/all.js +++ b/public/app/features/dashboard/all.js @@ -9,7 +9,7 @@ define([ './shareSnapshotCtrl', './dashboard_srv', './viewStateSrv', - './timeSrv', + './time_srv', './unsavedChangesSrv', './timepicker/timepicker', './graphiteImportCtrl', diff --git a/public/app/features/dashboard/specs/time_srv_specs.ts b/public/app/features/dashboard/specs/time_srv_specs.ts new file mode 100644 index 00000000000..3b008738430 --- /dev/null +++ b/public/app/features/dashboard/specs/time_srv_specs.ts @@ -0,0 +1,110 @@ +import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common'; + +import helpers from 'test/specs/helpers'; +import _ from 'lodash'; +import TimeSrv from '../time_srv'; +import moment from 'moment'; + +describe('timeSrv', function() { + var ctx = new helpers.ServiceTestContext(); + var _dashboard: any = { + time: {from: 'now-6h', to: 'now'}, + }; + + beforeEach(angularMocks.module('grafana.core')); + beforeEach(angularMocks.module('grafana.services')); + beforeEach(ctx.createService('timeSrv')); + + beforeEach(function() { + ctx.service.init(_dashboard); + }); + + describe('timeRange', function() { + it('should return unparsed when parse is false', function() { + ctx.service.setTime({from: 'now', to: 'now-1h' }); + var time = ctx.service.timeRange(); + expect(time.raw.from).to.be('now'); + expect(time.raw.to).to.be('now-1h'); + }); + + it('should return parsed when parse is true', function() { + ctx.service.setTime({from: 'now', to: 'now-1h' }); + var time = ctx.service.timeRange(); + expect(moment.isMoment(time.from)).to.be(true); + expect(moment.isMoment(time.to)).to.be(true); + }); + }); + + describe('init time from url', function() { + it('should handle relative times', function() { + ctx.$location.search({from: 'now-2d', to: 'now'}); + ctx.service.init(_dashboard); + var time = ctx.service.timeRange(); + expect(time.raw.from).to.be('now-2d'); + expect(time.raw.to).to.be('now'); + }); + + it('should handle formated dates', function() { + ctx.$location.search({from: '20140410T052010', to: '20140520T031022'}); + ctx.service.init(_dashboard); + var time = ctx.service.timeRange(true); + expect(time.from.valueOf()).to.equal(new Date("2014-04-10T05:20:10Z").getTime()); + expect(time.to.valueOf()).to.equal(new Date("2014-05-20T03:10:22Z").getTime()); + }); + + it('should handle formated dates without time', function() { + ctx.$location.search({from: '20140410', to: '20140520'}); + ctx.service.init(_dashboard); + var time = ctx.service.timeRange(true); + expect(time.from.valueOf()).to.equal(new Date("2014-04-10T00:00:00Z").getTime()); + expect(time.to.valueOf()).to.equal(new Date("2014-05-20T00:00:00Z").getTime()); + }); + + it('should handle epochs', function() { + ctx.$location.search({from: '1410337646373', to: '1410337665699'}); + ctx.service.init(_dashboard); + var time = ctx.service.timeRange(true); + expect(time.from.valueOf()).to.equal(1410337646373); + expect(time.to.valueOf()).to.equal(1410337665699); + }); + + it('should handle bad dates', function() { + ctx.$location.search({from: '20151126T00010%3C%2Fp%3E%3Cspan%20class', to: 'now'}); + _dashboard.time.from = 'now-6h'; + ctx.service.init(_dashboard); + expect(ctx.service.time.from).to.equal('now-6h'); + expect(ctx.service.time.to).to.equal('now'); + }); + }); + + describe('setTime', function() { + it('should return disable refresh if refresh is disabled for any range', function() { + _dashboard.refresh = false; + + ctx.service.setTime({from: '2011-01-01', to: '2015-01-01' }); + expect(_dashboard.refresh).to.be(false); + }); + + it('should restore refresh for absolute time range', function() { + _dashboard.refresh = '30s'; + + ctx.service.setTime({from: '2011-01-01', to: '2015-01-01' }); + expect(_dashboard.refresh).to.be('30s'); + }); + + it('should restore refresh after relative time range is set', function() { + _dashboard.refresh = '10s'; + ctx.service.setTime({from: moment([2011,1,1]), to: moment([2015,1,1])}); + expect(_dashboard.refresh).to.be(false); + ctx.service.setTime({from: '2011-01-01', to: 'now' }); + expect(_dashboard.refresh).to.be('10s'); + }); + + it('should keep refresh after relative time range is changed and now delay exists', function() { + _dashboard.refresh = '10s'; + ctx.service.setTime({from: 'now-1h', to: 'now-10s' }); + expect(_dashboard.refresh).to.be('10s'); + }); + }); + +}); diff --git a/public/app/features/dashboard/timeSrv.js b/public/app/features/dashboard/timeSrv.js deleted file mode 100644 index 8c600319589..00000000000 --- a/public/app/features/dashboard/timeSrv.js +++ /dev/null @@ -1,170 +0,0 @@ -define([ - 'angular', - 'lodash', - 'moment', - 'app/core/config', - 'app/core/utils/kbn', - 'app/core/utils/datemath' -], function (angular, _, moment, config, kbn, dateMath) { - 'use strict'; - - var module = angular.module('grafana.services'); - - module.service('timeSrv', function($rootScope, $timeout, $routeParams, timer, contextSrv) { - var self = this; - - // default time - this.time = {from: '6h', to: 'now'}; - - $rootScope.$on('zoom-out', function(e, factor) { self.zoomOut(factor); }); - - this.init = function(dashboard) { - timer.cancel_all(); - - this.dashboard = dashboard; - this.time = dashboard.time; - this.refresh = dashboard.refresh; - - this._initTimeFromUrl(); - this._parseTime(); - - if(this.refresh) { - this.setAutoRefresh(this.refresh); - } - }; - - this._parseTime = function() { - // when absolute time is saved in json it is turned to a string - if (_.isString(this.time.from) && this.time.from.indexOf('Z') >= 0) { - this.time.from = moment(this.time.from).utc(); - } - if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) { - this.time.to = moment(this.time.to).utc(); - } - }; - - this._parseUrlParam = function(value) { - if (value.indexOf('now') !== -1) { - return value; - } - if (value.length === 8) { - return moment.utc(value, 'YYYYMMDD'); - } - if (value.length === 15) { - return moment.utc(value, 'YYYYMMDDTHHmmss'); - } - - if (!isNaN(value)) { - var epoch = parseInt(value); - return moment.utc(epoch); - } - - return null; - }; - - this._initTimeFromUrl = function() { - if ($routeParams.from) { - this.time.from = this._parseUrlParam($routeParams.from) || this.time.from; - } - if ($routeParams.to) { - this.time.to = this._parseUrlParam($routeParams.to) || this.time.to; - } - if ($routeParams.refresh) { - this.refresh = $routeParams.refresh || this.refresh; - } - }; - - this.setAutoRefresh = function (interval) { - this.dashboard.refresh = interval; - if (interval) { - var interval_ms = kbn.interval_to_ms(interval); - $timeout(function () { - self.start_scheduled_refresh(interval_ms); - self.refreshDashboard(); - }, interval_ms); - } else { - this.cancel_scheduled_refresh(); - } - }; - - this.refreshDashboard = function() { - $rootScope.$broadcast('refresh'); - }; - - this.start_scheduled_refresh = function (after_ms) { - self.cancel_scheduled_refresh(); - self.refresh_timer = timer.register($timeout(function () { - self.start_scheduled_refresh(after_ms); - if (contextSrv.isGrafanaVisible()) { - self.refreshDashboard(); - } - }, after_ms)); - }; - - this.cancel_scheduled_refresh = function () { - timer.cancel(this.refresh_timer); - }; - - this.setTime = function(time, enableRefresh) { - _.extend(this.time, time); - - // disable refresh if zoom in or zoom out - if (!enableRefresh && moment.isMoment(time.to)) { - this.old_refresh = this.dashboard.refresh || this.old_refresh; - this.setAutoRefresh(false); - } - else if (this.old_refresh && this.old_refresh !== this.dashboard.refresh) { - this.setAutoRefresh(this.old_refresh); - this.old_refresh = null; - } - - $rootScope.appEvent('time-range-changed', this.time); - $timeout(this.refreshDashboard, 0); - }; - - this.timeRangeForUrl = function() { - var range = this.timeRange().raw; - - if (moment.isMoment(range.from)) { range.from = range.from.valueOf(); } - if (moment.isMoment(range.to)) { range.to = range.to.valueOf(); } - - return range; - }; - - this.timeRange = function() { - // make copies if they are moment (do not want to return out internal moment, because they are mutable!) - var range = { - from: moment.isMoment(this.time.from) ? moment(this.time.from) : this.time.from, - to: moment.isMoment(this.time.to) ? moment(this.time.to) : this.time.to, - }; - - range = { - from: dateMath.parse(range.from, false), - to: dateMath.parse(range.to, true), - raw: range - }; - - return range; - }; - - this.zoomOut = function(factor) { - var range = this.timeRange(); - - var timespan = (range.to.valueOf() - range.from.valueOf()); - var center = range.to.valueOf() - timespan/2; - - var to = (center + (timespan*factor)/2); - var from = (center - (timespan*factor)/2); - - if (to > Date.now() && range.to <= Date.now()) { - var offset = to - Date.now(); - from = from - offset; - to = Date.now(); - } - - this.setTime({from: moment.utc(from), to: moment.utc(to) }); - }; - - }); - -}); diff --git a/public/app/features/dashboard/time_srv.ts b/public/app/features/dashboard/time_srv.ts new file mode 100644 index 00000000000..1567adf05a4 --- /dev/null +++ b/public/app/features/dashboard/time_srv.ts @@ -0,0 +1,204 @@ +/// + +import config from 'app/core/config'; +import angular from 'angular'; +import moment from 'moment'; +import _ from 'lodash'; +import coreModule from 'app/core/core_module'; +import kbn from 'app/core/utils/kbn'; +import * as dateMath from 'app/core/utils/datemath'; + +class TimeSrv { + time: any; + refreshTimer: any; + refresh: boolean; + oldRefresh: boolean; + dashboard: any; + timeAtLoad: any; + + /** @ngInject **/ + constructor(private $rootScope, private $timeout, private $location, private timer, private contextSrv) { + // default time + this.time = {from: '6h', to: 'now'}; + + $rootScope.$on('zoom-out', this.zoomOut.bind(this)); + $rootScope.$on('$routeUpdate', this.routeUpdated.bind(this)); + } + + init(dashboard) { + this.timer.cancelAll(); + + this.dashboard = dashboard; + this.time = dashboard.time; + this.refresh = dashboard.refresh; + + this.initTimeFromUrl(); + this.parseTime(); + + // remember time at load so we can go back to it + this.timeAtLoad = _.cloneDeep(this.time); + + if (this.refresh) { + this.setAutoRefresh(this.refresh); + } + } + + private parseTime() { + // when absolute time is saved in json it is turned to a string + if (_.isString(this.time.from) && this.time.from.indexOf('Z') >= 0) { + this.time.from = moment(this.time.from).utc(); + } + if (_.isString(this.time.to) && this.time.to.indexOf('Z') >= 0) { + this.time.to = moment(this.time.to).utc(); + } + }; + + private parseUrlParam(value) { + if (value.indexOf('now') !== -1) { + return value; + } + if (value.length === 8) { + return moment.utc(value, 'YYYYMMDD'); + } + if (value.length === 15) { + return moment.utc(value, 'YYYYMMDDTHHmmss'); + } + + if (!isNaN(value)) { + var epoch = parseInt(value); + return moment.utc(epoch); + } + + return null; + } + + private initTimeFromUrl() { + var params = this.$location.search(); + if (params.from) { + this.time.from = this.parseUrlParam(params.from) || this.time.from; + } + if (params.to) { + this.time.to = this.parseUrlParam(params.to) || this.time.to; + } + if (params.refresh) { + this.refresh = params.refresh || this.refresh; + } + }; + + private routeUpdated() { + var params = this.$location.search(); + var urlRange = this.timeRangeForUrl(); + // check if url has time range + if (params.from && params.to) { + // is it different from what our current time range? + if (params.from !== urlRange.from || params.to !== urlRange.to) { + // issue update + this.initTimeFromUrl(); + this.setTime(this.time, true); + } + } else { + this.setTime(this.timeAtLoad, true); + } + } + + setAutoRefresh(interval) { + this.dashboard.refresh = interval; + if (interval) { + var intervalMs = kbn.interval_to_ms(interval); + + this.$timeout(() => { + this.startNextRefreshTimer(intervalMs); + this.refreshDashboard(); + }, intervalMs); + + } else { + this.cancelNextRefresh(); + } + } + + refreshDashboard() { + this.$rootScope.$broadcast('refresh'); + } + + private startNextRefreshTimer(afterMs) { + this.cancelNextRefresh(); + this.refreshTimer = this.timer.register(this.$timeout(() => { + this.startNextRefreshTimer(afterMs); + if (this.contextSrv.isGrafanaVisible()) { + this.refreshDashboard(); + } + }, afterMs)); + } + + private cancelNextRefresh() { + this.timer.cancel(this.refreshTimer); + }; + + setTime(time, fromRouteUpdate?) { + _.extend(this.time, time); + + // disable refresh if zoom in or zoom out + if (moment.isMoment(time.to)) { + this.oldRefresh = this.dashboard.refresh || this.oldRefresh; + this.setAutoRefresh(false); + } else if (this.oldRefresh && this.oldRefresh !== this.dashboard.refresh) { + this.setAutoRefresh(this.oldRefresh); + this.oldRefresh = null; + } + + // update url + if (fromRouteUpdate !== true) { + var urlRange = this.timeRangeForUrl(); + var urlParams = this.$location.search(); + urlParams.from = urlRange.from; + urlParams.to = urlRange.to; + this.$location.search(urlParams); + } + + this.$rootScope.appEvent('time-range-changed', this.time); + this.$timeout(this.refreshDashboard.bind(this), 0); + } + + timeRangeForUrl() { + var range = this.timeRange().raw; + + if (moment.isMoment(range.from)) { range.from = range.from.valueOf(); } + if (moment.isMoment(range.to)) { range.to = range.to.valueOf(); } + + return range; + } + + timeRange() { + // make copies if they are moment (do not want to return out internal moment, because they are mutable!) + var raw = { + from: moment.isMoment(this.time.from) ? moment(this.time.from) : this.time.from, + to: moment.isMoment(this.time.to) ? moment(this.time.to) : this.time.to, + }; + + return { + from: dateMath.parse(raw.from, false), + to: dateMath.parse(raw.to, true), + raw: raw + }; + } + + zoomOut(e, factor) { + var range = this.timeRange(); + + var timespan = (range.to.valueOf() - range.from.valueOf()); + var center = range.to.valueOf() - timespan/2; + + var to = (center + (timespan*factor)/2); + var from = (center - (timespan*factor)/2); + + if (to > Date.now() && range.to <= Date.now()) { + var offset = to - Date.now(); + from = from - offset; + to = Date.now(); + } + + this.setTime({from: moment.utc(from), to: moment.utc(to)}); + } +} + +coreModule.service('timeSrv', TimeSrv); diff --git a/public/app/features/dashboard/timepicker/timepicker.ts b/public/app/features/dashboard/timepicker/timepicker.ts index 49daad3df28..21404108678 100644 --- a/public/app/features/dashboard/timepicker/timepicker.ts +++ b/public/app/features/dashboard/timepicker/timepicker.ts @@ -97,8 +97,7 @@ export class TimePickerCtrl { from = range.from.valueOf(); } - this.timeSrv.setTime({from: moment.utc(from), to: moment.utc(to) }); - + this.timeSrv.setTime({from: moment.utc(from), to: moment.utc(to)}); } openDropdown() { @@ -126,7 +125,7 @@ export class TimePickerCtrl { this.timeSrv.setAutoRefresh(this.refresh.value); } - this.timeSrv.setTime(this.timeRaw, true); + this.timeSrv.setTime(this.timeRaw); this.$rootScope.appEvent('hide-dash-editor'); } diff --git a/public/app/features/dashboard/viewStateSrv.js b/public/app/features/dashboard/viewStateSrv.js index 993e8cfd09f..681e1377bad 100644 --- a/public/app/features/dashboard/viewStateSrv.js +++ b/public/app/features/dashboard/viewStateSrv.js @@ -8,7 +8,7 @@ function (angular, _, $) { var module = angular.module('grafana.services'); - module.factory('dashboardViewStateSrv', function($location, $timeout, templateSrv, contextSrv, timeSrv) { + module.factory('dashboardViewStateSrv', function($location, $timeout) { // represents the transient view state // like fullscreen panel & edit @@ -25,15 +25,6 @@ function (angular, _, $) { } }; - // update url on time range change - $scope.onAppEvent('time-range-changed', function() { - var urlParams = $location.search(); - var urlRange = timeSrv.timeRangeForUrl(); - urlParams.from = urlRange.from; - urlParams.to = urlRange.to; - $location.search(urlParams); - }); - $scope.onAppEvent('$routeUpdate', function() { var urlState = self.getQueryStringState(); if (self.needsSync(urlState)) { @@ -82,7 +73,7 @@ function (angular, _, $) { return urlState; }; - DashboardViewState.prototype.update = function(state) { + DashboardViewState.prototype.update = function(state, fromRouteUpdated) { // implement toggle logic if (state.toggle) { delete state.toggle; @@ -113,7 +104,12 @@ function (angular, _, $) { delete this.state.tab; } - $location.search(this.serializeToUrl()); + // do not update url params if we are here + // from routeUpdated event + if (fromRouteUpdated !== true) { + $location.search(this.serializeToUrl()); + } + this.syncState(); }; diff --git a/public/app/features/templating/specs/template_srv_specs.ts b/public/app/features/templating/specs/template_srv_specs.ts index f3e8ab3f58a..673273f240a 100644 --- a/public/app/features/templating/specs/template_srv_specs.ts +++ b/public/app/features/templating/specs/template_srv_specs.ts @@ -8,6 +8,9 @@ describe('templateSrv', function() { beforeEach(angularMocks.module('grafana.core')); beforeEach(angularMocks.module('grafana.services')); + beforeEach(angularMocks.module($provide => { + $provide.value('timeSrv', {}); + })); beforeEach(angularMocks.inject(function(variableSrv, templateSrv) { _templateSrv = templateSrv; diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts index 0c08117f59c..a0fcdb8d3ac 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts @@ -11,7 +11,7 @@ describe('ElasticDatasource', function() { beforeEach(angularMocks.module('grafana.core')); beforeEach(angularMocks.module('grafana.services')); - beforeEach(ctx.providePhase(['templateSrv', 'backendSrv'])); + beforeEach(ctx.providePhase(['templateSrv', 'backendSrv', 'timeSrv'])); beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { ctx.$q = $q; diff --git a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts index 5457fc0e16d..600e51f41c4 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource_specs.ts @@ -9,6 +9,8 @@ describe('PrometheusDatasource', function() { beforeEach(angularMocks.module('grafana.core')); beforeEach(angularMocks.module('grafana.services')); + beforeEach(ctx.providePhase(['timeSrv'])); + beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { ctx.$q = $q; ctx.$httpBackend = $httpBackend; diff --git a/public/test/specs/helpers.js b/public/test/specs/helpers.js index 424b190b6dd..96744672302 100644 --- a/public/test/specs/helpers.js +++ b/public/test/specs/helpers.js @@ -92,7 +92,6 @@ define([ self.timeSrv = new TimeSrvStub(); self.datasourceSrv = {}; self.backendSrv = {}; - self.$location = {}; self.$routeParams = {}; this.providePhase = function(mocks) { @@ -104,10 +103,11 @@ define([ }; this.createService = function(name) { - return inject(function($q, $rootScope, $httpBackend, $injector) { + return inject(function($q, $rootScope, $httpBackend, $injector, $location) { self.$q = $q; self.$rootScope = $rootScope; self.$httpBackend = $httpBackend; + self.$location = $location; self.$rootScope.onAppEvent = function() {}; self.$rootScope.appEvent = function() {}; diff --git a/public/test/specs/linkSrv-specs.js b/public/test/specs/linkSrv-specs.js index 9ecff29bc77..309fdb07076 100644 --- a/public/test/specs/linkSrv-specs.js +++ b/public/test/specs/linkSrv-specs.js @@ -1,5 +1,6 @@ define([ 'lodash', + 'app/features/dashboard/all', 'app/features/panellinks/linkSrv' ], function(_) { 'use strict'; diff --git a/public/test/specs/time_srv_specs.js b/public/test/specs/time_srv_specs.js deleted file mode 100644 index f95f2daecd2..00000000000 --- a/public/test/specs/time_srv_specs.js +++ /dev/null @@ -1,120 +0,0 @@ -define([ - 'test/mocks/dashboard-mock', - 'test/specs/helpers', - 'lodash', - 'moment', - 'app/core/services/timer', - 'app/features/dashboard/timeSrv' -], function(dashboardMock, helpers, _, moment) { - 'use strict'; - - describe('timeSrv', function() { - var ctx = new helpers.ServiceTestContext(); - var _dashboard; - - beforeEach(module('grafana.core')); - beforeEach(module('grafana.services')); - beforeEach(ctx.providePhase(['$routeParams'])); - beforeEach(ctx.createService('timeSrv')); - - beforeEach(function() { - _dashboard = dashboardMock.create(); - ctx.service.init(_dashboard); - }); - - describe('timeRange', function() { - it('should return unparsed when parse is false', function() { - ctx.service.setTime({from: 'now', to: 'now-1h' }); - var time = ctx.service.timeRange(); - expect(time.raw.from).to.be('now'); - expect(time.raw.to).to.be('now-1h'); - }); - - it('should return parsed when parse is true', function() { - ctx.service.setTime({from: 'now', to: 'now-1h' }); - var time = ctx.service.timeRange(); - expect(moment.isMoment(time.from)).to.be(true); - expect(moment.isMoment(time.to)).to.be(true); - }); - }); - - describe('init time from url', function() { - it('should handle relative times', function() { - ctx.$routeParams.from = 'now-2d'; - ctx.$routeParams.to = 'now'; - ctx.service.init(_dashboard); - var time = ctx.service.timeRange(); - expect(time.raw.from).to.be('now-2d'); - expect(time.raw.to).to.be('now'); - }); - - it('should handle formated dates', function() { - ctx.$routeParams.from = '20140410T052010'; - ctx.$routeParams.to = '20140520T031022'; - ctx.service.init(_dashboard); - var time = ctx.service.timeRange(true); - expect(time.from.valueOf()).to.equal(new Date("2014-04-10T05:20:10Z").getTime()); - expect(time.to.valueOf()).to.equal(new Date("2014-05-20T03:10:22Z").getTime()); - }); - - it('should handle formated dates without time', function() { - ctx.$routeParams.from = '20140410'; - ctx.$routeParams.to = '20140520'; - ctx.service.init(_dashboard); - var time = ctx.service.timeRange(true); - expect(time.from.valueOf()).to.equal(new Date("2014-04-10T00:00:00Z").getTime()); - expect(time.to.valueOf()).to.equal(new Date("2014-05-20T00:00:00Z").getTime()); - }); - - it('should handle epochs', function() { - ctx.$routeParams.from = '1410337646373'; - ctx.$routeParams.to = '1410337665699'; - ctx.service.init(_dashboard); - var time = ctx.service.timeRange(true); - expect(time.from.valueOf()).to.equal(1410337646373); - expect(time.to.valueOf()).to.equal(1410337665699); - }); - - it('should handle bad dates', function() { - ctx.$routeParams.from = '20151126T00010%3C%2Fp%3E%3Cspan%20class'; - ctx.$routeParams.to = 'now'; - _dashboard.time.from = 'now-6h'; - ctx.service.init(_dashboard); - expect(ctx.service.time.from).to.equal('now-6h'); - expect(ctx.service.time.to).to.equal('now'); - }); - }); - - describe('setTime', function() { - it('should return disable refresh if refresh is disabled for any range', function() { - _dashboard.refresh = false; - - ctx.service.setTime({from: '2011-01-01', to: '2015-01-01' }); - expect(_dashboard.refresh).to.be(false); - }); - - it('should restore refresh for absolute time range', function() { - _dashboard.refresh = '30s'; - - ctx.service.setTime({from: '2011-01-01', to: '2015-01-01' }); - expect(_dashboard.refresh).to.be('30s'); - }); - - it('should restore refresh after relative time range is set', function() { - _dashboard.refresh = '10s'; - ctx.service.setTime({from: moment([2011,1,1]), to: moment([2015,1,1])}); - expect(_dashboard.refresh).to.be(false); - ctx.service.setTime({from: '2011-01-01', to: 'now' }); - expect(_dashboard.refresh).to.be('10s'); - }); - - it('should keep refresh after relative time range is changed and now delay exists', function() { - _dashboard.refresh = '10s'; - ctx.service.setTime({from: 'now-1h', to: 'now-10s' }); - expect(_dashboard.refresh).to.be('10s'); - }); - }); - - }); - -});