From 89304df036e6db0615a6af0da72d6dad20275dc0 Mon Sep 17 00:00:00 2001 From: Vladimir Sheremet Date: Mon, 12 Oct 2026 11:26:43 +0200 Subject: [PATCH] chore: rename to --since-last-run --- docs/.vitepress/config.ts | 4 --- docs/.vitepress/scripts/cli-generator.ts | 2 +- docs/config/stale.md | 24 ------------- docs/guide/cli-generated.md | 7 ---- docs/guide/cli.md | 21 ++++++++++++ docs/guide/recipes/affected-tests.md | 34 +++++++++---------- packages/vitest/src/node/cli/cli-api.ts | 7 ++++ packages/vitest/src/node/cli/cli-config.ts | 6 ++-- .../vitest/src/node/config/resolveConfig.ts | 6 ++-- packages/vitest/src/node/types/config.ts | 7 ---- test/e2e/test/vcs-mtime.test.ts | 34 +++++++++---------- 11 files changed, 71 insertions(+), 81 deletions(-) delete mode 100644 docs/config/stale.md diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 12043cc47..47b2c9687 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -620,10 +620,6 @@ export default ({ mode }: { mode: string }) => { text: 'changed', link: '/config/changed', }, - { - text: 'stale', - link: '/config/stale', - }, { text: 'experimental', link: '/config/experimental', diff --git a/docs/.vitepress/scripts/cli-generator.ts b/docs/.vitepress/scripts/cli-generator.ts index 717b037b5..fcac7c9b8 100644 --- a/docs/.vitepress/scripts/cli-generator.ts +++ b/docs/.vitepress/scripts/cli-generator.ts @@ -9,7 +9,7 @@ const cliTablePath = resolve(docsDir, './guide/cli-generated.md') const nonNullable = (value: T): value is NonNullable => value !== null && value !== undefined -const skipCli = new Set(['mergeReports', 'shard']) +const skipCli = new Set(['mergeReports', 'shard', 'sinceLastRun']) const skipConfig = new Set([ 'config', diff --git a/docs/config/stale.md b/docs/config/stale.md deleted file mode 100644 index e17cd888e..000000000 --- a/docs/config/stale.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: stale | Config -outline: deep ---- - -### stale 5.1.0 {#stale} - -- **Type:** `boolean` -- **Default:** `false` -- **CLI:** `--stale` - -Run only the tests whose dependencies were modified since the last run of the test. A test is stale when one of the files it loaded during its last run has a newer modification time than that run, or was deleted. A test that never ran with this option has no record and always runs. A test file that failed in its last run also runs, until it passes, because the cause of a failure can be outside the recorded files, for example an environment variable or a server that was down. - -```sh -vitest run --stale -``` - -Every run with this option updates the records of the tests it ran, so the next run selects only the tests affected by changes made in between. To run every test again, run Vitest without the flag, or delete the cache with [`--clearCache`](/guide/cli#clearcache). - -The option requires [`cache`](/config/cache). Vitest throws an error if the cache is disabled. - -::: warning -The modification time comes from the file system. A `git checkout` or an unpacked archive that sets old modification times is not detected. Files that a test did not load during its last run, for example a new `.env` file or a new `__mocks__` file, are not detected either. See [`experimental.recordDependencies`](/config/experimental#experimental-recorddependencies) for the full list of recorded files. -::: diff --git a/docs/guide/cli-generated.md b/docs/guide/cli-generated.md index 381b1bcfe..b7d2ba51f 100644 --- a/docs/guide/cli-generated.md +++ b/docs/guide/cli-generated.md @@ -514,13 +514,6 @@ Ignore any unhandled errors that occur Run tests that are affected by the changed files (default: `false`) -### stale - -- **CLI:** `--stale` -- **Config:** [stale](/config/stale) - -Run only the tests whose recorded dependencies were modified since the last run of the test, and the tests that failed in their last run. Enables `--changed`, `--experimental.recordDependencies` and the `mtime` provider (default: `false`) - ### sequence.shuffle.files - **CLI:** `--sequence.shuffle.files` diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 14d6bcc25..59caa7408 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -271,3 +271,24 @@ Merges every blob report located in the specified folder (`.vitest/blob/` by def ```sh vitest --merge-reports --reporter=junit ``` + +### since-last-run 5.1.0 {#since-last-run} + +- **Type:** `boolean` +- **Default:** `false` + +Run only the tests whose dependencies were modified since the last run of the test. A test is selected when one of the files it loaded during its last run has a newer modification time than that run, or was deleted. A test that never ran with this flag has no record and always runs. A test file that failed in its last run also runs, until it passes, because the cause of a failure can be outside the recorded files, for example an environment variable or a server that was down. + +```sh +vitest run --since-last-run +``` + +The flag enables [`changed`](/config/changed), [`experimental.recordDependencies`](/config/experimental#experimental-recorddependencies) and the `mtime` [provider](/config/experimental#experimental-vcsprovider). Every run with this flag updates the records of the tests it ran, so the next run selects only the tests affected by changes made in between. To run every test again, run Vitest without the flag, or delete the cache with [`--clearCache`](#clearcache). + +The flag requires [`cache`](/config/cache). Vitest throws an error if the cache is disabled. + +::: warning +The modification time comes from the file system. A `git checkout` or an unpacked archive that sets old modification times is not detected. Files that a test did not load during its last run, for example a new `.env` file or a new `__mocks__` file, are not detected either. See [`experimental.recordDependencies`](/config/experimental#experimental-recorddependencies) for the full list of recorded files. +::: + +See [Running Only Affected Tests](/guide/recipes/affected-tests) for a comparison with `--changed`. diff --git a/docs/guide/recipes/affected-tests.md b/docs/guide/recipes/affected-tests.md index c94d66054..ac7973e5e 100644 --- a/docs/guide/recipes/affected-tests.md +++ b/docs/guide/recipes/affected-tests.md @@ -7,7 +7,7 @@ title: Running Only Affected Tests | Recipes A full run of a large suite is the right check before a merge, but it is slow feedback for a small change. If you edited one module, only the tests that load that module can change their result. Vitest has two flags that select those tests. They differ in one question: changed compared to what? - [`--changed`](/config/changed) compares the working tree with a Git reference. -- [`--stale`](/config/stale) compares every file a test loaded with the time of the test's last run. +- [`--since-last-run`](/guide/cli#since-last-run) compares every file a test loaded with the time of the test's last run. ## `--changed`: what differs from a Git reference @@ -21,38 +21,38 @@ Vitest asks Git for the changed files, then finds the test files that import the The reference is the baseline, so the same command gives the same selection until the baseline moves. This fits checks that are defined in terms of Git: a pull request against `origin/main`, or a pre-push hook that covers the commits you are about to push. It requires a Git repository, and a change that is committed and merged stops being "changed", even if the tests never ran against it on your machine. -## `--stale`: what changed since the test last ran +## `--since-last-run`: what changed since the test last ran ```sh -vitest run --stale +vitest run --since-last-run ``` -The first run with `--stale` runs every test and records the files each test loaded, including dynamic imports, mocks, setup files and snapshot files. Every later run compares the modification time of those files with the time of the run that recorded them. A test is stale when one of its files is newer or was deleted, or when the test has no record yet. A test file that failed in its last run is always stale, so it runs until it passes, even if the fix was outside the recorded files. +The first run with `--since-last-run` runs every test and records the files each test loaded, including dynamic imports, mocks, setup files and snapshot files. Every later run compares the modification time of those files with the time of the run that recorded them. A test runs again when one of its files is newer or was deleted, or when the test has no record yet. A test file that failed in its last run also runs again, until it passes, even if the fix was outside the recorded files. -Each run updates the records of the tests it ran, so the baseline moves with you: after a stale run, only the next edit makes tests stale again. Git is not involved, so the flag works in a fresh container, in a worktree with an unrelated history, or in a directory that is not a repository. A new test file, or one that was never recorded, always runs. +Each run updates the records of the tests it ran, so the baseline moves with you: after a run, only the next edit selects tests again. Git is not involved, so the flag works in a fresh container, in a worktree with an unrelated history, or in a directory that is not a repository. A new test file, or one that was never recorded, always runs. -The records live in the [cache](/config/cache). `vitest run --clearCache` deletes them, and the next `--stale` run is a full run again. +The records live in the [cache](/config/cache). `vitest run --clearCache` deletes them, and the next `--since-last-run` run is a full run again. ## Which one to pick -| You want to run the tests affected by… | Use | -| ---------------------------------------------------------------------- | ------------------------ | -| a branch, before opening or updating a pull request | `--changed origin/main` | -| the commits you are about to push | `--changed ` | -| your uncommitted edits, in a Git repository | `--changed` or `--stale` | -| your edits since you last ran the tests, committed or not | `--stale` | -| edits in a directory without Git, or with a history you do not control | `--stale` | +| You want to run the tests affected by… | Use | +| ---------------------------------------------------------------------- | --------------------------------- | +| a branch, before opening or updating a pull request | `--changed origin/main` | +| the commits you are about to push | `--changed ` | +| your uncommitted edits, in a Git repository | `--changed` or `--since-last-run` | +| your edits since you last ran the tests, committed or not | `--since-last-run` | +| edits in a directory without Git, or with a history you do not control | `--since-last-run` | -`--changed` is the better fit when the baseline is shared with other people: the reference is the same on every machine and in CI. `--stale` is the better fit when the baseline is your own last run: it does not matter whether you committed in between, or whether Git is there at all. +`--changed` is the better fit when the baseline is shared with other people: the reference is the same on every machine and in CI. `--since-last-run` is the better fit when the baseline is your own last run: it does not matter whether you committed in between, or whether Git is there at all. ## Agents -AI coding agents run tests after almost every edit. They often work in a container or a temporary worktree, commit in the middle of a task, and start a new process for every command. A Git baseline fits none of that: `--changed` is empty right after a commit, and it needs a repository. `--stale` tracks what the agent actually ran, so each `vitest run --stale` covers exactly the edits since the previous one, and the first command in a fresh environment is a full run that builds the records. +AI coding agents run tests after almost every edit. They often work in a container or a temporary worktree, commit in the middle of a task, and start a new process for every command. A Git baseline fits none of that: `--changed` is empty right after a commit, and it needs a repository. `--since-last-run` tracks what the agent actually ran, so each `vitest run --since-last-run` covers exactly the edits since the previous one, and the first command in a fresh environment is a full run that builds the records. Tell the agent about it in the instructions file of your project: ```md [AGENTS.md] -- After a change, run `vitest run --stale` to test only what the change affects. +- After a change, run `vitest run --since-last-run` to test only what the change affects. - Before you finish, run `vitest run` once for the whole suite. ``` @@ -61,6 +61,6 @@ Two things the records cannot see: a file that no test loaded during the last ru ## See also - [`changed`](/config/changed) -- [`stale`](/config/stale) +- [`--since-last-run`](/guide/cli#since-last-run) - [`experimental.recordDependencies`](/config/experimental#experimental-recorddependencies) - [Test Filtering](/guide/filtering) diff --git a/packages/vitest/src/node/cli/cli-api.ts b/packages/vitest/src/node/cli/cli-api.ts index 9802cda63..73ef6162a 100644 --- a/packages/vitest/src/node/cli/cli-api.ts +++ b/packages/vitest/src/node/cli/cli-api.ts @@ -54,6 +54,13 @@ export interface CliOptions extends UserConfig { */ configLoader?: ViteInlineConfig extends { configLoader?: infer T } ? T : never + /** + * Run only the tests whose recorded dependencies were modified since the last run of the test, + * and the tests that failed in their last run. Requires `cache`. + * @experimental + */ + sinceLastRun?: boolean + /** * Only run benchmark projects, filtering out all other projects. * Set automatically by `vitest bench`. diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index 06f1cfa60..b37ac683a 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -505,7 +505,8 @@ export const cliOptionsConfig: VitestCLIOptions = { description: 'Run tests that are affected by the changed files (default: `false`)', argument: '[since]', }, - stale: { + sinceLastRun: { + alias: 'since-last-run', description: 'Run only the tests whose recorded dependencies were modified since the last run of the test, and the tests that failed in their last run. Enables `--changed`, `--experimental.recordDependencies` and the `mtime` provider (default: `false`)', }, @@ -1035,7 +1036,8 @@ export const collectCliOptionsConfig: VitestCLIOptions = { description: 'Print only tests that are affected by the changed files (default: `false`)', argument: '[since]', }, - stale: { + sinceLastRun: { + alias: 'since-last-run', description: 'Print only the tests whose recorded dependencies were modified since the last run of the test, and the tests that failed in their last run (default: `false`)', }, diff --git a/packages/vitest/src/node/config/resolveConfig.ts b/packages/vitest/src/node/config/resolveConfig.ts index fbe7088b5..1fd34d5fd 100644 --- a/packages/vitest/src/node/config/resolveConfig.ts +++ b/packages/vitest/src/node/config/resolveConfig.ts @@ -4,6 +4,7 @@ import type { Plugin as VitePlugin, UserConfig as ViteUserConfig, } from 'vite' +import type { CliOptions } from '../cli/cli-api' import type { Logger } from '../logger' import type { ApiConfig, @@ -513,10 +514,11 @@ export function resolveTestConfig( } } } - if (resolved.stale) { + // CLI options are merged into the test config + if ((options as CliOptions).sinceLastRun) { if (resolved.cache === false) { throw new Error( - 'The "stale" option requires the "cache" option to store the dependencies of the test files.', + 'The "--since-last-run" flag requires the "cache" option to store the dependencies of the test files.', ) } resolved.changed ||= true diff --git a/packages/vitest/src/node/types/config.ts b/packages/vitest/src/node/types/config.ts index 010c2aa3e..cc2d7ad2c 100644 --- a/packages/vitest/src/node/types/config.ts +++ b/packages/vitest/src/node/types/config.ts @@ -1104,13 +1104,6 @@ export interface InlineConfig { * @default false */ changed?: boolean | string - - /** - * Runs only the tests whose recorded dependencies were modified since the last run of the test, - * and the tests that failed in their last run. Requires `cache`. - * @default false - */ - stale?: boolean } export interface TypecheckConfig { diff --git a/test/e2e/test/vcs-mtime.test.ts b/test/e2e/test/vcs-mtime.test.ts index 6fb1468c8..b40f58ce8 100644 --- a/test/e2e/test/vcs-mtime.test.ts +++ b/test/e2e/test/vcs-mtime.test.ts @@ -4,6 +4,7 @@ import { expect, test } from 'vitest' import { runInlineTests, runVitest } from '../../test-utils' const recorded = { cache: true, experimental: { recordDependencies: true, vcsProvider: 'mtime' } } +const sinceLastRun = { cache: true, $cliOptions: { sinceLastRun: true } } function testFile(name: string, imports = '') { return ` @@ -163,24 +164,23 @@ test('coverage of changed files uses the dependencies of the run', async () => { expect(Object.keys(report).map((file) => relative(fs.root, file))).toEqual(['src/helper.js']) }) -test('--stale records the dependencies and runs the stale tests', async () => { +test('--since-last-run records the dependencies and runs the affected tests', async () => { // the first run has no records, so every test runs - const { fs, stderr, testTree } = await runInlineTests(files, { cache: true, stale: true }) + const { fs, stderr, testTree } = await runInlineTests(files, sinceLastRun) expect(stderr).toBe('') expect(Object.keys(testTree()).sort()).toEqual(['a.test.js', 'b.test.js']) fs.editFile('src/other.js', () => 'export const other = 1') - const stale = await runVitest({ root: fs.root, cache: true, stale: true }) - expect(stale.stderr).toBe('') - expect(Object.keys(stale.testTree())).toEqual(['b.test.js']) + const changed = await runVitest({ root: fs.root, ...sinceLastRun }) + expect(changed.stderr).toBe('') + expect(Object.keys(changed.testTree())).toEqual(['b.test.js']) - const fresh = await runVitest({ root: fs.root, cache: true, stale: true }) - expect(fresh.stderr).toBe('') - expect(fresh.ctx!._sourceFilterResult).toEqual({ affected: 0, total: 2, failed: 0 }) + const unchanged = await runVitest({ root: fs.root, ...sinceLastRun }) + expect(unchanged.stderr).toBe('') + expect(unchanged.ctx!._sourceFilterResult).toEqual({ affected: 0, total: 2, failed: 0 }) }) -test('--stale runs a failed test file until it passes', async () => { - const stale = { cache: true, stale: true } +test('--since-last-run runs a failed test file until it passes', async () => { const database = { DATABASE_URL: 'postgres://localhost:5432/test' } const { fs, errorTree } = await runInlineTests( { @@ -192,7 +192,7 @@ test('--stale runs a failed test file until it passes', async () => { }) `, }, - stale, + sinceLastRun, ) expect(errorTree()).toMatchInlineSnapshot(` { @@ -207,7 +207,7 @@ test('--stale runs a failed test file until it passes', async () => { } `) - const failed = await runVitest({ root: fs.root, ...stale }) + const failed = await runVitest({ root: fs.root, ...sinceLastRun }) expect(failed.exitCode).toBe(1) expect(failed.errorTree()).toMatchInlineSnapshot(` { @@ -227,7 +227,7 @@ test('--stale runs a failed test file until it passes', async () => { expect(related.stderr).toBe('') expect(Object.keys(related.testTree())).toEqual(['a.test.js']) - const fixed = await runVitest({ root: fs.root, ...stale, env: database }) + const fixed = await runVitest({ root: fs.root, ...sinceLastRun, env: database }) expect(fixed.stderr).toBe('') expect(fixed.testTree()).toMatchInlineSnapshot(` { @@ -237,14 +237,14 @@ test('--stale runs a failed test file until it passes', async () => { } `) - const passed = await runVitest({ root: fs.root, ...stale, env: database }) + const passed = await runVitest({ root: fs.root, ...sinceLastRun, env: database }) expect(passed.stderr).toBe('') expect(passed.ctx!._sourceFilterResult).toEqual({ affected: 0, total: 2, failed: 0 }) }) -test('--stale requires the cache', async () => { - const { stderr } = await runInlineTests(files, { cache: false, stale: true }) +test('--since-last-run requires the cache', async () => { + const { stderr } = await runInlineTests(files, { ...sinceLastRun, cache: false }) expect(stderr.split('\n')[0]).toMatchInlineSnapshot( - `"Error: The "stale" option requires the "cache" option to store the dependencies of the test files."`, + `"Error: The "--since-last-run" flag requires the "cache" option to store the dependencies of the test files."`, ) }) -- 2.51.2