Files
toolbox-sdk/packages/testkit/test/check.test.ts

119 lines
3.6 KiB
TypeScript

// @vitest-environment node
import { mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { checkToolboxDist } from "../src/index.js";
const temporaryDirectories: string[] = [];
const manifest = (overrides: Record<string, unknown> = {}) => ({
schemaVersion: 1,
id: "de.add-ideas.example",
name: "Example",
version: "1.2.3",
description: "Example app",
entry: "./",
icon: "./icon.svg",
categories: ["example"],
tags: ["test"],
integration: {
contextVersion: 1,
launchModes: ["navigate"],
embedding: "unsupported",
},
requirements: {
secureContext: false,
workers: false,
indexedDb: false,
crossOriginIsolated: false,
},
privacy: {
processing: "local",
fileUploads: false,
telemetry: false,
},
source: {
repository: "https://git.example.test/example",
license: "MIT",
},
assets: ["./app.js"],
...overrides,
});
async function fixture(document = manifest()): Promise<string> {
const root = await mkdtemp(join(tmpdir(), "toolbox-check-"));
temporaryDirectories.push(root);
await Promise.all([
writeFile(join(root, "toolbox-app.json"), JSON.stringify(document)),
writeFile(
join(root, "index.html"),
'<!doctype html><link rel="manifest" href="./site.webmanifest"><link rel="icon" href="./icon.svg"><link rel="stylesheet" href="./style.css"><img src="./image.png" alt=""><script type="module" src="./app.js"></script><title>Example</title>',
),
writeFile(
join(root, "icon.svg"),
'<svg xmlns="http://www.w3.org/2000/svg"/>',
),
writeFile(join(root, "app.js"), "console.log('ok')"),
writeFile(join(root, "style.css"), "body { color: black; }"),
writeFile(join(root, "image.png"), "not-a-real-png"),
writeFile(
join(root, "site.webmanifest"),
JSON.stringify({ icons: [{ src: "./icon.svg" }] }),
),
]);
return root;
}
afterEach(async () => {
await Promise.all(
temporaryDirectories
.splice(0)
.map((path) => rm(path, { recursive: true, force: true })),
);
});
describe("toolbox-check", () => {
it("validates files and smoke-fetches nested standalone and context URLs", async () => {
const report = await checkToolboxDist(await fixture());
expect(report.app.id).toBe("de.add-ideas.example");
expect(report.checkedFiles).toHaveLength(3);
expect(new URL(report.smoke.standaloneUrl).pathname).toBe(
"/__toolbox-check__/deep/nested/app/",
);
expect(
new URL(report.smoke.contextualUrl).searchParams.get("toolbox"),
).toBe("/toolbox.catalog.json");
expect(report.smoke.fetchedUrls).toEqual(
expect.arrayContaining([
expect.stringContaining("/site.webmanifest"),
expect.stringContaining("/style.css"),
expect.stringContaining("/app.js"),
]),
);
});
it("rejects traversal in declared assets", async () => {
const root = await fixture(manifest({ assets: ["../secret.txt"] }));
await expect(checkToolboxDist(root)).rejects.toThrow(/unsafe path/u);
});
it("rejects a non-SemVer manifest version", async () => {
const root = await fixture(manifest({ version: "next" }));
await expect(checkToolboxDist(root)).rejects.toThrow(/not SemVer/u);
});
it("rejects root-absolute HTML assets that break nested deployment", async () => {
const root = await fixture();
await writeFile(
join(root, "index.html"),
'<!doctype html><script src="/app.js"></script>',
);
await expect(checkToolboxDist(root)).rejects.toThrow(/not relocatable/u);
});
});