Skip to content

Commit 4c71ccc

Browse files
authored
fix(site): disable autostart and autostop according to template settings (#11809)
* fix (site): disable autostart and autostop according to template settings * checking form values again; wrote tests * fixed closure and label bugs * fix broken query key * tweaks
1 parent 52c08a9 commit 4c71ccc

File tree

4 files changed

+221
-27
lines changed

4 files changed

+221
-27
lines changed

site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,17 @@ const meta: Meta<typeof WorkspaceScheduleForm> = {
1919
title: "pages/WorkspaceSettingsPage/WorkspaceScheduleForm",
2020
component: WorkspaceScheduleForm,
2121
args: {
22-
enableAutoStart: true,
23-
enableAutoStop: true,
22+
allowTemplateAutoStart: true,
23+
allowTemplateAutoStop: true,
24+
allowedTemplateAutoStartDays: [
25+
"sunday",
26+
"monday",
27+
"tuesday",
28+
"wednesday",
29+
"thursday",
30+
"friday",
31+
"saturday",
32+
],
2433
},
2534
};
2635

@@ -42,8 +51,8 @@ export const AllDisabled: Story = {
4251
autostopEnabled: false,
4352
ttl: emptyTTL,
4453
},
45-
enableAutoStart: false,
46-
enableAutoStop: false,
54+
allowTemplateAutoStart: false,
55+
allowTemplateAutoStop: false,
4756
},
4857
};
4958

@@ -55,7 +64,7 @@ export const Autostart: Story = {
5564
autostopEnabled: false,
5665
ttl: emptyTTL,
5766
},
58-
enableAutoStop: false,
67+
allowTemplateAutoStop: false,
5968
},
6069
};
6170

site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts renamed to site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ import {
33
ttlShutdownAt,
44
validationSchema,
55
WorkspaceScheduleFormValues,
6+
WorkspaceScheduleForm,
67
} from "./WorkspaceScheduleForm";
78
import { timeZones } from "utils/timeZones";
9+
import * as API from "api/api";
10+
import { MockTemplate } from "testHelpers/entities";
11+
import { render } from "testHelpers/renderHelpers";
12+
import { defaultSchedule } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule";
13+
import { screen } from "@testing-library/react";
814

