diff --git a/docs/config/experimental.md b/docs/config/experimental.md
index 13a01957504c..b7173db81ad9 100644
--- a/docs/config/experimental.md
+++ b/docs/config/experimental.md
@@ -187,17 +187,35 @@ Please leave feedback regarding this feature in a [GitHub Discussion](https://gi
```ts
interface ImportDurationsOptions {
/**
- * Print import breakdown to CLI terminal after tests finish.
+ * When to print import breakdown to CLI terminal.
+ * - false: Never print (default)
+ * - true: Always print
+ * - 'on-warn': Print only when any import exceeds warn threshold
*/
- print?: boolean
+ print?: boolean | 'on-warn'
+ /**
+ * Fail the test run if any import exceeds the danger threshold.
+ * When enabled and threshold exceeded, breakdown is always printed.
+ * @default false
+ */
+ failOnDanger?: boolean
/**
* Maximum number of imports to collect and display.
*/
limit?: number
+ /**
+ * Duration thresholds in milliseconds for coloring and warnings.
+ */
+ thresholds?: {
+ /** Threshold for yellow/warning color. @default 100 */
+ warn?: number
+ /** Threshold for red/danger color and failOnDanger. @default 500 */
+ danger?: number
+ }
}
```
-- **Default:** `{ print: false, limit: 0 }` (`limit` is 10 if `print` or UI is enabled)
+- **Default:** `{ print: false, failOnDanger: false, limit: 0, thresholds: { warn: 100, danger: 500 } }` (`limit` is 10 if `print` or UI is enabled)
Configure import duration collection and display.
@@ -206,26 +224,54 @@ The `print` option controls CLI terminal output. The `limit` option controls how
- Self: the time it took to import the module, excluding static imports;
- Total: the time it took to import the module, including static imports. Note that this does not include `transform` time of the current module.
-
+
+
Note that if the file path is too long, Vitest will truncate it at the start until it fits 45 character limit.
### experimental.importDurations.print {#experimental-importdurationsprint}
+- **Type:** `boolean | 'on-warn'`
+- **Default:** `false`
+
+Controls when to print import breakdown to CLI terminal after tests finish. This only works with [`default`](/guide/reporters#default), [`verbose`](/guide/reporters#verbose), or [`tree`](/guide/reporters#tree) reporters.
+
+- `false`: Never print breakdown
+- `true`: Always print breakdown
+- `'on-warn'`: Print only when any import exceeds the `thresholds.warn` value
+
+### experimental.importDurations.failOnDanger {#experimental-importdurationsfailondanger}
+
- **Type:** `boolean`
- **Default:** `false`
-Print import breakdown to CLI terminal after tests finish. This only works with [`default`](/guide/reporters#default), [`verbose`](/guide/reporters#verbose), or [`tree`](/guide/reporters#tree) reporters.
+Fail the test run if any import exceeds the `thresholds.danger` value. When enabled and the threshold is exceeded, the breakdown is always printed regardless of the `print` setting.
+
+This is useful for enforcing import performance budgets in CI:
+
+```bash
+vitest --experimental.importDurations.failOnDanger
+```
### experimental.importDurations.limit {#experimental-importdurationslimit}
- **Type:** `number`
-- **Default:** `0` (or `10` if `print` or UI is enabled)
+- **Default:** `0` (or `10` if `print`, `failOnDanger`, or UI is enabled)
Maximum number of imports to collect and display in CLI output, [Vitest UI](/guide/ui#import-breakdown), and third-party reporters.
+### experimental.importDurations.thresholds {#experimental-importdurationsthresholds}
+
+- **Type:** `{ warn?: number; danger?: number }`
+- **Default:** `{ warn: 100, danger: 500 }`
+
+Duration thresholds in milliseconds for coloring and warnings:
+
+- `warn`: Threshold for yellow/warning color (default: 100ms)
+- `danger`: Threshold for red/danger color and `failOnDanger` (default: 500ms)
+
::: info
-[Vitest UI](/guide/ui#import-breakdown) shows a breakdown of imports automatically if at least one file took longer than 500 milliseconds to load. You can manually set this option to `false` to disable this.
+[Vitest UI](/guide/ui#import-breakdown) shows a breakdown of imports automatically if at least one file took longer than the `danger` threshold to load.
:::
## experimental.viteModuleRunner 4.1.0 {#experimental-vitemodulerunner}
diff --git a/docs/guide/cli-generated.md b/docs/guide/cli-generated.md
index b1a61ddf1b5d..ec8a270b6621 100644
--- a/docs/guide/cli-generated.md
+++ b/docs/guide/cli-generated.md
@@ -840,10 +840,10 @@ Enable caching of modules on the file system between reruns.
### experimental.importDurations.print
-- **CLI:** `--experimental.importDurations.print`
+- **CLI:** `--experimental.importDurations.print `
- **Config:** [experimental.importDurations.print](/config/experimental#experimental-importdurations-print)
-Print import breakdown to CLI terminal after tests finish (default: false).
+When to print import breakdown to CLI terminal. Use `true` to always print, `false` to never print, or `on-warn` to print only when imports exceed the warn threshold (default: false).
### experimental.importDurations.limit
@@ -852,6 +852,27 @@ Print import breakdown to CLI terminal after tests finish (default: false).
Maximum number of imports to collect and display (default: 0, or 10 if print or UI is enabled).
+### experimental.importDurations.failOnDanger
+
+- **CLI:** `--experimental.importDurations.failOnDanger`
+- **Config:** [experimental.importDurations.failOnDanger](/config/experimental#experimental-importdurations-failondanger)
+
+Fail the test run if any import exceeds the danger threshold (default: false).
+
+### experimental.importDurations.thresholds.warn
+
+- **CLI:** `--experimental.importDurations.thresholds.warn `
+- **Config:** [experimental.importDurations.thresholds.warn](/config/experimental#experimental-importdurations-thresholds-warn)
+
+Warning threshold - imports exceeding this are shown in yellow/orange (default: 100).
+
+### experimental.importDurations.thresholds.danger
+
+- **CLI:** `--experimental.importDurations.thresholds.danger `
+- **Config:** [experimental.importDurations.thresholds.danger](/config/experimental#experimental-importdurations-thresholds-danger)
+
+Danger threshold - imports exceeding this are shown in red (default: 500).
+
### experimental.viteModuleRunner
- **CLI:** `--experimental.viteModuleRunner`
diff --git a/docs/guide/ui.md b/docs/guide/ui.md
index 9840f4115976..70e930997a29 100644
--- a/docs/guide/ui.md
+++ b/docs/guide/ui.md
@@ -107,7 +107,7 @@ If the module was inlined, you will see three more windows:
All static imports in the "Source" window show a total time it took to evaluate them by the current module. If the import was already evaluated in the module graph, it will show `0ms` because it is cached by that point.
-If the module took longer than 500 milliseconds to load, the time will be displayed in red. If the module took longer than 100 milliseconds, the time will be displayed in orange.
+If the module took longer than the [`danger` threshold](/config/experimental#experimental-importdurations-thresholds) (default: 500ms) to load, the time will be displayed in red. If the module took longer than the [`warn` threshold](/config/experimental#experimental-importdurations-thresholds) (default: 100ms), the time will be displayed in orange.
You can click on an import source to jump into that module and traverse the graph further (note `./support/assertions/index.ts` below).
@@ -142,6 +142,6 @@ You can click on the module to see the Module Info. If the module is external, i
The breakdown shows a list of modules with self time, total time, and a percentage relative to the time it took to load the whole test file.
-The "Show Import Breakdown" icon will have a red color if there is at least one file that took longer than 500 milliseconds to load, and it will be orange if there is at least one file that took longer than 100 milliseconds.
+The "Show Import Breakdown" icon will have a red color if there is at least one file that took longer than the [`danger` threshold](/config/experimental#experimental-importdurations-thresholds) (default: 500ms) to load, and it will be orange if there is at least one file that took longer than the [`warn` threshold](/config/experimental#experimental-importdurations-thresholds) (default: 100ms).
You can use [`experimental.importDurations.limit`](/config/experimental#experimental-importdurationslimit) to control the number of imports displayed.
diff --git a/docs/public/reporter-import-breakdown-light.png b/docs/public/reporter-import-breakdown-light.png
new file mode 100644
index 000000000000..01aaa33ac60d
Binary files /dev/null and b/docs/public/reporter-import-breakdown-light.png differ
diff --git a/docs/public/reporter-import-breakdown.png b/docs/public/reporter-import-breakdown.png
index 5158ef12a4f8..944ba67df934 100644
Binary files a/docs/public/reporter-import-breakdown.png and b/docs/public/reporter-import-breakdown.png differ
diff --git a/packages/ui/client/components/views/ViewModuleGraph.vue b/packages/ui/client/components/views/ViewModuleGraph.vue
index 3c5937511fe2..b43e29754ce4 100644
--- a/packages/ui/client/components/views/ViewModuleGraph.vue
+++ b/packages/ui/client/components/views/ViewModuleGraph.vue
@@ -17,7 +17,7 @@ import {
PositionInitializers,
} from 'd3-graph-controller'
import { computed, onMounted, onUnmounted, ref, shallowRef, toRefs, watch } from 'vue'
-import { isReport } from '~/composables/client'
+import { config, isReport } from '~/composables/client'
import { currentModule } from '~/composables/navigation'
import IconButton from '../IconButton.vue'
import Modal from '../Modal.vue'
@@ -43,14 +43,18 @@ const focusedNode = ref(null)
const filteredGraph = shallowRef(graph.value)
const breakdownIconClass = computed(() => {
let textClass = ''
- const importDurations = currentModule.value?.importDurations || {}
+ const importDurations = currentModule.value?.importDurations
+ if (!importDurations) {
+ return textClass
+ }
+ const thresholds = config.value.experimental.importDurations.thresholds
for (const moduleId in importDurations) {
const { totalTime } = importDurations[moduleId]
- if (totalTime >= 500) {
+ if (totalTime >= thresholds.danger) {
textClass = 'text-red'
break
}
- else if (totalTime >= 100) {
+ else if (totalTime >= thresholds.warn) {
textClass = 'text-orange'
}
}
diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts
index 753ea566f674..a291236a500b 100644
--- a/packages/vitest/src/node/cli/cli-config.ts
+++ b/packages/vitest/src/node/cli/cli-config.ts
@@ -821,12 +821,36 @@ export const cliOptionsConfig: VitestCLIOptions = {
},
subcommands: {
print: {
- description: 'Print import breakdown to CLI terminal after tests finish (default: false).',
+ description: 'When to print import breakdown to CLI terminal. Use `true` to always print, `false` to never print, or `on-warn` to print only when imports exceed the warn threshold (default: false).',
+ argument: '',
+ transform(value) {
+ if (value === 'on-warn') {
+ return 'on-warn'
+ }
+ return value
+ },
},
limit: {
description: 'Maximum number of imports to collect and display (default: 0, or 10 if print or UI is enabled).',
argument: '',
},
+ failOnDanger: {
+ description: 'Fail the test run if any import exceeds the danger threshold (default: false).',
+ },
+ thresholds: {
+ description: 'Duration thresholds in milliseconds for coloring and warnings.',
+ argument: '',
+ subcommands: {
+ warn: {
+ description: 'Warning threshold - imports exceeding this are shown in yellow/orange (default: 100).',
+ argument: '',
+ },
+ danger: {
+ description: 'Danger threshold - imports exceeding this are shown in red (default: 500).',
+ argument: '',
+ },
+ },
+ },
},
},
viteModuleRunner: {
diff --git a/packages/vitest/src/node/config/resolveConfig.ts b/packages/vitest/src/node/config/resolveConfig.ts
index cb98436cf551..4338ea857af8 100644
--- a/packages/vitest/src/node/config/resolveConfig.ts
+++ b/packages/vitest/src/node/config/resolveConfig.ts
@@ -869,10 +869,17 @@ export function resolveConfig(
}
resolved.experimental.importDurations ??= {} as any
resolved.experimental.importDurations.print ??= false
+ resolved.experimental.importDurations.failOnDanger ??= false
if (resolved.experimental.importDurations.limit == null) {
- const shouldCollect = resolved.experimental.importDurations.print || resolved.ui
+ const shouldCollect
+ = resolved.experimental.importDurations.print
+ || resolved.experimental.importDurations.failOnDanger
+ || resolved.ui
resolved.experimental.importDurations.limit = shouldCollect ? 10 : 0
}
+ resolved.experimental.importDurations.thresholds ??= {} as any
+ resolved.experimental.importDurations.thresholds.warn ??= 100
+ resolved.experimental.importDurations.thresholds.danger ??= 500
return resolved
}
diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts
index 5e85f4043763..207b9db2d3ed 100644
--- a/packages/vitest/src/node/reporters/base.ts
+++ b/packages/vitest/src/node/reporters/base.ts
@@ -607,14 +607,17 @@ export abstract class BaseReporter implements Reporter {
}
}
- if (this.ctx.config.experimental.importDurations.print) {
- this.printImportsBreakdown()
- }
+ this.reportImportDurations()
this.log()
}
- private printImportsBreakdown() {
+ private reportImportDurations() {
+ const { print, failOnDanger, thresholds } = this.ctx.config.experimental.importDurations
+ if (!print && !failOnDanger) {
+ return
+ };
+
const testModules = this.ctx.state.getTestModules()
interface ImportEntry {
@@ -647,6 +650,18 @@ export abstract class BaseReporter implements Reporter {
return
}
+ const dangerImports = allImports.filter(imp => imp.totalTime >= thresholds.danger)
+ const warnImports = allImports.filter(imp => imp.totalTime >= thresholds.warn)
+ const hasDangerImports = dangerImports.length > 0
+ const hasWarnImports = warnImports.length > 0
+
+ // Determine if we should print
+ const shouldFail = failOnDanger && hasDangerImports
+ const shouldPrint = (print === true) || (print === 'on-warn' && hasWarnImports) || shouldFail
+ if (!shouldPrint) {
+ return
+ }
+
const sortedImports = allImports.sort((a, b) => b.totalTime - a.totalTime)
const maxTotalTime = sortedImports[0].totalTime
const limit = this.ctx.config.experimental.importDurations.limit
@@ -657,21 +672,25 @@ export abstract class BaseReporter implements Reporter {
const slowestImport = sortedImports[0]
this.log()
- this.log(c.bold('Import Duration Breakdown') + c.dim(` (ordered by Total Time) (Top ${limit})`))
+ this.log(c.bold('Import Duration Breakdown') + c.dim(` (Top ${limit})`))
+ this.log()
+ this.log(c.dim(`${'Module'.padEnd(50)} ${'Self'.padStart(6)} ${'Total'.padStart(6)}`))
// if there are multiple files, it's highly possible that some of them will import the same large file
// we group them to show the distinction between those files more easily
- // Import Duration Breakdown (ordered by Total Time) (Top 10)
- // .../fields/FieldFile/__tests__/FieldFile.spec.ts self: 7ms total: 1.01s ████████████████████
- // ↳ tests/support/components/index.ts self: 0ms total: 861ms █████████████████░░░
- // ↳ tests/support/components/renderComponent.ts self: 59ms total: 861ms █████████████████░░░
- // ...s__/apps/desktop/form-updater.desktop.spec.ts self: 8ms total: 991ms ████████████████████
- // ...sts__/apps/mobile/form-updater.mobile.spec.ts self: 11ms total: 990ms ████████████████████
- // shared/components/Form/__tests__/Form.spec.ts self: 5ms total: 988ms ████████████████████
- // ↳ tests/support/components/index.ts self: 0ms total: 935ms ███████████████████░
- // ↳ tests/support/components/renderComponent.ts self: 61ms total: 935ms ███████████████████░
- // ...ditor/features/link/__test__/LinkForm.spec.ts self: 7ms total: 972ms ███████████████████░
- // ↳ tests/support/components/renderComponent.ts self: 56ms total: 936ms ███████████████████░
+ // Import Duration Breakdown (Top 10)
+ //
+ // Module Self Total
+ // .../fields/FieldFile/__tests__/FieldFile.spec.ts 7ms 1.01s ████████████████████
+ // ↳ tests/support/components/index.ts 0ms 861ms █████████████████░░░
+ // ↳ tests/support/components/renderComponent.ts 59ms 861ms █████████████████░░░
+ // ...s__/apps/desktop/form-updater.desktop.spec.ts 8ms 991ms ████████████████████
+ // ...sts__/apps/mobile/form-updater.mobile.spec.ts 11ms 990ms ████████████████████
+ // shared/components/Form/__tests__/Form.spec.ts 5ms 988ms ████████████████████
+ // ↳ tests/support/components/index.ts 0ms 935ms ███████████████████░
+ // ↳ tests/support/components/renderComponent.ts 61ms 935ms ███████████████████░
+ // ...ditor/features/link/__test__/LinkForm.spec.ts 7ms 972ms ███████████████████░
+ // ↳ tests/support/components/renderComponent.ts 56ms 936ms ███████████████████░
const groupedImports = Object.entries(
groupBy(topImports, i => i.testModule.id),
@@ -688,7 +707,7 @@ export abstract class BaseReporter implements Reporter {
const pathDisplay = this.ellipsisPath(imp.importedModuleId, imp.external, groupedImports.length > 1 && index > 0)
this.log(
- `${pathDisplay} ${c.dim('self:')} ${this.importDurationTime(imp.selfTime)} ${c.dim('total:')} ${this.importDurationTime(imp.totalTime)} ${bar}`,
+ `${pathDisplay} ${this.importDurationTime(imp.selfTime)} ${this.importDurationTime(imp.totalTime)} ${bar}`,
)
})
}
@@ -697,10 +716,20 @@ export abstract class BaseReporter implements Reporter {
this.log(c.dim('Total imports: ') + allImports.length)
this.log(c.dim('Slowest import (total-time): ') + formatTime(slowestImport.totalTime))
this.log(c.dim('Total import time (self/total): ') + formatTime(totalSelfTime) + c.dim(' / ') + formatTime(totalTotalTime))
+
+ // Fail if danger threshold exceeded
+ if (shouldFail) {
+ this.log()
+ this.ctx.logger.error(
+ `ERROR: ${dangerImports.length} import(s) exceeded the danger threshold of ${thresholds.danger}ms`,
+ )
+ process.exitCode = 1
+ }
}
private importDurationTime(duration: number) {
- const color = duration >= 500 ? c.red : duration >= 100 ? c.yellow : (c: string) => c
+ const { thresholds } = this.ctx.config.experimental.importDurations
+ const color = duration >= thresholds.danger ? c.red : duration >= thresholds.warn ? c.yellow : (c: string) => c
return color(formatTime(duration).padStart(6))
}
diff --git a/packages/vitest/src/node/types/config.ts b/packages/vitest/src/node/types/config.ts
index aeb025a06eb2..6a54fee0366a 100644
--- a/packages/vitest/src/node/types/config.ts
+++ b/packages/vitest/src/node/types/config.ts
@@ -864,15 +864,39 @@ export interface InlineConfig {
*/
importDurations?: {
/**
- * Print import breakdown to CLI terminal after tests finish.
+ * When to print import breakdown to CLI terminal after tests finish.
+ * - `true`: Always print
+ * - `false`: Never print (default)
+ * - `'on-warn'`: Print only when any import exceeds the warn threshold
* @default false
*/
- print?: boolean
+ print?: boolean | 'on-warn'
/**
* Maximum number of imports to collect and display.
* @default 0 (or 10 if `print` or UI is enabled)
*/
limit?: number
+ /**
+ * Fail the test run if any import exceeds the danger threshold.
+ * When failing, the breakdown is always printed regardless of `print` setting.
+ * @default false
+ */
+ failOnDanger?: boolean
+ /**
+ * Duration thresholds in milliseconds for coloring and warnings.
+ */
+ thresholds?: {
+ /**
+ * Warning threshold - imports exceeding this are shown in yellow/orange.
+ * @default 100
+ */
+ warn?: number
+ /**
+ * Danger threshold - imports exceeding this are shown in red.
+ * @default 500
+ */
+ danger?: number
+ }
}
/**
@@ -1158,8 +1182,13 @@ export interface ResolvedConfig
experimental: Omit['experimental'], 'importDurations'> & {
importDurations: {
- print: boolean
+ print: boolean | 'on-warn'
limit: number
+ failOnDanger: boolean
+ thresholds: {
+ warn: number
+ danger: number
+ }
}
}
}
diff --git a/packages/vitest/src/runtime/config.ts b/packages/vitest/src/runtime/config.ts
index 16c5d2be1419..92ff6c2735a4 100644
--- a/packages/vitest/src/runtime/config.ts
+++ b/packages/vitest/src/runtime/config.ts
@@ -120,8 +120,13 @@ export interface SerializedConfig {
experimental: {
fsModuleCache: boolean
importDurations: {
- print: boolean
+ print: boolean | 'on-warn'
limit: number
+ failOnDanger: boolean
+ thresholds: {
+ warn: number
+ danger: number
+ }
}
viteModuleRunner: boolean
nodeLoader: boolean
diff --git a/test/cli/test/reporters/import-durations.test.ts b/test/cli/test/reporters/import-durations.test.ts
index f0a56731f4ed..5cce634d25b9 100644
--- a/test/cli/test/reporters/import-durations.test.ts
+++ b/test/cli/test/reporters/import-durations.test.ts
@@ -1,18 +1,42 @@
+import { stripVTControlCharacters } from 'node:util'
import { runVitest } from '#test-utils'
import { resolve } from 'pathe'
import { describe, expect, it } from 'vitest'
+/**
+ * Extract import durations section and normalize variable values for snapshot testing.
+ * Replaces timing values and bar characters with placeholders.
+ */
+function normalizeImportDurationsOutput(stdout: string): string {
+ const plain = stripVTControlCharacters(stdout)
+ const start = plain.indexOf('Import Duration Breakdown')
+ const end = plain.indexOf('Total import time (self/total):')
+ if (start === -1 || end === -1) {
+ return ''
+ }
+ const endOfLine = plain.indexOf('\n', end)
+ const section = plain.slice(start, endOfLine === -1 ? undefined : endOfLine)
+ return section
+ // Normalize time values (e.g., "88ms", "1.01s") to "XXX"
+ // Negative lookahead to avoid matching times in filenames (e.g., "import-durations-50ms.ts")
+ .replace(/\d+(\.\d+)?(ms|s)(?![\w.])/g, 'XXX')
+ // Normalize bar characters to "[BAR]"
+ .replace(/[█░]+/g, '[BAR]')
+ // Normalize multiple spaces (from column padding) to single space
+ .replace(/ {2,}/g, ' ')
+}
+
describe('import durations', () => {
const root = resolve(import.meta.dirname, '..', '..', 'fixtures', 'reporters')
it('should populate importDurations on File with import durations during execution', async () => {
- const { exitCode, ctx } = await runVitest({
+ const { exitCode, ctx, stderr } = await runVitest({
root,
include: ['**/import-durations.test.ts'],
experimental: { importDurations: { limit: 10 } },
})
- expect(exitCode).toBe(0)
+ expect(exitCode, `Expected exit code 0 but got ${exitCode}. stderr: ${stderr}`).toBe(0)
const capturedFiles = ctx!.state.getFiles()
@@ -90,10 +114,19 @@ describe('import durations', () => {
},
})
- expect(stdout).toContain('Import Duration Breakdown')
- expect(stdout).toContain('(ordered by Total Time)')
- expect(stdout).toContain('Total imports:')
- expect(stdout).toContain('(Top 5)')
+ expect(normalizeImportDurationsOutput(stdout)).toMatchInlineSnapshot(`
+ "Import Duration Breakdown (Top 5)
+
+ Module Self Total
+ import-durations.test.ts XXX XXX [BAR]
+ import-durations-50ms.ts XXX XXX [BAR]
+ import-durations-25ms.ts XXX XXX [BAR]
+ ../../../../packages/vitest/dist/index.js XXX XXX [BAR]
+
+ Total imports: 4
+ Slowest import (total-time): XXX
+ Total import time (self/total): XXX / XXX"
+ `)
})
it('should not collect importDurations by default', async () => {
@@ -105,4 +138,66 @@ describe('import durations', () => {
const file = ctx!.state.getFiles()[0]
expect(file.importDurations).toEqual({})
})
+
+ it('should print on-warn only when threshold exceeded', async () => {
+ // With high threshold (500ms), should NOT print (imports are ~75-120ms depending on CI)
+ const { stdout: stdoutHigh } = await runVitest({
+ root,
+ include: ['**/import-durations.test.ts'],
+ experimental: {
+ importDurations: {
+ print: 'on-warn',
+ thresholds: { warn: 500 },
+ },
+ },
+ })
+
+ expect(stdoutHigh).not.toContain('Import Duration Breakdown')
+
+ // With lower threshold (50ms), should print (imports are ~75ms > 50ms)
+ const { stdout: stdoutLow } = await runVitest({
+ root,
+ include: ['**/import-durations.test.ts'],
+ experimental: {
+ importDurations: {
+ print: 'on-warn',
+ thresholds: { warn: 50 },
+ },
+ },
+ })
+
+ expect(stdoutLow).toContain('Import Duration Breakdown')
+ })
+
+ it('should fail when failOnDanger is enabled and threshold exceeded', async () => {
+ // With default danger threshold (500ms), should NOT fail (imports are ~75ms)
+ const { exitCode: exitCodeDefault, stderr: stderrDefault } = await runVitest({
+ root,
+ include: ['**/import-durations.test.ts'],
+ experimental: {
+ importDurations: {
+ failOnDanger: true,
+ },
+ },
+ })
+
+ expect(exitCodeDefault).toBe(0)
+ expect(stderrDefault).not.toContain('exceeded the danger threshold')
+
+ // With lower danger threshold (50ms), should fail (imports are ~75ms > 50ms)
+ const { exitCode: exitCodeLow, stderr: stderrLow, stdout: stdoutLow } = await runVitest({
+ root,
+ include: ['**/import-durations.test.ts'],
+ experimental: {
+ importDurations: {
+ failOnDanger: true,
+ thresholds: { danger: 50 },
+ },
+ },
+ })
+
+ expect(exitCodeLow).toBe(1)
+ expect(stderrLow).toContain('exceeded the danger threshold')
+ expect(stdoutLow).toContain('Import Duration Breakdown')
+ })
})
diff --git a/test/config/test/cli-config.test.ts b/test/config/test/cli-config.test.ts
index 23064dcca089..d0f3c98338b3 100644
--- a/test/config/test/cli-config.test.ts
+++ b/test/config/test/cli-config.test.ts
@@ -53,12 +53,12 @@ it('correctly inherit from the cli', async () => {
retry: 6,
passWithNoTests: true,
bail: 100,
- experimental: expect.objectContaining({
+ experimental: {
importDurations: {
print: true,
limit: 10,
},
- }),
+ },
})
expect(config.testNamePattern?.test('math')).toBe(true)
})