fix: update workspace button should properly update the workspace (#4228)

* resolves #4098

* PR comments
This commit is contained in:
Kira Pilot
2022-09-27 19:23:28 -04:00
committed by GitHub
parent fedb180735
commit 65ff604969
9 changed files with 79 additions and 14 deletions
@@ -17,6 +17,7 @@ export const Language = {
delete: "Delete",
cancel: "Cancel",
update: "Update",
updating: "Updating",
// these labels are used in WorkspaceActions.tsx
starting: "Starting...",
stopping: "Stopping...",
@@ -39,6 +39,7 @@ export interface WorkspaceProps {
handleDelete: () => void
handleUpdate: () => void
handleCancel: () => void
isUpdating: boolean
workspace: TypesGen.Workspace
resources?: TypesGen.WorkspaceResource[]
builds?: TypesGen.WorkspaceBuild[]
@@ -61,6 +62,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
handleUpdate,
handleCancel,
workspace,
isUpdating,
resources,
builds,
canUpdateWorkspace,
@@ -104,6 +106,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
handleDelete={handleDelete}
handleUpdate={handleUpdate}
handleCancel={handleCancel}
isUpdating={isUpdating}
/>
</Stack>
}
@@ -16,6 +16,7 @@ const defaultArgs = {
handleDelete: action("delete"),
handleUpdate: action("update"),
handleCancel: action("cancel"),
isUpdating: false,
}
export const Starting = Template.bind({})
@@ -77,3 +78,10 @@ Errored.args = {
...defaultArgs,
workspace: Mocks.MockFailedWorkspace,
}
export const Updating = Template.bind({})
Updating.args = {
...defaultArgs,
isUpdating: true,
workspace: Mocks.MockOutdatedWorkspace,
}
@@ -14,6 +14,7 @@ const renderComponent = async (props: Partial<WorkspaceActionsProps> = {}) => {
handleDelete={jest.fn()}
handleUpdate={jest.fn()}
handleCancel={jest.fn()}
isUpdating={false}
/>,
)
}
@@ -27,6 +28,7 @@ const renderAndClick = async (props: Partial<WorkspaceActionsProps> = {}) => {
handleDelete={jest.fn()}
handleUpdate={jest.fn()}
handleCancel={jest.fn()}
isUpdating={false}
/>,
)
const trigger = await screen.findByTestId("workspace-actions-button")
@@ -27,6 +27,7 @@ export interface WorkspaceActionsProps {
handleDelete: () => void
handleUpdate: () => void
handleCancel: () => void
isUpdating: boolean
children?: ReactNode
}
@@ -37,6 +38,7 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
handleDelete,
handleUpdate,
handleCancel,
isUpdating,
}) => {
const workspaceStatus: keyof typeof WorkspaceStateEnum = getWorkspaceStatus(
workspace.latest_build,
@@ -63,6 +65,7 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
// A mapping of button type to the corresponding React component
const buttonMapping: ButtonMapping = {
[ButtonTypesEnum.update]: <UpdateButton handleAction={handleUpdate} />,
[ButtonTypesEnum.updating]: <ActionLoadingButton label={Language.updating} />,
[ButtonTypesEnum.start]: <StartButton handleAction={handleStart} />,
[ButtonTypesEnum.starting]: <ActionLoadingButton label={Language.starting} />,
[ButtonTypesEnum.stop]: <StopButton handleAction={handleStop} />,
@@ -77,7 +80,9 @@ export const WorkspaceActions: FC<WorkspaceActionsProps> = ({
return (
<DropdownButton
primaryAction={buttonMapping[actions.primary]}
primaryAction={
isUpdating ? buttonMapping[ButtonTypesEnum.updating] : buttonMapping[actions.primary]
}
canCancel={actions.canCancel}
handleCancel={handleCancel}
secondaryActions={actions.secondary.map((action) => ({
@@ -10,6 +10,7 @@ export enum ButtonTypesEnum {
delete = "delete",
deleting = "deleting",
update = "update",
updating = "updating",
// disabled buttons
canceling = "canceling",
disabled = "disabled",
@@ -157,7 +157,32 @@ describe("WorkspacePage", () => {
return res(ctx.status(200), ctx.json(MockOutdatedWorkspace))
}),
)
testButton(Language.update, getTemplateMock)
await renderWorkspacePage()
const button = await screen.findByText(Language.update, { exact: true })
fireEvent.click(button)
// getTemplate is called twice: once when the machine starts, and once after the user requests to update
expect(getTemplateMock).toBeCalledTimes(2)
})
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))
}),
)
await renderWorkspacePage()
const button = await screen.findByText(Language.update, { exact: true })
fireEvent.click(button)
await waitFor(() =>
expect(api.startWorkspace).toBeCalledWith("test-workspace", "test-template-version"),
)
})
it("shows the Stopping status when the workspace is stopping", async () => {
await testStatus(MockStoppingWorkspace, DisplayStatusLanguage.stopping)
@@ -113,6 +113,7 @@ export const WorkspacePage: FC = () => {
return canExtendDeadline(deadline, workspace, template)
},
}}
isUpdating={workspaceState.hasTag("updating")}
workspace={workspace}
handleStart={() => workspaceSend("START")}
handleStop={() => workspaceSend("STOP")}
@@ -257,7 +257,7 @@ export const workspaceMachine = createMachine(
START: "requestingStart",
STOP: "requestingStop",
ASK_DELETE: "askingDelete",
UPDATE: "requestingStartWithLatestTemplate",
UPDATE: "updatingWorkspace",
CANCEL: "requestingCancel",
},
},
@@ -271,18 +271,37 @@ export const workspaceMachine = createMachine(
},
},
},
requestingStartWithLatestTemplate: {
entry: "clearBuildError",
invoke: {
id: "startWorkspaceWithLatestTemplate",
src: "startWorkspaceWithLatestTemplate",
onDone: {
target: "idle",
actions: ["assignBuild"],
updatingWorkspace: {
tags: "updating",
initial: "refreshingTemplate",
states: {
refreshingTemplate: {
invoke: {
id: "refreshTemplate",
src: "getTemplate",
onDone: {
target: "startingWithLatestTemplate",
actions: ["assignTemplate"],
},
onError: {
target: "#workspaceState.ready.build.idle",
actions: ["assignGetTemplateWarning"],
},
},
},
onError: {
target: "idle",
actions: ["assignBuildError"],
startingWithLatestTemplate: {
invoke: {
id: "startWorkspaceWithLatestTemplate",
src: "startWorkspaceWithLatestTemplate",
onDone: {
target: "#workspaceState.ready.build.idle",
actions: ["assignBuild"],
},
onError: {
target: "#workspaceState.ready.build.idle",
actions: ["assignBuildError"],
},
},
},
},
},