Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,20 @@ export const DefaultTTLHelperText = (props: { ttl?: number }) => {
export const ActivityBumpHelperText = (props: {
bump?: number;
defaultTTL?: number;
allowUserAutostop?: boolean;
}) => {
const { bump = 0, defaultTTL = 0 } = props;
const { bump = 0, defaultTTL = 0, allowUserAutostop = false } = props;

if (!defaultTTL) {
// Activity bump extends a workspace's scheduled stop time. If there is no
// default TTL AND users cannot set their own autostop, there is no stop
// time to bump, so the field has no effect.
if (!defaultTTL && !allowUserAutostop) {
return (
<span>
Activity bump only applies when a default TTL is configured. Set a
default TTL above to enable activity bumping.
Activity bump only applies when "Default autostop" is configured or
users are allowed to customize autostop. Set "Default autostop" above or
check "Allow users to customize autostop duration for workspaces" below
to enable activity bumping.
</span>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,12 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
default_ttl_ms: form.values.default_ttl_ms
? form.values.default_ttl_ms * MS_HOUR_CONVERSION
: undefined,
// Activity bump has no effect without a default TTL, so
// discard any stale value when default autostop is off.
// Activity bump has no effect without a scheduled stop time, so
// discard any stale value when there is no default TTL AND users
// cannot customize autostop on their workspaces.
activity_bump_ms:
form.values.default_ttl_ms && form.values.activity_bump_ms
(form.values.default_ttl_ms || form.values.allow_user_autostop) &&
form.values.activity_bump_ms
? form.values.activity_bump_ms * MS_HOUR_CONVERSION
: undefined,
failure_ttl_ms: form.values.failure_ttl_ms,
Expand Down Expand Up @@ -307,10 +309,14 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
<ActivityBumpHelperText
bump={form.values.activity_bump_ms}
defaultTTL={form.values.default_ttl_ms}
allowUserAutostop={form.values.allow_user_autostop}
/>
),
})}
disabled={isSubmitting || !form.values.default_ttl_ms}
disabled={
isSubmitting ||
(!form.values.default_ttl_ms && !form.values.allow_user_autostop)
}
fullWidth
inputProps={{ min: 0, step: 1 }}
label="Activity bump (hours)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,64 +349,4 @@ describe("TemplateSchedulePage", () => {
const validate = () => getValidationSchema().validateSync(values);
expect(validate).toThrowError();
});

it("disables activity bump field when default TTL is 0", async () => {
await renderTemplateSchedulePage();
const user = userEvent.setup();

const defaultTtlField = await screen.findByLabelText(
"Default autostop (hours)",
);
const activityBumpField = screen.getByLabelText("Activity bump (hours)");

// Activity bump should be enabled when default TTL is non-zero.
expect(activityBumpField).not.toBeDisabled();

// Set default TTL to 0.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "0");

// Activity bump should now be disabled.
expect(activityBumpField).toBeDisabled();
});

it("shows helper text on activity bump when default TTL is 0", async () => {
await renderTemplateSchedulePage();
const user = userEvent.setup();

const defaultTtlField = await screen.findByLabelText(
"Default autostop (hours)",
);

// Set default TTL to 0.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "0");

// Should show the explanatory helper text.
expect(
screen.getByText(
/activity bump only applies when a default TTL is configured/i,
),
).toBeInTheDocument();
});

it("re-enables activity bump field when default TTL is set back to non-zero", async () => {
await renderTemplateSchedulePage();
const user = userEvent.setup();

const defaultTtlField = await screen.findByLabelText(
"Default autostop (hours)",
);
const activityBumpField = screen.getByLabelText("Activity bump (hours)");

// Set default TTL to 0.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "0");
expect(activityBumpField).toBeDisabled();

// Set default TTL back to a non-zero value.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "8");
expect(activityBumpField).not.toBeDisabled();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export const SubmitClearsActivityBumpWhenDefaultTTLIsZero: Story = {
// Start with a non-zero activity bump so we can verify
// it gets discarded when default TTL is set to 0.
activity_bump_ms: 3 * 60 * 60 * 1000,
// Disable user autostop so the guard applies (activity bump
// remains editable when either default TTL or user autostop is
// enabled).
allow_user_autostop: false,
},
onSubmit: fn(),
},
Expand All @@ -70,6 +74,13 @@ export const SubmitClearsActivityBumpWhenDefaultTTLIsZero: Story = {

await expect(activityBumpField).toBeDisabled();

// Helper text explains why the field is disabled.
await expect(
canvas.getByText(
/activity bump only applies when "default autostop" is configured or users are allowed to customize autostop/i,
),
).toBeInTheDocument();

const submitButton = canvas.getByRole("button", { name: /save/i });
await user.click(submitButton);

Expand All @@ -82,3 +93,105 @@ export const SubmitClearsActivityBumpWhenDefaultTTLIsZero: Story = {
});
},
};

export const SubmitPreservesActivityBumpWhenAllowUserAutostopIsEnabled: Story =
{
args: {
...defaultArgs,
template: {
...MockTemplate,
activity_bump_ms: 3 * 60 * 60 * 1000,
// Users can customize autostop on their workspaces, so activity
// bump still has meaning even without a default TTL.
allow_user_autostop: true,
},
onSubmit: fn(),
},
play: async ({ canvasElement, args }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();

const defaultTtlField = await canvas.findByLabelText(
"Default autostop (hours)",
);
const activityBumpField = canvas.getByLabelText("Activity bump (hours)");

await user.clear(defaultTtlField);
await user.type(defaultTtlField, "0");

// Activity bump stays enabled because allow_user_autostop is on.
await expect(activityBumpField).not.toBeDisabled();

const submitButton = canvas.getByRole("button", { name: /save/i });
await user.click(submitButton);

await waitFor(() => {
expect(args.onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
activity_bump_ms: 3 * 60 * 60 * 1000,
}),
);
});
},
};

export const ReEnablesActivityBumpWhenDefaultTTLIsSetBack: Story = {
args: {
...defaultArgs,
template: {
...MockTemplate,
allow_user_autostop: false,
},
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();

const defaultTtlField = await canvas.findByLabelText(
"Default autostop (hours)",
);
const activityBumpField = canvas.getByLabelText("Activity bump (hours)");

// Guard fires: default TTL is 0 and user autostop is off.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "0");
await expect(activityBumpField).toBeDisabled();

// Setting default TTL back to a non-zero value re-enables the field.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "8");
await expect(activityBumpField).not.toBeDisabled();
},
};

export const ReEnablesActivityBumpWhenAllowUserAutostopIsToggledOn: Story = {
args: {
...defaultArgs,
template: {
...MockTemplate,
allow_user_autostop: false,
},
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const user = userEvent.setup();

const defaultTtlField = await canvas.findByLabelText(
"Default autostop (hours)",
);
const activityBumpField = canvas.getByLabelText("Activity bump (hours)");
const allowUserAutostopCheckbox = canvas.getByRole("checkbox", {
name: /allow users to customize autostop/i,
});

// Guard fires: default TTL is 0 and user autostop is off.
await user.clear(defaultTtlField);
await user.type(defaultTtlField, "0");
await expect(activityBumpField).toBeDisabled();

// Toggling user autostop back on re-enables the field without
// touching the default TTL.
await user.click(allowUserAutostopCheckbox);
await expect(activityBumpField).not.toBeDisabled();
},
};
Loading