From 0786256016786cf9a35a188188acea73ef74148c Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Wed, 24 Jan 2024 18:58:36 +0000 Subject: [PATCH 1/5] fix (site): disable autostart and autostop according to template settings --- .../WorkspaceScheduleForm.stories.tsx | 19 ++- ...test.ts => WorkspaceScheduleForm.test.tsx} | 109 ++++++++++++++++++ .../WorkspaceScheduleForm.tsx | 36 +++--- .../WorkspaceSchedulePage.tsx | 7 +- 4 files changed, 151 insertions(+), 20 deletions(-) rename site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/{WorkspaceScheduleForm.test.ts => WorkspaceScheduleForm.test.tsx} (68%) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx index 6b643fd911bff..ec028498053b1 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.stories.tsx @@ -19,8 +19,17 @@ const meta: Meta = { title: "pages/WorkspaceSettingsPage/WorkspaceScheduleForm", component: WorkspaceScheduleForm, args: { - enableAutoStart: true, - enableAutoStop: true, + allowTemplateAutoStart: true, + allowTemplateAutoStop: true, + allowedTemplateAutoStartDays: [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + ], }, }; @@ -42,8 +51,8 @@ export const AllDisabled: Story = { autostopEnabled: false, ttl: emptyTTL, }, - enableAutoStart: false, - enableAutoStop: false, + allowTemplateAutoStart: false, + allowTemplateAutoStop: false, }, }; @@ -55,7 +64,7 @@ export const Autostart: Story = { autostopEnabled: false, ttl: emptyTTL, }, - enableAutoStop: false, + allowTemplateAutoStop: false, }, }; diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx similarity index 68% rename from site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts rename to site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx index 5fdd6616357f9..e81054a8b5682 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.ts +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx @@ -3,8 +3,14 @@ import { ttlShutdownAt, validationSchema, WorkspaceScheduleFormValues, + WorkspaceScheduleForm, } from "./WorkspaceScheduleForm"; import { timeZones } from "utils/timeZones"; +import * as API from "api/api"; +import { MockTemplate } from "testHelpers/entities"; +import { render } from "testHelpers/renderHelpers"; +import { defaultSchedule } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule"; +import { screen } from "@testing-library/react"; const valid: WorkspaceScheduleFormValues = { autostartEnabled: true, @@ -237,3 +243,106 @@ describe("ttlShutdownAt", () => { expect(ttlShutdownAt(ttlHours)).toEqual(expected); }); }); + +const autoStartDays = [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", +]; +const defaultFormProps = { + submitScheduleError: "", + initialValues: { + ...defaultSchedule(), + autostartEnabled: true, + autostopEnabled: true, + ttl: 24, + }, + isLoading: false, + defaultTTL: 24, + onCancel: () => null, + onSubmit: () => null, + allowedTemplateAutoStartDays: autoStartDays, + allowTemplateAutoStart: true, + allowTemplateAutoStop: true, +}; + +describe("templateInheritance", () => { + it("disables the entire autostart feature appropriately", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const autoStartToggle = await screen.findByLabelText("Enable Autostart"); + expect(autoStartToggle).toBeDisabled(); + + const startTimeInput = await screen.findByLabelText("Start time"); + expect(startTimeInput).toBeDisabled(); + + const timezoneInput = await screen.findByLabelText("Timezone"); + // MUI's input is wrapped in a div so we look at the aria-attribute instead + expect(timezoneInput).toHaveAttribute("aria-disabled"); + + autoStartDays.forEach(async (label) => { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + }); + }); + it("disables the autostart days of the week appropriately", async () => { + const enabledDays = ["saturday", "sunday"]; + + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const autoStartToggle = await screen.findByLabelText("Enable Autostart"); + expect(autoStartToggle).toBeEnabled(); + + const startTimeInput = await screen.findByLabelText("Start time"); + expect(startTimeInput).toBeEnabled(); + + const timezoneInput = await screen.findByLabelText("Timezone"); + // MUI's input is wrapped in a div so we look at the aria-attribute instead + expect(timezoneInput).not.toHaveAttribute("aria-disabled"); + + enabledDays.forEach(async (label) => { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeEnabled(); + }); + + autoStartDays + .filter((day) => !enabledDays.includes(day)) + .forEach(async (label) => { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + }); + }); + it("disables the entire autostop feature appropriately", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const autoStopToggle = await screen.findByLabelText("Enable Autostop"); + expect(autoStopToggle).toBeDisabled(); + + const ttlInput = await screen.findByLabelText( + "Time until shutdown (hours)", + ); + expect(ttlInput).toBeDisabled(); + }); +}); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx index fc72ffb3089e1..1a807e8514fdb 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx @@ -32,6 +32,8 @@ import { timeZones } from "utils/timeZones"; import Tooltip from "@mui/material/Tooltip"; import { formatDuration, intervalToDuration } from "date-fns"; import { DisabledBadge } from "components/Badges/Badges"; +import { TemplateAutostartRequirement } from "api/typesGenerated"; +import { PropsWithChildren } from "react"; // REMARK: some plugins depend on utc, so it's listed first. Otherwise they're // sorted alphabetically. @@ -73,8 +75,9 @@ export interface WorkspaceScheduleFormProps { submitScheduleError?: unknown; initialValues: WorkspaceScheduleFormValues; isLoading: boolean; - enableAutoStop: boolean; - enableAutoStart: boolean; + allowedTemplateAutoStartDays: TemplateAutostartRequirement["days_of_week"]; + allowTemplateAutoStop: boolean; + allowTemplateAutoStart: boolean; onCancel: () => void; onSubmit: (values: WorkspaceScheduleFormValues) => void; // for storybook @@ -182,7 +185,7 @@ export const validationSchema = Yup.object({ }); export const WorkspaceScheduleForm: FC< - React.PropsWithChildren + PropsWithChildren > = ({ submitScheduleError, initialValues, @@ -191,8 +194,9 @@ export const WorkspaceScheduleForm: FC< onSubmit, initialTouched, defaultTTL, - enableAutoStop, - enableAutoStart, + allowedTemplateAutoStartDays, + allowTemplateAutoStop, + allowTemplateAutoStart, }) => { const form = useFormik({ initialValues, @@ -288,7 +292,7 @@ export const WorkspaceScheduleForm: FC< Select the time and days of week on which you want the workspace starting automatically. - {!enableAutoStart && ( + {!allowTemplateAutoStart && ( @@ -300,7 +304,7 @@ export const WorkspaceScheduleForm: FC< - {!enableAutoStop && ( + {!allowTemplateAutoStop && ( @@ -393,7 +403,7 @@ export const WorkspaceScheduleForm: FC< name="autostopEnabled" checked={form.values.autostopEnabled} onChange={handleToggleAutostop} - disabled={!enableAutoStop} + disabled={!allowTemplateAutoStop} /> } label={Language.stopSwitch} @@ -403,7 +413,7 @@ export const WorkspaceScheduleForm: FC< helperText: ttlShutdownAt(form.values.ttl), backendFieldName: "ttl_ms", })} - disabled={isLoading || !form.values.autostopEnabled} + disabled={isLoading || !allowTemplateAutoStop} inputProps={{ min: 0, step: "any" }} label={Language.ttlLabel} type="number" diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index b54edcf437e61..712641d5e2cc7 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -98,8 +98,11 @@ export const WorkspaceSchedulePage: FC = () => { {template && ( Date: Thu, 25 Jan 2024 17:57:46 +0000 Subject: [PATCH 2/5] checking form values again; wrote tests --- .../WorkspaceScheduleForm.test.tsx | 41 +++++++++++++++++++ .../WorkspaceScheduleForm.tsx | 30 +++++++++++--- 2 files changed, 66 insertions(+), 5 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx index e81054a8b5682..8adfd808efa03 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx @@ -340,6 +340,47 @@ describe("templateInheritance", () => { const autoStopToggle = await screen.findByLabelText("Enable Autostop"); expect(autoStopToggle).toBeDisabled(); + const ttlInput = await screen.findByLabelText( + "Time until shutdown (hours)", + ); + expect(ttlInput).toBeDisabled(); + }); + it("disables secondary autostart fields if main feature switch is toggled off", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + + const startTimeInput = await screen.findByLabelText("Start time"); + expect(startTimeInput).toBeDisabled(); + + const timezoneInput = await screen.findByLabelText("Timezone"); + // MUI's input is wrapped in a div so we look at the aria-attribute instead + expect(timezoneInput).toHaveAttribute("aria-disabled"); + + autoStartDays.forEach(async (label) => { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + }); + }); + it("disables secondary autostop fields if main feature switch is toggled off", async () => { + jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); + render( + , + ); + const ttlInput = await screen.findByLabelText( "Time until shutdown (hours)", ); diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx index 1a807e8514fdb..7ccdbef613618 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx @@ -315,14 +315,26 @@ export const WorkspaceScheduleForm: FC< Date: Fri, 26 Jan 2024 03:13:32 +0000 Subject: [PATCH 3/5] fixed closure and label bugs --- .../WorkspaceScheduleForm.test.tsx | 38 ++++++++----------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx index 8adfd808efa03..e43a8617d1dcc 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.test.tsx @@ -244,15 +244,7 @@ describe("ttlShutdownAt", () => { }); }); -const autoStartDays = [ - "sunday", - "monday", - "tuesday", - "wednesday", - "thursday", - "friday", - "saturday", -]; +const autoStartDayLabels = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; const defaultFormProps = { submitScheduleError: "", initialValues: { @@ -265,7 +257,7 @@ const defaultFormProps = { defaultTTL: 24, onCancel: () => null, onSubmit: () => null, - allowedTemplateAutoStartDays: autoStartDays, + allowedTemplateAutoStartDays: autoStartDayLabels, allowTemplateAutoStart: true, allowTemplateAutoStop: true, }; @@ -290,19 +282,19 @@ describe("templateInheritance", () => { // MUI's input is wrapped in a div so we look at the aria-attribute instead expect(timezoneInput).toHaveAttribute("aria-disabled"); - autoStartDays.forEach(async (label) => { + for (const label of autoStartDayLabels) { const checkbox = await screen.findByLabelText(label); expect(checkbox).toBeDisabled(); - }); + } }); it("disables the autostart days of the week appropriately", async () => { - const enabledDays = ["saturday", "sunday"]; + const enabledDayLabels = ["Sat", "Sun"]; jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); render( , ); @@ -316,17 +308,17 @@ describe("templateInheritance", () => { // MUI's input is wrapped in a div so we look at the aria-attribute instead expect(timezoneInput).not.toHaveAttribute("aria-disabled"); - enabledDays.forEach(async (label) => { + for (const label of enabledDayLabels) { const checkbox = await screen.findByLabelText(label); expect(checkbox).toBeEnabled(); - }); + } - autoStartDays - .filter((day) => !enabledDays.includes(day)) - .forEach(async (label) => { - const checkbox = await screen.findByLabelText(label); - expect(checkbox).toBeDisabled(); - }); + for (const label of autoStartDayLabels.filter( + (day) => !enabledDayLabels.includes(day), + )) { + const checkbox = await screen.findByLabelText(label); + expect(checkbox).toBeDisabled(); + } }); it("disables the entire autostop feature appropriately", async () => { jest.spyOn(API, "getTemplateByName").mockResolvedValue(MockTemplate); @@ -364,7 +356,7 @@ describe("templateInheritance", () => { // MUI's input is wrapped in a div so we look at the aria-attribute instead expect(timezoneInput).toHaveAttribute("aria-disabled"); - autoStartDays.forEach(async (label) => { + autoStartDayLabels.forEach(async (label) => { const checkbox = await screen.findByLabelText(label); expect(checkbox).toBeDisabled(); }); From becf9cae6adcb31bca01f041741addb3311c0d7b Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 26 Jan 2024 04:08:50 +0000 Subject: [PATCH 4/5] fix broken query key --- .../WorkspaceSchedulePage/WorkspaceSchedulePage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 712641d5e2cc7..2c1eefc545aa0 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -59,7 +59,10 @@ export const WorkspaceSchedulePage: FC = () => { mutationFn: submitSchedule, onSuccess: async () => { await queryClient.invalidateQueries( - workspaceByOwnerAndNameKey(params.username, params.workspace), + workspaceByOwnerAndNameKey( + params.username.replace(/^@/, ""), + params.workspace, + ), ); }, }); From a9ef49902b26a608408e2a820480a204ace8af5b Mon Sep 17 00:00:00 2001 From: Kira Pilot Date: Fri, 26 Jan 2024 11:51:07 +0000 Subject: [PATCH 5/5] tweaks --- .../WorkspaceScheduleForm.tsx | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx index 7ccdbef613618..21120df0e62e9 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceScheduleForm.tsx @@ -211,11 +211,6 @@ export const WorkspaceScheduleForm: FC< ); const checkboxes: Array<{ value: boolean; name: string; label: string }> = [ - { - value: form.values.sunday, - name: "sunday", - label: Language.daySundayLabel, - }, { value: form.values.monday, name: "monday", @@ -246,6 +241,11 @@ export const WorkspaceScheduleForm: FC< name: "saturday", label: Language.daySaturdayLabel, }, + { + value: form.values.sunday, + name: "sunday", + label: Language.daySundayLabel, + }, ]; const handleToggleAutostart = async (e: ChangeEvent) => { @@ -441,7 +441,14 @@ export const WorkspaceScheduleForm: FC< /> - + ); };