Skip to content

Commit b3f6b8c

Browse files
committed
Fix tests
1 parent 87fee06 commit b3f6b8c

File tree

2 files changed

+123
-53
lines changed

2 files changed

+123
-53
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,56 @@
11
import { screen } from "@testing-library/react"
22
import { rest } from "msw"
33
import React from "react"
4+
import * as api from "../../api/api"
5+
import { Template, Workspace, WorkspaceBuild } from "../../api/typesGenerated"
6+
import { Language } from "../../components/WorkspaceStatusBar/WorkspaceStatusBar"
47
import {
8+
MockCancelingWorkspace,
9+
MockDeletedWorkspace,
10+
MockDeletingWorkspace,
511
MockFailedWorkspace,
612
MockOutdatedWorkspace,
13+
MockStartingWorkspace,
714
MockStoppedWorkspace,
15+
MockStoppingWorkspace,
816
MockTemplate,
917
MockWorkspace,
18+
MockWorkspaceBuild,
1019
renderWithAuth,
1120
} from "../../testHelpers/renderHelpers"
1221
import { server } from "../../testHelpers/server"
1322
import { WorkspacePage } from "./WorkspacePage"
1423

24+
/**
25+
* Requests and responses related to workspace status are unrelated, so we can't test in the usual way.
26+
* Instead, test that button clicks produce the correct requests and that responses produce the correct UI.
27+
* We don't need to test the UI exhaustively because Storybook does that; just enough to prove that the
28+
* workspaceStatus was calculated correctly.
29+
*/
30+
31+
const testButton = async (
32+
label: string,
33+
mock:
34+
| jest.SpyInstance<Promise<WorkspaceBuild>, [workspaceId: string, templateVersionId?: string | undefined]>
35+
| jest.SpyInstance<Promise<Template>, [templateId: string]>,
36+
) => {
37+
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
38+
const button = await screen.findByText(label)
39+
button.click()
40+
expect(mock).toHaveBeenCalled()
41+
}
42+
43+
const testStatus = async (mock: Workspace, label: string) => {
44+
server.use(
45+
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
46+
return res(ctx.status(200), ctx.json(mock))
47+
}),
48+
)
49+
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
50+
const status = await screen.findByRole("status")
51+
expect(status).toHaveTextContent(label)
52+
}
53+
1554
describe("Workspace Page", () => {
1655
it("shows a workspace", async () => {
1756
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
@@ -25,54 +64,66 @@ describe("Workspace Page", () => {
2564
const status = await screen.findByRole("status")
2665
expect(status).toHaveTextContent("Running")
2766
})
28-
it("stops the workspace when the user presses Stop", async () => {
29-
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
30-
const status = await screen.findByText("Running")
31-
expect(status).toBeDefined()
32-
const stopButton = await screen.findByText("Stop")
33-
stopButton.click()
34-
const laterStatus = await screen.findByText("Stopping")
35-
expect(laterStatus).toBeDefined()
67+
it("requests a stop job when the user presses Stop", async () => {
68+
const stopWorkspaceMock = jest
69+
.spyOn(api, "stopWorkspace")
70+
.mockImplementation(() => Promise.resolve(MockWorkspaceBuild))
71+
testButton(Language.start, stopWorkspaceMock)
72+
}),
73+
it("requests a start job when the user presses Start", async () => {
74+
const startWorkspaceMock = jest
75+
.spyOn(api, "startWorkspace")
76+
.mockImplementation(() => Promise.resolve(MockWorkspaceBuild))
77+
testButton(Language.start, startWorkspaceMock)
78+
}),
79+
it("requests a start job when the user presses Retry after trying to start", async () => {
80+
const startWorkspaceMock = jest
81+
.spyOn(api, "startWorkspace")
82+
.mockImplementation(() => Promise.resolve(MockWorkspaceBuild))
83+
testButton(Language.retry, startWorkspaceMock)
84+
}),
85+
it("requests a stop job when the user presses Retry after trying to stop", async () => {
86+
const stopWorkspaceMock = jest
87+
.spyOn(api, "stopWorkspace")
88+
.mockImplementation(() => Promise.resolve(MockWorkspaceBuild))
89+
server.use(
90+
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
91+
return res(ctx.status(200), ctx.json(MockStoppedWorkspace))
92+
}),
93+
)
94+
testButton(Language.start, stopWorkspaceMock)
95+
}),
96+
it("requests a template when the user presses Update", async () => {
97+
const getTemplateMock = jest.spyOn(api, "getTemplate").mockImplementation(() => Promise.resolve(MockTemplate))
98+
server.use(
99+
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
100+
return res(ctx.status(200), ctx.json(MockOutdatedWorkspace))
101+
}),
102+
)
103+
testButton(Language.update, getTemplateMock)
104+
}),
105+
it("shows the Stopping status when the workspace is stopping", async () => {
106+
testStatus(MockStoppingWorkspace, Language.stopping)
107+
})
108+
it("shows the Stopped status when the workspace is stopped", async () => {
109+
testStatus(MockStoppedWorkspace, Language.stopped)
36110
})
37-
it("starts the workspace when the user presses Start", async () => {
38-
server.use(
39-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
40-
return res(ctx.status(200), ctx.json(MockStoppedWorkspace))
41-
}),
42-
)
43-
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
44-
const startButton = await screen.findByText("Start")
45-
const status = await screen.findByText("Stopped")
46-
expect(status).toBeDefined()
47-
startButton.click()
48-
const laterStatus = await screen.findByText("Building")
49-
expect(laterStatus).toBeDefined()
111+
it("shows the Building status when the workspace is starting", async () => {
112+
testStatus(MockStartingWorkspace, Language.starting)
50113
})
51-
it("retries starting the workspace when the user presses Retry", async () => {
52-
// MockFailedWorkspace.latest_build.transition is start so Retry will attempt to start
53-
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
54-
server.use(
55-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
56-
return res(ctx.status(200), ctx.json(MockFailedWorkspace))
57-
}),
58-
)
59-
const status = await screen.findByText("Build Failed")
60-
expect(status).toBeDefined()
61-
const retryButton = await screen.findByText("Retry")
62-
retryButton.click()
63-
const laterStatus = await screen.findByText("Building")
64-
expect(laterStatus).toBeDefined()
114+
it("shows the Running status when the workspace is started", async () => {
115+
testStatus(MockWorkspace, Language.started)
65116
})
66-
it("restarts the workspace when the user presses Update", async () => {
67-
renderWithAuth(<WorkspacePage />, { route: `/workspaces/${MockWorkspace.id}`, path: "/workspaces/:workspace" })
68-
server.use(
69-
rest.get(`/api/v2/workspaces/${MockWorkspace.id}`, (req, res, ctx) => {
70-
return res(ctx.status(200), ctx.json(MockOutdatedWorkspace))
71-
}),
72-
)
73-
const updateButton = await screen.findByText("Update")
74-
updateButton.click()
75-
const status = await screen.findByText("Building")
76-
expect(status).toBeDefined()
117+
it("shows the Error status when the workspace is failed or canceled", async () => {
118+
testStatus(MockFailedWorkspace, Language.error)
119+
})
120+
it("shows the Loading status when the workspace is canceling", async () => {
121+
testStatus(MockCancelingWorkspace, Language.canceling)
122+
})
123+
it("shows the Deleting status when the workspace is deleting", async () => {
124+
testStatus(MockDeletingWorkspace, Language.canceling)
125+
})
126+
it("shows the Deleted status when the workspace is deleted", async () => {
127+
testStatus(MockDeletedWorkspace, Language.canceling)
77128
})
78129
})

site/src/testHelpers/entities.ts

+26-7
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ export const MockProvisionerJob: TypesGen.ProvisionerJob = {
7272
}
7373

7474
export const MockFailedProvisionerJob = { ...MockProvisionerJob, status: "failed" as TypesGen.ProvisionerJobStatus }
75+
export const MockCancelingProvisionerJob = {
76+
...MockProvisionerJob,
77+
status: "canceling" as TypesGen.ProvisionerJobStatus,
78+
}
79+
export const MockRunningProvisionerJob = { ...MockProvisionerJob, status: "running" as TypesGen.ProvisionerJobStatus }
7580

7681
export const MockTemplate: TypesGen.Template = {
7782
id: "test-template",
@@ -117,11 +122,6 @@ export const MockWorkspaceBuild: TypesGen.WorkspaceBuild = {
117122
workspace_id: "test-workspace",
118123
}
119124

120-
export const MockFailedWorkspaceBuild = {
121-
...MockWorkspaceBuild,
122-
job: MockFailedProvisionerJob,
123-
}
124-
125125
export const MockWorkspaceBuildStop = {
126126
...MockWorkspaceBuild,
127127
transition: "stop",
@@ -147,8 +147,27 @@ export const MockWorkspace: TypesGen.Workspace = {
147147
}
148148

149149
export const MockStoppedWorkspace: TypesGen.Workspace = { ...MockWorkspace, latest_build: MockWorkspaceBuildStop }
150-
151-
export const MockFailedWorkspace: TypesGen.Workspace = { ...MockWorkspace, latest_build: MockFailedWorkspaceBuild }
150+
export const MockStoppingWorkspace: TypesGen.Workspace = {
151+
...MockWorkspace,
152+
latest_build: { ...MockWorkspaceBuildStop, job: MockRunningProvisionerJob },
153+
}
154+
export const MockStartingWorkspace: TypesGen.Workspace = {
155+
...MockWorkspace,
156+
latest_build: { ...MockWorkspaceBuild, job: MockRunningProvisionerJob },
157+
}
158+
export const MockCancelingWorkspace: TypesGen.Workspace = {
159+
...MockWorkspace,
160+
latest_build: { ...MockWorkspaceBuild, job: MockCancelingProvisionerJob },
161+
}
162+
export const MockFailedWorkspace: TypesGen.Workspace = {
163+
...MockWorkspace,
164+
latest_build: { ...MockWorkspaceBuild, job: MockFailedProvisionerJob },
165+
}
166+
export const MockDeletingWorkspace: TypesGen.Workspace = {
167+
...MockWorkspace,
168+
latest_build: { ...MockWorkspaceBuildDelete, job: MockRunningProvisionerJob },
169+
}
170+
export const MockDeletedWorkspace: TypesGen.Workspace = { ...MockWorkspace, latest_build: MockWorkspaceBuildDelete }
152171

153172
export const MockOutdatedWorkspace: TypesGen.Workspace = { ...MockWorkspace, outdated: true }
154173

0 commit comments

Comments
 (0)