Overriding global $fetch with `ofetch.create` will break the test run · Issue #798 · nuxt/test-utils · GitHub
Skip to content

Overriding global $fetch with ofetch.create will break the test run #798

Open

Description

@jrutila

Environment

Nuxt project info:


  • Operating System: Linux
  • Node Version: v18.18.0
  • Nuxt Version: 3.11.1
  • CLI Version: 3.11.0
  • Nitro Version: 2.9.4
  • Package Manager: npm@10.2.3
  • Builder: -
  • User Config: devtools
  • Runtime Modules: -
  • Build Modules: -

@nuxt/test-utils version: 3.12.0
vitest version: 1.4.0

Reproduction

Here is a reproduce stackblitz: https://stackblitz.com/edit/github-lxyzpn?file=plugins%2Fonrequest.client.ts

Create a plugin that will override the default $fetch. This is handy when trying to inject your own headers into the HTTP requests. If you google this, you will find a helpful SO solution: https://stackoverflow.com/a/75871291/216846

import { ofetch } from 'ofetch';

export default defineNuxtPlugin((_nuxtApp) => {
  globalThis.$fetch = ofetch.create({ // <- this is the important part!
    onRequest({ request, options }) {
      options.headers = { Authorization: `Bearer token` };
      console.log('onRequest', request);
    },
    onRequestError({ error }) {
      console.log(error);
    },
  });
});

This works fine when running the dev environment or prod environment. When you now run @nuxt/test-utils based test case, the tests pass, but the run fails to this error (see the whole log in logs):

TypeError: Cannot set property request of FetchError: [GET] "/_nuxt/builds/meta/test.json": <no response> Failed to parse URL from /_nuxt/builds/meta/test.json which has only a getter

Describe the bug

The correct way to implement this kind of plugin that edits the HTTP requests is described here: https://nuxt.com/docs/examples/advanced/use-custom-fetch-composable. Instead of returning you just inject the globalThis.$fetch with the created fetch instance.

The bug is that in dev and prod environments you can as well do it like this globalThis.$fetch = ofetch.create. Everything works, except when you run unit tests with @nuxt/test-utils, you get errors described above.

So, again, the correct way is to do globalThis.$fetch = $fetch.create but I think there should be a warning or something guiding the devs to do it correctly. Or at least they find this bug issue and can fix it in their code.

Additional context

No response

Logs

~/projects/vbwirlala.github 4m 11s
❯ npm run test

> test
> vitest


 DEV  v1.4.0 /home/projects/vbwirlala.github

stdout | Object.onRequest (/home/projects/vbwirlala.github/plugins/onrequest.client.ts:8:15)
onRequest /_nuxt/builds/meta/test.json
onRequest /_nuxt/builds/meta/test.json

stdout | Object.onRequestError (/home/projects/vbwirlala.github/plugins/onrequest.client.ts:11:15)
TypeError: Failed to parse URL from /_nuxt/builds/meta/test.json
    at _0x88e3f9 (https://vbwirlalagithub-bvqi.w-credentialless-staticblitz.com/blitz.810981ba.js:352:614955)
    at _0x303d3a._0x5ec96b (https://vbwirlalagithub-bvqi.w-credentialless-staticblitz.com/blitz.810981ba.js:352:625681)
    at Object.t.exports.fetch (node:internal/deps/undici/undici:194:308109)
    at $fetchRaw2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:236:26)
    at $fetch2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:278:15) {
  [cause]: TypeError [ERR_INVALID_URL]: Invalid URL
      at __node_internal_ (node:internal/errors:36:5406)
      at new <anonymous> (node:internal/errors:36:4168)
      at new URL (node:internal/url:48:8018)
      at new URL (/home/projects/vbwirlala.github/node_modules/happy-dom/lib/url/URL.js:25:1)
      at new oe (node:internal/deps/undici/undici:194:83653)
      at fetch (node:internal/deps/undici/undici:194:307099)
      at Object.t.exports.fetch (node:internal/deps/undici/undici:194:308078)
      at node:internal/process/pre_execution:56:2507
      at $fetchRaw2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:236:32)
      at $fetch2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:278:15) {
    input: '/_nuxt/builds/meta/test.json',
    code: 'ERR_INVALID_URL'
  }
}
TypeError: Failed to parse URL from /_nuxt/builds/meta/test.json
    at _0x88e3f9 (https://vbwirlalagithub-bvqi.w-credentialless-staticblitz.com/blitz.810981ba.js:352:614955)
    at _0x303d3a._0x5ec96b (https://vbwirlalagithub-bvqi.w-credentialless-staticblitz.com/blitz.810981ba.js:352:625681)
    at Object.t.exports.fetch (node:internal/deps/undici/undici:194:308109)
    at $fetchRaw2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:236:26)
    at $fetchRaw2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:245:14)
    at $fetch2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:278:15) {
  [cause]: TypeError [ERR_INVALID_URL]: Invalid URL
      at __node_internal_ (node:internal/errors:36:5406)
      at new <anonymous> (node:internal/errors:36:4168)
      at new URL (node:internal/url:48:8018)
      at new URL (/home/projects/vbwirlala.github/node_modules/happy-dom/lib/url/URL.js:25:1)
      at new oe (node:internal/deps/undici/undici:194:83653)
      at fetch (node:internal/deps/undici/undici:194:307099)
      at Object.t.exports.fetch (node:internal/deps/undici/undici:194:308078)
      at node:internal/process/pre_execution:56:2507
      at $fetchRaw2 (/home/projects/vbwirlala.github/node_modules/ofetch/dist/shared/ofetch.00501375.mjs:236:32) {
    input: '/_nuxt/builds/meta/test.json',
    code: 'ERR_INVALID_URL'
  }
}

