Files
vac-optimizer/src/test-setup.ts
T
nessarandClaude Opus 5.5 94d57add2c test: run the suite and benchmark with Bun's native test runner
Replace Vitest with bun test across the whole suite. A preload compiles
components through Angular's JIT transform with inlined templates, sets
up jsdom, TestBed and zoneless change detection, and a small shim covers
the Vitest APIs bun:test lacks. Files run isolated, real-worker specs
spawn the running Bun, and line coverage from lcov is converted to the
existing HTML and Cobertura reports. The CI benchmark replaces its heap
cap with a container memory limit. Vitest, Jest, Nx and their Vite
configuration are removed.

Refs #291

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:40:42 +02:00

127 lines
4.0 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
import { plugin } from 'bun';
import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import * as bt from 'bun:test';
// shortcut: imports Analog's internal jitTransform by path; replace when Angular or Analog exports a JIT transform.
import { jitTransform } from '../node_modules/@analogjs/vite-plugin-angular/src/lib/compiler/jit-transform.js';
import { vi } from './testing/vitest-compat';
// Inline templateUrl/styleUrl(s) before Bun transpiles: JIT has no resource loader.
plugin({
name: 'angular-jit',
setup(build) {
build.onLoad({ filter: /src\/app\/(?!.*\.spec\.ts$).*\.ts$/ }, ({ path }) => {
let src = readFileSync(path, 'utf8');
if (!/@(Component|Directive|Injectable|Pipe|NgModule)\(/.test(src)) {
return { contents: src, loader: 'ts' };
}
const read = (p: string) =>
JSON.stringify(readFileSync(resolve(dirname(path), p), 'utf8'));
src = src.replace(/templateUrl:\s*'([^']+)'/, (_, p) => `template: ${read(p)}`);
// Styles are not compiled; they do not matter to the tests.
src = src.replace(/styleUrl:\s*'([^']+)'/, 'styles: []');
src = src.replace(/styleUrls:\s*\[[^\]]*\]/, 'styles: []');
// Signal input()/output()/viewChild() become decorators and @Component becomes ɵcompileComponent.
return { contents: jitTransform(src, path).code, loader: 'ts' };
});
},
});
const { JSDOM } = await import('jsdom');
const dom = new JSDOM('<!doctype html><html><body></body></html>', {
url: 'http://localhost:3000/',
pretendToBeVisual: true,
});
const win = dom.window as any;
const keep = new Set([
'console',
'globalThis',
'undefined',
'Infinity',
'NaN',
'Object',
'Function',
'Array',
'Error',
'Promise',
'Symbol',
'Map',
'Set',
'JSON',
'Math',
'Date',
'process',
'window',
'self',
]);
const force = new Set([
'document',
'navigator',
'location',
'localStorage',
'sessionStorage',
'history',
'getComputedStyle',
]);
const domNames =
/^(HTML|SVG|DOM|Mutation|Node|Element|Event|Text|Comment|Document|Range|Selection|CSS|Shadow|Custom|Keyboard|Mouse|Focus|Input|UI|Wheel|Pointer|Touch|Animation|File|Blob|FormData|Window|Storage|Headers|Abort)/;
for (const k of Object.getOwnPropertyNames(win)) {
if (keep.has(k) || (k in globalThis && !force.has(k) && !domNames.test(k))) continue;
// Live accessors: spying on window.x also affects bare x (jsdom window !== globalThis here).
Object.defineProperty(globalThis, k, {
configurable: true,
get: () => {
const v = win[k];
return typeof v === 'function' && /^[a-z]/.test(k) ? v.bind(win) : v;
},
set: (v) => {
win[k] = v;
},
});
}
for (const k of ['window', 'self']) {
Object.defineProperty(globalThis, k, { configurable: true, writable: true, value: win });
}
Object.assign(globalThis, {
vi,
describe: bt.describe,
it: bt.it,
test: bt.test,
expect: bt.expect,
beforeEach: bt.beforeEach,
afterEach: bt.afterEach,
beforeAll: bt.beforeAll,
afterAll: bt.afterAll,
});
await import('@angular/compiler');
const { getTestBed, ɵgetCleanupHook } = await import('@angular/core/testing');
const { BrowserTestingModule, platformBrowserTesting } =
await import('@angular/platform-browser/testing');
const { provideZonelessChangeDetection } = await import('@angular/core');
getTestBed().initTestEnvironment([BrowserTestingModule], platformBrowserTesting(), {
teardown: { destroyAfterEach: true },
});
getTestBed().configureTestingModule({ providers: [provideZonelessChangeDetection()] });
bt.beforeEach(ɵgetCleanupHook(false));
bt.afterEach(ɵgetCleanupHook(true));
// Bun has a native Worker; the app must see none, as under jsdom.
delete (globalThis as any).Worker;
vi.stubGlobal('Worker', undefined);
Object.defineProperty(window, 'matchMedia', {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: false,
media: query,
onchange: null,
addListener: vi.fn(),
removeListener: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});