Files
vercel__next.js/test/unit/instant-config-normalization.test.ts
Aurora Scharff a0c6a6140e instant: enable navigation validation by default (#94312)
### What?

Flips the default `experimental.instantInsights.validationLevel` from
`'manual-warning'` to `'warning'` so Cache Components apps get
instant-navigation validation across all pages by default.

### Why?

`'manual-warning'` only validates pages that explicitly export
`unstable_instant`, so apps see nothing unless they opt in. `'warning'`
is what users have been turning on manually to actually see the feature
(`v0`, `vercel-site`).

### Test coverage

- Unit: `instant-config-normalization.test.ts` pins the framework
default at `'warning'`.
- Integration: new `instant-validation-level-default/` fixture (no
`instantInsights` in config) asserts implicit dev validation fires, and
that build is unaffected.

### Collateral test fixtures

Tests whose intent is unrelated to instant validation
(router-autoscroll, owner-stack, hmr-iframe, next-image,
server-source-maps, etc.) now hit new redboxes/console warnings because
their fixtures incidentally use dynamic data. Each opts out with
`experimental: { instantInsights: { validationLevel: 'manual-warning' }
}` in `next.config`.

### Open question

With no `'off'` / `'info'` tier today, silencing Insights after this
change requires setting `validationLevel: 'manual-warning'`. Is that
acceptable? Should we inform of this anywhere?

<!-- NEXT_JS_LLM_PR -->
2026-06-03 01:26:11 +02:00

63 lines
2.0 KiB
TypeScript

import path from 'path'
import loadConfig from 'next/dist/server/config'
import { PHASE_PRODUCTION_SERVER } from 'next/constants'
// `loadConfig` caches its result keyed on `dir` + a boolean "hasCustomConfig".
// Each test uses a unique subdirectory so the cache doesn't bleed between
// cases. The subdirectory doesn't need to exist — only the string matters
// for the cache key, and `loadEnvConfig` tolerates missing dirs.
function uniqueDir(tag: string) {
return path.join(__dirname, `__instant_normalization_${tag}__`)
}
// Covers the `finalizeConfig` step in loadConfig: resolving
// `experimental.instantInsights.validationLevel` to a concrete value in
// one place so consumers don't each need to know the current framework default.
describe('experimental.instantInsights validationLevel normalization', () => {
it('defaults to warning when the instantInsights config is absent', async () => {
const config = await loadConfig(
PHASE_PRODUCTION_SERVER,
uniqueDir('absent'),
{
customConfig: {},
}
)
expect(config.experimental.instantInsights).toEqual({
validationLevel: 'warning',
})
})
it('defaults to warning when experimental.instantInsights is an empty object', async () => {
const config = await loadConfig(
PHASE_PRODUCTION_SERVER,
uniqueDir('empty'),
{
customConfig: { experimental: { instantInsights: {} } },
}
)
expect(config.experimental.instantInsights).toEqual({
validationLevel: 'warning',
})
})
it.each([
'warning',
'manual-warning',
'experimental-error',
'experimental-manual-error',
] as const)('preserves explicit validationLevel: %s', async (level) => {
const config = await loadConfig(
PHASE_PRODUCTION_SERVER,
uniqueDir(`level-${level}`),
{
customConfig: {
experimental: { instantInsights: { validationLevel: level } },
},
}
)
expect(config.experimental.instantInsights).toEqual({
validationLevel: level,
})
})
})