stdout | createSuspenseBoundary (/home/projects/vbwirlala.github/node_modules/@vue/runtime-core/dist/runtime-core.cjs.js:1442:43)
<Suspense> is an experimental feature and its API will likely change.

 ✓ test/my.spec.ts (1)
   ✓ abba (1)
     ✓ should be abba

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

Vitest caught 1 unhandled error during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Uncaught Exception ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
TypeError: Cannot set property request of FetchError: [GET] "/_nuxt/builds/meta/test.json": <no response> Failed to parse URL from /_nuxt/builds/meta/test.json which has only a getter
 ❯ _0x46d1e6 ../../../blitz.810981ba.js:352:617597
 ❯ _0x1eee56 ../../../blitz.810981ba.js:352:615189
 ❯ FetchError.get ../../../blitz.810981ba.js:352:615388
 ❯ Module.processError node_modules/@vitest/utils/dist/error.js:95:11
 ❯ catchError node_modules/vitest/dist/vendor/execute.2_yoIC01.js:396:39
 ❯ process.unhandledRejection node_modules/vitest/dist/vendor/execute.2_yoIC01.js:406:37
 ❯ EventEmitter.emit node:events:42:9202
 ❯ emit node:internal/process/promises:230:1176
 ❯ processPromiseRejections node:internal/process/promises:230:3782
 ❯ processTicksAndRejections node:internal/process/task_queues:107:1089

This error originated in "test/my.spec.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯

 Test Files  1 passed (1)
      Tests  1 passed (1)
     Errors  1 error
   Start at  08:55:23
   Duration  4.31s (transform 467ms, setup 535ms, collect 11ms, tests 2ms, environment 800ms, prepare 1.37s)


 FAIL  Tests failed. Watching for file changes...
       press h to show help, press q to quit

Activity

  1. mathiasrando commented on Mar 21, 2024

    @mathiasrando

    If you don't actually need the plugin for the test I would assume you could add an early return in your defineNuxtPlugin based on the environment. Maybe you can useimport.meta.test or pass a variable yourself and check for that using import.meta.env.YOUR_VARIABLE and your script in package.json being YOUR_VARIABLE=true vitest.

    https://nuxt.com/docs/api/advanced/import-meta

  2. jrutila commented on Mar 22, 2024

    @jrutila
    SponsorContributorAuthor

    If you don't actually need the plugin for the test I would assume you could add an early return in your defineNuxtPlugin based on the environment.

    This is a good tip if you don't need the plugin. Still, cluttering the plugin code with that kind of a check is also little smelly. I investigated and I don't think there is a way to disable, say in vitest.config, certain autoloaded plugins in tests run by test-utils? That is totally different topic, though.

  3. yamachi4416 commented on Oct 27, 2025

    @yamachi4416
    Member

    @jrutila

    hi!
    i checked the nuxt documentation, but i couldn’t find any code that overwrites globalThis.$fetch.
    so I believe the code mentioned on Stack Overflow is incorrect.
    it might make sense to close this issue, but i’d like to hear your thoughts.

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions