Skip to content

fix(runtime): avoid error when vue/test-utils is not installed#1646

Open
yamachi4416 wants to merge 1 commit intonuxt:mainfrom
yamachi4416:fix/avoid-error-vue-test-utils-not-installed
Open

fix(runtime): avoid error when vue/test-utils is not installed#1646
yamachi4416 wants to merge 1 commit intonuxt:mainfrom
yamachi4416:fix/avoid-error-vue-test-utils-not-installed

Conversation

@yamachi4416
Copy link
Copy Markdown
Collaborator

@yamachi4416 yamachi4416 commented Apr 6, 2026

🔗 Linked issue

📚 Description

Fixed an error that occurs when @vue/test-utils is not installed.

Reproduction

Before:

@vue/test-utils is not installed
https://stackblitz.com/edit/nuxt-test-utils-pull-1646-fail?file=package.json

After:

@vue/test-utils is not installed
https://stackblitz.com/edit/nuxt-test-utils-pull-1646-resolve1?file=package.json

@vue/test-utils is installed
https://stackblitz.com/edit/nuxt-test-utils-pull-1646-resolve2?file=package.json

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Apr 6, 2026

Open in StackBlitz

npm i https://pkg.pr.new/nuxt/test-utils/@nuxt/test-utils@1646
npm i https://pkg.pr.new/nuxt/test-utils/vitest-environment-nuxt@1646

commit: 62c7076

@yamachi4416 yamachi4416 force-pushed the fix/avoid-error-vue-test-utils-not-installed branch from d255a2b to 20ccb22 Compare April 7, 2026 11:48
@yamachi4416 yamachi4416 changed the title fix(runtime-utils): avoid error when vue/test-utils is not installed fix(runtime): avoid error when vue/test-utils is not installed Apr 7, 2026
@yamachi4416 yamachi4416 marked this pull request as ready for review April 7, 2026 11:58
@yamachi4416 yamachi4416 requested a review from danielroe as a code owner April 7, 2026 11:58
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

📝 Walkthrough

Walkthrough

