Skip to content

Commit 91a04c0

Browse files
authored
chore: enhance tests for TemplateSchedulePage (#9801)
* chore: Add benchmark logs to test * chore: Remove benchmark logic * chore: add hard cutoff for waitFor calls * refactor: clean up waitFor cut-off logic * chore: add assertion that submit button is not disabled * chore: Remove disabled check at the start of the test * fix: extend cutoff for waitFor config
1 parent 201a6c0 commit 91a04c0

File tree

1 file changed

+49
-21
lines changed

1 file changed

+49
-21
lines changed

site/src/pages/TemplateSettingsPage/TemplateSchedulePage/TemplateSchedulePage.test.tsx

+49-21
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,22 @@ const renderTemplateSchedulePage = async () => {
3636
await waitForLoaderToBeRemoved();
3737
};
3838

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+
3948
const fillAndSubmitForm = async ({
4049
default_ttl_ms,
4150
max_ttl_ms,
4251
failure_ttl_ms,
4352
time_til_dormant_ms,
4453
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) => {
5255
const user = userEvent.setup();
5356

5457
if (default_ttl_ms) {
@@ -61,6 +64,7 @@ const fillAndSubmitForm = async ({
6164

6265
if (max_ttl_ms) {
6366
const maxTtlField = await screen.findByLabelText("Max lifetime (hours)");
67+
6468
await user.clear(maxTtlField);
6569
await user.type(maxTtlField, max_ttl_ms.toString());
6670
}
@@ -89,16 +93,31 @@ const fillAndSubmitForm = async ({
8993
);
9094
}
9195

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();
95101
await user.click(submitButton);
96102

97-
// User needs to confirm dormancy and autodeletion fields.
103+
// User needs to confirm dormancy and auto-deletion fields.
98104
const confirmButton = await screen.findByTestId("confirm-button");
99105
await user.click(confirmButton);
100106
};
101107

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+
102121
describe("TemplateSchedulePage", () => {
103122
beforeEach(() => {
104123
jest
@@ -109,14 +128,17 @@ describe("TemplateSchedulePage", () => {
109128
jest.spyOn(API, "getExperiments").mockResolvedValue(["workspace_actions"]);
110129
});
111130

112-
it("succeeds", async () => {
131+
it("Calls the API when user fills in and submits a form", async () => {
113132
await renderTemplateSchedulePage();
114133
jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
115134
...MockTemplate,
116135
...validFormValues,
117136
});
137+
118138
await fillAndSubmitForm(validFormValues);
119-
await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1));
139+
await waitForWithCutoff(() =>
140+
expect(API.updateTemplateMeta).toBeCalledTimes(1),
141+
);
120142
});
121143

122144
test("default and max ttl is converted to and from hours", async () => {
@@ -128,16 +150,19 @@ describe("TemplateSchedulePage", () => {
128150
});
129151

130152
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(() => {
133158
expect(API.updateTemplateMeta).toBeCalledWith(
134159
"test-template",
135160
expect.objectContaining({
136161
default_ttl_ms: (validFormValues.default_ttl_ms || 0) * 3600000,
137162
max_ttl_ms: (validFormValues.max_ttl_ms || 0) * 3600000,
138163
}),
139-
),
140-
);
164+
);
165+
});
141166
});
142167

143168
test("failure, dormancy, and dormancy auto-deletion converted to and from days", async () => {
@@ -149,8 +174,11 @@ describe("TemplateSchedulePage", () => {
149174
});
150175

151176
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(() => {
154182
expect(API.updateTemplateMeta).toBeCalledWith(
155183
"test-template",
156184
expect.objectContaining({
@@ -160,8 +188,8 @@ describe("TemplateSchedulePage", () => {
160188
time_til_dormant_autodelete_ms:
161189
(validFormValues.time_til_dormant_autodelete_ms || 0) * 86400000,
162190
}),
163-
),
164-
);
191+
);
192+
});
165193
});
166194

167195
it("allows a default ttl of 7 days", () => {

0 commit comments

Comments
 (0)