Files
Oleg Isonen d291e4b671 feat: Builder api runtime contracts guardrails for MCP (#5857)
This PR expands Webstudio MCP and CLI into a more complete agent
authoring interface. Agents can discover project capabilities, edit real
Webstudio content, use data/resources, verify visual output, and report
useful issues when workflows fail.

## MCP User Capabilities

- Create, update, duplicate, reorder, and delete pages.
- Create pages from templates and manage reusable page templates.
- Inspect project pages, instances, props, styles, assets, variables,
resources, and metadata.
- Insert Webstudio components and authored JSX fragments into pages.
- Edit text, props, styles, breakpoints, design tokens, and CSS
variables.
- Update page metadata, SEO fields, redirects, marketplace metadata, and
project settings.
- Create and use static data variables.
- Create and use dynamic resources fetched over HTTP.
- Use REST, GraphQL, and built-in system resources in authored pages.
- Bind data variables and resources to visible page content.
- Create resource-backed pages such as sitemap XML, structured SEO data,
and API-driven content sections.
- Preview current project state without requiring a publish step.
- Capture screenshots for visual verification.
- Verify responsive designs across desktop, tablet, and mobile
breakpoints.
- Use screenshot diff and OCR-style checks to inspect rendered output.
- Reset and refresh local MCP project state when needed.
- Discover available CLI/MCP commands from generated manuals.
- Use clearer examples for resources, data variables, JSX authoring,
previewing, and visual checks.
- Read marketplace product metadata without requiring a full project
snapshot.
- Get safer guidance when using low-level patch tools.
- Get a ready-to-copy Discord report template when MCP/CLI behavior is
confusing or broken.
2026-07-10 02:13:22 +02:00

92 lines
2.7 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { createDefaultPages } from "@webstudio-is/project-build";
import { $, renderData } from "@webstudio-is/template";
import { buildInstancePath } from "./lookup";
describe("buildInstancePath", () => {
const createPages = () =>
createDefaultPages({
homePageId: "homePageId",
rootInstanceId: "rootId",
systemDataSourceId: "systemId",
});
test("returns empty array when instance has no selector", () => {
const pages = createPages();
const instances = new Map();
const result = buildInstancePath("nonexistent", pages, instances);
expect(result).toEqual([]);
});
test("returns empty array for root instance (no ancestors)", () => {
const { instances } = renderData(
<$.Body ws:id="rootId">
<$.Box ws:id="boxId"></$.Box>
</$.Body>
);
const pages = createPages();
const result = buildInstancePath("rootId", pages, instances);
expect(result).toEqual([]);
});
test("builds path for single-level nesting", () => {
const { instances } = renderData(
<$.Body ws:id="rootId">
<$.Box ws:id="boxId"></$.Box>
</$.Body>
);
const pages = createPages();
const result = buildInstancePath("boxId", pages, instances);
expect(result).toEqual(["Body"]);
});
test("builds path for multi-level nesting", () => {
const { instances } = renderData(
<$.Body ws:id="rootId">
<$.Box ws:id="containerId">
<$.Heading ws:id="headingId"></$.Heading>
</$.Box>
</$.Body>
);
const pages = createPages();
const result = buildInstancePath("headingId", pages, instances);
expect(result).toEqual(["Body", "Box"]);
});
test("builds path for deeply nested instance", () => {
const { instances } = renderData(
<$.Body ws:id="rootId">
<$.Box ws:id="sectionId">
<$.Box ws:id="articleId">
<$.Box ws:id="divId">
<$.Text ws:id="textId"></$.Text>
</$.Box>
</$.Box>
</$.Box>
</$.Body>
);
const pages = createPages();
const result = buildInstancePath("textId", pages, instances);
expect(result).toEqual(["Body", "Box", "Box", "Box"]);
});
test("handles instances with custom labels", () => {
const { instances } = renderData(
<$.Body ws:id="rootId" ws:label="Main Body">
<$.Box ws:id="navId" ws:label="Navigation">
<$.Link ws:id="linkId" ws:label="Home Link"></$.Link>
</$.Box>
</$.Body>
);
const pages = createPages();
const result = buildInstancePath("linkId", pages, instances);
expect(result).toEqual(["Main Body", "Navigation"]);
});
});