Files
wehub-resource-sync b3a7f98e5a
CI / E2E Cloudflare (4/8) (push) Failing after 0s
CI / E2E Cloudflare (8/8) (push) Failing after 1s
CI / Lint (push) Failing after 1s
Auto Extract / Extract (push) Failing after 4s
CI / Version Check (push) Failing after 9s
CI / Integration Tests (push) Failing after 1s
CI / E2E tests (1/8) (push) Failing after 1s
CI / E2E tests (2/8) (push) Failing after 2s
CI / E2E tests (3/8) (push) Failing after 2s
CI / Browser Tests (push) Failing after 1s
CI / E2E tests (5/8) (push) Failing after 1s
CI / E2E Cloudflare (2/8) (push) Failing after 2s
CI / Typecheck (push) Failing after 1s
CI / Changeset Validation (push) Failing after 2s
CI / E2E Cloudflare (5/8) (push) Failing after 1s
CI / E2E Cloudflare (6/8) (push) Failing after 1s
CI / E2E Cloudflare (7/8) (push) Failing after 1s
CodeQL / Analyze (javascript-typescript) (push) Failing after 1s
Format / Format (push) Failing after 0s
CodeQL / Analyze (actions) (push) Failing after 4s
CI / E2E tests (4/8) (push) Failing after 1s
CI / E2E tests (6/8) (push) Failing after 1s
CI / E2E tests (7/8) (push) Failing after 2s
CI / E2E tests (8/8) (push) Failing after 1s
CI / E2E Cloudflare (1/8) (push) Failing after 1s
CI / E2E Cloudflare (3/8) (push) Failing after 2s
Preview Releases / Publish Preview (push) Failing after 0s
zizmor / Run zizmor (push) Failing after 1s
Release / Release (push) Failing after 2s
CI / Smoke Tests (push) Failing after 5m36s
CI / Tests (push) Failing after 6m36s
Release / Sync Templates (push) Has been skipped
CI / E2E Tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:23:53 +08:00

114 lines
3.5 KiB
TypeScript

import { mkdtemp, rm, writeFile, mkdir } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import type { PluginDescriptor } from "../../../src/astro/integration/runtime.js";
import { generateSandboxedPluginsModule } from "../../../src/astro/integration/virtual-modules.js";
function descriptor(overrides: Partial<PluginDescriptor> = {}): PluginDescriptor {
return {
id: "test-plugin",
version: "1.0.0",
entrypoint: "@test/plugin/sandbox",
format: "standard",
capabilities: [],
allowedHosts: [],
storage: {},
adminPages: [],
adminWidgets: [],
...overrides,
};
}
describe("generateSandboxedPluginsModule", () => {
let tmpDir: string;
beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "emdash-vm-test-"));
});
afterEach(async () => {
await rm(tmpDir, { recursive: true, force: true });
});
async function setupFakeProject(exportPath: string, content: string) {
// Create a fake project root with package.json
await writeFile(join(tmpDir, "package.json"), JSON.stringify({ name: "test-project" }));
// Create the plugin package inside node_modules
const pluginDir = join(tmpDir, "node_modules", "@test", "plugin");
await mkdir(pluginDir, { recursive: true });
// Determine the directory for the export file
const fileParts = exportPath.split("/");
if (fileParts.length > 1) {
const dir = join(pluginDir, ...fileParts.slice(0, -1));
await mkdir(dir, { recursive: true });
}
await writeFile(join(pluginDir, exportPath), content);
await writeFile(
join(pluginDir, "package.json"),
JSON.stringify({
name: "@test/plugin",
exports: { "./sandbox": `./${exportPath}` },
}),
);
}
it("returns empty module when no plugins configured", () => {
const result = generateSandboxedPluginsModule([], tmpDir);
expect(result).toContain("export const sandboxedPlugins = []");
});
it("embeds pre-built JavaScript successfully", async () => {
await setupFakeProject("dist/sandbox-entry.mjs", "export default { hooks: {} };");
const result = generateSandboxedPluginsModule(
[descriptor({ entrypoint: "@test/plugin/sandbox" })],
tmpDir,
);
expect(result).toContain("sandboxedPlugins");
expect(result).toContain("test-plugin");
expect(result).toContain("export default { hooks: {} };");
});
it("throws for .ts source files", async () => {
await setupFakeProject("src/sandbox-entry.ts", "export default {};");
expect(() =>
generateSandboxedPluginsModule([descriptor({ entrypoint: "@test/plugin/sandbox" })], tmpDir),
).toThrow(/unbuilt source/);
});
it("throws for .tsx source files", async () => {
await setupFakeProject("src/sandbox-entry.tsx", "export default {};");
expect(() =>
generateSandboxedPluginsModule([descriptor({ entrypoint: "@test/plugin/sandbox" })], tmpDir),
).toThrow(/unbuilt source/);
});
it("throws for .mts source files", async () => {
await setupFakeProject("src/sandbox-entry.mts", "export default {};");
expect(() =>
generateSandboxedPluginsModule([descriptor({ entrypoint: "@test/plugin/sandbox" })], tmpDir),
).toThrow(/unbuilt source/);
});
it("includes plugin id in error message", async () => {
await setupFakeProject("src/sandbox-entry.ts", "export default {};");
expect(() =>
generateSandboxedPluginsModule(
[descriptor({ id: "my-broken-plugin", entrypoint: "@test/plugin/sandbox" })],
tmpDir,
),
).toThrow(/my-broken-plugin/);
});
});