Files
paseo/packages/app/src/screens/new-workspace-initial-context.test.ts
T
Mohamed BoudraandGitHub f94ec0e430 Keep New Workspace on the current project (#1806)
* fix(app): keep New Workspace on the active project

New Workspace preselection was falling back to stale remembered host state when multiple hosts existed. Pass durable active-project context from workspace entry points and centralize initial host selection so stale offline hosts cannot steal the form while startup restore remains last-workspace only.

* fix(app): address New Workspace review feedback

* fix(app): stabilize New Workspace host selection

* fix(app): preserve New Workspace host fallback

* test(app): loosen stale-host project assertion

* fix(app): prefer reachable new workspace hosts

* fix(app): avoid stale offline workspace preselect

* fix(app): prefer online project hosts
2026-06-29 20:44:18 +02:00

284 lines
9.8 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { HostProjectListItem } from "@/projects/host-projects";
import type { HostRuntimeConnectionStatus } from "@/runtime/host-runtime";
import {
resolveNewWorkspaceAutomaticServerId,
resolveNewWorkspaceInitialServerId,
} from "./new-workspace-initial-context";
function projectFor(serverId: string, key = "project"): HostProjectListItem {
return {
projectKey: key,
projectName: key,
projectKind: "git",
iconWorkingDir: `/work/${key}`,
hosts: [{ serverId, iconWorkingDir: `/work/${key}`, canCreateWorktree: true }],
workspaceKeys: [],
};
}
function statuses(
entries: Record<string, HostRuntimeConnectionStatus>,
): ReadonlyMap<string, HostRuntimeConnectionStatus> {
return new Map(Object.entries(entries));
}
function multiplicity(entries: Record<string, boolean> = {}): ReadonlyMap<string, boolean> {
return new Map(Object.entries(entries));
}
describe("resolveNewWorkspaceInitialServerId", () => {
it("prefers explicit route host context over online-host fallback", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "online"],
routeServerId: "offline",
lastActiveProject: null,
projects: [projectFor("online")],
hostConnectionStatusByServerId: statuses({ offline: "offline", online: "online" }),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("offline");
});
it("prefers the sole online host over a stale offline project", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "online"],
routeServerId: null,
lastActiveProject: projectFor("offline"),
projects: [projectFor("offline")],
hostConnectionStatusByServerId: statuses({ offline: "offline", online: "online" }),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("online");
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "online"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("online")],
hostConnectionStatusByServerId: statuses({ offline: "offline", online: "online" }),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("online");
});
it("uses the last active project when there is no sole online host", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "other"],
routeServerId: null,
lastActiveProject: projectFor("offline"),
projects: [projectFor("offline")],
hostConnectionStatusByServerId: statuses({ offline: "offline", other: "offline" }),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("offline");
});
it("prefers a connecting project host over a stale offline last active project", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "connecting"],
routeServerId: null,
lastActiveProject: projectFor("offline", "remembered"),
projects: [projectFor("offline", "remembered"), projectFor("connecting", "current")],
hostConnectionStatusByServerId: statuses({
offline: "offline",
connecting: "connecting",
}),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("connecting");
});
it("prefers the online last active project over another hydrated online project", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["host-a", "host-b"],
routeServerId: null,
lastActiveProject: projectFor("host-b", "remembered"),
projects: [projectFor("host-a")],
hostConnectionStatusByServerId: statuses({
"host-a": "online",
"host-b": "online",
}),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("host-b");
});
it("falls back to the only online host even before projects have hydrated", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline-a", "online", "offline-b"],
routeServerId: null,
lastActiveProject: null,
projects: [],
hostConnectionStatusByServerId: statuses({
"offline-a": "offline",
online: "online",
"offline-b": "offline",
}),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("online");
});
it("prefers an online host over the only cached offline project", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "online-a", "online-b"],
routeServerId: null,
lastActiveProject: projectFor("offline"),
projects: [projectFor("offline")],
hostConnectionStatusByServerId: statuses({
offline: "offline",
"online-a": "online",
"online-b": "online",
}),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("online-a");
});
it("prefers the first online project host over an empty online host", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["empty-online", "project-online-a", "project-online-b"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("project-online-a", "a"), projectFor("project-online-b", "b")],
hostConnectionStatusByServerId: statuses({
"empty-online": "online",
"project-online-a": "online",
"project-online-b": "online",
}),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("project-online-a");
});
it("uses the only host with selectable projects even before runtime status is online", () => {
expect(
resolveNewWorkspaceInitialServerId({
allServerIds: ["offline", "connected"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("connected")],
hostConnectionStatusByServerId: statuses({
offline: "offline",
connected: "connecting",
}),
workspaceMultiplicityByServerId: multiplicity(),
}),
).toBe("connected");
});
});
describe("resolveNewWorkspaceAutomaticServerId", () => {
it("keeps a usable automatic host stable when the computed default changes", () => {
expect(
resolveNewWorkspaceAutomaticServerId({
allServerIds: ["host-a", "host-b"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("host-a"), projectFor("host-b")],
hostConnectionStatusByServerId: statuses({ "host-a": "online", "host-b": "online" }),
workspaceMultiplicityByServerId: multiplicity(),
currentServerId: "host-a",
nextServerId: "host-b",
}),
).toBe("host-a");
});
it("switches to the remembered online host after it hydrates", () => {
expect(
resolveNewWorkspaceAutomaticServerId({
allServerIds: ["host-a", "host-b"],
routeServerId: null,
lastActiveProject: projectFor("host-b", "remembered"),
projects: [projectFor("host-a"), projectFor("host-b", "remembered")],
hostConnectionStatusByServerId: statuses({
"host-a": "online",
"host-b": "online",
}),
workspaceMultiplicityByServerId: multiplicity(),
currentServerId: "host-a",
nextServerId: "host-b",
}),
).toBe("host-b");
});
it("switches from an offline automatic host to the online default", () => {
expect(
resolveNewWorkspaceAutomaticServerId({
allServerIds: ["offline", "online"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("offline")],
hostConnectionStatusByServerId: statuses({ offline: "offline", online: "online" }),
workspaceMultiplicityByServerId: multiplicity(),
currentServerId: "offline",
nextServerId: "online",
}),
).toBe("online");
});
it("switches from an offline automatic host to a connecting default with projects", () => {
expect(
resolveNewWorkspaceAutomaticServerId({
allServerIds: ["offline", "connecting"],
routeServerId: null,
lastActiveProject: projectFor("offline", "remembered"),
projects: [projectFor("offline", "remembered"), projectFor("connecting", "current")],
hostConnectionStatusByServerId: statuses({
offline: "offline",
connecting: "connecting",
}),
workspaceMultiplicityByServerId: multiplicity(),
currentServerId: "offline",
nextServerId: "connecting",
}),
).toBe("connecting");
});
it("switches to the default when the current automatic host has no selectable projects", () => {
expect(
resolveNewWorkspaceAutomaticServerId({
allServerIds: ["empty", "with-project"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("with-project")],
hostConnectionStatusByServerId: statuses({
empty: "connecting",
"with-project": "connecting",
}),
workspaceMultiplicityByServerId: multiplicity(),
currentServerId: "empty",
nextServerId: "with-project",
}),
).toBe("with-project");
});
it("does not switch from an online host to an offline cached project", () => {
expect(
resolveNewWorkspaceAutomaticServerId({
allServerIds: ["online-empty", "offline-project"],
routeServerId: null,
lastActiveProject: null,
projects: [projectFor("offline-project")],
hostConnectionStatusByServerId: statuses({
"online-empty": "online",
"offline-project": "offline",
}),
workspaceMultiplicityByServerId: multiplicity(),
currentServerId: "online-empty",
nextServerId: "offline-project",
}),
).toBe("online-empty");
});
});