2021-11-01 10:39:06 -05:00
import {
addDays ,
addHours ,
differenceInCalendarDays ,
differenceInMinutes ,
format ,
isBefore ,
parseISO ,
toDate ,
} from 'date-fns' ;
2020-05-11 04:46:57 -05:00
2023-09-08 10:51:59 -05:00
import { e2e } from '../utils' ;
2022-04-22 08:33:13 -05:00
2023-09-13 07:24:20 -05:00
describe ( 'Dashboard time zone support' , ( ) = > {
beforeEach ( ( ) = > {
2023-09-27 05:33:00 -05:00
e2e . flows . login ( Cypress . env ( 'USERNAME' ) , Cypress . env ( 'PASSWORD' ) ) ;
2023-09-13 07:24:20 -05:00
} ) ;
2024-03-22 11:09:53 -05:00
it . skip ( 'Tests dashboard time zone scenarios' , ( ) = > {
2020-06-26 12:33:05 -05:00
e2e . flows . openDashboard ( { uid : '5SdHCasdf' } ) ;
2020-05-11 04:46:57 -05:00
2021-11-08 10:05:01 -06:00
const fromTimeZone = 'UTC' ;
2020-05-11 04:46:57 -05:00
const toTimeZone = 'America/Chicago' ;
2021-11-08 10:05:01 -06:00
const offset = offsetBetweenTimeZones ( toTimeZone , fromTimeZone ) ;
2020-05-11 04:46:57 -05: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 10:51:59 -05:00
e2e . components . Panels . Panel . title ( title )
2020-05-11 04:46:57 -05:00
. should ( 'be.visible' )
2023-09-13 07:24:20 -05:00
. within ( ( ) = > {
e2e . components . Panels . Visualization . Graph . xAxis . labels ( ) . should ( 'be.visible' ) ;
2020-05-11 04:46:57 -05:00
e2e . components . Panels . Visualization . Graph . xAxis
. labels ( )
. last ( )
2021-01-20 00:59:48 -06:00
. should ( ( element ) = > {
2020-05-11 04:46:57 -05:00
timesInUtc [ title ] = element . text ( ) ;
2023-09-13 07:24:20 -05:00
} ) ;
} ) ;
2020-05-11 04:46:57 -05:00
}
2021-01-27 08:02:04 -06:00
e2e . components . PageToolbar . item ( 'Dashboard settings' ) . click ( ) ;
2020-05-11 04:46:57 -05:00
2021-11-17 07:45:45 -06:00
e2e . components . TimeZonePicker . containerV2 ( )
2020-05-11 04:46:57 -05:00
. should ( 'be.visible' )
. within ( ( ) = > {
2021-11-26 09:38:48 -06:00
e2e . components . Select . singleValue ( ) . should ( 'have.text' , 'Coordinated Universal Time' ) ;
2021-01-20 00:59:48 -06:00
e2e . components . Select . input ( ) . should ( 'be.visible' ) . click ( ) ;
2020-05-11 04:46:57 -05:00
} ) ;
2021-07-14 08:04:23 -05:00
e2e . components . Select . option ( ) . should ( 'be.visible' ) . contains ( toTimeZone ) . click ( ) ;
2021-06-16 07:34:56 -05:00
// click to go back to the dashboard.
2023-09-08 10:51:59 -05:00
e2e . pages . Dashboard . Settings . Actions . close ( ) . click ( ) ;
2022-10-05 08:00:33 -05:00
e2e . components . RefreshPicker . runButtonV2 ( ) . should ( 'be.visible' ) . click ( ) ;
2020-05-11 04:46:57 -05:00
for ( const title of panelsToCheck ) {
2023-09-08 10:51:59 -05:00
e2e . components . Panels . Panel . title ( title )
2020-05-11 04:46:57 -05:00
. should ( 'be.visible' )
2023-09-18 06:02:32 -05:00
. within ( ( ) = > {
e2e . components . Panels . Visualization . Graph . xAxis . labels ( ) . should ( 'be.visible' ) ;
2020-05-11 04:46:57 -05:00
e2e . components . Panels . Visualization . Graph . xAxis
. labels ( )
. last ( )
2021-01-20 00:59:48 -06:00
. should ( ( element ) = > {
2021-06-16 07:34:56 -05:00
const inUtc = timesInUtc [ title ] ;
const inTz = element . text ( ) ;
const isCorrect = isTimeCorrect ( inUtc , inTz , offset ) ;
2023-09-08 10:51:59 -05:00
expect ( isCorrect ) . to . be . equal ( true ) ;
2023-09-18 06:02:32 -05:00
} ) ;
} ) ;
2020-05-11 04:46:57 -05:00
}
2023-09-13 07:24:20 -05:00
} ) ;
2020-05-11 04:46:57 -05:00
2023-09-13 07:24:20 -05:00
it ( 'Tests relative timezone support and overrides' , ( ) = > {
2023-09-04 04:38:35 -05:00
// Open dashboard
e2e . flows . openDashboard ( {
uid : 'd41dbaa2-a39e-4536-ab2b-caca52f1a9c8' ,
} ) ;
2023-09-11 05:20:54 -05:00
cy . intercept ( '/api/ds/query*' ) . as ( 'dataQuery' ) ;
2023-09-04 04:38:35 -05: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 05:20:54 -05:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 04:38:35 -05:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05:00
} ) ;
// Today so far, still in Browser timezone
e2e . flows . setTimeRange ( {
from : 'now/d' ,
2024-04-09 03:54:57 -05: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 04:38:35 -05:00
to : 'now' ,
} ) ;
// Need to wait for 2 calls as there's 2 panels
2023-09-11 05:20:54 -05:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 04:38:35 -05:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05:00
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05: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 05:20:54 -05:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 04:38:35 -05:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05: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 05:20:54 -05:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 04:38:35 -05:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05:00
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05: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 05:20:54 -05:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 04:38:35 -05:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05: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 05:20:54 -05:00
cy . wait ( [ '@dataQuery' , '@dataQuery' ] ) ;
2023-09-04 04:38:35 -05:00
e2e . components . Panels . Panel . title ( 'Panel with relative time override' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05:00
} ) ;
e2e . components . Panels . Panel . title ( 'Panel in timezone' )
. should ( 'be.visible' )
. within ( ( ) = > {
2023-09-11 05:20:54 -05:00
cy . contains ( '[role="row"]' , '00:00:00' ) . should ( 'be.visible' ) ;
2023-09-04 04:38:35 -05:00
} ) ;
2023-09-13 07:24:20 -05:00
} ) ;
2023-09-04 04:38:35 -05:00
} ) ;
2021-06-16 07:34:56 -05: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 04:46:57 -05:00
2021-11-01 10:39:06 -05:00
const reference = format ( new Date ( ) , 'yyyy-LL-dd' ) ;
2020-05-11 04:46:57 -05:00
2021-11-01 10:39:06 -05:00
const utcDate = toDate ( parseISO ( ` ${ reference } ${ inUtc } ` ) ) ;
const utcDateWithOffset = addHours ( toDate ( parseISO ( ` ${ reference } ${ inUtc } ` ) ) , offset ) ;
2021-06-16 07:34:56 -05: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 10:39:06 -05:00
const tzDate = addDays ( toDate ( parseISO ( ` ${ reference } ${ inTz } ` ) ) , dayOffset ) ; // adjust tzDate with any dayOffset
2021-06-16 07:34:56 -05:00
const diff = Math . abs ( differenceInMinutes ( utcDate , tzDate ) ) ; // use Math.abs if tzDate is in future
2020-05-11 04:46:57 -05:00
2021-06-16 07:34:56 -05:00
return diff <= Math . abs ( offset * 60 ) ;
2020-05-11 04:46:57 -05:00
} ;
2021-11-08 10:05:01 -06: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 ) ;
} ;