The change updates src/runtime/shared/vue-wrapper-plugin.ts: it replaces the Options type alias with an explicit Options interface exposing hasNuxtPage(): boolean, converts the @vue/test-utils config import to an awaited dynamic import with failure handling (allowing config to be undefined), and adds a guard returning a fallback Options (hasNuxtPage: () => false) when config is unavailable. When config loads successfully, the existing plugin discovery/installation via config.plugins.VueWrapper... remains in place.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: avoiding an error when vue/test-utils is not installed, which aligns with the changeset.
Description check ✅ Passed The description is related to the changeset, explaining the problem and providing reproduction examples before and after the fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/runtime/shared/vue-wrapper-plugin.ts`:
- Around line 9-18: The current import of '@vue/test-utils' swallows all errors
and hides real issues; change the dynamic import logic used to set config (the
import('@vue/test-utils') => r.config pattern) so the catch only converts
"module not found" resolution errors into undefined and rethrows any other
errors; in practice replace the blanket .catch(() => undefined) with logic that
inspects the thrown error (e.g. error.code === 'MODULE_NOT_FOUND' or
'ERR_MODULE_NOT_FOUND' or message patterns for missing module) and returns
undefined only for those cases, otherwise rethrow so getVueWrapperPlugin and its
hasNuxtPage behavior aren't masking broken installations or runtime failures.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c9747a13-6ab0-440a-9c58-9564e75a3028

📥 Commits

Reviewing files that changed from the base of the PR and between c608a42 and 20ccb22.

📒 Files selected for processing (1)
  • src/runtime/shared/vue-wrapper-plugin.ts

Comment on lines +9 to +18
const config = await import('@vue/test-utils')
.then(r => r.config)
.catch(() => undefined)

export function getVueWrapperPlugin(): Options {
if (!config) {
return {
hasNuxtPage: () => false,
}
}
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the vue-wrapper-plugin.ts file to see the current state
cat -n src/runtime/shared/vue-wrapper-plugin.ts

Repository: nuxt/test-utils

Length of output: 2045


🏁 Script executed:

# Now check how getVueWrapperPlugin() is consumed in nuxt.ts
cat -n src/runtime/shared/nuxt.ts | head -30

Repository: nuxt/test-utils

Length of output: 938


🏁 Script executed:

# Check if there are any other usages of getVueWrapperPlugin in the codebase
rg "getVueWrapperPlugin" --type ts --type tsx -n

Repository: nuxt/test-utils

Length of output: 86


🏁 Script executed:

# Retry search for getVueWrapperPlugin with simpler approach
rg "getVueWrapperPlugin" -n

Repository: nuxt/test-utils

Length of output: 531


🏁 Script executed:

# Check package.json to see if vue/test-utils is optional or required
cat package.json | grep -A 5 -B 5 "test-utils"

Repository: nuxt/test-utils

Length of output: 2056


🏁 Script executed:

# Check Node.js version and what error codes are typical for module not found
# Also verify how top-level await works with module initialization
python3 << 'EOF'
import json

# Document error code behavior for dynamic import failures
error_behaviors = {
    "ERR_MODULE_NOT_FOUND": "Module does not exist in filesystem/registry",
    "ERR_UNKNOWN_FILE_EXTENSION": "File has unrecognized extension",
    "SyntaxError": "Module has syntax error",
    "ReferenceError": "Module has undefined reference",
    "TypeError": "Module execution throws TypeError",
}

print("Dynamic import failure scenarios:")
for code, desc in error_behaviors.items():
    print(f"  {code}: {desc}")

print("\nTop-level await behavior:")
print("  - Module with top-level await becomes 'async module'")
print("  - Importers of that module wait for initialization to complete")
print("  - But functions IN that module remain synchronous")
EOF

Repository: nuxt/test-utils

Length of output: 557


🏁 Script executed:

# Look at the full package.json structure
head -40 package.json

Repository: nuxt/test-utils

Length of output: 1019


🏁 Script executed:

# Verify standard Node.js error codes for module not found
python3 << 'EOF'
# Node.js module resolution error patterns
print("Node.js Module Resolution Errors:")
print()
print("When require/import fails to resolve a module:")
print("  - Error.code === 'ERR_MODULE_NOT_FOUND' (most common)")
print("  - Error.message typically includes: 'Cannot find module ...'")
print("  - Error.message typically includes: 'no such file or directory'")
print()
print("When a module exists but fails during evaluation:")
print("  - Error.code may be undefined")
print("  - Error could be SyntaxError, ReferenceError, TypeError, etc.")
print("  - These are real bugs that should NOT be silently swallowed")
print()
print("Key insight for `@vue/test-utils`:")
print("  - If package is genuinely optional, catch only resolution failures")
print("  - If package exists but is broken, that's a configuration bug")
print("  - Silently returning hasNuxtPage: () => false hides the real problem")
EOF

Repository: nuxt/test-utils

Length of output: 772


🏁 Script executed:

# Check if there's any existing error handling pattern in the codebase
rg "ERR_MODULE_NOT_FOUND|error\.code" --type ts -A 3 -B 1 | head -50

Repository: nuxt/test-utils

Length of output: 41


🏁 Script executed:

# Verify the exact structure of r.config to confirm the .then(r => r.config) is correct
# Look for any type definitions or usage
rg "@vue/test-utils" src/ -A 2 -B 2 --type ts | head -40

Repository: nuxt/test-utils

Length of output: 1741


Distinguish between missing and broken @vue/test-utils.

The blanket .catch(() => undefined) treats all import failures identically—missing packages, broken installations, unsupported export shapes, and evaluation errors all silently become hasNuxtPage: () => false. This hides real configuration bugs. Since @vue/test-utils is optional, catch only module resolution failures and rethrow everything else.

Recommended approach
-const config = await import('@vue/test-utils')
-  .then(r => r.config)
-  .catch(() => undefined)
+const config = await import('@vue/test-utils')
+  .then((r) => {
+    if (!r.config) {
+      throw new Error('Unsupported `@vue/test-utils` version: missing config export')
+    }
+    return r.config
+  })
+  .catch((error: any) => {
+    const isMissingVueTestUtils =
+      error?.code === 'ERR_MODULE_NOT_FOUND'
+      && String(error.message).includes('@vue/test-utils')
+    if (isMissingVueTestUtils) return undefined
+    throw error
+  })

Adjust the error predicate as needed for your target module systems, but the key point is to avoid swallowing non-resolution failures.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const config = await import('@vue/test-utils')
.then(r => r.config)
.catch(() => undefined)
export function getVueWrapperPlugin(): Options {
if (!config) {
return {
hasNuxtPage: () => false,
}
}
const config = await import('@vue/test-utils')
.then((r) => {
if (!r.config) {
throw new Error('Unsupported `@vue/test-utils` version: missing config export')
}
return r.config
})
.catch((error: any) => {
const isMissingVueTestUtils =
error?.code === 'ERR_MODULE_NOT_FOUND'
&& String(error.message).includes('@vue/test-utils')
if (isMissingVueTestUtils) return undefined
throw error
})
export function getVueWrapperPlugin(): Options {
if (!config) {
return {
hasNuxtPage: () => false,
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/shared/vue-wrapper-plugin.ts` around lines 9 - 18, The current
import of '@vue/test-utils' swallows all errors and hides real issues; change
the dynamic import logic used to set config (the import('@vue/test-utils') =>
r.config pattern) so the catch only converts "module not found" resolution
errors into undefined and rethrows any other errors; in practice replace the
blanket .catch(() => undefined) with logic that inspects the thrown error (e.g.
error.code === 'MODULE_NOT_FOUND' or 'ERR_MODULE_NOT_FOUND' or message patterns
for missing module) and returns undefined only for those cases, otherwise
rethrow so getVueWrapperPlugin and its hasNuxtPage behavior aren't masking
broken installations or runtime failures.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasNuxtPage is only meaningful when @vue/test-utils is available and used in tests.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yamachi4416, that's a fair point — since hasNuxtPage() ultimately relies on VueWrapper instances that only exist when @vue/test-utils is installed and actively used in tests, returning false is always the correct semantic fallback regardless of the reason the import failed. I'll withdraw my suggestion.


