Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Fix test for schedule
  • Loading branch information
BrunoQuaresma committed Mar 22, 2023
commit e17ca38c088adba42582086bae1a1227baf8cfd1
Original file line number Diff line number Diff line change
Expand Up @@ -3,111 +3,71 @@ import userEvent from "@testing-library/user-event"
import * as API from "api/api"
import { UpdateTemplateMeta } from "api/typesGenerated"
import { Language as FooterFormLanguage } from "components/FormFooter/FormFooter"
import { MockTemplate } from "../../../testHelpers/entities"
import { renderWithAuth } from "../../../testHelpers/renderHelpers"
import {
MockEntitlementsWithScheduling,
MockTemplate,
} from "../../../testHelpers/entities"
import {
renderWithTemplateSettingsLayout,
waitForLoaderToBeRemoved,
} from "../../../testHelpers/renderHelpers"
import { getValidationSchema } from "./TemplateScheduleForm"
import TemplateSchedulePage from "./TemplateSchedulePage"
import i18next from "i18next"

const { t } = i18next

const validFormValues = {
name: "Name",
display_name: "A display name",
description: "A description",
icon: "vscode.png",
// these are the form values which are actually hours
default_ttl_ms: 1,
max_ttl_ms: 2,
allow_user_cancel_workspace_jobs: false,
}

