Files
webstudio/apps/builder/e2e/flows/canvas-selection.ts
Oleg Isonen 392c9089be fix: content mode permissions (#5795)
**Summary**

Fixes content-mode edit permissions so editor-only users can perform
allowed content workflows without backend “build permission” failures.

**Changes**

- Added shared content-mode permission validation used by backend
authorization.
- Aligned content-mode capabilities around editable props, editable
instances, content roots, pages, assets, and template insertion.
- Allowed content-safe instance/text/page/template/style changes while
keeping design/data/resource changes build-only.
- Updated template insertion to preserve content-safe local styles and
reuse tokens by id.
- Added accessible `SyncStatusDot` for e2e-visible sync state without
replacing the existing sync status UI.
- Added builder e2e setup with Postgres/PostgREST and a first
content-mode browser test:
  - edit existing text as an editor-only user
  - wait for saved state via the sync status dot
  - reload and verify persistence

**Verification**

- `pnpm --filter=@webstudio-is/builder test -- sync-status.test.tsx`
- `pnpm --filter=@webstudio-is/builder typecheck`
- `git diff --check`
- `pnpm e2e:builder`
2026-06-09 01:48:23 +02:00

82 lines
1.8 KiB
TypeScript

import type { Page } from "playwright";
import { waitForCanvasFrame } from "./builder";
const delay = (ms: number) =>
new Promise<void>((resolve) => setTimeout(resolve, ms));
export const selectCanvasTextInstanceForProps = async ({
page,
text,
propertyLabel,
}: {
page: Page;
text: string;
propertyLabel: string;
}) => {
const startedAt = Date.now();
let lastBodyText = "";
while (Date.now() - startedAt < 10_000) {
const canvas = await waitForCanvasFrame({ page });
await canvas.getByText(text).click();
await delay(300);
await page.keyboard.press("Escape");
if (
await page
.getByText(propertyLabel, { exact: true })
.first()
.isVisible({ timeout: 500 })
.catch(() => false)
) {
return;
}
lastBodyText = await page
.locator("body")
.innerText()
.catch(() => "");
await delay(250);
}
throw new Error(
`Expected selected canvas text "${text}" to show property "${propertyLabel}". Page text: ${lastBodyText}`
);
};
export const selectCanvasTextInstance = async ({
page,
text,
}: {
page: Page;
text: string;
}) => {
const startedAt = Date.now();
let lastBodyText = "";
while (Date.now() - startedAt < 10_000) {
const canvas = await waitForCanvasFrame({ page });
await canvas.getByText(text).click();
await delay(300);
if (
(await page
.getByText("No instance selected", { exact: true })
.isVisible({ timeout: 500 })
.catch(() => false)) === false
) {
return;
}
lastBodyText = await page
.locator("body")
.innerText()
.catch(() => "");
await delay(250);
}
throw new Error(
`Expected to select canvas text "${text}". Page text: ${lastBodyText}`
);
};