// @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 = {}) => ({ 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 { 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"), 'Example', ), writeFile( join(root, "icon.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"), '', ); await expect(checkToolboxDist(root)).rejects.toThrow(/not relocatable/u); }); });