const renderTemplateSchedulePage = async () => {
renderWithAuth(<TemplateSchedulePage />, {
route: `/templates/${MockTemplate.name}/settings`,
path: `/templates/:template/settings`,
extraRoutes: [{ path: "templates/:template", element: <></> }],
renderWithTemplateSettingsLayout(<TemplateSchedulePage />, {
route: `/templates/${MockTemplate.name}/settings/schedule`,
path: `/templates/:template/settings/schedule`,
})
// Wait the form to be rendered
const label = t("nameLabel", { ns: "TemplateSchedulePage" })
await screen.findAllByLabelText(label)
await waitForLoaderToBeRemoved()
}

const fillAndSubmitForm = async ({
name,
display_name,
description,
default_ttl_ms,
max_ttl_ms,
icon,
allow_user_cancel_workspace_jobs,
}: Required<UpdateTemplateMeta>) => {
const label = t("nameLabel", { ns: "TemplateSchedulePage" })
const nameField = await screen.findByLabelText(label)
await userEvent.clear(nameField)
await userEvent.type(nameField, name)

const displayNameLabel = t("displayNameLabel", { ns: "TemplateSchedulePage" })

const displayNameField = await screen.findByLabelText(displayNameLabel)
await userEvent.clear(displayNameField)
await userEvent.type(displayNameField, display_name)

const descriptionLabel = t("descriptionLabel", { ns: "TemplateSchedulePage" })
const descriptionField = await screen.findByLabelText(descriptionLabel)
await userEvent.clear(descriptionField)
await userEvent.type(descriptionField, description)

const iconLabel = t("iconLabel", { ns: "TemplateSchedulePage" })
const iconField = await screen.findByLabelText(iconLabel)
await userEvent.clear(iconField)
await userEvent.type(iconField, icon)

const defaultTtlLabel = t("defaultTtlLabel", { ns: "TemplateSchedulePage" })
}: {
default_ttl_ms: number
max_ttl_ms: number
}) => {
const user = userEvent.setup()
const defaultTtlLabel = t("defaultTtlLabel", { ns: "templateSettingsPage" })
const defaultTtlField = await screen.findByLabelText(defaultTtlLabel)
await userEvent.clear(defaultTtlField)
await userEvent.type(defaultTtlField, default_ttl_ms.toString())

const entitlements = await API.getEntitlements()
if (entitlements.features["advanced_template_scheduling"].enabled) {
const maxTtlLabel = t("maxTtlLabel", { ns: "TemplateSchedulePage" })
const maxTtlField = await screen.findByLabelText(maxTtlLabel)
await userEvent.clear(maxTtlField)
await userEvent.type(maxTtlField, max_ttl_ms.toString())
}
await user.clear(defaultTtlField)
await user.type(defaultTtlField, default_ttl_ms.toString())

const allowCancelJobsField = screen.getByRole("checkbox")
// checkbox is checked by default, so it must be clicked to get unchecked
if (!allow_user_cancel_workspace_jobs) {
await userEvent.click(allowCancelJobsField)
}
const maxTtlLabel = t("maxTtlLabel", { ns: "templateSettingsPage" })
const maxTtlField = await screen.findByLabelText(maxTtlLabel)
await user.clear(maxTtlField)
await user.type(maxTtlField, max_ttl_ms.toString())

const submitButton = await screen.findByText(
FooterFormLanguage.defaultSubmitLabel,
)
await userEvent.click(submitButton)
await user.click(submitButton)
}

describe("TemplateSchedulePage", () => {
it("renders", async () => {
const { t } = i18next
const pageTitle = t("title", {
ns: "TemplateSchedulePage",
})
await renderTemplateSchedulePage()
const element = await screen.findByText(pageTitle)
expect(element).toBeDefined()
beforeEach(() => {
jest
.spyOn(API, "getEntitlements")
.mockResolvedValue(MockEntitlementsWithScheduling)
})

it("succeeds", async () => {
await renderTemplateSchedulePage()

jest.spyOn(API, "updateTemplateMeta").mockResolvedValueOnce({
...MockTemplate,
...validFormValues,
})
await fillAndSubmitForm(validFormValues)

await waitFor(() => expect(API.updateTemplateMeta).toBeCalledTimes(1))
})

Expand All @@ -125,11 +85,8 @@ describe("TemplateSchedulePage", () => {
expect(API.updateTemplateMeta).toBeCalledWith(
"test-template",
expect.objectContaining({
...validFormValues,
// convert from the display value (hours) to ms
default_ttl_ms: validFormValues.default_ttl_ms * 3600000,
// this value is undefined if not entitled
max_ttl_ms: undefined,
max_ttl_ms: validFormValues.max_ttl_ms * 3600000,
}),
),
)
Expand Down Expand Up @@ -160,29 +117,7 @@ describe("TemplateSchedulePage", () => {
}
const validate = () => getValidationSchema().validateSync(values)
expect(validate).toThrowError(
t("defaultTTLMaxError", { ns: "TemplateSchedulePage" }),
)
})

it("allows a description of 128 chars", () => {
const values: UpdateTemplateMeta = {
...validFormValues,
description:
"Nam quis nulla. Integer malesuada. In in enim a arcu imperdiet malesuada. Sed vel lectus. Donec odio urna, tempus molestie, port",
}
const validate = () => getValidationSchema().validateSync(values)
expect(validate).not.toThrowError()
})

it("disallows a description of 128 + 1 chars", () => {
const values: UpdateTemplateMeta = {
...validFormValues,
description:
"Nam quis nulla. Integer malesuada. In in enim a arcu imperdiet malesuada. Sed vel lectus. Donec odio urna, tempus molestie, port a",
}
const validate = () => getValidationSchema().validateSync(values)
expect(validate).toThrowError(
t("descriptionMaxError", { ns: "TemplateSchedulePage" }),
t("defaultTTLMaxError", { ns: "templateSettingsPage" }),
)
})
})
17 changes: 16 additions & 1 deletion site/src/testHelpers/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,9 +657,10 @@ export const MockWorkspace: TypesGen.Workspace = {
owner_id: MockUser.id,
owner_name: MockUser.username,
autostart_schedule: MockWorkspaceAutostartEnabled.schedule,
ttl_ms: 2 * 60 * 60 * 1000, // 2 hours as milliseconds
ttl_ms: 2 * 60 * 60 * 1000,
latest_build: MockWorkspaceBuild,
last_used_at: "",
organization_id: MockOrganization.id,
}

export const MockStoppedWorkspace: TypesGen.Workspace = {
Expand Down Expand Up @@ -1270,6 +1271,20 @@ export const MockEntitlementsWithAuditLog: TypesGen.Entitlements = {
}),
}

export const MockEntitlementsWithScheduling: TypesGen.Entitlements = {
errors: [],
warnings: [],
has_license: true,
require_telemetry: false,
trial: false,
features: withDefaultFeatures({
advanced_template_scheduling: {
enabled: true,
entitlement: "entitled",
},
}),
}

export const MockExperiments: TypesGen.Experiment[] = []

export const MockAuditLog: TypesGen.AuditLog = {
Expand Down
45 changes: 45 additions & 0 deletions site/src/testHelpers/renderHelpers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AppProviders } from "app"
import { DashboardLayout } from "components/Dashboard/DashboardLayout"
import { createMemoryHistory } from "history"
import { i18n } from "i18n"
import { TemplateSettingsLayout } from "pages/TemplateSettingsPage/TemplateSettingsLayout"
import { FC, ReactElement } from "react"
import { I18nextProvider } from "react-i18next"
import {
Expand Down Expand Up @@ -86,6 +87,50 @@ export function renderWithAuth(
}
}

export function renderWithTemplateSettingsLayout(
element: JSX.Element,
{
path = "/",
route = "/",
extraRoutes = [],
nonAuthenticatedRoutes = [],
}: RenderWithAuthOptions = {},
) {
const routes: RouteObject[] = [
{
element: <RequireAuth />,
children: [
{
element: <DashboardLayout />,
children: [
{
element: <TemplateSettingsLayout />,
children: [{ path, element }, ...extraRoutes],
},
],
},
],
},
...nonAuthenticatedRoutes,
]

const router = createMemoryRouter(routes, { initialEntries: [route] })

const renderResult = wrappedRender(
<I18nextProvider i18n={i18n}>
<AppProviders>
<RouterProvider router={router} />
</AppProviders>
</I18nextProvider>,
)

return {
user: MockUser,
router,
...renderResult,
}
}

export const waitForLoaderToBeRemoved = (): Promise<void> =>
waitForElementToBeRemoved(() => screen.getByTestId("loader"))

Expand Down
2 changes: 1 addition & 1 deletion site/src/xServices/entitlements/entitlementsXService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const entitlementsMachine = createMachine(
}),
},
services: {
getEntitlements: API.getEntitlements,
getEntitlements: () => API.getEntitlements(),
},
},
)