🧠 Learnings used
Learnt from: Archetipo95
Repo: nuxt/test-utils PR: 0
File: :0-0
Timestamp: 2026-03-30T10:17:20.984Z
Learning: In nuxt/test-utils, the `RouterLink` stub in `src/runtime-utils/components/RouterLink.ts` uses `useLink(props)` from vue-router to derive `isActive`, `isExactActive`, `href`, `route`, and `navigate`. The custom slot provides all five. Nested-route parent-active state does not work in `mountSuspended` because VTU strips static properties (including `.useLink`) from components registered via `global.components`, so NuxtLink falls back to exact path comparison. This is tracked as a known limitation with an `it.fails` regression test.

@yamachi4416 yamachi4416 force-pushed the fix/avoid-error-vue-test-utils-not-installed branch from 20ccb22 to 62c7076 Compare April 7, 2026 12:36
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/runtime/shared/vue-wrapper-plugin.ts (1)

11-13: ⚠️ Potential issue | 🟠 Major

Handle only “module not found” here; don’t swallow all import errors.

The current .catch(() => undefined) hides real failures (broken package, runtime/evaluation errors, incompatible export shape) and makes debugging much harder. Keep the optional-dependency behavior, but only suppress true resolution failures for @vue/test-utils.

Proposed fix
 const config = await import('@vue/test-utils')
-  .then(r => r.config)
-  .catch(() => undefined)
+  .then((r) => {
+    if (!r.config) {
+      throw new Error('Unsupported `@vue/test-utils` version: missing `config` export')
+    }
+    return r.config
+  })
+  .catch((error: unknown) => {
+    const e = error as { code?: string; message?: string }
+    const isModuleNotFound =
+      (e.code === 'ERR_MODULE_NOT_FOUND' || e.code === 'MODULE_NOT_FOUND')
+      && String(e.message ?? '').includes('@vue/test-utils')
+    if (isModuleNotFound) return undefined
+    throw error
+  })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/shared/vue-wrapper-plugin.ts` around lines 11 - 13, The import of
'@vue/test-utils' assigns to the const config but the catch currently swallows
all errors; change the catch on the dynamic import so it only returns undefined
for a module-not-found/resolution error and rethrows any other errors (so keep
the optional-dependency behavior for missing package but surface actual
runtime/evaluation/shape errors). Concretely, update the promise chain around
the dynamic import of '@vue/test-utils' that sets config to inspect the caught
error (e.g., check err.code === 'ERR_MODULE_NOT_FOUND' or test err.message for
"Cannot find module" depending on runtime) and return undefined only in that
case; otherwise throw the error so functions referencing config will fail fast.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/runtime/shared/vue-wrapper-plugin.ts`:
- Around line 11-13: The import of '@vue/test-utils' assigns to the const config
but the catch currently swallows all errors; change the catch on the dynamic
import so it only returns undefined for a module-not-found/resolution error and
rethrows any other errors (so keep the optional-dependency behavior for missing
package but surface actual runtime/evaluation/shape errors). Concretely, update
the promise chain around the dynamic import of '@vue/test-utils' that sets
config to inspect the caught error (e.g., check err.code ===
'ERR_MODULE_NOT_FOUND' or test err.message for "Cannot find module" depending on
runtime) and return undefined only in that case; otherwise throw the error so
functions referencing config will fail fast.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 86100996-22fa-47b9-99f8-79a8fbf2bc13

📥 Commits

Reviewing files that changed from the base of the PR and between 20ccb22 and 62c7076.

📒 Files selected for processing (1)
  • src/runtime/shared/vue-wrapper-plugin.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant