[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat(cli): add `--repeats` CLI option (#10504)

authored by

Todor Andonov and committed by
GitHub
(Jun 7, 2026, 4:53 PM +0200) ee48b959 4c84cbcf

+78 -1
+4
docs/.vitepress/config.ts
··· 508 508 link: '/config/retry', 509 509 }, 510 510 { 511 + text: 'repeats', 512 + link: '/config/repeats', 513 + }, 514 + { 511 515 text: 'onConsoleLog', 512 516 link: '/config/onconsolelog', 513 517 },
+14
docs/config/repeats.md
··· 1 + --- 2 + title: repeats | Config 3 + outline: deep 4 + --- 5 + 6 + # repeats 7 + 8 + - **Type:** `number` 9 + - **Default:** `0` 10 + - **CLI:** `--repeats=<number>` 11 + 12 + Repeat every test a specific number of times regardless of the result. A test that uses the [`repeats`](/api/test#repeats) test option takes precedence over this value. 13 + 14 + This is useful for verifying that tests are stable across multiple runs. If a test fails on any repetition, the whole test is reported as failed.
+7
docs/guide/cli-generated.md
··· 637 637 638 638 Regex pattern to match error messages that should trigger a retry. Only errors matching this pattern will cause a retry (default: retry on all errors) 639 639 640 + ### repeats 641 + 642 + - **CLI:** `--repeats <number>` 643 + - **Config:** [repeats](/config/repeats) 644 + 645 + Repeat every test a specific number of times regardless of the result (default: `0`) 646 + 640 647 ### diff.aAnnotation 641 648 642 649 - **CLI:** `--diff.aAnnotation <annotation>`
+2
test/e2e/test/cli-config.test.ts
··· 27 27 globals: true, 28 28 expandSnapshotDiff: true, 29 29 retry: 6, 30 + repeats: 3, 30 31 testNamePattern: 'math', 31 32 passWithNoTests: true, 32 33 bail: 100, ··· 50 51 globals: true, 51 52 expandSnapshotDiff: true, 52 53 retry: 6, 54 + repeats: 3, 53 55 passWithNoTests: true, 54 56 bail: 100, 55 57 experimental: {
+26
test/e2e/test/repeats.test.ts
··· 1 + import type { TestModule } from 'vitest/node' 2 + import { resolve } from 'pathe' 3 + import { expect, test } from 'vitest' 4 + import { runVitest } from '../../test-utils' 5 + 6 + const root = resolve(__dirname, '..', 'fixtures', 'repeats') 7 + 8 + test('repeats config option is exposed to tests and repeats execution', async () => { 9 + const { ctx } = await runVitest({ 10 + root, 11 + include: ['repeats-config.test.ts'], 12 + repeats: 3, 13 + }) 14 + 15 + const file = ctx!.state.getFiles()[0] 16 + const testModule = ctx!.state.getReportedEntity(file)! as TestModule 17 + const tests = [...testModule.children.allTests()] 18 + 19 + const fromConfig = tests.find(t => t.name === 'uses repeats from config')! 20 + expect(fromConfig.options.repeats).toBe(3) 21 + expect(fromConfig.diagnostic()!.repeatCount).toBe(3) 22 + 23 + const overridden = tests.find(t => t.name === 'test option overrides config')! 24 + expect(overridden.options.repeats).toBe(1) 25 + expect(overridden.diagnostic()!.repeatCount).toBe(1) 26 + })
+1
packages/vitest/src/runtime/config.ts
··· 31 31 testTimeout: number 32 32 hookTimeout: number 33 33 retry: SerializableRetry 34 + repeats?: number 34 35 includeTaskLocation: boolean | undefined 35 36 tags: TestTagDefinition[] 36 37 tagsFilter: string[] | undefined
+9
test/e2e/fixtures/repeats/repeats-config.test.ts
··· 1 + import { expect, test } from 'vitest' 2 + 3 + test('uses repeats from config', () => { 4 + expect(1 + 1).toBe(2) 5 + }) 6 + 7 + test('test option overrides config', { repeats: 1 }, () => { 8 + expect(1 + 1).toBe(2) 9 + })
+5
packages/vitest/src/node/cli/cli-config.ts
··· 630 630 }, 631 631 }, 632 632 }, 633 + repeats: { 634 + description: 635 + 'Repeat every test a specific number of times regardless of the result (default: `0`)', 636 + argument: '<number>', 637 + }, 633 638 diff: { 634 639 description: 635 640 'DiffOptions object or a path to a module which exports DiffOptions object',
+1
packages/vitest/src/node/config/serializeConfig.ts
··· 45 45 // TODO: non serializable function? 46 46 diff: config.diff, 47 47 retry: config.retry, 48 + repeats: config.repeats, 48 49 disableConsoleIntercept: config.disableConsoleIntercept, 49 50 root: config.root, 50 51 name: config.name,
+1
packages/vitest/src/node/projects/resolveProjects.ts
··· 52 52 'expandSnapshotDiff', 53 53 'disableConsoleIntercept', 54 54 'retry', 55 + 'repeats', 55 56 'testNamePattern', 56 57 'passWithNoTests', 57 58 'bail',
+7
packages/vitest/src/node/types/config.ts
··· 831 831 retry?: SerializableRetry 832 832 833 833 /** 834 + * Repeat every test a specific number of times regardless of the result. 835 + * 836 + * @default 0 // Don't repeat 837 + */ 838 + repeats?: number 839 + 840 + /** 834 841 * Show full diff when snapshot fails instead of a patch. 835 842 */ 836 843 expandSnapshotDiff?: boolean
+1 -1
packages/vitest/src/runtime/runner/suite.ts
··· 387 387 file: (currentSuite?.file ?? collectorContext.currentSuite?.file)!, 388 388 timeout, 389 389 retry: options.retry ?? runner.config.retry, 390 - repeats: options.repeats, 390 + repeats: options.repeats ?? runner.config.repeats, 391 391 mode: options.only 392 392 ? 'only' 393 393 : options.skip