add: unit and integration testing

This commit is contained in:
odweta
2026-09-07 18:09:36 +02:00
parent 76fde42334
commit 52a25aeaf5
7230 changed files with 561560 additions and 135112 deletions
+22
View File
@@ -0,0 +1,22 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Copyright Contributors to the Jest project.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+190
View File
@@ -0,0 +1,190 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Measures what loading a module costs, for both CJS `require` and ESM
* `import`. A generated binary tree of modules is loaded from a single test
* file, `jest.resetModules()` between rounds so every round pays full price:
* resolution, the mock decision, the transform lookup and the file read all
* start cold, as they do in a fresh worker. Every module also requires
* `node:path`, so the core-module path is exercised too.
*
* `transform: {}` keeps Babel out of the measurement.
*
* Needs `yarn build:js` first — this spawns the built CLI rather than reaching
* into `src`, because a `Runtime` cannot be built without a config, an
* environment and a haste map around it.
*
* To start the test, run:
* node test.js
* node test.js 30 cjs
*
* A single number here means nothing: absolute timings move with the machine,
* the Node version and whatever else is running. To compare two revisions,
* swap `packages/jest-runtime/src` between them and rebuild between
* measurements, alternating instead of measuring each revision once, so drift
* lands on both sides. Read the `modules` column, not `total` — `startup` is
* the same fixture with zero rounds, and it dominates a short run.
*
* Measured on an M-series laptop, Node v26.7.0, 30 rounds, alternating twice
* against the per-require work removed in #16376, reading the `modules`
* column:
*
* CJS 1992 ms -> 1893 ms and 2013 ms -> 1886 ms
* ESM 3031 ms -> 2937 ms and 3018 ms -> 2962 ms
*
* about 5-6% of CJS module-loading time and 2-3% of ESM's. Re-runs of the same
* pair minutes apart moved by more than that gap, which is why two
* alternations are the minimum worth reporting.
*/
'use strict';
const {spawnSync} = require('node:child_process');
const os = require('node:os');
const path = require('node:path');
const fs = require('graceful-fs');
const NODE_COUNT = 2047; // a complete binary tree, 11 deep
const RUNS = 5;
const JEST_BIN = path.resolve(__dirname, '../../jest-cli/bin/jest.js');
const iterations = Number(process.argv[2] ?? 30);
const onlyMode = process.argv[3];
function writeFixture(root, mode) {
const extension = mode === 'esm' ? '.mjs' : '.js';
fs.mkdirSync(path.join(root, 'modules'), {recursive: true});
fs.mkdirSync(path.join(root, '__tests__'), {recursive: true});
const manifest = {
jest: {testEnvironment: 'node', testTimeout: 600_000, transform: {}},
name: `jest-runtime-benchmark-${mode}`,
private: true,
version: '0.0.0',
};
if (mode === 'esm') {
manifest.type = 'module';
}
fs.writeFileSync(
path.join(root, 'package.json'),
`${JSON.stringify(manifest, null, 2)}\n`,
);
for (let index = 0; index < NODE_COUNT; index++) {
const children = [2 * index + 1, 2 * index + 2].filter(
child => child < NODE_COUNT,
);
const sum = children.map(child => ` + m${child}`).join('');
const lines =
mode === 'esm'
? [
"import * as path from 'node:path';",
...children.map(
child => `import m${child} from './m${child}.mjs';`,
),
`export default ${index} + path.sep.length${sum};`,
]
: [
"const path = require('node:path');",
...children.map(
child => `const m${child} = require('./m${child}.js');`,
),
`module.exports = ${index} + path.sep.length${sum};`,
];
fs.writeFileSync(
path.join(root, 'modules', `m${index}${extension}`),
`${lines.join('\n')}\n`,
);
}
const test =
mode === 'esm'
? [
"import {expect, jest, test} from '@jest/globals';",
'const rounds = Number(process.env.BENCHMARK_ROUNDS);',
"test('loads the tree', async () => {",
' for (let round = 0; round < rounds; round++) {',
' jest.resetModules();',
" const {default: root} = await import('../modules/m0.mjs');",
" expect(typeof root).toBe('number');",
' }',
'});',
]
: [
'const rounds = Number(process.env.BENCHMARK_ROUNDS);',
"test('loads the tree', () => {",
' for (let round = 0; round < rounds; round++) {',
' jest.resetModules();',
" expect(typeof require('../modules/m0.js')).toBe('number');",
' }',
'});',
];
fs.writeFileSync(
path.join(root, '__tests__', `benchmark.test${extension}`),
`${test.join('\n')}\n`,
);
}
function median(cwd, mode, rounds) {
const samples = [];
// The first run is a warmup: it pays for the OS file cache the rest reuse.
for (let run = 0; run <= RUNS; run++) {
const start = process.hrtime.bigint();
const result = spawnSync(
process.execPath,
[JEST_BIN, '--no-cache', '--runInBand', '--silent'],
{
cwd,
env: {
...process.env,
BENCHMARK_ROUNDS: String(rounds),
...(mode === 'esm'
? {NODE_OPTIONS: '--experimental-vm-modules'}
: undefined),
},
stdio: 'pipe',
},
);
if (result.status !== 0) {
console.error(result.stderr.toString());
throw new Error(`the ${mode} fixture failed to run`);
}
if (run > 0) {
samples.push(Number(process.hrtime.bigint() - start) / 1e6);
}
}
samples.sort((left, right) => left - right);
return samples;
}
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'jest-runtime-benchmark-'));
try {
for (const mode of ['cjs', 'esm']) {
if (onlyMode && mode !== onlyMode) continue;
const cwd = path.join(root, mode);
writeFixture(cwd, mode);
const startup = median(cwd, mode, 0);
const samples = median(cwd, mode, iterations);
const total = samples[Math.floor(samples.length / 2)];
const floor = startup[Math.floor(startup.length / 2)];
console.log(
`${mode} total ${total.toFixed(0)} ms startup ${floor.toFixed(
0,
)} ms modules ${(total - floor).toFixed(0)} ms (samples ${samples
.map(sample => sample.toFixed(0))
.join(' ')})`,
);
}
} finally {
fs.rmSync(root, {force: true, recursive: true});
}
+135
View File
@@ -0,0 +1,135 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {JestEnvironment} from '@jest/environment';
import {expect} from '@jest/globals';
import {SourceMapRegistry} from '@jest/source-map';
import {TestContext, V8CoverageResult} from '@jest/test-result';
import {
CallerTransformOptions,
ScriptTransformer,
ShouldInstrumentOptions,
shouldInstrument,
} from '@jest/transform';
import {Config, Global as Global_2} from '@jest/types';
import {IHasteMap, IModuleMap} from 'jest-haste-map';
import Resolver from 'jest-resolve';
declare interface EnvironmentGlobals extends Global_2.TestFrameworkGlobals {
expect: typeof expect;
}
declare type HasteMapOptions = {
console?: Console;
maxWorkers: number;
resetCache: boolean;
watch?: boolean;
watchman: boolean;
workerThreads?: boolean;
};
declare class Runtime {
private readonly fileCache;
private readonly _config;
private readonly _coverageOptions;
private readonly _environment;
private readonly mockState;
private readonly registries;
private readonly testMainModule;
private readonly requireBuilder;
private readonly executor;
private readonly esmLoader;
private readonly cjsLoader;
private readonly _moduleMocker;
private readonly cjsExportsCache;
private readonly _testPath;
private readonly _resolution;
private readonly transformCache;
private readonly v8Coverage;
private readonly coreModule;
private readonly jestGlobals;
private readonly testState;
private readonly loggedReferenceErrors;
constructor(
config: Config.ProjectConfig,
environment: JestEnvironment,
resolver: Resolver,
transformer: ScriptTransformer,
cacheFS: Map<string, string>,
coverageOptions: ShouldInstrumentOptions,
testPath: string,
globalConfig: Config.GlobalConfig,
);
static shouldInstrument: typeof shouldInstrument;
static createContext(
config: Config.ProjectConfig,
options: {
console?: Console;
maxWorkers: number;
watch?: boolean;
watchman: boolean;
},
): Promise<TestContext>;
static createHasteMap(
config: Config.ProjectConfig,
options?: HasteMapOptions,
): Promise<IHasteMap>;
static createResolver(
config: Config.ProjectConfig,
moduleMap: IModuleMap,
): Resolver;
unstable_shouldLoadAsEsm(modulePath: string): boolean;
unstable_importModule(
from: string,
moduleName?: string,
): Promise<unknown | void>;
requireModule<T = unknown>(
from: string,
moduleName?: string,
options?: TransformOptions,
isRequireActual?: boolean,
): T;
requireInternalModule<T = unknown>(from: string, to?: string): T;
requireActual<T = unknown>(from: string, moduleName: string): T;
requireMock<T = unknown>(from: string, moduleName: string): T;
private _requireMockWithId;
private installGetBuiltinModule;
private _esmMockTarget;
private _getFullTransformationOptions;
requireModuleOrMock<T = unknown>(from: string, moduleName: string): T;
isolateModules(fn: () => void): void;
isolateModulesAsync(fn: () => Promise<void>): Promise<void>;
resetModules(): void;
collectV8Coverage(): Promise<void>;
stopCollectingV8Coverage(): Promise<void>;
getAllCoverageInfoCopy(): JestEnvironment['global']['__coverage__'];
getAllV8CoverageInfoCopy(): V8CoverageResult;
getSourceMaps(): SourceMapRegistry;
setMock(
from: string,
moduleName: string,
mockFactory: () => unknown,
options?: {
virtual?: boolean;
},
): void;
private setModuleMock;
restoreAllMocks(): void;
resetAllMocks(): void;
clearAllMocks(): void;
enterTestCode(): void;
leaveTestCode(): void;
teardown(): void;
private _generateMock;
private _logFormattedReferenceError;
setGlobalsForRuntime(globals: EnvironmentGlobals): void;
}
export default Runtime;
declare interface TransformOptions extends Required<CallerTransformOptions> {
isInternalModule: boolean;
}
+5254
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
import cjsModule from './index.js';
export default cjsModule.default;
+58
View File
@@ -0,0 +1,58 @@
{
"name": "jest-runtime",
"version": "30.5.1",
"repository": {
"type": "git",
"url": "https://github.com/jestjs/jest.git",
"directory": "packages/jest-runtime"
},
"license": "MIT",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"exports": {
".": {
"types": "./build/index.d.ts",
"require": "./build/index.js",
"import": "./build/index.mjs",
"default": "./build/index.js"
},
"./package.json": "./package.json"
},
"dependencies": {
"@jest/environment": "30.5.1",
"@jest/fake-timers": "30.5.1",
"@jest/globals": "30.5.1",
"@jest/source-map": "30.5.0",
"@jest/test-result": "30.5.1",
"@jest/transform": "30.5.1",
"@jest/types": "30.5.1",
"@types/node": "*",
"chalk": "^4.1.2",
"cjs-module-lexer": "^2.2.0",
"collect-v8-coverage": "^1.0.2",
"es-module-lexer": "^2.1.0",
"glob": "^13.0.6",
"graceful-fs": "^4.2.11",
"jest-haste-map": "30.5.1",
"jest-message-util": "30.5.1",
"jest-mock": "30.5.1",
"jest-regex-util": "30.5.0",
"jest-resolve": "30.5.1",
"jest-snapshot": "30.5.1",
"jest-util": "30.5.1",
"slash": "^3.0.0",
"strip-bom": "^4.0.0"
},
"devDependencies": {
"@jest/test-utils": "30.5.1",
"@types/graceful-fs": "^4.1.9",
"jest-environment-node": "30.5.1"
},
"engines": {
"node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
},
"publishConfig": {
"access": "public"
},
"gitHead": "9ab14feccd6c15fec1334dbcb03a53a079d153d3"
}