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>
This commit is contained in:
1 parent
558c70aa24
commit
94d57add2c
44 files changed
+446
-6481
No files matched your search
+110
-7
@@ -1,13 +1,116 @@
|
||||
import '@angular/compiler';
|
||||
import { vi } from 'vitest';
|
||||
import '@analogjs/vitest-angular/setup-snapshots';
|
||||
import '@analogjs/vitest-angular/setup-serializers';
|
||||
import { setupTestBed } from '@analogjs/vitest-angular/setup-testbed';
|
||||
/* 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';
|
||||
|
||||
setupTestBed();
|
||||
// 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) => ({
|
||||
|
||||
Reference in new issue
Block a user