> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Rstest instance


[Added in v0.12.0](https://github.com/web-infra-dev/rstest/releases/tag/v0.12.0)

Everything on this page is exported from the `@rstest/core/api` entry of [@rstest/core](https://github.com/web-infra-dev/rstest/tree/main/packages/core). Use these APIs to create Rstest instances, run or list tests, start watch sessions, and merge blob reports.

```ts
import { createRstest } from '@rstest/core/api';
```

:::warning
All exports are currently experimental and may change before Rstest 1.0.0. Pin `@rstest/core` to an exact version for now to ensure API stability.
:::

## createRstest

The `createRstest` function creates and returns an Rstest instance. It resolves `config` once during instance creation and reuses it as the instance's base config. Options passed to instance methods apply to the current operation without mutating the base config.

`cwd` defaults to `process.cwd()` and is the base for resolving a relative `config.root`; omitting `config` uses an empty inline config and does not discover a config file in `cwd`.

`createRstest` sets `RSTEST=true`, sets `NODE_ENV=test` only when it is unset, and never restores either environment variable.

### Example

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest({
  cwd: './packages/app',
  config: {
    include: ['src/**/*.test.ts'],
    reporters: [],
  },
});

const result = await rstest.run();
console.log(result.status);
```

### Load a config file

Use [`loadConfig`](/api/javascript-api/rstest-core.md#loadconfig) from the main entry to load a config file, then pass its return value (`{ content, filePath, dependencies }`) directly as `config`.

```ts
import { loadConfig } from '@rstest/core';
import { createRstest } from '@rstest/core/api';

const loaded = await loadConfig();
const rstest = await createRstest({ config: loaded });
```

### CreateRstestOptions

- **Type:**

```ts
interface LoadedRstestConfig {
  content: RstestConfig; // Loaded config content.
  filePath: string | null; // Source config file path returned by `loadConfig`, or `null`.
  dependencies?: string[];
}

interface CreateRstestOptions {
  cwd?: string;
  config?: RstestConfig | LoadedRstestConfig;
  configLoader?: 'auto' | 'jiti' | 'native'; // Loader for configs discovered through `projects`; defaults to `auto`.
}

function createRstest(options?: CreateRstestOptions): Promise<RstestInstance>;
```

## rstest.context

`rstest.context` is a read-only object resolved once when the instance is created. You can inspect the resolved state without running tests: use `rootPath` to map `testPath` values from results back to the workspace, `projects` to render or filter a multi-project setup, `config` to inspect the effective configuration, and `version` for compatibility checks.

### Example

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest({
  config: {
    projects: [
      {
        name: 'unit',
        include: ['tests/**/*.test.ts'],
      },
    ],
  },
});

console.log(rstest.context.version);
console.log(rstest.context.rootPath);

for (const project of rstest.context.projects) {
  console.log(project.name, project.rootPath);
}
```

### RstestContext

- **Type:**

```ts
interface ProjectContext {
  name: string; // Project name.
  rootPath: string; // Absolute project root path.
  configFilePath?: string; // Project config file path, when one is associated with the project.
  configFileDependencies?: string[]; // Project config dependencies, including parent project configuration files.
}

interface RstestContext {
  readonly version: string;
  readonly rootPath: string;
  readonly config: Readonly<NormalizedConfig>;
  readonly projects: readonly ProjectContext[];
}
```

### context.version

The version of `@rstest/core` currently in use.

- **Type:** `string`

### context.rootPath

The absolute root path of the Rstest instance. It is resolved from `config.root`, using `cwd` as the base for a relative value.

- **Type:** `string`

### context.config

The normalized config of the Rstest instance.

- **Type:** `Readonly<NormalizedConfig>`

### context.projects

The resolved project contexts. Without an explicit `projects` config, the array contains the default project named by `config.name` (`'rstest'` by default).

- **Type:** `readonly ProjectContext[]`

## rstest.run

`rstest.run()` runs one test cycle and returns a `TestRunResult`. It rejects when the run cannot start due to invalid options, configuration or compiler errors; test failures are reported through `status` without rejecting the promise; see [TestRunResult](#testrunresult).

### Example

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest({
  config: {
    include: ['src/**/*.test.ts'],
  },
});

const result = await rstest.run({
  filters: ['"src/foo.test.ts"'],
});

console.log(result.status);
console.log(result.summary.tests);
```

### RunOptions

`RunOptions` accepts every `rstest run` flag except `config`, `configLoader`, `root` and `trace`. See [CLI options](/guide/basic/cli.md#cli-options) for the full flag list and [`packages/core/src/api/types.ts`](https://github.com/web-infra-dev/rstest/blob/main/packages/core/src/api/types.ts) for the exact types.

`filters` is API-only; the CLI passes filters as positional arguments. It uses case-insensitive substring matching by default. Wrap a filter in matching single or double quotes to match an exact absolute or root-relative path, for example `filters: ['"src/foo.test.ts"', 'utils']`; quoted and unquoted filters can be mixed. Omit `filters` to select all files; an explicit empty array selects none.

- **Type:**

```ts
interface RunOptions {
  filters?: string[];
  // ...plus every other `rstest run` flag as an override of the config option
  // of the same name, e.g. `testTimeout`, `coverage`, `reporters`.
}

interface RstestInstance {
  run(options?: RunOptions): Promise<TestRunResult>;
}
```

### TestRunResult

- **Type:**

```ts
type TestRunStatus = 'pass' | 'fail' | 'error';

interface TestRunResult {
  status: TestRunStatus; // Overall status of the run.
  // Every other field matches the `onTestRunEnd` payload, minus `getSourcemap`.
}
```

The remaining fields (`results`, `testResults`, `summary`, `duration`, `snapshotSummary`, `unhandledErrors`, `coverage`, `rerunTestPaths`) are documented under [`onTestRunEnd`](/api/javascript-api/reporter.md#ontestrunend) in the Reporter API.

`status` is `'error'` when `unhandledErrors` is non-empty. It is `'fail'` when the run completed with a non-zero exit status, such as from failing tests or test files, a coverage threshold violation, no tests being found without `passWithNoTests`, or a `globalSetup` teardown failure. In watch mode, it is also `'fail'` while the session still holds a failing test file, even if the current cycle passed. Otherwise, it is `'pass'`.

Every entry in `unhandledErrors` is a plain object that can be serialized to JSON, not an `Error` instance.

## rstest.watch

`rstest.watch()` starts a watch session and returns a watcher that closes it.

### Example

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest({
  config: {
    include: ['src/**/*.test.ts'],
  },
});

const watcher = await rstest.watch({
  onResult(result) {
    console.log(
      result.status,
      result.results.map((file) => file.testPath),
      result.rerunTestPaths,
    );
  },
});

await watcher.close();
```

`onResult` runs after every completed cycle, including the first. `results`, `summary`, and `status` reflect the whole watch session; `rerunTestPaths` lists the files executed in the current cycle.

Browser projects are supported. A rerun execution error is delivered through `onResult` with `status: 'error'` and does not close the session; startup failures, including a browser `globalSetup` failure, reject `watch()`.

Errors thrown by `onResult` are isolated and do not stop the watch session. Calling `watcher.close()` releases the compiler, workers, file watchers, and pending `globalSetup` teardown. It is idempotent, repeated calls observe the same result, and it rejects if teardown fails.

`rstest.watch()` rejects `related` and `changed` because they select a fixed file set, while a watch session must pick up newly related tests. Use `rstest.run()` for these options. `related: false` and `changed: false` are ignored.

### WatchOptions

- **Type:**

```ts
interface WatchOptions {
  onResult?: (result: TestRunResult & { rerunTestPaths: string[] }) => void;
}

interface RstestWatcher {
  close(): Promise<void>;
}

interface RstestInstance {
  watch(options?: WatchOptions & RunOptions): Promise<RstestWatcher>;
}
```

## rstest.listTests

`rstest.listTests()` collects test files and test declarations without running test bodies.

### Example

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest({
  config: {
    include: ['src/**/*.test.ts'],
  },
});

const tests = await rstest.listTests({
  includeSuites: true,
  includeTaskLocation: true,
});

console.log(tests);
```

Set `filesOnly` to skip collecting declarations. `includeSuites` includes named suites as separate entries. `includeTaskLocation` from `RunOptions` adds source locations.

Every entry includes `testPath` and `project`. Entries from the implicit default project use `config.name` (`'rstest'` by default). Declaration entries include their own `name`, a suite-prefixed `fullName`, and `parentNames` for the hierarchy; file entries omit `name` and `fullName`. Skipped and todo declarations are included with `runMode` set to `skip` or `todo`; runnable declarations omit `runMode`.

Entries are returned in depth-first declaration order. A suite immediately precedes its descendants, and all entries from one file stay contiguous.

`rstest.listTests()` rejects with `ListTestsError` when collection fails instead of returning a partial or empty list.

`shard` applies only when passed explicitly to `rstest.listTests()`, limiting the result to the selected shard. When it is omitted, every file is listed; a `shard` value from the instance config is dropped as before.

### ListOptions

- **Type:**

```ts
interface ListOptions {
  filesOnly?: boolean;
  includeSuites?: boolean;
}

interface ListedTest {
  testPath: string; // Path of the test file.
  name?: string; // Declaration's own name; absent for file entries.
  fullName?: string; // Suite-prefixed display name; absent for file entries.
  parentNames?: string[]; // Names of the enclosing suites.
  project: string; // Project name.
  location?: TestLocation; // Source location when requested.
  runMode?: 'skip' | 'todo'; // Skip or todo mode for non-runnable declarations.
  type: 'file' | 'suite' | 'case'; // Entry kind.
}

interface RstestInstance {
  listTests(options?: ListOptions & RunOptions): Promise<ListedTest[]>;
}
```

## rstest.mergeReports

`rstest.mergeReports()` merges blob reports and returns the same result model as `rstest.run()`.

It rejects when the operation cannot produce a run result; failures in the merged tests are reported through `status` without rejecting the promise.

### Example

```ts
import { createRstest } from '@rstest/core/api';

const rstest = await createRstest();

const result = await rstest.mergeReports({
  path: './.rstest-reports',
  cleanup: true,
});

console.log(result.status);
```

`path` selects the blob-report directory. `cleanup` removes consumed reports after a successful merge.

### MergeReportsOptions

- **Type:**

```ts
interface MergeReportsOptions {
  path?: string;
  cleanup?: boolean;
}

interface RstestInstance {
  mergeReports(options?: MergeReportsOptions): Promise<TestRunResult>;
}
```

## runCLI

`runCLI` runs the Rstest command line in the current process. It parses `argv`, sets `process.exitCode`, and installs the CLI's signal handling. Use it when you want the CLI's process behavior; instance methods do not set `process.exitCode` or install signal handlers.

### Example

```ts
import { runCLI } from '@rstest/core/api';

runCLI({
  argv: [...process.argv.slice(0, 2), 'run', 'src/foo.test.ts', '--update'],
});
```

`argv` matches the shape of Node.js `process.argv` and defaults to `process.argv`: the first two entries are the executable and script paths and are ignored, while the command, filters, and flags start at index 2.
This is the same shape used by Rsbuild's and Rspress's `runCLI`.

See the [CLI documentation](/guide/basic/cli.md) for all available commands and flags.

### RunCLIOptions

- **Type:**

```ts
interface RunCLIOptions {
  argv?: string[];
}

function runCLI(options?: RunCLIOptions): void;
```
