8000 feat(site): Ask for parameter values when update a workspace by BrunoQuaresma · Pull Request #6586 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat(site): Ask for parameter values when update a workspace #6586

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 14, 2023
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
Fix tests
  • Loading branch information
BrunoQuaresma committed Mar 14, 2023
commit c3351484f186fa57cdc0557973a34c4a5ea43c11
54 changes: 45 additions & 9 deletions site/src/api/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import axios from "axios"
import { getApiKey, getURLWithSearchParams, login, logout } from "./api"
import {
MockTemplate,
MockTemplateVersionParameter1,
MockWorkspace,
MockWorkspaceBuild,
} from "testHelpers/entities"
import * as api from "./api"
import * as TypesGen from "./typesGenerated"

describe("api.ts", () => {
Expand All @@ -12,7 +18,7 @@ describe("api.ts", () => {
jest.spyOn(axios, "post").mockResolvedValueOnce({ data: loginResponse })

// when
const result = await login("test", "123")
const result = await api.login("test", "123")

// then
expect(axios.post).toHaveBeenCalled()
Expand All @@ -33,7 +39,7 @@ describe("api.ts", () => {
axios.post = axiosMockPost

try {
await login("test", "123")
await api.login("test", "123")
} catch (error) {
expect(error).toStrictEqual(expectedError)
}
Expand All @@ -49,7 +55,7 @@ describe("api.ts", () => {
axios.post = axiosMockPost

// when
await logout()
await api.logout()

// then
expect(axiosMockPost).toHaveBeenCalled()
Expand All @@ -68,7 +74,7 @@ describe("api.ts", () => {
axios.post = axiosMockPost

try {
await logout()
await api.logout()
} catch (error) {
expect(error).toStrictEqual(expectedError)
}
Expand All @@ -87,7 +93,7 @@ describe("api.ts", () => {
axios.post = axiosMockPost

// when
const result = await getApiKey()
const result = await api.getApiKey()

// then
expect(axiosMockPost).toHaveBeenCalled()
Expand All @@ -107,7 +113,7 @@ describe("api.ts", () => {
axios.post = axiosMockPost

try {
await getApiKey()
await api.getApiKey()
} catch (error) {
expect(error).toStrictEqual(expectedError)
}
Expand All @@ -133,7 +139,7 @@ describe("api.ts", () => {
])(
`Workspaces - getURLWithSearchParams(%p, %p) returns %p`,
(basePath, filter, expected) => {
expect(getURLWithSearchParams(basePath, filter)).toBe(expected)
expect(api.getURLWithSearchParams(basePath, filter)).toBe(expected)
},
)
})
Expand All @@ -150,8 +156,38 @@ describe("api.ts", () => {
])(
`Users - getURLWithSearchParams(%p, %p) returns %p`,
(basePath, filter, expected) => {
expect(getURLWithSearchParams(basePath, filter)).toBe(expected)
expect(api.getURLWithSearchParams(basePath, filter)).toBe(expected)
},
)
})

describe("update", () => {
it("creates a build with start and the latest template", async () => {
jest
.spyOn(api, "postWorkspaceBuild")
.mockResolvedValueOnce(MockWorkspaceBuild)
jest.spyOn(api, "getTemplate").mockResolvedValueOnce(MockTemplate)
await api.updateWorkspace(MockWorkspace)
expect(api.postWorkspaceBuild).toHaveBeenCalledWith(MockWorkspace.id, {
transition: "start",
template_version_id: MockTemplate.active_version_id,
rich_parameter_values: [],
})
})

it("fails when having missing parameters", async () => {
jest
.spyOn(api, "postWorkspaceBuild")
.mockResolvedValueOnce(MockWorkspaceBuild)
jest.spyOn(api, "getTemplate").mockResolvedValueOnce(MockTemplate)
jest.spyOn(api, "getWorkspaceBuildParameters").mockResolvedValueOnce([])
jest
.spyOn(api, "getTemplateVersionRichParameters")
.mockResolvedValueOnce([MockTemplateVersionParameter1])

await expect(api.updateWorkspace(MockWorkspace)).rejects.toThrow(
api.MissingBuildParameters,
)
})
})
})
1 change: 0 additions & 1 deletion site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -932,7 +932,6 @@ export const updateWorkspace = async (
newBuildParameters,
templateParameters,
)

if (missingParameters.length > 0) {
throw new MissingBuildParameters(missingParameters)
}
Expand Down
193 changes: 83 additions & 110 deletions site/src/pages/WorkspacePage/WorkspacePage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,52 +173,79 @@ describe("WorkspacePage", () => {

expect(cancelWorkspaceMock).toBeCalled()
})
it("requests a template when the user presses Update", async () => {
const getTemplateMock = jest
.spyOn(api, "getTemplate")
.mockResolvedValueOnce(MockTemplate)
server.use(
rest.get(
`/api/v2/users/:userId/workspace/:workspaceName`,
(req, res, ctx) => {
return res(ctx.status(200), ctx.json(MockOutdatedWorkspace))
},
),
)

await renderWorkspacePage()
const buttonText = t("actionButton.update", { ns: "workspacePage" })
const button = await screen.findByText(buttonText, { exact: true })
await userEvent.setup().click(button)
it("requests an update when the user presses Update", async () => {
jest
.spyOn(api, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce(MockOutdatedWorkspace)
const updateWorkspaceMock = jest
.spyOn(api, "updateWorkspace")
.mockResolvedValueOnce(MockWorkspaceBuild)

// getTemplate is called twice: once when the machine starts, and once after the user requests to update
expect(getTemplateMock).toBeCalledTimes(2)
await testButton(
t("actionButton.update", { ns: "workspacePage" }),
updateWorkspaceMock,
)
})
it("after an update postWorkspaceBuild is called with the latest template active version id", async () => {
jest.spyOn(api, "getTemplate").mockResolvedValueOnce(MockTemplate) // active_version_id = "test-template-version"
jest.spyOn(api, "startWorkspace").mockResolvedValueOnce({
...MockWorkspaceBuild,
})

server.use(
rest.get(
`/api/v2/users/:userId/workspace/:workspaceName`,
(req, res, ctx) => {
return res(ctx.status(200), ctx.json(MockOutdatedWorkspace))
},
),
it("updates the parameters when they are missing during update", async () => {
// Setup mocks
const user = userEvent.setup()
jest
.spyOn(api, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce(MockOutdatedWorkspace)
const updateWorkspaceSpy = jest
.spyOn(api, "updateWorkspace")
.mockRejectedValueOnce(
new api.MissingBuildParameters([
MockTemplateVersionParameter1,
MockTemplateVersionParameter2,
]),
)
// Render page and wait for it to be loaded
renderWithAuth(<WorkspacePage />, {
route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}`,
path: "/@:username/:workspace",
})
await waitForLoaderToBeRemoved()
// Click on the update button
const workspaceActions = screen.getByTestId("workspace-actions")
await user.click(
within(workspaceActions).getByRole("button", { name: "Update" }),
)
await renderWorkspacePage()
const buttonText = t("actionButton.update", { ns: "workspacePage" })
const button = await screen.findByText(buttonText, { exact: true })
await userEvent.setup().click(button)

await waitFor(() =>
expect(api.startWorkspace).toBeCalledWith(
"test-outdated-workspace",
"test-template-version",
),
await waitFor(() => {
expect(api.updateWorkspace).toBeCalled()
// We want to clear this mock to use it later
updateWorkspaceSpy.mockClear()
})
// Fill the parameters and send the form
const dialog = await screen.findByTestId("dialog")
const firstParameterInput = within(dialog).getByLabelText(
MockTemplateVersionParameter1.name,
{ exact: false },
)
await user.clear(firstParameterInput)
await user.type(firstParameterInput, "some-value")
const secondParameterInput = within(dialog).getByLabelText(
MockTemplateVersionParameter2.name,
{ exact: false },
)
await user.clear(secondParameterInput)
await user.type(secondParameterInput, "2")
await user.click(within(dialog).getByRole("button", { name: "Update" }))
// Check if the update was called using the values from the form
await waitFor(() => {
expect(api.updateWorkspace).toBeCalledWith(MockOutdatedWorkspace, [
{
name: MockTemplateVersionParameter1.name,
value: "some-value",
},
{
name: MockTemplateVersionParameter2.name,
value: "2",
},
])
})
})

it("shows the Stopping status when the workspace is stopping", async () => {
Expand All @@ -227,126 +254,72 @@ describe("WorkspacePage", () => {
t("workspaceStatus.stopping", { ns: "common" }),
)
})

it("shows the Stopped status when the workspace is stopped", async () => {
await testStatus(
MockStoppedWorkspace,
t("workspaceStatus.stopped", { ns: "common" }),
)
})

it("shows the Building status when the workspace is starting", async () => {
await testStatus(
MockStartingWorkspace,
t("workspaceStatus.starting", { ns: "common" }),
)
})

it("shows the Running status when the workspace is running", async () => {
await testStatus(
MockWorkspace,
t("workspaceStatus.running", { ns: "common" }),
)
})

it("shows the Failed status when the workspace is failed or canceled", async () => {
await testStatus(
MockFailedWorkspace,
t("workspaceStatus.failed", { ns: "common" }),
)
})

it("shows the Canceling status when the workspace is canceling", async () => {
await testStatus(
MockCancelingWorkspace,
t("workspaceStatus.canceling", { ns: "common" }),
)
})

it("shows the Canceled status when the workspace is canceling", async () => {
await testStatus(
MockCanceledWorkspace,
t("workspaceStatus.canceled", { ns: "common" }),
)
})

it("shows the Deleting status when the workspace is deleting", async () => {
await testStatus(
MockDeletingWorkspace,
t("workspaceStatus.deleting", { ns: "common" }),
)
})

it("shows the Deleted status when the workspace is deleted", async () => {
await testStatus(
MockDeletedWorkspace,
t("workspaceStatus.deleted", { ns: "common" }),
)
})

describe("Timeline", () => {
it("shows the timeline build", async () => {
await renderWorkspacePage()
const table = await screen.findByTestId("builds-table")

// Wait for the results to be loaded
await waitFor(async () => {
const rows = table.querySelectorAll("tbody > tr")
// Added +1 because of the date row
expect(rows).toHaveLength(MockBuilds.length + 1)
})
})
})
it("shows the timeline build", async () => {
await renderWorkspacePage()
const table = await screen.findByTestId("builds-table")

it("Workspace update when having new parameters on the template version", async () => {
// Setup mocks
const user = userEvent.setup()
jest
.spyOn(api, "getWorkspaceByOwnerAndName")
.mockResolvedValueOnce(MockOutdatedWorkspace)
const updateWorkspaceSpy = jest
.spyOn(api, "updateWorkspace")
.mockRejectedValueOnce(
new api.MissingBuildParameters([
MockTemplateVersionParameter1,
MockTemplateVersionParameter2,
]),
)
// Render page and wait for it to be loaded
renderWithAuth(<WorkspacePage />, {
route: `/@${MockWorkspace.owner_name}/${MockWorkspace.name}`,
path: "/@:username/:workspace",
})
await waitForLoaderToBeRemoved()
// Click on the update button
const workspaceActions = screen.getByTestId("workspace-actions")
await user.click(
within(workspaceActions).getByRole("button", { name: "Update" }),
)
await waitFor(() => {
expect(api.updateWorkspace).toBeCalled()
// We want to clear this mock to use it later
updateWorkspaceSpy.mockClear()
})
// Fill the parameters and send the form
const dialog = await screen.findByTestId("dialog")
const firstParameterInput = within(dialog).getByLabelText(
MockTemplateVersionParameter1.name,
{ exact: false },
)
await user.clear(firstParameterInput)
await user.type(firstParameterInput, "some-value")
const secondParameterInput = within(dialog).getByLabelText(
MockTemplateVersionParameter2.name,
{ exact: false },
)
await user.clear(secondParameterInput)
await user.type(secondParameterInput, "2")
await user.click(within(dialog).getByRole("button", { name: "Update" }))
// Check if the update was called using the values from the form
await waitFor(() => {
expect(api.updateWorkspace).toBeCalledWith(MockOutdatedWorkspace, [
{
name: MockTemplateVersionParameter1.name,
value: "some-value",
},
{
name: MockTemplateVersionParameter2.name,
value: "2",
},
])
// Wait for the results to be loaded
await waitFor(async () => {
const rows = table.querySelectorAll("tbody > tr")
// Added +1 because of the date row
expect(rows).toHaveLength(MockBuilds.length + 1)
})
})
})
0