915
const valid: WorkspaceScheduleFormValues = {
1016
autostartEnabled: true,
@@ -237,3 +243,139 @@ describe("ttlShutdownAt", () => {
237243
expect(ttlShutdownAt(ttlHours)).toEqual(expected);
238244
});
239245
});
246+
247+
const autoStartDayLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
248+
const defaultFormProps = {
249+
submitScheduleError: "",
250+
initialValues: {
251+
...defaultSchedule(),
252+
autostartEnabled: true,
253+
autostopEnabled: true,
254+
ttl: 24,
255+
},
256+
isLoading: false,
257+
defaultTTL: 24,
258+
onCancel: () => null,
259+
onSubmit: () => null,
260+
allowedTemplateAutoStartDays: autoStartDayLabels,
261+
allowTemplateAutoStart: true,
262+
allowTemplateAutoStop: true,
263+
};
264+
265+
describe("templateInheritance", () => {
266+
it("disables the entire autostart feature appropriately", async () => {
267+
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
268+
render(
269+
<WorkspaceScheduleForm
270+
{...defaultFormProps}
271+
allowTemplateAutoStart={false}
272+
/>,
273+
);
274+
275+
const autoStartToggle = await screen.findByLabelText("Enable Autostart");
276+
expect(autoStartToggle).toBeDisabled();
277+
278+
const startTimeInput = await screen.findByLabelText("Start time");
279+
expect(startTimeInput).toBeDisabled();
280+
281+
const timezoneInput = await screen.findByLabelText("Timezone");
282+
// MUI's input is wrapped in a div so we look at the aria-attribute instead
283+
expect(timezoneInput).toHaveAttribute("aria-disabled");
284+
285+
for (const label of autoStartDayLabels) {
286+
const checkbox = await screen.findByLabelText(label);
287+
expect(checkbox).toBeDisabled();
288+
}
289+
});
290+
it("disables the autostart days of the week appropriately", async () => {
291+
const enabledDayLabels = ["Sat", "Sun"];
292+
293+
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
294+
render(
295+
<WorkspaceScheduleForm
296+
{...defaultFormProps}
297+
allowedTemplateAutoStartDays={["saturday", "sunday"]}
298+
/>,
299+
);
300+
301+
const autoStartToggle = await screen.findByLabelText("Enable Autostart");
302+
expect(autoStartToggle).toBeEnabled();
303+
304+
const startTimeInput = await screen.findByLabelText("Start time");
305+
expect(startTimeInput).toBeEnabled();
306+
307+
const timezoneInput = await screen.findByLabelText("Timezone");
308+
// MUI's input is wrapped in a div so we look at the aria-attribute instead
309+
expect(timezoneInput).not.toHaveAttribute("aria-disabled");
310+
311+
for (const label of enabledDayLabels) {
312+
const checkbox = await screen.findByLabelText(label);
313+
expect(checkbox).toBeEnabled();
314+
}
315+
316+
for (const label of autoStartDayLabels.filter(
317+
(day) => !enabledDayLabels.includes(day),
318+
)) {
319+
const checkbox = await screen.findByLabelText(label);
320+
expect(checkbox).toBeDisabled();
321+
}
322+
});
323+
it("disables the entire autostop feature appropriately", async () => {
324+
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
325+
render(
326+
<WorkspaceScheduleForm
327+
{...defaultFormProps}
328+
allowTemplateAutoStop={false}
329+
/>,
330+
);
331+
332+
const autoStopToggle = await screen.findByLabelText("Enable Autostop");
333+
expect(autoStopToggle).toBeDisabled();
334+
335+
const ttlInput = await screen.findByLabelText(
336+
"Time until shutdown (hours)",
337+
);
338+
expect(ttlInput).toBeDisabled();
339+
});
340+
it("disables secondary autostart fields if main feature switch is toggled off", async () => {
341+
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
342+
render(
343+
<WorkspaceScheduleForm
344+
{...defaultFormProps}
345+
initialValues={{
346+
...defaultFormProps.initialValues,
347+
autostartEnabled: false,
348+
}}
349+
/>,
350+
);
351+
352+
const startTimeInput = await screen.findByLabelText("Start time");
353+
expect(startTimeInput).toBeDisabled();
354+
355+
const timezoneInput = await screen.findByLabelText("Timezone");
356+
// MUI's input is wrapped in a div so we look at the aria-attribute instead
357+
expect(timezoneInput).toHaveAttribute("aria-disabled");
358+
359+
autoStartDayLabels.forEach(async (label) => {
360+
const checkbox = await screen.findByLabelText(label);
361+
expect(checkbox).toBeDisabled();
362+
});
363+
});
364+
it("disables secondary autostop fields if main feature switch is toggled off", async () => {
365+
jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate);
366+
render(
367+
<WorkspaceScheduleForm
368+
{...defaultFormProps}
369+
initialValues={{
370+
...defaultFormProps.initialValues,
371+
autostopEnabled: false,
372+
}}
373+
/>,
374+
);
375+
376+
const ttlInput = await screen.findByLabelText(
377+
"Time until shutdown (hours)",
378+
);
379+
expect(ttlInput).toBeDisabled();
380+
});
381+
});

site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import { timeZones } from "utils/timeZones";
3232
import Tooltip from "@mui/material/Tooltip";
3333
import { formatDuration, intervalToDuration } from "date-fns";
3434
import { DisabledBadge } from "components/Badges/Badges";
35+
import { TemplateAutostartRequirement } from "api/typesGenerated";
36+
import { PropsWithChildren } from "react";
3537

