test: migrate WorkspacesPage tests to Storybook play stories (#26958)

Migrates the WorkspacesPage tests from vitest to Storybook play-function stories.

The old `WorkspacesPage.test.tsx` rendered the page through `renderWithAuth` and MSW, which is slow and contributes to `test-js` timeout flakes. The new stories seed the react-query cache directly and assert the same behavior in `play` functions, so they run as Storybook interaction tests instead of in the vitest `unit` project.

Coverage is preserved across all flows from the old test: rendering the empty and filled pages, deleting only the selected workspaces, the three batch-update cases (skipping up-to-date workspaces, updating a running workspace after acknowledging the restart risk, and ignoring dormant workspaces), stopping only the selected running workspaces, starting only the selected stopped workspaces, filtering workspace apps by health and visibility, and hiding the start button for an outdated stopped always-update workspace. The entire `WorkspacesPage.test.tsx` file is removed since it contained no pure-logic tests to keep.

Closes CODAGT-686
This commit is contained in:
Ethan
2026-07-08 12:12:34 +10:00
committed by GitHub
parent 9a1aab7986
commit 195dffc651
2 changed files with 386 additions and 416 deletions
@@ -15,15 +15,24 @@ import {
templateVersionsQueryKey,
} from "#/api/queries/templates";
import { workspacesKey } from "#/api/queries/workspaces";
import type { Workspace } from "#/api/typesGenerated";
import type { Workspace, WorkspaceAppHealth } from "#/api/typesGenerated";
import { workspaceChecks } from "#/modules/workspaces/permissions";
import {
MockDefaultOrganization,
MockDormantOutdatedWorkspace,
MockDormantWorkspace,
MockOutdatedStoppedWorkspaceAlwaysUpdate,
MockOutdatedWorkspace,
MockRunningOutdatedWorkspace,
MockStoppedWorkspace,
MockTemplate,
MockTemplateVersion,
MockUserOwner,
MockWorkspace,
MockWorkspaceAgent,
MockWorkspaceApp,
MockWorkspaceBuild,
MockWorkspacesResponse,
} from "#/testHelpers/entities";
import {
withAuthProvider,
@@ -216,3 +225,379 @@ export const PaginationChangesQueryKey: Story = {
});
},
};
export const Empty: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({ workspaces: [], count: 0 });
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByRole("heading", { name: /Create a workspace/ });
},
};
export const RendersWorkspaces: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue(MockWorkspacesResponse);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByText(`${MockWorkspace.name}1`);
const templateDisplayNames = await canvas.findAllByText(
MockWorkspace.template_display_name,
);
expect(templateDisplayNames).toHaveLength(MockWorkspacesResponse.count);
},
};
const deletableWorkspaces: Workspace[] = [
{ ...MockWorkspace, id: "1" },
{ ...MockWorkspace, id: "2" },
{ ...MockWorkspace, id: "3" },
];
export const DeletesOnlySelectedWorkspaces: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: deletableWorkspaces,
count: deletableWorkspaces.length,
});
spyOn(API, "deleteWorkspace").mockResolvedValue(MockWorkspaceBuild);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const body = within(document.body);
const user = userEvent.setup();
await selectWorkspaces(canvas, user, ["1", "2"]);
await openBulkActions(canvas, user);
await user.click(await body.findByRole("menuitem", { name: /delete/i }));
const confirmButton = await body.findByTestId("confirm-button");
await user.click(confirmButton);
await user.click(confirmButton);
await user.click(confirmButton);
await waitFor(() => expect(API.deleteWorkspace).toHaveBeenCalledTimes(2));
expect(API.deleteWorkspace).toHaveBeenCalledWith("1");
expect(API.deleteWorkspace).toHaveBeenCalledWith("2");
},
};
const skipUpToDateWorkspaces: Workspace[] = [
{ ...MockWorkspace, id: "1" },
{ ...MockDormantWorkspace, id: "2" },
{ ...MockOutdatedWorkspace, id: "3" },
{
...MockOutdatedWorkspace,
id: "4",
latest_build: { ...MockOutdatedWorkspace.latest_build, status: "running" },
},
];
export const BatchUpdateSkipsUpToDateWorkspaces: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: skipUpToDateWorkspaces,
count: skipUpToDateWorkspaces.length,
});
spyOn(API, "updateWorkspace").mockResolvedValue(MockWorkspaceBuild);
spyOn(API, "getTemplateVersion").mockResolvedValue(MockTemplateVersion);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const body = within(document.body);
const user = userEvent.setup();
await selectWorkspaces(canvas, user, ["1", "2", "3", "4"]);
await openBulkActions(canvas, user);
await user.click(await body.findByRole("menuitem", { name: /Update/ }));
const modal = await body.findByRole("dialog", { name: /Review Updates/i });
await user.click(
within(modal).getByRole("checkbox", {
name: /I acknowledge these risks\./,
}),
);
await user.click(within(modal).getByRole("button", { name: /Update/ }));
await waitFor(() => expect(API.updateWorkspace).toHaveBeenCalledTimes(2));
expect(API.updateWorkspace).toHaveBeenCalledWith(
skipUpToDateWorkspaces[2],
[],
false,
);
expect(API.updateWorkspace).toHaveBeenCalledWith(
skipUpToDateWorkspaces[3],
[],
false,
);
},
};
const updateRunningWorkspaces: Workspace[] = [
{ ...MockRunningOutdatedWorkspace, id: "1" },
{ ...MockOutdatedWorkspace, id: "2" },
{ ...MockOutdatedWorkspace, id: "3" },
];
export const BatchUpdateRunningWorkspace: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: updateRunningWorkspaces,
count: updateRunningWorkspaces.length,
});
spyOn(API, "updateWorkspace").mockResolvedValue(MockWorkspaceBuild);
spyOn(API, "getTemplateVersion").mockResolvedValue(MockTemplateVersion);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const body = within(document.body);
const user = userEvent.setup();
await selectWorkspaces(canvas, user, ["1", "2", "3"]);
await openBulkActions(canvas, user);
await user.click(await body.findByRole("menuitem", { name: /Update/ }));
const modal = await body.findByRole("dialog", { name: /Review Updates/i });
await user.click(
within(modal).getByRole("checkbox", {
name: /I acknowledge these risks\./,
}),
);
await user.click(within(modal).getByRole("button", { name: /Update/ }));
await waitFor(() => expect(API.updateWorkspace).toHaveBeenCalledTimes(3));
expect(API.updateWorkspace).toHaveBeenCalledWith(
updateRunningWorkspaces[0],
[],
false,
);
expect(API.updateWorkspace).toHaveBeenCalledWith(
updateRunningWorkspaces[1],
[],
false,
);
expect(API.updateWorkspace).toHaveBeenCalledWith(
updateRunningWorkspaces[2],
[],
false,
);
},
};
const ignoreDormantWorkspaces: Workspace[] = [
{ ...MockDormantOutdatedWorkspace, id: "1" },
{ ...MockOutdatedWorkspace, id: "2" },
{ ...MockOutdatedWorkspace, id: "3" },
];
export const BatchUpdateIgnoresDormantWorkspaces: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: ignoreDormantWorkspaces,
count: ignoreDormantWorkspaces.length,
});
spyOn(API, "updateWorkspace").mockResolvedValue(MockWorkspaceBuild);
spyOn(API, "getTemplateVersion").mockResolvedValue(MockTemplateVersion);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const body = within(document.body);
const user = userEvent.setup();
await selectWorkspaces(canvas, user, ["1", "2", "3"]);
await openBulkActions(canvas, user);
await user.click(await body.findByRole("menuitem", { name: /Update/ }));
const modal = await body.findByRole("dialog", { name: /Review Updates/i });
await user.click(within(modal).getByRole("button", { name: /Update/ }));
await waitFor(() => expect(API.updateWorkspace).toHaveBeenCalledTimes(2));
expect(API.updateWorkspace).toHaveBeenCalledWith(
ignoreDormantWorkspaces[1],
[],
false,
);
expect(API.updateWorkspace).toHaveBeenCalledWith(
ignoreDormantWorkspaces[2],
[],
false,
);
},
};
const runningWorkspaces: Workspace[] = [
{ ...MockWorkspace, id: "1" },
{ ...MockWorkspace, id: "2" },
{ ...MockWorkspace, id: "3" },
];
export const StopsOnlySelectedWorkspaces: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: runningWorkspaces,
count: runningWorkspaces.length,
});
spyOn(API, "stopWorkspace").mockResolvedValue(MockWorkspaceBuild);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const body = within(document.body);
const user = userEvent.setup();
await selectWorkspaces(canvas, user, ["1", "2"]);
await openBulkActions(canvas, user);
await user.click(await body.findByRole("menuitem", { name: /stop/i }));
await waitFor(() => expect(API.stopWorkspace).toHaveBeenCalledTimes(2));
expect(API.stopWorkspace).toHaveBeenCalledWith("1");
expect(API.stopWorkspace).toHaveBeenCalledWith("2");
},
};
const stoppedWorkspaces: Workspace[] = [
{ ...MockStoppedWorkspace, id: "1" },
{ ...MockStoppedWorkspace, id: "2" },
{ ...MockStoppedWorkspace, id: "3" },
];
export const StartsOnlySelectedWorkspaces: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: stoppedWorkspaces,
count: stoppedWorkspaces.length,
});
spyOn(API, "startWorkspace").mockResolvedValue(MockWorkspaceBuild);
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const body = within(document.body);
const user = userEvent.setup();
await selectWorkspaces(canvas, user, ["1", "2"]);
await openBulkActions(canvas, user);
await user.click(await body.findByRole("menuitem", { name: /start/i }));
await waitFor(() => expect(API.startWorkspace).toHaveBeenCalledTimes(2));
expect(API.startWorkspace).toHaveBeenCalledWith(
"1",
MockStoppedWorkspace.latest_build.template_version_id,
);
expect(API.startWorkspace).toHaveBeenCalledWith(
"2",
MockStoppedWorkspace.latest_build.template_version_id,
);
},
};
const appHealthStatuses: [WorkspaceAppHealth, boolean][] = [
["healthy", true],
["disabled", true],
["unhealthy", false],
["initializing", false],
];
const appsWorkspace: Workspace = {
...MockWorkspace,
latest_build: {
...MockWorkspace.latest_build,
status: "running",
resources: [
{
...MockWorkspace.latest_build.resources[0],
agents: [
{
...MockWorkspaceAgent,
display_apps: [],
apps: [
...appHealthStatuses.map(([health]) => ({
...MockWorkspaceApp,
id: `${health}-app`,
slug: `${health}-app`,
display_name: `${health} App`,
health,
hidden: false,
})),
{
...MockWorkspaceApp,
id: "hidden-app",
slug: "hidden-app",
display_name: "Hidden App",
health: "healthy",
hidden: true,
},
],
},
],
},
],
},
};
export const FiltersWorkspaceAppsByHealthAndVisibility: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: [appsWorkspace],
count: 1,
});
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByRole("link", {
name: (name) => name.toLowerCase().includes("healthy app"),
});
for (const [health, shouldBeVisible] of appHealthStatuses) {
const link = canvas.queryByRole("link", {
name: (name) => name.toLowerCase().includes(`${health} app`),
});
if (shouldBeVisible) {
expect(link).toBeInTheDocument();
} else {
expect(link).not.toBeInTheDocument();
}
}
expect(
canvas.queryByRole("link", {
name: (name) => name.toLowerCase().includes("hidden app"),
}),
).not.toBeInTheDocument();
},
};
export const OutdatedStoppedAlwaysUpdateHidesStartButton: Story = {
beforeEach: () => {
spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: [MockOutdatedStoppedWorkspaceAlwaysUpdate],
count: 1,
});
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await canvas.findByRole("button", { name: "Update and start workspace" });
expect(
canvas.queryByRole("button", { name: "Start workspace" }),
).not.toBeInTheDocument();
},
};
async function selectWorkspaces(
canvas: ReturnType<typeof within>,
user: ReturnType<typeof userEvent.setup>,
ids: string[],
) {
for (const id of ids) {
await user.click(await canvas.findByTestId(`checkbox-${id}`));
}
}
async function openBulkActions(
canvas: ReturnType<typeof within>,
user: ReturnType<typeof userEvent.setup>,
) {
await user.click(
await canvas.findByRole("button", { name: /bulk actions/i }),
);
}
@@ -1,415 +0,0 @@
import { screen, waitFor, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { HttpResponse, http } from "msw";
import { API } from "#/api/api";
import type {
Workspace,
WorkspaceApp,
WorkspaceAppHealth,
WorkspacesResponse,
} from "#/api/typesGenerated";
import {
MockDormantOutdatedWorkspace,
MockDormantWorkspace,
MockOutdatedStoppedWorkspaceAlwaysUpdate,
MockOutdatedWorkspace,
MockRunningOutdatedWorkspace,
MockStoppedWorkspace,
MockWorkspace,
MockWorkspaceAgent,
MockWorkspaceApp,
MockWorkspacesResponse,
} from "#/testHelpers/entities";
import {
renderWithAuth,
waitForLoaderToBeRemoved,
} from "#/testHelpers/renderHelpers";
import { server } from "#/testHelpers/server";
import * as CreateDayString from "#/utils/createDayString";
import WorkspacesPage from "./WorkspacesPage";
describe("WorkspacesPage", () => {
beforeEach(() => {
// Mocking the dayjs module within the createDayString file
const mock = vi.spyOn(CreateDayString, "createDayString");
mock.mockImplementation(() => "a minute ago");
});
it("renders an empty workspaces page", async () => {
server.use(
http.get("/api/v2/workspaces", async () => {
return HttpResponse.json<WorkspacesResponse>({
workspaces: [],
count: 0,
});
}),
);
renderWithAuth(<WorkspacesPage />);
await screen.findByRole("heading", { name: /Create a workspace/ });
});
it("renders a filled workspaces page", async () => {
renderWithAuth(<WorkspacesPage />);
await screen.findByText(`${MockWorkspace.name}1`);
const templateDisplayNames = await screen.findAllByText(
`${MockWorkspace.template_display_name}`,
);
expect(templateDisplayNames).toHaveLength(MockWorkspacesResponse.count);
});
it("deletes only the selected workspaces", async () => {
const workspaces = [
{ ...MockWorkspace, id: "1" },
{ ...MockWorkspace, id: "2" },
{ ...MockWorkspace, id: "3" },
];
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces,
count: workspaces.length,
});
const deleteWorkspace = vi.spyOn(API, "deleteWorkspace");
const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
await user.click(getWorkspaceCheckbox(workspaces[0]));
await user.click(getWorkspaceCheckbox(workspaces[1]));
await user.click(screen.getByRole("button", { name: /bulk actions/i }));
const deleteButton = await screen.findByText(/delete/i);
await user.click(deleteButton);
// The button changes its text, and advances the content of the modal,
// but it is technically the same button being clicked 3 times.
const confirmButton = await screen.findByTestId("confirm-button");
await user.click(confirmButton);
await user.click(confirmButton);
await user.click(confirmButton);
await waitFor(() => {
expect(deleteWorkspace).toHaveBeenCalledTimes(2);
});
expect(deleteWorkspace).toHaveBeenCalledWith(workspaces[0].id);
expect(deleteWorkspace).toHaveBeenCalledWith(workspaces[1].id);
});
describe("batch updates", () => {
it("skips up-to-date workspaces after confirming update", async () => {
const workspaces: readonly Workspace[] = [
// Not outdated but running; should have no warning
{ ...MockWorkspace, id: "1" },
// Dormant; no warning
{ ...MockDormantWorkspace, id: "2" },
// Out of date but not running; no warning
{ ...MockOutdatedWorkspace, id: "3" },
// Out of date but running; should issue warning
{
...MockOutdatedWorkspace,
id: "4",
latest_build: {
...MockOutdatedWorkspace.latest_build,
status: "running",
},
},
];
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces,
count: workspaces.length,
});
const updateWorkspace = vi.spyOn(API, "updateWorkspace");
const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
for (const workspace of workspaces) {
await user.click(getWorkspaceCheckbox(workspace));
}
await user.click(screen.getByRole("button", { name: /bulk actions/i }));
const dropdownItem = await screen.findByRole("menuitem", {
name: /Update/,
});
await user.click(dropdownItem);
const modal = await screen.findByRole("dialog", {
name: /Review Updates/i,
});
const confirmCheckbox = within(modal).getByRole("checkbox", {
name: /I acknowledge these risks\./,
});
await user.click(confirmCheckbox);
const updateModalButton = within(modal).getByRole("button", {
name: /Update/,
});
await user.click(updateModalButton);
// `workspaces[0]` was up-to-date, and running
// `workspaces[1]` was dormant
await waitFor(() => expect(updateWorkspace).toHaveBeenCalledTimes(2));
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[2], [], false);
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[3], [], false);
});
it("lets user update a running workspace (after user goes through warning)", async () => {
const workspaces: readonly Workspace[] = [
{ ...MockRunningOutdatedWorkspace, id: "1" },
{ ...MockOutdatedWorkspace, id: "2" },
{ ...MockOutdatedWorkspace, id: "3" },
];
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces,
count: workspaces.length,
});
const updateWorkspace = vi.spyOn(API, "updateWorkspace");
const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
for (const workspace of workspaces) {
await user.click(getWorkspaceCheckbox(workspace));
}
await user.click(screen.getByRole("button", { name: /bulk actions/i }));
const dropdownItem = await screen.findByRole("menuitem", {
name: /Update/,
});
await user.click(dropdownItem);
const modal = await screen.findByRole("dialog", {
name: /Review Updates/i,
});
const confirmCheckbox = within(modal).getByRole("checkbox", {
name: /I acknowledge these risks\./,
});
await user.click(confirmCheckbox);
const updateModalButton = within(modal).getByRole("button", {
name: /Update/,
});
await user.click(updateModalButton);
await waitFor(() => expect(updateWorkspace).toHaveBeenCalledTimes(3));
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[0], [], false);
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[1], [], false);
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[2], [], false);
});
it("warns about and ignores dormant workspaces", async () => {
const workspaces = [
{ ...MockDormantOutdatedWorkspace, id: "1" },
{ ...MockOutdatedWorkspace, id: "2" },
{ ...MockOutdatedWorkspace, id: "3" },
];
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces,
count: workspaces.length,
});
const updateWorkspace = vi.spyOn(API, "updateWorkspace");
const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
for (const workspace of workspaces) {
await user.click(getWorkspaceCheckbox(workspace));
}
await user.click(screen.getByRole("button", { name: /bulk actions/i }));
const dropdownItem = await screen.findByRole("menuitem", {
name: /Update/,
});
await user.click(dropdownItem);
const modal = await screen.findByRole("dialog", {
name: /Review Updates/i,
});
const updateModalButton = within(modal).getByRole("button", {
name: /Update/,
});
await user.click(updateModalButton);
// `workspaces[0]` was dormant
await waitFor(() => expect(updateWorkspace).toHaveBeenCalledTimes(2));
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[1], [], false);
expect(updateWorkspace).toHaveBeenCalledWith(workspaces[2], [], false);
});
});
it("stops only the running and selected workspaces", async () => {
const workspaces = [
{ ...MockWorkspace, id: "1" },
{ ...MockWorkspace, id: "2" },
{ ...MockWorkspace, id: "3" },
];
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces,
count: workspaces.length,
});
const stopWorkspace = vi.spyOn(API, "stopWorkspace");
const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
await user.click(getWorkspaceCheckbox(workspaces[0]));
await user.click(getWorkspaceCheckbox(workspaces[1]));
await user.click(screen.getByRole("button", { name: /bulk actions/i }));
const stopButton = await screen.findByRole("menuitem", { name: /stop/i });
await user.click(stopButton);
await waitFor(() => {
expect(stopWorkspace).toHaveBeenCalledTimes(2);
});
expect(stopWorkspace).toHaveBeenCalledWith(workspaces[0].id);
expect(stopWorkspace).toHaveBeenCalledWith(workspaces[1].id);
});
it("starts only the stopped and selected workspaces", async () => {
const workspaces = [
{ ...MockStoppedWorkspace, id: "1" },
{ ...MockStoppedWorkspace, id: "2" },
{ ...MockStoppedWorkspace, id: "3" },
];
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces,
count: workspaces.length,
});
const startWorkspace = vi.spyOn(API, "startWorkspace");
const user = userEvent.setup();
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
await user.click(getWorkspaceCheckbox(workspaces[0]));
await user.click(getWorkspaceCheckbox(workspaces[1]));
await user.click(screen.getByRole("button", { name: /bulk actions/i }));
const startButton = await screen.findByRole("menuitem", { name: /start/i });
await user.click(startButton);
await waitFor(() => {
expect(startWorkspace).toHaveBeenCalledTimes(2);
});
expect(startWorkspace).toHaveBeenCalledWith(
workspaces[0].id,
MockStoppedWorkspace.latest_build.template_version_id,
);
expect(startWorkspace).toHaveBeenCalledWith(
workspaces[1].id,
MockStoppedWorkspace.latest_build.template_version_id,
);
});
});
const getWorkspaceCheckbox = (workspace: Workspace) => {
return screen.getByTestId(`checkbox-${workspace.id}`);
};
describe("WorkspaceApps filtering", () => {
it.each<[WorkspaceAppHealth, boolean]>([
["healthy", true],
["disabled", true],
["unhealthy", false],
["initializing", false],
])("apps with '%s' health status should be shown: %s", async (health, shouldBeVisible) => {
const app: WorkspaceApp = {
...MockWorkspaceApp,
id: `${health}-app`,
display_name: `${health} App`,
health,
hidden: false,
};
const workspace: Workspace = {
...MockWorkspace,
latest_build: {
...MockWorkspace.latest_build,
status: "running",
resources: [
{
...MockWorkspace.latest_build.resources[0],
agents: [
{
...MockWorkspaceAgent,
apps: [app],
},
],
},
],
},
};
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: [workspace],
count: 1,
});
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
const appLink = screen.queryByRole("link", {
name: (name) =>
name.toLowerCase().includes(app.display_name!.toLowerCase()),
});
if (shouldBeVisible) {
expect(appLink).toBeInTheDocument();
} else {
expect(appLink).not.toBeInTheDocument();
}
});
it("does not show hidden apps regardless of health status", async () => {
const hiddenApp: WorkspaceApp = {
...MockWorkspaceApp,
id: "hidden-app",
display_name: "Hidden App",
health: "healthy",
hidden: true,
};
const workspace: Workspace = {
...MockWorkspace,
latest_build: {
...MockWorkspace.latest_build,
status: "running",
resources: [
{
...MockWorkspace.latest_build.resources[0],
agents: [
{
...MockWorkspaceAgent,
apps: [hiddenApp],
},
],
},
],
},
};
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: [workspace],
count: 1,
});
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
expect(
screen.queryByRole("link", {
name: (name) =>
name.toLowerCase().includes(hiddenApp.display_name!.toLowerCase()),
}),
).not.toBeInTheDocument();
});
it("does not show start button for stopped outdated workspace with automatic_updates always", async () => {
vi.spyOn(API, "getWorkspaces").mockResolvedValue({
workspaces: [MockOutdatedStoppedWorkspaceAlwaysUpdate],
count: 1,
});
renderWithAuth(<WorkspacesPage />);
await waitForLoaderToBeRemoved();
expect(
screen.queryByRole("button", { name: "Start workspace" }),
).not.toBeInTheDocument();
expect(
screen.getByRole("button", { name: "Update and start workspace" }),
).toBeInTheDocument();
});
});