2021-11-01 15:39:06 +00:00
import {
addDays ,
addHours ,
differenceInCalendarDays ,
differenceInMinutes ,
format ,
isBefore ,
parseISO ,
toDate ,
} from 'date-fns' ;
2020-05-11 11:46:57 +02:00
2023-09-08 16:51:59 +01:00
import { e2e } from '../utils' ;
2022-04-22 14:33:13 +01:00
2023-09-13 13:24:20 +01:00
describe ( 'Dashboard time zone support' , ( ) = > {
beforeEach ( ( ) = > {
2023-09-27 11:33:00 +01:00
e2e . flows . login ( Cypress . env ( 'USERNAME' ) , Cypress . env ( 'PASSWORD' ) ) ;
2023-09-13 13:24:20 +01:00
} ) ;
2024-03-22 17:09:53 +01:00
it . skip ( 'Tests dashboard time zone scenarios' , ( ) = > {
2020-06-26 13:33:05 -04:00
e2e . flows . openDashboard ( { uid : '5SdHCasdf' } ) ;
2020-05-11 11:46:57 +02:00
2021-11-08 17:05:01 +01:00
const fromTimeZone = 'UTC' ;
2020-05-11 11:46:57 +02:00
const toTimeZone = 'America/Chicago' ;
2021-11-08 17:05:01 +01:00
const offset = offsetBetweenTimeZones ( toTimeZone , fromTimeZone ) ;
2020-05-11 11:46:57 +02:00
const panelsToCheck = [
'Random walk series' ,
'Millisecond res x-axis and tooltip' ,
'2 yaxis and axis labels' ,
'Stacking value ontop of nulls' ,
'Null between points' ,
'Legend Table No Scroll Visible' ,
] ;
const timesInUtc : Record < string , string > = { } ;
for ( const title of panelsToCheck ) {
2023-09-08 16:51:59 +01:00
e2e . components . Panels . Panel . title ( title )
2020-05-11 11:46:57 +02:00
. should ( 'be.visible' )
2023-09-13 13:24:20 +01:00
. within ( ( ) = > {
e2e . components . Panels . Visualization . Graph . xAxis . labels ( ) . should ( 'be.visible' ) ;
2020-05-11 11:46:57 +02:00
e2e . components . Panels . Visualization . Graph . xAxis
. labels ( )
. last ( )
2021-01-20 07:59:48 +01:00
. should ( ( element ) = > {
2020-05-11 11:46:57 +02:00
timesInUtc [ title ] = element . text ( ) ;
2023-09-13 13:24:20 +01:00
} ) ;
} ) ;
2020-05-11 11:46:57 +02:00
}
2021-01-27 15:02:04 +01:00
e2e . components . PageToolbar . item ( 'Dashboard settings' ) . click ( ) ;
2020-05-11 11:46:57 +02:00
2021-11-17 14:45:45 +01:00
e2e . components . TimeZonePicker . containerV2 ( )
2020-05-11 11:46:57 +02:00
. should ( 'be.visible' )
. within ( ( ) = > {
2021-11-26 16:38:48 +01:00
e2e . components . Select . singleValue ( ) . should ( 'have.text' , 'Coordinated Universal Time' ) ;
2021-01-20 07:59:48 +01:00
e2e . components . Select . input ( ) . should ( 'be.visible' ) . click ( ) ;
2020-05-11 11:46:57 +02:00
} ) ;
2021-07-14 14:04:23 +01:00
e2e . components . Select . option ( ) . should ( 'be.visible' ) . contains ( toTimeZone ) . click ( ) ;
2021-06-16 14:34:56 +02:00
// click to go back to the dashboard.
2023-09-08 16:51:59 +01:00
e2e . pages . Dashboard . Settings . Actions . close ( ) . click ( ) ;
2022-10-05 15:00:33 +02:00
e2e . components . RefreshPicker . runButtonV2 ( ) . should ( 'be.visible' ) . click ( ) ;
2020-05-11 11:46:57 +02:00
for ( const title of panelsToCheck ) {
2023-09-08 16:51:59 +01:00
e2e . components . Panels . Panel . title ( title )
2020-05-11 11:46:57 +02:00
. should ( 'be.visible' )
2023-09-18 12:02:32 +01:00
. within ( ( ) = > {
e2e . components . Panels . Visualization . Graph . xAxis . labels ( ) . should ( 'be.visible' ) ;
2020-05-11 11:46:57 +02:00
e2e . components . Panels . Visualization . Graph . xAxis
. labels ( )
. last ( )
2021-01-20 07:59:48 +01:00
. should ( ( element ) = > {
2021-06-16 14:34:56 +02:00
const inUtc = timesInUtc [ title ] ;
const inTz = element . text ( ) ;
const isCorrect = isTimeCorrect ( inUtc , inTz , offset ) ;
2023-09-08 16:51:59 +01:00
expect ( isCorrect ) . to . be . equal ( true ) ;
2023-09-18 12:02:32 +01:00
} ) ;
} ) ;
2020-05-11 11:46:57 +02:00
}
2023-09-13 13:24:20 +01:00
} ) ;
2020-05-11 11:46:57 +02:00
2023-09-13 13:24:20 +01:00
it ( 'Tests relative timezone support and overrides' , ( ) = > {
2023-09-04 10:38:35 +01:00
// Open dashboard
e2e . flows . openDashboard ( {
uid : 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8' ,
} ) ;
2023-09-11 11:20:54 +01:00
cy . intercept ( '/api/ds/query*' ) . as ( 'dataQuery' ) ;
2023-09-04 10:38:35 +01:00
// Switch to Browser timezone
e2e . flows . setTimeRange ( {
from : 'now-6h' ,
to : 'now' ,
zone : 'Browser' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 11:20:54 +01:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 10:38:35 +01:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
// Today so far, still in Browser timezone
e2e . flows . setTimeRange ( {
from : 'now/d' ,
2024-04-09 09:54:57 +01:00
to : 'now' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
} ) ;
// Test UTC timezone
e2e . flows . setTimeRange ( {
from : 'now-6h' ,
to : 'now' ,
zone : 'Coordinated Universal Time' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
} ) ;
// Today so far, still in UTC timezone
e2e . flows . setTimeRange ( {
from : 'now/d' ,
2023-09-04 10:38:35 +01:00
to : 'now' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 11:20:54 +01:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 10:38:35 +01:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
// Test Tokyo timezone
e2e . flows . setTimeRange ( {
from : 'now-6h' ,
to : 'now' ,
zone : 'Asia/Tokyo' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 11:20:54 +01:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 10:38:35 +01:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
// Today so far, still in Tokyo timezone
e2e . flows . setTimeRange ( {
from : 'now/d' ,
to : 'now' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 11:20:54 +01:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 10:38:35 +01:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
// Test LA timezone
e2e . flows . setTimeRange ( {
from : 'now-6h' ,
to : 'now' ,
zone : 'America/Los_Angeles' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 11:20:54 +01:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 10:38:35 +01:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
// Today so far, still in LA timezone
e2e . flows . setTimeRange ( {
from : 'now/d' ,
to : 'now' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 11:20:54 +01:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 10:38:35 +01:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 11:20:54 +01:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 10:38:35 +01:00
} ) ;
2023-09-13 13:24:20 +01:00
} ) ;
2023-09-04 10:38:35 +01:00
} ) ;
2021-06-16 14:34:56 +02:00
const isTimeCorrect = ( inUtc : string , inTz : string , offset : number ) : boolean = > {
if ( inUtc === inTz ) {
// we need to catch issues when timezone isn't changed for some reason like https://github.com/grafana/grafana/issues/35504
return false ;
}
2020-05-11 11:46:57 +02:00
2021-11-01 15:39:06 +00:00
const reference = format ( new Date ( ) , 'yyyy-LL-dd' ) ;
2020-05-11 11:46:57 +02:00
2021-11-01 15:39:06 +00:00
const utcDate = toDate ( parseISO ( ` ${ reference } ${ inUtc } ` ) ) ;
const utcDateWithOffset = addHours ( toDate ( parseISO ( ` ${ reference } ${ inUtc } ` ) ) , offset ) ;
2021-06-16 14:34:56 +02:00
const dayDifference = differenceInCalendarDays ( utcDate , utcDateWithOffset ) ; // if the utcDate +/- offset is the day before/after then we need to adjust reference
const dayOffset = isBefore ( utcDateWithOffset , utcDate ) ? dayDifference * - 1 : dayDifference ;
2021-11-01 15:39:06 +00:00
const tzDate = addDays ( toDate ( parseISO ( ` ${ reference } ${ inTz } ` ) ) , dayOffset ) ; // adjust tzDate with any dayOffset
2021-06-16 14:34:56 +02:00
const diff = Math . abs ( differenceInMinutes ( utcDate , tzDate ) ) ; // use Math.abs if tzDate is in future
2020-05-11 11:46:57 +02:00
2021-06-16 14:34:56 +02:00
return diff <= Math . abs ( offset * 60 ) ;
2020-05-11 11:46:57 +02:00
} ;
2021-11-08 17:05:01 +01:00
const offsetBetweenTimeZones = ( timeZone1 : string , timeZone2 : string , when : Date = new Date ( ) ) : number = > {
const t1 = convertDateToAnotherTimeZone ( when , timeZone1 ) ;
const t2 = convertDateToAnotherTimeZone ( when , timeZone2 ) ;
return ( t1 . getTime ( ) - t2 . getTime ( ) ) / ( 1000 * 60 * 60 ) ;
} ;
const convertDateToAnotherTimeZone = ( date : Date , timeZone : string ) : Date = > {
const dateString = date . toLocaleString ( 'en-US' , {
timeZone : timeZone ,
} ) ;
return new Date ( dateString ) ;
} ;