Skip to content

feat(Programmatic API): Add programmatic API #14062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
908c73d
feat(Programmatic API): Add programmatic API
nicojs Apr 9, 2023
e2c65f8
Implement review comments
nicojs Apr 10, 2023
6a75acf
Fix lint: add copyright header
nicojs Apr 10, 2023
7d70600
Import performance from `perf_hooks`
nicojs Apr 11, 2023
8155081
Fix type error
nicojs Apr 12, 2023
79dbfd5
Remove pnpapi from types
nicojs Apr 20, 2023
d0fd9a9
Add e2e test for runCore
nicojs Apr 21, 2023
b9e9278
Update e2e test for programmatic run
nicojs Apr 21, 2023
d8f6e26
Fix lint e2e
nicojs Apr 21, 2023
68c924f
fix prettier
nicojs Apr 21, 2023
68ac3ef
Add unit tests
nicojs Apr 21, 2023
a375b0d
Add copyright header
nicojs Apr 21, 2023
0f8f6f2
Add docs for programmatic api
nicojs Apr 22, 2023
1935947
Rename `result` -> `results` to align with `runCLI` api
nicojs Apr 22, 2023
77723b3
Reorder exports
nicojs Apr 22, 2023
a749243
Format
nicojs Apr 22, 2023
48a5a94
Merge branch 'main' into feat/programmatic-api
nicojs Apr 30, 2023
ac2b879
Implement object oriented API proposal
nicojs May 2, 2023
e5c254f
Merge branch 'main' into feat/programmatic-api
nicojs May 2, 2023
59cfa1a
Fix lint
nicojs May 2, 2023
d570bc8
Undo unwanted changes
nicojs May 2, 2023
80d3c23
Undo export of `readConfigs` and `readInitialOptions`
nicojs May 2, 2023
0b6f93d
Merge branch 'main' into feat/programmatic-api
SimenB Oct 3, 2023
6877134
Merge branch 'main' into feat/programmatic-api
SimenB Oct 3, 2023
26e87e1
remove unused types field in tsconfig
SimenB Oct 3, 2023
f3e41ef
Merge branch 'main' into feat/programmatic-api
SimenB Oct 3, 2023
c3b95f8
fix(expect, jest-snapshot): Pass `test.failing` tests when containing…
KhaledElmorsy Oct 3, 2023
2677e1d
chore: update ESLint dependencies (#14595)
SimenB Oct 3, 2023
8ec975a
chore(deps): update yarn to v3.6.4 (#14600)
renovate[bot] Oct 4, 2023
ccc7e4e
feat: add `jest.advanceTimersToFrame()` (#14598)
alexreardon Oct 5, 2023
f04118d
fix: coverage badge is broken at readme (#14611)
nolddor Oct 10, 2023
f8e0259
docs: provide README for `jest-haste-map` package (#14613)
Mutesa-Cedric Oct 12, 2023
098cc21
docs: update with "experimental" message
nicojs Oct 13, 2023
65fe53f
docs: remove mention of old `runCore`
nicojs Oct 13, 2023
9f1a113
Replace old api docs with the new api using jest instance.
nicojs Oct 14, 2023
8da9922
Merge branch 'main' into feat/programmatic-api
nicojs Oct 14, 2023
6a26f7f
prettier
nicojs Oct 14, 2023
f6d0c5a
Merge branch 'main' into feat/programmatic-api
SimenB Oct 30, 2023
fe45d4f
Merge branch 'main' into feat/programmatic-api
nicojs Oct 31, 2023
ef783ed
refactor: remove unessesary config
nicojs Oct 31, 2023
46a7ce9
fix: use jest's native esm api in e2e test
nicojs Oct 31, 2023
9f056b8
refactor: remove `_` prefix
nicojs Oct 31, 2023
92d6d44
docs: remove reference to old `readInitialOptions`.
nicojs Oct 31, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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