@@ -36,19 +36,22 @@ const renderTemplateSchedulePage = async () => {
36
36
await waitForLoaderToBeRemoved ( ) ;
37
37
} ;
38
38
39
+ // Extracts all properties from TemplateScheduleFormValues that have a key that
40
+ // ends in _ms, and makes those properties optional. Defined as mapped type to
41
+ // ensure this stays in sync as TemplateScheduleFormValues changes
42
+ type FillAndSubmitConfig = {
43
+ [ Key in keyof TemplateScheduleFormValues as Key extends `${string } _ms`
44
+ ? Key
45
+ : never ] ?: TemplateScheduleFormValues [ Key ] | undefined ;
46
+ } ;
47
+
39
48
const fillAndSubmitForm = async ( {
40
49
default_ttl_ms,
41
50
max_ttl_ms,
42
51
failure_ttl_ms,
43
52
time_til_dormant_ms,
44
53
time_til_dormant_autodelete_ms,
45
- } : {
46
- default_ttl_ms ?: number ;
47
- max_ttl_ms ?: number ;
48
- failure_ttl_ms ?: number ;
49
- time_til_dormant_ms ?: number ;
50
- time_til_dormant_autodelete_ms ?: number ;
51
- } ) => {
54
+ } : FillAndSubmitConfig ) => {
52
55
const user = userEvent . setup ( ) ;
53
56
54
57
if ( default_ttl_ms ) {
@@ -61,6 +64,7 @@ const fillAndSubmitForm = async ({
61
64
62
65
if ( max_ttl_ms ) {
63
66
const maxTtlField = await screen . findByLabelText ( "Max lifetime (hours)" ) ;
67
+
64
68
await user . clear ( maxTtlField ) ;
65
69
await user . type ( maxTtlField , max_ttl_ms . toString ( ) ) ;
66
70
}
@@ -89,16 +93,31 @@ const fillAndSubmitForm = async ({
89
93
) ;
90
94
}
91
95
92
- const submitButton = await screen . findByText (
93
- FooterFormLanguage . defaultSubmitLabel ,
94
- ) ;
96
+ const submitButton = screen . getByRole ( "button" , {
97
+ name : FooterFormLanguage . defaultSubmitLabel ,
98
+ } ) ;
99
+
100
+ expect ( submitButton ) . not . toBeDisabled ( ) ;
95
101
await user . click ( submitButton ) ;
96
102
97
- // User needs to confirm dormancy and autodeletion fields.
103
+ // User needs to confirm dormancy and auto-deletion fields.
98
104
const confirmButton = await screen . findByTestId ( "confirm-button" ) ;
99
105
await user . click ( confirmButton ) ;
100
106
} ;
101
107
108
+ // One problem with the waitFor function is that if no additional config options
109
+ // are passed in, it will hang indefinitely as it keeps retrying an assertion.
110
+ // Even if Jest runs out of time and kills the test, you won't get a good error
111
+ // message. Adding options to force test to give up before test timeout
112
+ function waitForWithCutoff ( callback : ( ) => void | Promise < void > ) {
113
+ return waitFor ( callback , {
114
+ // Defined to end 500ms before global cut-off time of 20s. Wanted to define
115
+ // this in terms of an exported constant from jest.config, but since Jest
116
+ // is CJS-based, that would've involved weird CJS-ESM interop issues
117
+ timeout : 19_500 ,
118
+ } ) ;
119
+ }
120
+
102
121
describe ( "TemplateSchedulePage" , ( ) => {
103
122
beforeEach ( ( ) => {
104
123
jest
@@ -109,14 +128,17 @@ describe("TemplateSchedulePage", () => {
109
128
jest . spyOn ( API , "getExperiments" ) . mockResolvedValue ( [ "workspace_actions" ] ) ;
110
129
} ) ;
111
130
112
- it ( "succeeds " , async ( ) => {
131
+ it ( "Calls the API when user fills in and submits a form " , async ( ) => {
113
132
await renderTemplateSchedulePage ( ) ;
114
133
jest . spyOn ( API , "updateTemplateMeta" ) . mockResolvedValueOnce ( {
115
134
...MockTemplate ,
116
135
...validFormValues ,
117
136
} ) ;
137
+
118
138
await fillAndSubmitForm ( validFormValues ) ;
119
- await waitFor ( ( ) => expect ( API . updateTemplateMeta ) . toBeCalledTimes ( 1 ) ) ;
139
+ await waitForWithCutoff ( ( ) =>
140
+ expect ( API . updateTemplateMeta ) . toBeCalledTimes ( 1 ) ,
141
+ ) ;
120
142
} ) ;
121
143
122
144
test ( "default and max ttl is converted to and from hours" , async ( ) => {
@@ -128,16 +150,19 @@ describe("TemplateSchedulePage", () => {
128
150
} ) ;
129
151
130
152
await fillAndSubmitForm ( validFormValues ) ;
131
- await waitFor ( ( ) => expect ( API . updateTemplateMeta ) . toBeCalledTimes ( 1 ) ) ;
132
- await waitFor ( ( ) =>
153
+ await waitForWithCutoff ( ( ) =>
154
+ expect ( API . updateTemplateMeta ) . toBeCalledTimes ( 1 ) ,
155
+ ) ;
156
+
157
+ await waitForWithCutoff ( ( ) => {
133
158
expect ( API . updateTemplateMeta ) . toBeCalledWith (
134
159
"test-template" ,
135
160
expect . objectContaining ( {
136
161
default_ttl_ms : ( validFormValues . default_ttl_ms || 0 ) * 3600000 ,
137
162
max_ttl_ms : ( validFormValues . max_ttl_ms || 0 ) * 3600000 ,
138
163
} ) ,
139
- ) ,
140
- ) ;
164
+ ) ;
165
+ } ) ;
141
166
} ) ;
142
167
143
168
test ( "failure, dormancy, and dormancy auto-deletion converted to and from days" , async ( ) => {
@@ -149,8 +174,11 @@ describe("TemplateSchedulePage", () => {
149
174
} ) ;
150
175
151
176
await fillAndSubmitForm ( validFormValues ) ;
152
- await waitFor ( ( ) => expect ( API . updateTemplateMeta ) . toBeCalledTimes ( 1 ) ) ;
153
- await waitFor ( ( ) =>
177
+ await waitForWithCutoff ( ( ) =>
178
+ expect ( API . updateTemplateMeta ) . toBeCalledTimes ( 1 ) ,
179
+ ) ;
180
+
181
+ await waitForWithCutoff ( ( ) => {
154
182
expect ( API . updateTemplateMeta ) . toBeCalledWith (
155
183
"test-template" ,
156
184
expect . objectContaining ( {
@@ -160,8 +188,8 @@ describe("TemplateSchedulePage", () => {
160
188
time_til_dormant_autodelete_ms :
161
189
( validFormValues . time_til_dormant_autodelete_ms || 0 ) * 86400000 ,
162
190
} ) ,
163
- ) ,
164
- ) ;
191
+ ) ;
192
+ } ) ;
165
193
} ) ;
166
194
167
195
it ( "allows a default ttl of 7 days" , ( ) => {
0 commit comments