test: extract node builtin mock helpers

This commit is contained in:
Peter Steinberger
2026-04-03 18:37:48 +01:00
parent 47b8be7116
commit 636a23b73e
11 changed files with 182 additions and 75 deletions

View File

@@ -34,6 +34,24 @@ function walk(dir: string, entries: string[] = []): string[] {
return entries;
}
function walkCode(dir: string, entries: string[] = []): string[] {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") {
continue;
}
walkCode(fullPath, entries);
continue;
}
if (!entry.name.endsWith(".ts") && !entry.name.endsWith(".tsx")) {
continue;
}
entries.push(path.relative(repoRoot, fullPath).replaceAll(path.sep, "/"));
}
return entries;
}
function findExtensionImports(source: string): string[] {
return [
...source.matchAll(/from\s+["']((?:\.\.\/)+extensions\/[^"']+)["']/g),
@@ -124,4 +142,21 @@ describe("non-extension test boundaries", () => {
expect(imports).toEqual([]);
});
it("keeps bundled plugin sync test-api loaders out of core tests", () => {
const files = [
...walkCode(path.join(repoRoot, "src")),
...walkCode(path.join(repoRoot, "test")),
]
.filter((file) => !file.startsWith(BUNDLED_PLUGIN_PATH_PREFIX))
.filter((file) => !file.startsWith("test/helpers/"))
.filter((file) => file !== "test/extension-test-boundary.test.ts");
const offenders = files.filter((file) => {
const source = fs.readFileSync(path.join(repoRoot, file), "utf8");
return source.includes("loadBundledPluginTestApiSync(");
});
expect(offenders).toEqual([]);
});
});

View File

@@ -0,0 +1,54 @@
import { describe, expect, it } from "vitest";
import { mockNodeBuiltinModule } from "./node-builtin-mocks.js";
describe("mockNodeBuiltinModule", () => {
it("merges partial overrides into the original module", async () => {
const actual = { readFileSync: () => "actual", watch: () => "watch" };
const readFileSync = () => "mock";
const mocked = await mockNodeBuiltinModule(async () => actual, {
readFileSync,
});
expect(mocked.readFileSync).toBe(readFileSync);
expect(mocked.watch).toBe(actual.watch);
expect("default" in mocked).toBe(false);
});
it("mirrors overrides into the default export when requested", async () => {
const homedir = () => "/tmp/home";
const mocked = await mockNodeBuiltinModule(
async () => ({ tmpdir: () => "/tmp" }),
{ homedir },
{ mirrorToDefault: true },
);
expect(mocked.default).toMatchObject({
homedir,
tmpdir: expect.any(Function),
});
});
it("preserves existing default exports while overriding members", async () => {
const actual = {
readFileSync: () => "actual",
default: {
readFileSync: () => "actual",
statSync: () => "stat",
},
};
const readFileSync = () => "mock";
const mocked = await mockNodeBuiltinModule(
async () => actual,
{ readFileSync },
{ mirrorToDefault: true },
);
expect(mocked.default).toMatchObject({
readFileSync,
statSync: expect.any(Function),
});
});
});

View File

@@ -0,0 +1,43 @@
type MockFactory<TModule extends object> =
| Partial<TModule>
| ((actual: TModule) => Partial<TModule>);
function resolveMockOverrides<TModule extends object>(
actual: TModule,
factory: MockFactory<TModule>,
): Partial<TModule> {
return typeof factory === "function" ? factory(actual) : factory;
}
function resolveDefaultBase<TModule extends object>(actual: TModule): Record<string, unknown> {
const defaultExport = (actual as TModule & { default?: unknown }).default;
if (defaultExport && typeof defaultExport === "object") {
return defaultExport as Record<string, unknown>;
}
return actual as Record<string, unknown>;
}
export async function mockNodeBuiltinModule<TModule extends object>(
importOriginal: () => Promise<TModule>,
factory: MockFactory<TModule>,
options?: { mirrorToDefault?: boolean },
): Promise<TModule> {
const actual = await importOriginal();
const overrides = resolveMockOverrides(actual, factory);
const mocked = {
...actual,
...overrides,
} as TModule & { default?: Record<string, unknown> };
if (!options?.mirrorToDefault) {
return mocked;
}
return {
...mocked,
default: {
...resolveDefaultBase(actual),
...overrides,
},
} as TModule;
}