Files
coder/site/src/utils/workspace.test.ts
T
Ethan 7632e73608 refactor(site/src): extract shared workspace-app frame module (#26088)
Extracts the workspace app iframe, wildcard warning, and workspace-app
helper functions out of TaskPage into shared `site/src/modules/apps`
modules. Existing agent and app lookups in the task chat helpers,
download-logs dialog, and workspaces table now route through the shared
`workspaceApps` helpers instead of duplicating resource-flattening
logic. The extracted frame preserves the existing preview-only toolbar
behavior, and its open-in-new-tab link gains `rel="noreferrer"` to
harden against tabnabbing.

Relates to CODAGT-346
2026-06-12 11:51:20 +10:00

228 lines
6.2 KiB
TypeScript

import dayjs from "dayjs";
import type * as TypesGen from "#/api/typesGenerated";
import * as Mocks from "#/testHelpers/entities";
import {
agentVersionStatus,
defaultWorkspaceExtension,
findWorkspaceAgent,
getDisplayVersionStatus,
getDisplayWorkspaceBuildInitiatedBy,
getDisplayWorkspaceTemplateName,
getMatchingAgentOrFirst,
getWorkspaceAgents,
isWorkspaceOn,
} from "./workspace";
function buildWorkspace(
resourceAgents: readonly TypesGen.WorkspaceAgent[][],
): TypesGen.Workspace {
const resourceTemplate = Mocks.MockWorkspace.latest_build.resources[0];
return {
...Mocks.MockWorkspace,
latest_build: {
...Mocks.MockWorkspace.latest_build,
resources: resourceAgents.map((agents) => ({
...resourceTemplate,
agents,
})),
},
};
}
function buildAgent(id: string): TypesGen.WorkspaceAgent {
return {
...Mocks.MockWorkspaceAgent,
id,
name: id,
};
}
describe("util > workspace", () => {
describe("isWorkspaceOn", () => {
it.each<
[TypesGen.WorkspaceTransition, TypesGen.ProvisionerJobStatus, boolean]
>([
["delete", "canceled", false],
["delete", "canceling", false],
["delete", "failed", false],
["delete", "pending", false],
["delete", "running", false],
["delete", "succeeded", false],
["stop", "canceled", false],
["stop", "canceling", false],
["stop", "failed", false],
["stop", "pending", false],
["stop", "running", false],
["stop", "succeeded", false],
["start", "canceled", false],
["start", "canceling", false],
["start", "failed", false],
["start", "pending", false],
["start", "running", false],
["start", "succeeded", true],
])("transition=%p, status=%p, isWorkspaceOn=%p", (transition, status, isOn) => {
const workspace: TypesGen.Workspace = {
...Mocks.MockWorkspace,
latest_build: {
...Mocks.MockWorkspaceBuild,
job: {
...Mocks.MockProvisionerJob,
status,
},
transition,
},
};
expect(isWorkspaceOn(workspace)).toBe(isOn);
});
});
describe("defaultWorkspaceExtension", () => {
it.each<[string, TypesGen.PutExtendWorkspaceRequest]>([
[
"2022-06-02T14:56:34Z",
{
deadline: "2022-06-02T18:56:34Z",
},
],
// This case is the same as above, but in a different timezone to prove
// that UTC conversion for deadline works as expected
[
"2022-06-02T10:56:20-04:00",
{
deadline: "2022-06-02T18:56:20Z",
},
],
])("defaultWorkspaceExtension(%p) returns %p", (startTime, request) => {
expect(defaultWorkspaceExtension(dayjs(startTime))).toEqual(request);
});
});
describe("getDisplayWorkspaceBuildInitiatedBy", () => {
it.each<[TypesGen.WorkspaceBuild, string]>([
[Mocks.MockWorkspaceBuild, "TestUser"],
[
{
...Mocks.MockWorkspaceBuild,
reason: "autostart",
},
"Coder",
],
[
{
...Mocks.MockWorkspaceBuild,
reason: "autostop",
},
"Coder",
],
[
{
...Mocks.MockWorkspaceBuild,
reason: "task_auto_pause",
},
"Coder",
],
])("getDisplayWorkspaceBuildInitiatedBy(%p) returns %p", (build, initiatedBy) => {
expect(getDisplayWorkspaceBuildInitiatedBy(build)).toEqual(initiatedBy);
});
});
describe("getDisplayVersionStatus", () => {
it.each<[string, string, string, string, string, agentVersionStatus]>([
["", "", "", "", "Unknown", agentVersionStatus.Updated],
["", "v1.2.3", "", "", "Unknown", agentVersionStatus.Updated],
["v1.2.3", "", "", "", "v1.2.3", agentVersionStatus.Updated],
["v1.2.3", "v1.2.3", "", "", "v1.2.3", agentVersionStatus.Updated],
["v1.2.3", "v1.2.4", "", "", "v1.2.3", agentVersionStatus.Outdated],
["v1.2.4", "v1.2.3", "", "", "v1.2.4", agentVersionStatus.Updated],
["foo", "bar", "", "", "foo", agentVersionStatus.Updated],
[
"v1.2.3",
"v1.2.4",
"1.8",
"2.1",
"v1.2.3",
agentVersionStatus.Deprecated,
],
])("getDisplayVersionStatus(theme, %p, %p, %p, %p) returns (%p, %p)", (agentVersion, serverVersion, agentAPIVersion, serverAPIVersion, expectedVersion, expectedStatus) => {
const { displayVersion, status } = getDisplayVersionStatus(
agentVersion,
serverVersion,
agentAPIVersion,
serverAPIVersion,
);
expect(displayVersion).toEqual(expectedVersion);
expect(status).toEqual(expectedStatus);
});
});
describe("getDisplayWorkspaceTemplateName", () => {
it("display name is not set", async () => {
const workspace: TypesGen.Workspace = {
...Mocks.MockWorkspace,
template_display_name: "",
};
const displayed = getDisplayWorkspaceTemplateName(workspace);
expect(displayed).toEqual(workspace.template_name);
});
it("display name is set", async () => {
const workspace: TypesGen.Workspace = {
...Mocks.MockWorkspace,
};
const displayed = getDisplayWorkspaceTemplateName(workspace);
expect(displayed).toEqual(workspace.template_display_name);
});
});
describe("getWorkspaceAgents", () => {
it("flattens agents across workspace resources", () => {
const workspace = buildWorkspace([
[buildAgent("agent-1")],
[buildAgent("agent-2")],
]);
expect(getWorkspaceAgents(workspace).map((agent) => agent.id)).toEqual([
"agent-1",
"agent-2",
]);
});
});
describe("findWorkspaceAgent", () => {
it("returns the matching agent by ID", () => {
const workspace = buildWorkspace([[buildAgent("agent-1")]]);
expect(findWorkspaceAgent(workspace, "agent-1")?.name).toBe("agent-1");
expect(findWorkspaceAgent(workspace, "missing")).toBeUndefined();
});
});
describe("getMatchingAgentOrFirst", () => {
it("returns the agent matching by name across resources", () => {
const workspace = buildWorkspace([
[buildAgent("agent-1")],
[buildAgent("agent-2")],
]);
expect(getMatchingAgentOrFirst(workspace, "agent-2")?.id).toBe("agent-2");
});
it("returns the first agent when no name is given", () => {
const workspace = buildWorkspace([
[buildAgent("agent-1")],
[buildAgent("agent-2")],
]);
expect(getMatchingAgentOrFirst(workspace, undefined)?.id).toBe("agent-1");
});
it("returns undefined when no agent matches the name", () => {
const workspace = buildWorkspace([[buildAgent("agent-1")]]);
expect(getMatchingAgentOrFirst(workspace, "missing")).toBeUndefined();
});
});
});