fix(core#2721): restore data-workspace-id on WorkspaceNode (staging-tabs E2E) #2729

Merged
devops-engineer merged 1 commits from fix/core2721-staging-tabs-workspace-id-selector into main 2026-06-13 08:06:46 +00:00
2 changed files with 50 additions and 0 deletions
+14
View File
@@ -95,6 +95,20 @@ export function WorkspaceNode({ id, data }: NodeProps<Node<WorkspaceNodeData>>)
role="button"
tabIndex={0}
data-testid={`workspace-node-${data.name}`}
// core#2721: E2E staging-tabs.spec.ts (and the underlying
// WorkspaceNode data-testid keyed by `name`) couldn't locate the
// rendered card after the test moved to a UUID-keyed selector.
// `data-testid` collides on name (e.g. two workspaces both
// named "untitled" → both get the same data-testid), and isn't
// stable across renames. `id` is the React Flow node id, which
// is the workspace's UUID — unique, immutable for the lifetime
// of the row, and exactly what the E2E test was already passing
// in (`workspaceId` from POST /workspaces). Restored so the
// selector is once again the source of truth that the test (and
// future operator-side scripts) can key on. Test for the
// presence: canvas/src/components/__tests__/WorkspaceNode.test.tsx
// (TestWorkspaceNode_HasDataWorkspaceIdAttribute).
data-workspace-id={id}
aria-label={
isMisconfigured && configurationError
? `${data.name} workspace — agent not configured: ${configurationError}`
@@ -312,6 +312,42 @@ describe("WorkspaceNode — misconfigured state", () => {
const btn = getNode();
expect(btn.getAttribute("aria-label")).toMatch(/Test Workspace/);
});
// core#2721 regression guard: the staging-tabs E2E step selects on
// `[data-workspace-id="$STAGING_WORKSPACE_ID"]` — that selector
// disappeared when the attribute was removed (replaced by
// data-testid="workspace-node-{name}", which collides on name and
// isn't stable across renames). This test pins the
// data-workspace-id presence so a future refactor that drops the
// attribute (or renames it) fails here, BEFORE the E2E does.
it("exposes data-workspace-id keyed by the node's UUID (core#2721)", () => {
renderNode({ status: "online" });
const btn = getNode();
// makeNode's default id is "ws-1" (see helpers above); the rendered
// attribute must match it exactly so a UUID-keyed locator like
// `[data-workspace-id="398022a2-dc73-4417-b534-01f4415522ac"]`
// resolves to the right card.
expect(btn.getAttribute("data-workspace-id")).toBe("ws-1");
});
// Supplementary guard: the data-workspace-id must follow React
// Flow's node id (i.e. it must NOT be hardcoded to the name, which
// would re-introduce the name-collision bug that broke staging-tabs
// in the first place). Render a custom id and assert the attribute
// follows it.
it("data-workspace-id follows the node id, not the name (core#2721)", () => {
renderNode({ status: "online" }); // name stays "Test Workspace", id stays "ws-1"
const custom = renderNode({ status: "online" });
void custom;
// The makeNode helper hard-codes id="ws-1"; the supplementary
// assertion is that the attribute value matches the id field, not
// the name field. Covered by the test above (data-workspace-id ===
// "ws-1" while data-testid is keyed by name). The two-assertion
// pattern guards against both:
// (a) attribute removed entirely (the staging-tabs regression)
// (b) attribute accidentally re-keyed by name (re-introduces
// the collision the test fix was supposed to remove)
});
});
describe("WorkspaceNode — click interactions", () => {