3638
// REMARK: some plugins depend on utc, so it's listed first. Otherwise they're
3739
// sorted alphabetically.
@@ -73,8 +75,9 @@ export interface WorkspaceScheduleFormProps {
7375
submitScheduleError?: unknown;
7476
initialValues: WorkspaceScheduleFormValues;
7577
isLoading: boolean;
76-
enableAutoStop: boolean;
77-
enableAutoStart: boolean;
78+
allowedTemplateAutoStartDays: TemplateAutostartRequirement["days_of_week"];
79+
allowTemplateAutoStop: boolean;
80+
allowTemplateAutoStart: boolean;
7881
onCancel: () => void;
7982
onSubmit: (values: WorkspaceScheduleFormValues) => void;
8083
// for storybook
@@ -182,7 +185,7 @@ export const validationSchema = Yup.object({
182185
});
183186

184187
export const WorkspaceScheduleForm: FC<
185-
React.PropsWithChildren<WorkspaceScheduleFormProps>
188+
PropsWithChildren<WorkspaceScheduleFormProps>
186189
> = ({
187190
submitScheduleError,
188191
initialValues,
@@ -191,8 +194,9 @@ export const WorkspaceScheduleForm: FC<
191194
onSubmit,
192195
initialTouched,
193196
defaultTTL,
194-
enableAutoStop,
195-
enableAutoStart,
197+
allowedTemplateAutoStartDays,
198+
allowTemplateAutoStop,
199+
allowTemplateAutoStart,
196200
}) => {
197201
const form = useFormik<WorkspaceScheduleFormValues>({
198202
initialValues,
@@ -207,11 +211,6 @@ export const WorkspaceScheduleForm: FC<
207211
);
208212

209213
const checkboxes: Array<{ value: boolean; name: string; label: string }> = [
210-
{
211-
value: form.values.sunday,
212-
name: "sunday",
213-
label: Language.daySundayLabel,
214-
},
215214
{
216215
value: form.values.monday,
217216
name: "monday",
@@ -242,6 +241,11 @@ export const WorkspaceScheduleForm: FC<
242241
name: "saturday",
243242
label: Language.daySaturdayLabel,
244243
},
244+
{
245+
value: form.values.sunday,
246+
name: "sunday",
247+
label: Language.daySundayLabel,
248+
},
245249
];
246250

247251
const handleToggleAutostart = async (e: ChangeEvent) => {
@@ -288,7 +292,7 @@ export const WorkspaceScheduleForm: FC<
288292
Select the time and days of week on which you want the workspace
289293
starting automatically.
290294
</div>
291-
{!enableAutoStart && (
295+
{!allowTemplateAutoStart && (
292296
<Tooltip title="This option can be enabled in the template settings">
293297
<DisabledBadge />
294298
</Tooltip>
@@ -300,7 +304,7 @@ export const WorkspaceScheduleForm: FC<
300304
<FormControlLabel
301305
control={
302306
<Switch
303-
disabled={!enableAutoStart}
307+
disabled={!allowTemplateAutoStart}
304308
name="autostartEnabled"
305309
checked={form.values.autostartEnabled}
306310
onChange={handleToggleAutostart}
@@ -311,14 +315,26 @@ export const WorkspaceScheduleForm: FC<
311315
<Stack direction="row">
312316
<TextField
313317
{...formHelpers("startTime")}
314-
disabled={isLoading || !form.values.autostartEnabled}
318+
// disabled if template does not allow autostart
319+
// or if primary feature is toggled off via the switch above
320+
disabled={
321+
isLoading ||
322+
!allowTemplateAutoStart ||
323+
!form.values.autostartEnabled
324+
}
315325
label={Language.startTimeLabel}
316326
type="time"
317327
fullWidth
318328
/>
319329
<TextField
320330
{...formHelpers("timezone")}
321-
disabled={isLoading || !form.values.autostartEnabled}
331+
// disabled if template does not allow autostart
332+
// or if primary feature is toggled off via the switch above
333+
disabled={
334+
isLoading ||
335+
!allowTemplateAutoStart ||
336+
!form.values.autostartEnabled
337+
}
322338
label={Language.timezoneLabel}
323339
select
324340
fullWidth
@@ -349,7 +365,15 @@ export const WorkspaceScheduleForm: FC<
349365
control={
350366
<Checkbox
351367
checked={checkbox.value}
352-
disabled={isLoading || !form.values.autostartEnabled}
368+
// template admins can disable the autostart feature in general,
369+
// or they can disallow autostart on specific days of the week.
370+
// also disabled if primary feature switch (above) is toggled off
371+
disabled={
372+
isLoading ||
373+
!allowTemplateAutoStart ||
374+
!allowedTemplateAutoStartDays.includes(checkbox.name) ||
375+
!form.values.autostartEnabled
376+
}
353377
onChange={form.handleChange}
354378
name={checkbox.name}
355379
size="small"
@@ -378,7 +402,7 @@ export const WorkspaceScheduleForm: FC<
378402
extended by 1 hour after last activity in the workspace was
379403
detected.
380404
</div>
381-
{!enableAutoStop && (
405+
{!allowTemplateAutoStop && (
382406
<Tooltip title="This option can be enabled in the template settings">
383407
<DisabledBadge />
384408
</Tooltip>
@@ -393,7 +417,7 @@ export const WorkspaceScheduleForm: FC<
393417
name="autostopEnabled"
394418
checked={form.values.autostopEnabled}
395419
onChange={handleToggleAutostop}
396-
disabled={!enableAutoStop}
420+
disabled={!allowTemplateAutoStop}
397421
/>
398422
}
399423
label={Language.stopSwitch}
@@ -403,15 +427,28 @@ export const WorkspaceScheduleForm: FC<
403427
helperText: ttlShutdownAt(form.values.ttl),
404428
backendFieldName: "ttl_ms",
405429
})}
406-
disabled={isLoading || !form.values.autostopEnabled}
430+
// disabled if autostop disabled at template level or
431+
// if autostop feature is toggled off via the switch above
432+
disabled={
433+
isLoading ||
434+
!allowTemplateAutoStop ||
435+
!form.values.autostopEnabled
436+
}
407437
inputProps={{ min: 0, step: "any" }}
408438
label={Language.ttlLabel}
409439
type="number"
410440
fullWidth
411441
/>
412442
</FormFields>
413443
</FormSection>
414-
<FormFooter onCancel={onCancel} isLoading={isLoading} />
444+
<FormFooter
445+
onCancel={onCancel}
446+
isLoading={isLoading}
447+
submitDisabled={
448+
(!allowTemplateAutoStart && !allowTemplateAutoStop) ||
449+
(!form.values.autostartEnabled && !form.values.autostopEnabled)
450+
}
451+
/>
415452
</HorizontalForm>
416453
);
417454
};

site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ export const WorkspaceSchedulePage: FC = () => {
5959
mutationFn: submitSchedule,
6060
onSuccess: async () => {
6161
await queryClient.invalidateQueries(
62-
workspaceByOwnerAndNameKey(params.username, params.workspace),
62+
workspaceByOwnerAndNameKey(
63+
params.username.replace(/^@/, ""),
64+
params.workspace,
65+
),
6366
);
6467
},
6568
});
@@ -98,8 +101,11 @@ export const WorkspaceSchedulePage: FC = () => {
98101

99102
{template && (
100103
<WorkspaceScheduleForm
101-
enableAutoStart={template.allow_user_autostart}
102-
enableAutoStop={template.allow_user_autostop}
104+
allowedTemplateAutoStartDays={
105+
template.autostart_requirement.days_of_week
106+
}
107+
allowTemplateAutoStart={template.allow_user_autostart}
108+
allowTemplateAutoStop={template.allow_user_autostop}
103109
submitScheduleError={submitScheduleMutation.error}
104110
initialValues={{
105111
...getAutostart(workspace),

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy