@@ -24,65 +24,210 @@ import {
2424 setEndOffset ,
2525 setFixedTimeMode ,
2626 setRealTimeMode ,
27- setStartOffset ,
28- setTimeConductorBounds
27+ setStartOffset
2928} from '../../../../appActions.js' ;
3029import { expect , test } from '../../../../pluginFixtures.js' ;
3130
3231test . describe ( 'Time conductor operations' , ( ) => {
33- test ( 'validate start time does not exceed end time' , async ( { page } ) => {
32+ const DAY = '2024-01-01' ;
33+ const DAY_AFTER = '2024-01-02' ;
34+ const ONE_O_CLOCK = '01:00:00' ;
35+ const TWO_O_CLOCK = '02:00:00' ;
36+
37+ test . beforeEach ( async ( { page } ) => {
3438 // Go to baseURL
3539 await page . goto ( './' , { waitUntil : 'domcontentloaded' } ) ;
36- const year = new Date ( ) . getFullYear ( ) ;
40+ } ) ;
41+
42+ test ( 'validate date and time inputs are validated on input event' , async ( { page } ) => {
43+ const submitButtonLocator = page . getByLabel ( 'Submit time bounds' ) ;
44+
45+ // Open the time conductor popup
46+ await page . getByRole ( 'button' , { name : 'Time Conductor Mode' , exact : true } ) . click ( ) ;
47+
48+ await test . step ( 'invalid start date disables submit button' , async ( ) => {
49+ const initialStartDate = await page . getByLabel ( 'Start date' ) . inputValue ( ) ;
50+ const invalidStartDate = `${ initialStartDate . substring ( 0 , 5 ) } ${ initialStartDate . substring ( 6 ) } ` ;
51+
52+ await page . getByLabel ( 'Start date' ) . fill ( invalidStartDate ) ;
53+ await expect ( submitButtonLocator ) . toBeDisabled ( ) ;
54+ await page . getByLabel ( 'Start date' ) . fill ( initialStartDate ) ;
55+ await expect ( submitButtonLocator ) . toBeEnabled ( ) ;
56+ } ) ;
57+
58+ await test . step ( 'invalid start time disables submit button' , async ( ) => {
59+ const initialStartTime = await page . getByLabel ( 'Start time' ) . inputValue ( ) ;
60+ const invalidStartTime = `${ initialStartTime . substring ( 0 , 5 ) } ${ initialStartTime . substring ( 6 ) } ` ;
61+
62+ await page . getByLabel ( 'Start time' ) . fill ( invalidStartTime ) ;
63+ await expect ( submitButtonLocator ) . toBeDisabled ( ) ;
64+ await page . getByLabel ( 'Start time' ) . fill ( initialStartTime ) ;
65+ await expect ( submitButtonLocator ) . toBeEnabled ( ) ;
66+ } ) ;
67+
68+ await test . step ( 'disable/enable submit button also works with multiple invalid inputs' , async ( ) => {
69+ const initialEndDate = await page . getByLabel ( 'End date' ) . inputValue ( ) ;
70+ const invalidEndDate = `${ initialEndDate . substring ( 0 , 5 ) } ${ initialEndDate . substring ( 6 ) } ` ;
71+ const initialStartTime = await page . getByLabel ( 'Start time' ) . inputValue ( ) ;
72+ const invalidStartTime = `${ initialStartTime . substring ( 0 , 5 ) } ${ initialStartTime . substring ( 6 ) } ` ;
73+
74+ await page . getByLabel ( 'Start time' ) . fill ( invalidStartTime ) ;
75+ await expect ( submitButtonLocator ) . toBeDisabled ( ) ;
76+ await page . getByLabel ( 'End date' ) . fill ( invalidEndDate ) ;
77+ await expect ( submitButtonLocator ) . toBeDisabled ( ) ;
78+ await page . getByLabel ( 'End date' ) . fill ( initialEndDate ) ;
79+ await expect ( submitButtonLocator ) . toBeDisabled ( ) ;
80+ await page . getByLabel ( 'Start time' ) . fill ( initialStartTime ) ;
81+ await expect ( submitButtonLocator ) . toBeEnabled ( ) ;
82+ } ) ;
83+ } ) ;
84+
85+ test ( 'validate date and time inputs validation is reported on change event' , async ( { page } ) => {
86+ // Open the time conductor popup
87+ await page . getByRole ( 'button' , { name : 'Time Conductor Mode' , exact : true } ) . click ( ) ;
88+
89+ await test . step ( 'invalid start date is reported on change event, not on input event' , async ( ) => {
90+ const initialStartDate = await page . getByLabel ( 'Start date' ) . inputValue ( ) ;
91+ const invalidStartDate = `${ initialStartDate . substring ( 0 , 5 ) } ${ initialStartDate . substring ( 6 ) } ` ;
92+
93+ await page . getByLabel ( 'Start date' ) . fill ( invalidStartDate ) ;
94+ await expect ( page . getByLabel ( 'Start date' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Date' ) ;
95+ await page . getByLabel ( 'Start date' ) . press ( 'Tab' ) ;
96+ await expect ( page . getByLabel ( 'Start date' ) ) . toHaveAttribute ( 'title' , 'Invalid Date' ) ;
97+ await page . getByLabel ( 'Start date' ) . fill ( initialStartDate ) ;
98+ await expect ( page . getByLabel ( 'Start date' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Date' ) ;
99+ } ) ;
100+
101+ await test . step ( 'invalid start time is reported on change event, not on input event' , async ( ) => {
102+ const initialStartTime = await page . getByLabel ( 'Start time' ) . inputValue ( ) ;
103+ const invalidStartTime = `${ initialStartTime . substring ( 0 , 5 ) } ${ initialStartTime . substring ( 6 ) } ` ;
104+
105+ await page . getByLabel ( 'Start time' ) . fill ( invalidStartTime ) ;
106+ await expect ( page . getByLabel ( 'Start time' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Time' ) ;
107+ await page . getByLabel ( 'Start time' ) . press ( 'Tab' ) ;
108+ await expect ( page . getByLabel ( 'Start time' ) ) . toHaveAttribute ( 'title' , 'Invalid Time' ) ;
109+ await page . getByLabel ( 'Start time' ) . fill ( initialStartTime ) ;
110+ await expect ( page . getByLabel ( 'Start time' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Time' ) ;
111+ } ) ;
112+
113+ await test . step ( 'invalid end date is reported on change event, not on input event' , async ( ) => {
114+ const initialEndDate = await page . getByLabel ( 'End date' ) . inputValue ( ) ;
115+ const invalidEndDate = `${ initialEndDate . substring ( 0 , 5 ) } ${ initialEndDate . substring ( 6 ) } ` ;
116+
117+ await page . getByLabel ( 'End date' ) . fill ( invalidEndDate ) ;
118+ await expect ( page . getByLabel ( 'End date' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Date' ) ;
119+ await page . getByLabel ( 'End date' ) . press ( 'Tab' ) ;
120+ await expect ( page . getByLabel ( 'End date' ) ) . toHaveAttribute ( 'title' , 'Invalid Date' ) ;
121+ await page . getByLabel ( 'End date' ) . fill ( initialEndDate ) ;
122+ await expect ( page . getByLabel ( 'End date' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Date' ) ;
123+ } ) ;
124+
125+ await test . step ( 'invalid end time is reported on change event, not on input event' , async ( ) => {
126+ const initialEndTime = await page . getByLabel ( 'End time' ) . inputValue ( ) ;
127+ const invalidEndTime = `${ initialEndTime . substring ( 0 , 5 ) } ${ initialEndTime . substring ( 6 ) } ` ;
128+
129+ await page . getByLabel ( 'End time' ) . fill ( invalidEndTime ) ;
130+ await expect ( page . getByLabel ( 'End time' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Time' ) ;
131+ await page . getByLabel ( 'End time' ) . press ( 'Tab' ) ;
132+ await expect ( page . getByLabel ( 'End time' ) ) . toHaveAttribute ( 'title' , 'Invalid Time' ) ;
133+ await page . getByLabel ( 'End time' ) . fill ( initialEndTime ) ;
134+ await expect ( page . getByLabel ( 'End time' ) ) . not . toHaveAttribute ( 'title' , 'Invalid Time' ) ;
135+ } ) ;
136+ } ) ;
137+
138+ test ( 'validate start time does not exceed end time on submit' , async ( { page } ) => {
139+ // Open the time conductor popup
140+ await page . getByRole ( 'button' , { name : 'Time Conductor Mode' , exact : true } ) . click ( ) ;
141+
142+ // FIXME: https://github.com/nasa/openmct/pull/7818
143+ // eslint-disable-next-line playwright/no-wait-for-timeout
144+ await page . waitForTimeout ( 500 ) ;
145+
146+ await page . getByLabel ( 'Start date' ) . fill ( DAY ) ;
147+ await page . getByLabel ( 'Start time' ) . fill ( TWO_O_CLOCK ) ;
148+ await page . getByLabel ( 'End date' ) . fill ( DAY ) ;
149+ await page . getByLabel ( 'End time' ) . fill ( ONE_O_CLOCK ) ;
150+ await page . getByLabel ( 'Submit time bounds' ) . click ( ) ;
37151
38- // Set initial valid time bounds
39- const startDate = `${ year } -01-01` ;
40- const startTime = '01:00:00' ;
41- const endDate = `${ year } -01-01` ;
42- const endTime = '02:00:00' ;
43- await setTimeConductorBounds ( page , { startDate, startTime, endDate, endTime } ) ;
152+ await expect ( page . getByLabel ( 'Start date' ) ) . toHaveAttribute (
153+ 'title' ,
154+ 'Specified start date exceeds end bound'
155+ ) ;
156+ await expect ( page . getByLabel ( 'Start bounds' ) ) . not . toHaveText ( `${ DAY } ${ TWO_O_CLOCK } .000Z` ) ;
157+ await expect ( page . getByLabel ( 'End bounds' ) ) . not . toHaveText ( `${ DAY } ${ ONE_O_CLOCK } .000Z` ) ;
158+
159+ await page . getByLabel ( 'Start date' ) . fill ( DAY ) ;
160+ await page . getByLabel ( 'Start time' ) . fill ( ONE_O_CLOCK ) ;
161+ await page . getByLabel ( 'End date' ) . fill ( DAY ) ;
162+ await page . getByLabel ( 'End time' ) . fill ( TWO_O_CLOCK ) ;
163+ await page . getByLabel ( 'Submit time bounds' ) . click ( ) ;
164+
165+ await expect ( page . getByLabel ( 'Start bounds' ) ) . toHaveText ( `${ DAY } ${ ONE_O_CLOCK } .000Z` ) ;
166+ await expect ( page . getByLabel ( 'End bounds' ) ) . toHaveText ( `${ DAY } ${ TWO_O_CLOCK } .000Z` ) ;
167+ } ) ;
44168
169+ test ( 'validate start datetime does not exceed end datetime on submit' , async ( { page } ) => {
45170 // Open the time conductor popup
46171 await page . getByRole ( 'button' , { name : 'Time Conductor Mode' , exact : true } ) . click ( ) ;
47172
48- // Test invalid start date
49- const invalidStartDate = `${ year } -01-02` ;
50- await page . getByLabel ( 'Start date' ) . fill ( invalidStartDate ) ;
51- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
52- await page . getByLabel ( 'Start date' ) . fill ( startDate ) ;
53- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
54-
55- // Test invalid end date
56- const invalidEndDate = `${ year - 1 } -12-31` ;
57- await page . getByLabel ( 'End date' ) . fill ( invalidEndDate ) ;
58- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
59- await page . getByLabel ( 'End date' ) . fill ( endDate ) ;
60- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
61-
62- // Test invalid start time
63- const invalidStartTime = '42:00:00' ;
64- await page . getByLabel ( 'Start time' ) . fill ( invalidStartTime ) ;
65- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
66- await page . getByLabel ( 'Start time' ) . fill ( startTime ) ;
67- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
68-
69- // Test invalid end time
70- const invalidEndTime = '43:00:00' ;
71- await page . getByLabel ( 'End time' ) . fill ( invalidEndTime ) ;
72- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
73- await page . getByLabel ( 'End time' ) . fill ( endTime ) ;
74- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
75-
76- // Submit valid time bounds
173+ // FIXME: https://github.com/nasa/openmct/pull/7818
174+ // eslint-disable-next-line playwright/no-wait-for-timeout
175+ await page . waitForTimeout ( 500 ) ;
176+
177+ await page . getByLabel ( 'Start date' ) . fill ( DAY_AFTER ) ;
178+ await page . getByLabel ( 'Start time' ) . fill ( ONE_O_CLOCK ) ;
179+ await page . getByLabel ( 'End date' ) . fill ( DAY ) ;
180+ await page . getByLabel ( 'End time' ) . fill ( ONE_O_CLOCK ) ;
77181 await page . getByLabel ( 'Submit time bounds' ) . click ( ) ;
78182
79- // Verify the submitted time bounds
80- await expect ( page . getByLabel ( 'Start bounds' ) ) . toHaveText (
81- new RegExp ( ` ${ startDate } ${ startTime } .000Z` )
183+ await expect ( page . getByLabel ( 'Start date' ) ) . toHaveAttribute (
184+ 'title' ,
185+ 'Specified start date exceeds end bound'
82186 ) ;
83- await expect ( page . getByLabel ( 'End bounds' ) ) . toHaveText (
84- new RegExp ( `${ endDate } ${ endTime } .000Z` )
187+ await expect ( page . getByLabel ( 'Start bounds' ) ) . not . toHaveText (
188+ `${ DAY_AFTER } ${ ONE_O_CLOCK } .000Z`
85189 ) ;
190+ await expect ( page . getByLabel ( 'End bounds' ) ) . not . toHaveText ( `${ DAY } ${ ONE_O_CLOCK } .000Z` ) ;
191+
192+ await page . getByLabel ( 'Start date' ) . fill ( DAY ) ;
193+ await page . getByLabel ( 'Start time' ) . fill ( ONE_O_CLOCK ) ;
194+ await page . getByLabel ( 'End date' ) . fill ( DAY_AFTER ) ;
195+ await page . getByLabel ( 'End time' ) . fill ( ONE_O_CLOCK ) ;
196+ await page . getByLabel ( 'Submit time bounds' ) . click ( ) ;
197+
198+ await expect ( page . getByLabel ( 'Start bounds' ) ) . toHaveText ( `${ DAY } ${ ONE_O_CLOCK } .000Z` ) ;
199+ await expect ( page . getByLabel ( 'End bounds' ) ) . toHaveText ( `${ DAY_AFTER } ${ ONE_O_CLOCK } .000Z` ) ;
200+ } ) ;
201+
202+ test ( 'cancelling form does not set bounds' , async ( { page } ) => {
203+ test . info ( ) . annotations . push ( {
204+ type : 'issue' ,
205+ description : 'https://github.com/nasa/openmct/issues/7791'
206+ } ) ;
207+
208+ // Open the time conductor popup
209+ await page . getByRole ( 'button' , { name : 'Time Conductor Mode' , exact : true } ) . click ( ) ;
210+
211+ await page . getByLabel ( 'Start date' ) . fill ( DAY ) ;
212+ await page . getByLabel ( 'Start time' ) . fill ( ONE_O_CLOCK ) ;
213+ await page . getByLabel ( 'End date' ) . fill ( DAY_AFTER ) ;
214+ await page . getByLabel ( 'End time' ) . fill ( ONE_O_CLOCK ) ;
215+ await page . getByLabel ( 'Discard changes and close time popup' ) . click ( ) ;
216+
217+ await expect ( page . getByLabel ( 'Start bounds' ) ) . not . toHaveText ( `${ DAY } ${ ONE_O_CLOCK } .000Z` ) ;
218+ await expect ( page . getByLabel ( 'End bounds' ) ) . not . toHaveText ( `${ DAY_AFTER } ${ ONE_O_CLOCK } .000Z` ) ;
219+
220+ // Open the time conductor popup
221+ await page . getByRole ( 'button' , { name : 'Time Conductor Mode' , exact : true } ) . click ( ) ;
222+
223+ await page . getByLabel ( 'Start date' ) . fill ( DAY ) ;
224+ await page . getByLabel ( 'Start time' ) . fill ( ONE_O_CLOCK ) ;
225+ await page . getByLabel ( 'End date' ) . fill ( DAY_AFTER ) ;
226+ await page . getByLabel ( 'End time' ) . fill ( ONE_O_CLOCK ) ;
227+ await page . getByLabel ( 'Submit time bounds' ) . click ( ) ;
228+
229+ await expect ( page . getByLabel ( 'Start bounds' ) ) . toHaveText ( `${ DAY } ${ ONE_O_CLOCK } .000Z` ) ;
230+ await expect ( page . getByLabel ( 'End bounds' ) ) . toHaveText ( `${ DAY_AFTER } ${ ONE_O_CLOCK } .000Z` ) ;
86231 } ) ;
87232} ) ;
88233
@@ -131,77 +276,6 @@ test.describe('Global Time Conductor', () => {
131276 await expect ( page . getByLabel ( 'End offset: 01:30:31' ) ) . toBeVisible ( ) ;
132277 } ) ;
133278
134- test ( 'Input field validation: fixed time mode' , async ( { page } ) => {
135- test . info ( ) . annotations . push ( {
136- type : 'issue' ,
137- description : 'https://github.com/nasa/openmct/issues/7791'
138- } ) ;
139- // Switch to fixed time mode
140- await setFixedTimeMode ( page ) ;
141-
142- // Define valid time bounds for testing
143- const validBounds = {
144- startDate : '2024-04-20' ,
145- startTime : '00:04:20' ,
146- endDate : '2024-04-20' ,
147- endTime : '16:04:20'
148- } ;
149- // Set valid time conductor bounds ✌️
150- await setTimeConductorBounds ( page , validBounds ) ;
151-
152- // Verify that the time bounds are set correctly
153- await expect ( page . getByLabel ( `Start bounds: 2024-04-20 00:04:20.000Z` ) ) . toBeVisible ( ) ;
154- await expect ( page . getByLabel ( `End bounds: 2024-04-20 16:04:20.000Z` ) ) . toBeVisible ( ) ;
155-
156- // Open the Time Conductor Mode popup
157- await page . getByLabel ( 'Time Conductor Mode' ) . click ( ) ;
158-
159- // Test invalid start date
160- const invalidStartDate = '2024-04-21' ;
161- await page . getByLabel ( 'Start date' ) . fill ( invalidStartDate ) ;
162- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
163- await page . getByLabel ( 'Start date' ) . fill ( validBounds . startDate ) ;
164- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
165-
166- // Test invalid end date
167- const invalidEndDate = '2024-04-19' ;
168- await page . getByLabel ( 'End date' ) . fill ( invalidEndDate ) ;
169- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
170- await page . getByLabel ( 'End date' ) . fill ( validBounds . endDate ) ;
171- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
172-
173- // Test invalid start time
174- const invalidStartTime = '16:04:21' ;
175- await page . getByLabel ( 'Start time' ) . fill ( invalidStartTime ) ;
176- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
177- await page . getByLabel ( 'Start time' ) . fill ( validBounds . startTime ) ;
178- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
179-
180- // Test invalid end time
181- const invalidEndTime = '00:04:19' ;
182- await page . getByLabel ( 'End time' ) . fill ( invalidEndTime ) ;
183- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeDisabled ( ) ;
184- await page . getByLabel ( 'End time' ) . fill ( validBounds . endTime ) ;
185- await expect ( page . getByLabel ( 'Submit time bounds' ) ) . toBeEnabled ( ) ;
186-
187- // Verify that the time bounds remain unchanged after invalid inputs
188- await expect ( page . getByLabel ( `Start bounds: 2024-04-20 00:04:20.000Z` ) ) . toBeVisible ( ) ;
189- await expect ( page . getByLabel ( `End bounds: 2024-04-20 16:04:20.000Z` ) ) . toBeVisible ( ) ;
190-
191- // Discard changes and verify that bounds remain unchanged
192- await setTimeConductorBounds ( page , {
193- startDate : validBounds . startDate ,
194- startTime : '04:20:00' ,
195- endDate : validBounds . endDate ,
196- endTime : '04:20:20' ,
197- submitChanges : false
198- } ) ;
199-
200- // Verify that the original time bounds are still displayed after discarding changes
201- await expect ( page . getByLabel ( `Start bounds: 2024-04-20 00:04:20.000Z` ) ) . toBeVisible ( ) ;
202- await expect ( page . getByLabel ( `End bounds: 2024-04-20 16:04:20.000Z` ) ) . toBeVisible ( ) ;
203- } ) ;
204-
205279 /**
206280 * Verify that offsets and url params are preserved when switching
207281 * between fixed timespan and real-time mode.
0 commit comments