1import { TimeSrv } from './TimeSrv'; 2import { ContextSrvStub } from 'test/specs/helpers'; 3import { dateTime, isDateTime } from '@grafana/data'; 4import * as H from 'history'; 5import { HistoryWrapper, locationService, setLocationService } from '@grafana/runtime'; 6import { beforeEach } from '../../../../test/lib/common'; 7 8jest.mock('app/core/core', () => ({ 9 appEvents: { 10 subscribe: () => {}, 11 }, 12})); 13 14describe('timeSrv', () => { 15 let timeSrv: TimeSrv; 16 let _dashboard: any; 17 let locationUpdates: H.Location[] = []; 18 19 beforeEach(() => { 20 _dashboard = { 21 time: { from: 'now-6h', to: 'now' }, 22 getTimezone: jest.fn(() => 'browser'), 23 refresh: false, 24 timeRangeUpdated: jest.fn(() => {}), 25 }; 26 27 timeSrv = new TimeSrv(new ContextSrvStub() as any); 28 timeSrv.init(_dashboard); 29 30 beforeEach(() => { 31 locationUpdates = []; 32 const history = new HistoryWrapper(); 33 history.getHistory().listen((x) => locationUpdates.push(x)); 34 setLocationService(history); 35 }); 36 }); 37 38 describe('timeRange', () => { 39 it('should return unparsed when parse is false', () => { 40 timeSrv.setTime({ from: 'now', to: 'now-1h' }); 41 const time = timeSrv.timeRange(); 42 expect(time.raw.from).toBe('now'); 43 expect(time.raw.to).toBe('now-1h'); 44 }); 45 46 it('should return parsed when parse is true', () => { 47 timeSrv.setTime({ from: 'now', to: 'now-1h' }); 48 const time = timeSrv.timeRange(); 49 expect(isDateTime(time.from)).toBe(true); 50 expect(isDateTime(time.to)).toBe(true); 51 }); 52 }); 53 54 describe('init time from url', () => { 55 it('should handle relative times', () => { 56 locationService.push('/d/id?from=now-2d&to=now'); 57 58 timeSrv = new TimeSrv(new ContextSrvStub() as any); 59 60 timeSrv.init(_dashboard); 61 const time = timeSrv.timeRange(); 62 expect(time.raw.from).toBe('now-2d'); 63 expect(time.raw.to).toBe('now'); 64 }); 65 66 it('should handle formatted dates', () => { 67 locationService.push('/d/id?from=20140410T052010&to=20140520T031022'); 68 69 timeSrv = new TimeSrv(new ContextSrvStub() as any); 70 71 timeSrv.init(_dashboard); 72 const time = timeSrv.timeRange(); 73 expect(time.from.valueOf()).toEqual(new Date('2014-04-10T05:20:10Z').getTime()); 74 expect(time.to.valueOf()).toEqual(new Date('2014-05-20T03:10:22Z').getTime()); 75 }); 76 77 it('should ignore refresh if time absolute', () => { 78 locationService.push('/d/id?from=20140410T052010&to=20140520T031022'); 79 80 timeSrv = new TimeSrv(new ContextSrvStub() as any); 81 82 // dashboard saved with refresh on 83 _dashboard.refresh = true; 84 timeSrv.init(_dashboard); 85 86 expect(timeSrv.refresh).toBe(false); 87 }); 88 89 it('should handle formatted dates without time', () => { 90 locationService.push('/d/id?from=20140410&to=20140520'); 91 92 timeSrv = new TimeSrv(new ContextSrvStub() as any); 93 94 timeSrv.init(_dashboard); 95 const time = timeSrv.timeRange(); 96 expect(time.from.valueOf()).toEqual(new Date('2014-04-10T00:00:00Z').getTime()); 97 expect(time.to.valueOf()).toEqual(new Date('2014-05-20T00:00:00Z').getTime()); 98 }); 99 100 it('should handle epochs', () => { 101 locationService.push('/d/id?from=1410337646373&to=1410337665699'); 102 103 timeSrv = new TimeSrv(new ContextSrvStub() as any); 104 105 timeSrv.init(_dashboard); 106 const time = timeSrv.timeRange(); 107 expect(time.from.valueOf()).toEqual(1410337646373); 108 expect(time.to.valueOf()).toEqual(1410337665699); 109 }); 110 111 it('should handle epochs that look like formatted date without time', () => { 112 locationService.push('/d/id?from=20149999&to=20159999'); 113 114 timeSrv = new TimeSrv(new ContextSrvStub() as any); 115 116 timeSrv.init(_dashboard); 117 const time = timeSrv.timeRange(); 118 expect(time.from.valueOf()).toEqual(20149999); 119 expect(time.to.valueOf()).toEqual(20159999); 120 }); 121 122 it('should handle epochs that look like formatted date', () => { 123 locationService.push('/d/id?from=201499991234567&to=201599991234567'); 124 125 timeSrv = new TimeSrv(new ContextSrvStub() as any); 126 127 timeSrv.init(_dashboard); 128 const time = timeSrv.timeRange(); 129 expect(time.from.valueOf()).toEqual(201499991234567); 130 expect(time.to.valueOf()).toEqual(201599991234567); 131 }); 132 133 it('should handle bad dates', () => { 134 locationService.push('/d/id?from=20151126T00010%3C%2Fp%3E%3Cspan%20class&to=now'); 135 136 timeSrv = new TimeSrv(new ContextSrvStub() as any); 137 138 _dashboard.time.from = 'now-6h'; 139 timeSrv.init(_dashboard); 140 expect(timeSrv.time.from).toEqual('now-6h'); 141 expect(timeSrv.time.to).toEqual('now'); 142 }); 143 144 it('should handle refresh_intervals=null when refresh is enabled', () => { 145 locationService.push('/d/id?refresh=30s'); 146 147 timeSrv = new TimeSrv(new ContextSrvStub() as any); 148 149 _dashboard.timepicker = { 150 refresh_intervals: null, 151 }; 152 expect(() => timeSrv.init(_dashboard)).not.toThrow(); 153 }); 154 155 describe('data point windowing', () => { 156 it('handles time window specfied as interval string', () => { 157 locationService.push('/d/id?time=1410337645000&time.window=10s'); 158 159 timeSrv = new TimeSrv(new ContextSrvStub() as any); 160 161 timeSrv.init(_dashboard); 162 const time = timeSrv.timeRange(); 163 expect(time.from.valueOf()).toEqual(1410337640000); 164 expect(time.to.valueOf()).toEqual(1410337650000); 165 }); 166 167 it('handles time window specified in ms', () => { 168 locationService.push('/d/id?time=1410337645000&time.window=10000'); 169 170 timeSrv = new TimeSrv(new ContextSrvStub() as any); 171 172 timeSrv.init(_dashboard); 173 const time = timeSrv.timeRange(); 174 expect(time.from.valueOf()).toEqual(1410337640000); 175 expect(time.to.valueOf()).toEqual(1410337650000); 176 }); 177 178 it('corrects inverted from/to dates in ms', () => { 179 locationService.push('/d/id?from=1621436828909&to=1621436818909'); 180 181 timeSrv = new TimeSrv(new ContextSrvStub() as any); 182 183 timeSrv.init(_dashboard); 184 const time = timeSrv.timeRange(); 185 expect(time.from.valueOf()).toEqual(1621436818909); 186 expect(time.to.valueOf()).toEqual(1621436828909); 187 }); 188 189 it('corrects inverted from/to dates as relative times', () => { 190 locationService.push('/d/id?from=now&to=now-1h'); 191 192 timeSrv = new TimeSrv(new ContextSrvStub() as any); 193 194 timeSrv.init(_dashboard); 195 const time = timeSrv.timeRange(); 196 expect(time.raw.from).toBe('now-1h'); 197 expect(time.raw.to).toBe('now'); 198 }); 199 }); 200 }); 201 202 describe('setTime', () => { 203 it('should return disable refresh if refresh is disabled for any range', () => { 204 _dashboard.refresh = false; 205 206 timeSrv.setTime({ from: '2011-01-01', to: '2015-01-01' }); 207 expect(_dashboard.refresh).toBe(false); 208 }); 209 210 it('should restore refresh for absolute time range', () => { 211 _dashboard.refresh = '30s'; 212 213 timeSrv.setTime({ from: '2011-01-01', to: '2015-01-01' }); 214 expect(_dashboard.refresh).toBe('30s'); 215 }); 216 217 it('should restore refresh after relative time range is set', () => { 218 _dashboard.refresh = '10s'; 219 timeSrv.setTime({ 220 from: dateTime([2011, 1, 1]), 221 to: dateTime([2015, 1, 1]), 222 }); 223 expect(_dashboard.refresh).toBe(false); 224 timeSrv.setTime({ from: '2011-01-01', to: 'now' }); 225 expect(_dashboard.refresh).toBe('10s'); 226 }); 227 228 it('should keep refresh after relative time range is changed and now delay exists', () => { 229 _dashboard.refresh = '10s'; 230 timeSrv.setTime({ from: 'now-1h', to: 'now-10s' }); 231 expect(_dashboard.refresh).toBe('10s'); 232 }); 233 234 it('should update location only once for consecutive calls with the same range', () => { 235 timeSrv.setTime({ from: 'now-1h', to: 'now-10s' }); 236 timeSrv.setTime({ from: 'now-1h', to: 'now-10s' }); 237 238 expect(locationUpdates.length).toBe(1); 239 }); 240 241 it('should update location so that bool params are preserved', () => { 242 locationService.partial({ kiosk: true }); 243 244 timeSrv.setTime({ from: 'now-1h', to: 'now-10s' }); 245 timeSrv.setTime({ from: 'now-1h', to: 'now-10s' }); 246 247 expect(locationUpdates[1].search).toEqual('?kiosk&from=now-1h&to=now-10s'); 248 }); 249 }); 250 251 describe('pauseAutoRefresh', () => { 252 it('should set refresh to empty value', () => { 253 _dashboard.refresh = '10s'; 254 timeSrv.pauseAutoRefresh(); 255 expect(_dashboard.refresh).toBe(''); 256 }); 257 258 it('should set previousAutoRefresh value', () => { 259 _dashboard.refresh = '10s'; 260 timeSrv.pauseAutoRefresh(); 261 expect(timeSrv.previousAutoRefresh).toBe('10s'); 262 }); 263 }); 264 265 describe('resumeAutoRefresh', () => { 266 it('should set refresh to empty value', () => { 267 timeSrv.previousAutoRefresh = '10s'; 268 timeSrv.resumeAutoRefresh(); 269 expect(_dashboard.refresh).toBe('10s'); 270 }); 271 }); 272 273 describe('isRefreshOutsideThreshold', () => { 274 const originalNow = Date.now; 275 276 beforeEach(() => { 277 Date.now = jest.fn(() => 60000); 278 }); 279 280 afterEach(() => { 281 Date.now = originalNow; 282 }); 283 284 describe('when called and current time range is absolute', () => { 285 it('then it should return false', () => { 286 timeSrv.setTime({ from: dateTime(), to: dateTime() }); 287 288 expect(timeSrv.isRefreshOutsideThreshold(0, 0.05)).toBe(false); 289 }); 290 }); 291 292 describe('when called and current time range is relative', () => { 293 describe('and last refresh is within threshold', () => { 294 it('then it should return false', () => { 295 timeSrv.setTime({ from: 'now-1m', to: 'now' }); 296 297 expect(timeSrv.isRefreshOutsideThreshold(57001, 0.05)).toBe(false); 298 }); 299 }); 300 301 describe('and last refresh is outside the threshold', () => { 302 it('then it should return true', () => { 303 timeSrv.setTime({ from: 'now-1m', to: 'now' }); 304 305 expect(timeSrv.isRefreshOutsideThreshold(57000, 0.05)).toBe(true); 306 }); 307 }); 308 }); 309 }); 310}); 311