Open eriksLapins opened 5 days ago
Hey @eriksLapins! 👋 I'm here to help you with your software bugs, questions, and becoming a contributor.
The issue you're encountering with registerEndpoint
not exposing the endpoint to $fetch
from @nuxt/test-utils
(ofetch) in your Nuxt 3.13.1 environment is due to the fact that @nuxt/test-utils/runtime
and @nuxt/test-utils/e2e
need to run in different testing environments and cannot be used in the same file.
To resolve this, you should split your tests into separate files and specify the appropriate test environment for each file. Here’s how you can do it:
Unit Test File (app.nuxt.spec.ts
):
import { registerEndpoint } from '@nuxt/test-utils/runtime'
registerEndpoint('/test/', () => ({
test: 'test-field'
}))
// Your unit tests here
End-to-End Test File (app.e2e.spec.ts
):
import { setup, $fetch } from '@nuxt/test-utils/e2e'
await setup({
setupTimeout: 10000,
})
// Your end-to-end tests here
This separation ensures that the registerEndpoint
functionality is correctly utilized within the appropriate testing environment [1][2].
Additionally, it's important to understand how $fetch
works in Nuxt. $fetch
is a utility provided by the ofetch library and is auto-imported globally across your application. It is used behind the scenes by useFetch
and useAsyncData
composables to perform data fetching. However, using $fetch
alone does not provide network calls de-duplication and navigation prevention, which are essential for efficient data fetching in Nuxt applications. Therefore, it is recommended to use $fetch
for client-side interactions or in combination with useAsyncData
for fetching initial component data [2].
For more details, you can refer to the Nuxt documentation on testing [1].
To continue talking to Dosu, mention @dosu.
By the suggestion of @dosu I split into two files and added three tests in each, but now it cannot even setup, it just times out after 10 seconds because it cannot setup. If I just add createContext() at least it will setup, hovewer, it still cannot get the mocked response. For the index page I found that useAsyncData will return an empty response (no data to be tested for the app) and onMounted the $fetch will turn out in an error.
To address the setup timeout issue and ensure that mocked responses are correctly fetched using useAsyncData
and $fetch
, you can follow these steps:
Increase the Setup Timeout: Ensure that the setup timeout is sufficient for your environment. You can increase the setupTimeout
in your setup
function.
Ensure Proper Context Initialization: Make sure that the test context is properly initialized and exposed to the environment.
Mock Endpoints Correctly: Ensure that the endpoints are correctly registered and accessible during the tests.
Here’s an example of how you can set up your tests:
app.nuxt.spec.ts
):import { registerEndpoint } from '@nuxt/test-utils/runtime'
import { defineEventHandler } from 'h3'
registerEndpoint('/test/', defineEventHandler(() => ({
test: 'test-field'
})))
registerEndpoint('/api/hello', defineEventHandler(() => ({
message: 'Hello World'
})))
registerEndpoint('/api/hey', defineEventHandler(() => ({
foo: 'bar',
baz: 'qux'
})))
// Your unit tests here
app.e2e.spec.ts
):import { setup, $fetch } from '@nuxt/test-utils/e2e'
await setup({
setupTimeout: 30000, // Increase the setup timeout
})
describe('App', () => {
it('fetches data from /test/ endpoint', async () => {
const response = await $fetch('/test/')
expect(response).toEqual({ test: 'test-field' })
})
it('fetches data from /api/hello endpoint', async () => {
const { data } = await useAsyncData('api-hello', () => $fetch('/api/hello'))
expect(data.value).toEqual({ message: 'Hello World' })
})
it('fetches data from /api/hey endpoint', async () => {
const { data } = await useAsyncData('api-hey', () => $fetch('/api/hey'))
expect(data.value).toEqual({ foo: 'bar', baz: 'qux' })
})
})
useAsyncData
and $fetch
:Ensure that the timeout is correctly handled and the request is aborted if it exceeds the specified timeout duration:
const timeoutLength = toValue(opts.timeout)
let timeoutId: NodeJS.Timeout
if (timeoutLength) {
timeoutId = setTimeout(() => controller.abort('Request aborted due to timeout.'), timeoutLength)
controller.signal.onabort = () => clearTimeout(timeoutId)
}
let _$fetch = opts.$fetch || globalThis.$fetch
if (import.meta.server && !opts.$fetch) {
const isLocalFetch = typeof _request.value === 'string' && _request.value[0] === '/' && (!toValue(opts.baseURL) || toValue(opts.baseURL)![0] === '/')
if (isLocalFetch) {
_$fetch = useRequestFetch()
}
}
return _$fetch(_request.value, { signal: controller.signal, ..._fetchOptions } as any).finally(() => { clearTimeout(timeoutId) }) as Promise<_ResT>
You can use composables like useSleep
and useCounter
to simulate async data fetching and ensure that the mocked responses are correctly fetched:
export const useSleep = () => useAsyncData('sleep', async () => {
await new Promise(resolve => setTimeout(resolve, 50))
return 'Slept!'
})
export const useCounter = () => useFetch('/api/useAsyncData/count')
These steps should help resolve the setup timeout issue and ensure that mocked responses are correctly fetched using useAsyncData
and $fetch
in your tests [1][2][3].
To continue talking to Dosu, mention @dosu.
Environment
Build Modules: -
Reproduction
A minimal repro showing that nitro's (I think) $fetch works while the one from test-utils module (ofetch/happy-dom) does not
Describe the bug
Using $fetch as in the examples from the documentation will not be able to find registered server routes in the tests. The problem is that in case you want to render component/page in the tests, it will use $fetch from happy-dom which cannot find these registered routes.
Additional context
No response
Logs