Skip to content

Commit

Permalink
feat(Programmatic API): Add programmatic API
Browse files Browse the repository at this point in the history
Add a documented programmatic API to run jest. It allows this use case:

```ts
import { run, readConfigs } from 'jest';
const { globalConfig, configs } = await readConfigs(process.argv, [process.cwd()]);
// change globalConfig or configs as you see fit
const results = await run(globalConfig, configs);
console.log(results);
```
  • Loading branch information
nicojs committed Apr 9, 2023
1 parent 285b40d commit 908c73d
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 39 deletions.
36 changes: 36 additions & 0 deletions packages/jest-core/src/__tests__/runCore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* 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 {tmpdir} from 'os';
import {resolve} from 'path';
import {makeGlobalConfig, makeProjectConfig} from '@jest/test-utils';
import {runCore} from '../';
import runJest from '../runJest';

jest.mock('jest-runtime', () => ({
createHasteMap: () => ({
build: jest.fn(),
}),
}));
jest.mock('../lib/createContext', () => jest.fn());
jest.mock('../runJest', () =>
jest.fn(({onComplete}) => {
onComplete({results: {success: true}});
}),
);

describe(runCore, () => {
it('should run once and provide the result', async () => {
const actualResult = await runCore(makeGlobalConfig(), [
makeProjectConfig({
cacheDirectory: resolve(tmpdir(), 'jest_runCore_test'),
}),
]);
expect(jest.mocked(runJest)).toHaveBeenCalled();
expect(actualResult).toEqual({results: {success: true}});
});
});
90 changes: 52 additions & 38 deletions packages/jest-core/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ export async function runCLI(
globalConfig: Config.GlobalConfig;
}> {
performance.mark('jest/runCLI:start');
let results: AggregatedResult | undefined;

// If we output a JSON object, we can't write anything to stdout, since
// it'll break the JSON structure and it won't be valid.
Expand Down Expand Up @@ -96,24 +95,13 @@ export async function runCLI(
);
}

await _run10000(
const results = await runCore(
globalConfig,
configsOfProjectsToRun,
hasDeprecationWarnings,
outputStream,
r => {
results = r;
},
);

if (argv.watch || argv.watchAll) {
// If in watch mode, return the promise that will never resolve.
// If the watch mode is interrupted, watch should handle the process
// shutdown.
// eslint-disable-next-line @typescript-eslint/no-empty-function
return new Promise(() => {});
}

if (!results) {
throw new Error(
'AggregatedResult must be present after test run is complete',
Expand Down Expand Up @@ -167,13 +155,28 @@ const buildContextsAndHasteMaps = async (
return {contexts, hasteMapInstances};
};

const _run10000 = async (
/**
* Runs Jest either in watch mode or as a one-off. This is a lower-level API than `runCLI` and is intended for internal use by `runCLI` or externally.
* Note that `process.exit` might be called when using `globalConfig.watch` or `globalConfig.watchAll` is true.
*
* @param globalConfig The global configuration to use for this run. It can be obtained using `readConfigs` (imported from 'jest-config').
* @param configs The project configurations to run. It can be obtained using `readConfigs` (imported from 'jest-config').
* @param warnForDeprecations Whether or not to warn for deprecation messages when `globalConfig.watch` or `globalConfig.watchAll` is true.
* @param outputStream The stream to write output to. If not provided, it defaults to `process.stdout`.
* @returns A Promise that resolves to the result, or never resolves when `globalConfig.watch` or `globalConfig.watchAll` is true.
* @example
* import { runCore, readConfigs } from 'jest';
*
* const { globalConfig, configs } = await readConfigs(process.argv, [process.cwd()]);
* const results = await runCore(globalConfig, configs);
* console.log(results);
*/
export const runCore = async (
globalConfig: Config.GlobalConfig,
configs: Array<Config.ProjectConfig>,
hasDeprecationWarnings: boolean,
outputStream: NodeJS.WriteStream,
onComplete: OnCompleteCallback,
) => {
warnForDeprecations = false,
outputStream: NodeJS.WriteStream = process.stdout,
): Promise<AggregatedResult> => {
// Queries to hg/git can take a while, so we need to start the process
// as soon as possible, so by the time we need the result it's already there.
const changedFilesPromise = getChangedFilesPromise(globalConfig, configs);
Expand Down Expand Up @@ -222,36 +225,47 @@ const _run10000 = async (
);
performance.mark('jest/buildContextsAndHasteMaps:end');

globalConfig.watch || globalConfig.watchAll
? await runWatch(
contexts,
configs,
hasDeprecationWarnings,
globalConfig,
outputStream,
hasteMapInstances,
filter,
)
: await runWithoutWatch(
globalConfig,
contexts,
outputStream,
onComplete,
changedFilesPromise,
filter,
);
if (globalConfig.watch || globalConfig.watchAll) {
await runWatch(
contexts,
configs,
warnForDeprecations,
globalConfig,
outputStream,
hasteMapInstances,
filter,
);
// If in watch mode, return the promise that will never resolve.
// If the watch mode is interrupted, watch should handle the process
// shutdown.
// eslint-disable-next-line @typescript-eslint/no-empty-function
return new Promise(() => {});
} else {
let result: AggregatedResult;
await runWithoutWatch(
globalConfig,
contexts,
outputStream,
r => {
result = r;
},
changedFilesPromise,
filter,
);
return result!;
}
};

const runWatch = async (
contexts: Array<TestContext>,
_configs: Array<Config.ProjectConfig>,
hasDeprecationWarnings: boolean,
warnForDeprecations: boolean,
globalConfig: Config.GlobalConfig,
outputStream: NodeJS.WriteStream,
hasteMapInstances: Array<IHasteMap>,
filter?: Filter,
) => {
if (hasDeprecationWarnings) {
if (warnForDeprecations) {
try {
await handleDeprecationWarnings(outputStream, process.stdin);
return await watch(
Expand Down
3 changes: 2 additions & 1 deletion packages/jest-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@

export {default as SearchSource} from './SearchSource';
export {createTestScheduler} from './TestScheduler';
export {runCLI} from './cli';
export {runCLI, runCore} from './cli';
export {default as getVersion} from './version';
export {readConfigs, readInitialOptions} from 'jest-config';
3 changes: 3 additions & 0 deletions packages/jest/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export {
createTestScheduler,
getVersion,
runCLI,
readConfigs,
readInitialOptions,
runCore,
} from '@jest/core';

export {run} from 'jest-cli';
Expand Down

0 comments on commit 908c73d

Please # to comment.