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
2 changes: 1 addition & 1 deletion evals/browser/lib/scaffold-case.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ async function moduleSpecifier(fromDirectory, target) {
const targetPath = await realpath(target);
const relativePath = relative(await realpath(fromDirectory), targetPath);
if (isAbsolute(relativePath)) return pathToFileURL(targetPath).href;
const path = relativePath.split(sep).join("/");
const path = relativePath.split(sep).map(encodeURIComponent).join("/");
return path.startsWith(".") ? path : `./${path}`;
}

Expand Down
31 changes: 30 additions & 1 deletion evals/browser/tests/eval.test.mjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { copyFile, mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join, resolve } from "node:path";
import test from "node:test";
import { pathToFileURL } from "node:url";

import { runCommandTask } from "../lib/agent-runner.mjs";
import { loadCaseManifests, validateCaseManifest } from "../lib/case-loader.mjs";
Expand Down Expand Up @@ -279,6 +280,34 @@ test("badcase scaffold creates a loadable, escaped, self-contained case", async
}
});

test("badcase scaffold escapes URL characters in relative fixture imports", async () => {
const temporaryRoot = await mkdtemp(join(tmpdir(), "browser-eval-import-"));
try {
const directoryNames = ["checkout#fragment", "checkout%20literal"];
if (process.platform !== "win32") directoryNames.push("checkout?query");
for (const directoryName of directoryNames) {
const helpers = join(temporaryRoot, directoryName, "lib");
await mkdir(helpers, { recursive: true });
for (const name of ["scaffold-case.mjs", "paths.mjs", "fixtures.mjs"]) {
await copyFile(new URL(`../lib/${name}`, import.meta.url), join(helpers, name));
}
const { scaffoldCase: scaffold } = await import(
pathToFileURL(join(helpers, "scaffold-case.mjs")).href
);
const result = await scaffold({
id: "escaped-import",
rootCasesDirectory: join(temporaryRoot, `cases-${directoryName}`),
});
const source = await readFile(result.files.fixture, "utf8");
assert.match(source, /from "\.\.\//);
const { default: fixture } = await import(pathToFileURL(result.files.fixture).href);
assert.match(fixture.render(), /CASE-ESCAPED_IMPORT/);
}
} finally {
await rm(temporaryRoot, { recursive: true, force: true });
}
});

test("CLI accepts pnpm separator and supports suite and tag filters", async () => {
const cli = resolve("evals/browser/cli.mjs");
const coverageResult = await runProcess(process.execPath, [cli, "--", "coverage", "--json"], {
Expand Down
Loading