Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion packages/studio/tests/e2e/edit-accuracy/case.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { spawn } from "node:child_process";
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { COMPOSITION, PLAYHEAD } from "./grid.mjs";
import { COMPOSITION, FIXTURE_CDN, PLAYHEAD, localAsset } from "./grid.mjs";
import {
angleOf,
centre,
Expand Down Expand Up @@ -772,13 +772,42 @@ async function nudgeGesture(ctx, pre) {
};
}

const blockedCdnUrls = new Set();

/** Serves the fixtures' CDN requests from the repo; any other CDN URL is blocked and named once. */
async function serveFixtureAssetsLocally(page) {
const cdp = await page.createCDPSession();
cdp.on("Fetch.requestPaused", ({ requestId, request }) => {
const file = localAsset(request.url);
if (!file) {
if (!blockedCdnUrls.has(request.url)) console.warn(`edit bench: blocked ${request.url}`);
blockedCdnUrls.add(request.url);
cdp
.send("Fetch.failRequest", { requestId, errorReason: "BlockedByClient" })
.catch(() => undefined);
return;
}
// A request whose frame went away rejects; that must not end the run.
cdp
.send("Fetch.fulfillRequest", {
requestId,
responseCode: 200,
responseHeaders: [{ name: "Content-Type", value: "text/javascript" }],
body: readFileSync(file).toString("base64"),
})
.catch(() => undefined);
});
await cdp.send("Fetch.enable", { patterns: [{ urlPattern: `${FIXTURE_CDN}*` }] });
}

/**
* Studio open on the case in a fresh browser context, snapping off, at the case's zoom, target selected;
* `drive` measures the rest. A failure keeps a screenshot, and the context always closes.
*/
export async function inStudio({ browser, spec, dir, files, url, evidence }, drive) {
const context = await browser.createBrowserContext();
const page = await context.newPage();
await serveFixtureAssetsLocally(page);
const ctx = {
page,
dir,
Expand Down
19 changes: 16 additions & 3 deletions packages/studio/tests/e2e/edit-accuracy/grid.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// The edit accuracy grid: one flat-coloured element in a generated project, crossed with one gesture.
// Projects are written to a tmp dir per case; nothing checked in is edited.
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { mkdirSync, statSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import { dragCases } from "./drags.mjs";

export const COMPOSITION = { width: 1920, height: 1080 };
Expand All @@ -10,7 +11,19 @@ export const PLAYHEAD = 1;
export const TARGET = { width: 240, height: 160, color: "#f0c020" };
export const BACKGROUND = "#202020";
const NESTED_HOST = { left: 160, top: 90, width: 1600, height: 900 };
const GSAP_CDN = "https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js";
// Fixtures keep the CDN URL users author; the bench serves it from the repo's gsap (`localAsset`).
const require = createRequire(import.meta.url);
export const FIXTURE_CDN = "https://cdn.jsdelivr.net/";
const GSAP_DIST = `${FIXTURE_CDN}npm/gsap@${require("gsap/package.json").version}/dist/`;
const GSAP_CDN = `${GSAP_DIST}gsap.min.js`;
const LOCAL_GSAP_DIST = dirname(require.resolve("gsap/dist/gsap.min.js"));

/** The repo file a CDN URL is served from (the installed gsap's dist), so no case waits on the network. */
export function localAsset(url) {
if (!url.startsWith(GSAP_DIST)) return undefined;
const file = join(LOCAL_GSAP_DIST, url.slice(GSAP_DIST.length));
return statSync(file, { throwIfNoEntry: false })?.isFile() ? file : undefined;
}

// Studio has corner handles only (ResizeHandle is nw|ne|sw|se); its edge strips crop, so there is no edge resize.
const GESTURES = ["move", "resize", "rotate", "crop", "nudge"];
Expand Down
30 changes: 30 additions & 0 deletions packages/studio/tests/e2e/edit-accuracy/grid.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { mkdtempSync, readdirSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { FIXTURE_CDN, buildGrid, localAsset, writeFixture } from "./grid.mjs";

function fixtureUrls(spec) {
const dir = mkdtempSync(join(tmpdir(), "edit-bench-urls-"));
writeFixture(spec, dir);
const pages = readdirSync(dir, { recursive: true }).filter((f) => String(f).endsWith(".html"));
const urls = pages.flatMap((f) =>
[...readFileSync(join(dir, String(f)), "utf8").matchAll(/https?:\/\/[^"'\s)]+/g)].map(
(m) => m[0],
),
);
rmSync(dir, { recursive: true });
return urls;
}

// Only the CDN host is intercepted, so a mapped URL anywhere else would still reach the network.
const servedLocally = (url) => url.startsWith(FIXTURE_CDN) && localAsset(url) !== undefined;

describe("grid fixtures", () => {
it("reference only URLs the bench serves from the repo", () => {
const unserved = buildGrid("full")
.flatMap(fixtureUrls)
.filter((url) => !servedLocally(url));
expect([...new Set(unserved)]).toEqual([]);
}, 30_000);
});
Loading