Skip to content

Commit 8b26a38

Browse files
committed
feat(core): stabilize programmatic JS API with createRstest + /api surface
Move all programmatic entrypoints to `@rstest/core/api`; the main `@rstest/core` entry now exposes only in-test runtime globals, config helpers (defineConfig/loadConfig/merge*), and types. - Replace the standalone `runRstest` with an async `createRstest()` factory (rsbuild-`createRsbuild`-style) returning an `RstestInstance` with `run`/`listTests`/`mergeReports`/`close`. Construction carries static host wiring + config: `configFile` (a path to load) and `config` (an inline override). `config` accepts either an object (deep-merged over the disk config) or a `(loadedConfig) => RstestConfig` callback that receives the resolved disk config and returns the final config — letting callers transform it directly instead of merging. Per-invocation selection/control lives in `RunOptions` (filters/related/changed/shard/bail/...). - Add a jest-`runCLI`-aligned public `runCli(argv, { cwd })` that takes a parsed-object argv (positionals in `argv._`), stays host-safe (never calls `process.exit`), and returns a structured `TestRunResult`. - Keep the raw-argv CLI router (`startCli`) off the public surface: build it as its own internal `dist/cli.js` chunk (not a `package.json` export) and have the `rstest` bin import that artifact directly, mirroring how vitest builds `src/node/cli.ts` -> `dist/cli.js`. - Rename the internal sync factory `createRstest` -> `createRstestContext` and the internal type `RstestInstance` -> `RstestRunner`; share public result types + assembly helpers via `src/api/result.ts`, and dedupe the filter-resolve/context-build tail into `buildResolvedRunner`. - Migrate the vscode worker and the programmatic e2e fixtures. BREAKING CHANGE: programmatic APIs are no longer exported from `@rstest/core` (`createRstest`, `initCli`, `runCLI` removed from the main entry). Import them from `@rstest/core/api`. `runRstest(opts)` is removed -- use `const r = await createRstest(opts); await r.run()`.
1 parent 2791bd6 commit 8b26a38

28 files changed

Lines changed: 1028 additions & 494 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expect, it } from '@rstest/core';
2+
3+
it('a passes', () => {
4+
expect(1).toBe(1);
5+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { expect, it } from '@rstest/core';
2+
3+
it('b passes', () => {
4+
expect(2).toBe(2);
5+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { defineConfig } from '@rstest/core';
2+
3+
// Disk config includes BOTH test files; the programmatic `config` callback
4+
// receives this resolved object and narrows `include` to a single file.
5+
export default defineConfig({
6+
include: ['a.test.ts', 'b.test.ts'],
7+
reporters: [],
8+
});
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { describe, expect, it } from '@rstest/core';
2+
import { add } from './src/math';
3+
4+
describe('math', () => {
5+
it('adds', () => {
6+
expect(add(1, 2)).toBe(3);
7+
});
8+
});
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { defineConfig } from '@rstest/core';
2+
3+
export default defineConfig({
4+
include: ['**/*.test.ts'],
5+
reporters: [],
6+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const add = (a: number, b: number): number => a + b;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { describe, expect, it } from '@rstest/core';
2+
3+
describe('unrelated', () => {
4+
it('does not import the math source', () => {
5+
expect(true).toBe(true);
6+
});
7+
});
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { dirname, join } from 'node:path';
2+
import { fileURLToPath } from 'node:url';
3+
import { createRstest } from '@rstest/core/api';
4+
5+
const __dirname = dirname(fileURLToPath(import.meta.url));
6+
const cwd = join(__dirname, 'config-fn');
7+
8+
// Function-form `config`: the callback receives the resolved disk config and
9+
// returns the final config — here it narrows `include` from two files to one.
10+
const rstest = await createRstest({
11+
cwd,
12+
configFile: 'rstest.config.ts',
13+
config: (loaded) => {
14+
// Prove the disk config was loaded and handed in before transforming it.
15+
if (!loaded.include?.includes('b.test.ts')) {
16+
throw new Error(
17+
`config callback did not receive disk config; got include=${JSON.stringify(loaded.include)}`,
18+
);
19+
}
20+
return { ...loaded, include: ['a.test.ts'] };
21+
},
22+
});
23+
const result = await rstest.run();
24+
await rstest.close();
25+
26+
console.log(
27+
`__RSTEST_API_RESULT__${JSON.stringify({
28+
ok: result.ok,
29+
stats: result.stats,
30+
files: result.files
31+
.map((f) => ({ status: f.status, testPath: f.testPath.split('/').pop() }))
32+
.sort((a, b) => a.testPath.localeCompare(b.testPath)),
33+
})}__END__`,
34+
);

e2e/programmatic/fixtures/run-failing.mjs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import { fileURLToPath } from 'node:url';
22
import { dirname, join } from 'node:path';
3-
import { runRstest } from '@rstest/core/api';
3+
import { createRstest } from '@rstest/core/api';
44

55
const __dirname = dirname(fileURLToPath(import.meta.url));
66
const cwd = join(__dirname, 'disk');
77

8-
const result = await runRstest({
8+
const rstest = await createRstest({
99
cwd,
10-
inlineConfig: {
10+
config: {
1111
include: ['failing.test.ts'],
1212
reporters: [],
1313
},
1414
});
15+
const result = await rstest.run();
16+
await rstest.close();
1517

1618
console.log(
1719
`__RSTEST_API_RESULT__${JSON.stringify({

e2e/programmatic/fixtures/run-inline.mjs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
import { fileURLToPath } from 'node:url';
22
import { dirname, join } from 'node:path';
3-
import { runRstest } from '@rstest/core/api';
3+
import { createRstest } from '@rstest/core/api';
44

55
const __dirname = dirname(fileURLToPath(import.meta.url));
66
const cwd = join(__dirname, 'disk');
77

8-
const result = await runRstest({
8+
const rstest = await createRstest({
99
cwd,
10-
inlineConfig: {
10+
config: {
1111
include: ['*.test.ts'],
1212
exclude: ['failing.test.ts'],
1313
reporters: [],
1414
},
1515
});
16+
const result = await rstest.run();
17+
await rstest.close();
1618

1719
console.log(
1820
`__RSTEST_API_RESULT__${JSON.stringify({

0 commit comments

Comments
 (0)