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>
127 lines
4.0 KiB
TypeScript
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(),
|
|
})),
|
|
});
|