diff --git a/site/e2e/helpers.ts b/site/e2e/helpers.ts index 756c307ff59c9..f655b14c192e3 100644 --- a/site/e2e/helpers.ts +++ b/site/e2e/helpers.ts @@ -31,6 +31,8 @@ export const createWorkspace = async ( await page.goto("/templates/" + templateName + "/workspace", { waitUntil: "networkidle", }) + await expect(page).toHaveURL("/templates/" + templateName + "/workspace") + const name = randomName() await page.getByLabel("name").fill(name) @@ -117,7 +119,10 @@ export const createTemplate = async ( await page.addInitScript({ content: "window.playwright = true", }) + await page.goto("/templates/new", { waitUntil: "networkidle" }) + await expect(page).toHaveURL("/templates/new") + await page.getByTestId("file-upload").setInputFiles({ buffer: await createTemplateVersionTar(responses), mimeType: "application/x-tar", @@ -616,3 +621,99 @@ export const fillParameters = async ( } } } + +export const updateTemplate = async ( + page: Page, + templateName: string, + responses?: EchoProvisionerResponses, +) => { + const tarball = await createTemplateVersionTar(responses) + + const sessionToken = await findSessionToken(page) + const child = spawn( + "go", + [ + "run", + coderMainPath(), + "templates", + "push", + "--test.provisioner", + "echo", + "-y", + "-d", + "-", + templateName, + ], + { + env: { + ...process.env, + CODER_SESSION_TOKEN: sessionToken, + CODER_URL: "http://localhost:3000", + }, + }, + ) + + const uploaded = new Awaiter() + child.on("exit", (code) => { + if (code === 0) { + uploaded.done() + return + } + + throw new Error(`coder templates push failed with code ${code}`) + }) + + child.stdin.write(tarball) + child.stdin.end() + + await uploaded.wait() +} + +export const updateWorkspace = async ( + page: Page, + workspaceName: string, + richParameters: RichParameter[] = [], + buildParameters: WorkspaceBuildParameter[] = [], +) => { + await page.goto("/@admin/" + workspaceName, { + waitUntil: "domcontentloaded", + }) + await expect(page).toHaveURL("/@admin/" + workspaceName) + + await page.getByTestId("workspace-update-button").click() + await page.getByTestId("confirm-button").click() + + await fillParameters(page, richParameters, buildParameters) + await page.getByTestId("form-submit").click() + + await page.waitForSelector( + "span[data-testid='build-status'] >> text=Running", + { + state: "visible", + }, + ) +} + +export const updateWorkspaceParameters = async ( + page: Page, + workspaceName: string, + richParameters: RichParameter[] = [], + buildParameters: WorkspaceBuildParameter[] = [], +) => { + await page.goto("/@admin/" + workspaceName + "/settings/parameters", { + waitUntil: "domcontentloaded", + }) + await expect(page).toHaveURL( + "/@admin/" + workspaceName + "/settings/parameters", + ) + + await fillParameters(page, richParameters, buildParameters) + await page.getByTestId("form-submit").click() + + await page.waitForSelector( + "span[data-testid='build-status'] >> text=Running", + { + state: "visible", + }, + ) +} diff --git a/site/e2e/playwright.config.ts b/site/e2e/playwright.config.ts index 78eb4495ce371..b25393e529e4a 100644 --- a/site/e2e/playwright.config.ts +++ b/site/e2e/playwright.config.ts @@ -41,6 +41,7 @@ export default defineConfig({ `--access-url=http://localhost:${port} ` + `--http-address=localhost:${port} ` + `--in-memory --telemetry=false ` + + `--dangerous-disable-rate-limits ` + `--provisioner-daemons 10 ` + `--provisioner-daemons-echo ` + `--provisioner-daemon-poll-interval 50ms`, diff --git a/site/e2e/tests/createWorkspace.spec.ts b/site/e2e/tests/createWorkspace.spec.ts index 1effc01976651..8a4937ee019ea 100644 --- a/site/e2e/tests/createWorkspace.spec.ts +++ b/site/e2e/tests/createWorkspace.spec.ts @@ -1,4 +1,4 @@ -import { test } from "@playwright/test" +import { test, Page } from "@playwright/test" import { createTemplate, createWorkspace, @@ -17,6 +17,11 @@ import { } from "../parameters" import { RichParameter } from "../provisionerGenerated" +test.beforeEach(async ({ page }: { page: Page }) => { + // eslint-disable-next-line no-console -- For debugging purposes + page.on("console", (msg) => console.log("Console: " + msg.text())) +}) + test("create workspace", async ({ page }) => { const template = await createTemplate(page, { apply: [ @@ -107,6 +112,7 @@ test("create workspace and overwrite default parameters", async ({ page }) => { page, echoResponsesWithParameters(richParameters), ) + const workspaceName = await createWorkspace( page, template, diff --git a/site/e2e/tests/updateWorkspace.spec.ts b/site/e2e/tests/updateWorkspace.spec.ts new file mode 100644 index 0000000000000..ef84f19ba4ee0 --- /dev/null +++ b/site/e2e/tests/updateWorkspace.spec.ts @@ -0,0 +1,136 @@ +import { test, Page } from "@playwright/test" + +import { + createTemplate, + createWorkspace, + echoResponsesWithParameters, + updateTemplate, + updateWorkspace, + updateWorkspaceParameters, + verifyParameters, +} from "../helpers" + +import { + fifthParameter, + firstParameter, + secondParameter, + sixthParameter, + secondBuildOption, +} from "../parameters" +import { RichParameter } from "../provisionerGenerated" + +test.beforeEach(async ({ page }: { page: Page }) => { + // eslint-disable-next-line no-console -- For debugging purposes + page.on("console", (msg) => console.log("Console: " + msg.text())) +}) + +test("update workspace, new optional, immutable parameter added", async ({ + page, +}) => { + const richParameters: RichParameter[] = [firstParameter, secondParameter] + const template = await createTemplate( + page, + echoResponsesWithParameters(richParameters), + ) + + const workspaceName = await createWorkspace(page, template) + + // Verify that parameter values are default. + await verifyParameters(page, workspaceName, richParameters, [ + { name: firstParameter.name, value: firstParameter.defaultValue }, + { name: secondParameter.name, value: secondParameter.defaultValue }, + ]) + + // Push updated template. + const updatedRichParameters = [...richParameters, fifthParameter] + await updateTemplate( + page, + template, + echoResponsesWithParameters(updatedRichParameters), + ) + + // Now, update the workspace, and select the value for immutable parameter. + await updateWorkspace(page, workspaceName, updatedRichParameters, [ + { name: fifthParameter.name, value: fifthParameter.options[0].value }, + ]) + + // Verify parameter values. + await verifyParameters(page, workspaceName, updatedRichParameters, [ + { name: firstParameter.name, value: firstParameter.defaultValue }, + { name: secondParameter.name, value: secondParameter.defaultValue }, + { name: fifthParameter.name, value: fifthParameter.options[0].value }, + ]) +}) + +test("update workspace, new required, mutable parameter added", async ({ + page, +}) => { + const richParameters: RichParameter[] = [firstParameter, secondParameter] + const template = await createTemplate( + page, + echoResponsesWithParameters(richParameters), + ) + + const workspaceName = await createWorkspace(page, template) + + // Verify that parameter values are default. + await verifyParameters(page, workspaceName, richParameters, [ + { name: firstParameter.name, value: firstParameter.defaultValue }, + { name: secondParameter.name, value: secondParameter.defaultValue }, + ]) + + // Push updated template. + const updatedRichParameters = [...richParameters, sixthParameter] + await updateTemplate( + page, + template, + echoResponsesWithParameters(updatedRichParameters), + ) + + // Now, update the workspace, and provide the parameter value. + const buildParameters = [{ name: sixthParameter.name, value: "99" }] + await updateWorkspace( + page, + workspaceName, + updatedRichParameters, + buildParameters, + ) + + // Verify parameter values. + await verifyParameters(page, workspaceName, updatedRichParameters, [ + { name: firstParameter.name, value: firstParameter.defaultValue }, + { name: secondParameter.name, value: secondParameter.defaultValue }, + ...buildParameters, + ]) +}) + +test("update workspace with ephemeral parameter enabled", async ({ page }) => { + const richParameters: RichParameter[] = [firstParameter, secondBuildOption] + const template = await createTemplate( + page, + echoResponsesWithParameters(richParameters), + ) + + const workspaceName = await createWorkspace(page, template) + + // Verify that parameter values are default. + await verifyParameters(page, workspaceName, richParameters, [ + { name: firstParameter.name, value: firstParameter.defaultValue }, + { name: secondBuildOption.name, value: secondBuildOption.defaultValue }, + ]) + + // Now, update the workspace, and select the value for ephemeral parameter. + const buildParameters = [{ name: secondBuildOption.name, value: "true" }] + await updateWorkspaceParameters( + page, + workspaceName, + richParameters, + buildParameters, + ) + + // Verify that parameter values are default. + await verifyParameters(page, workspaceName, richParameters, [ + { name: firstParameter.name, value: firstParameter.defaultValue }, + { name: secondBuildOption.name, value: secondBuildOption.defaultValue }, + ]) +}) diff --git a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx index 860969267fb7e..2293fb530ffb3 100644 --- a/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx +++ b/site/src/pages/WorkspacePage/UpdateBuildParametersDialog.tsx @@ -111,7 +111,13 @@ export const UpdateBuildParametersDialog: FC< -