mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
feat(test-utils): allow mounting single component for testing (#5723)
This commit is contained in:
parent
3fc9a75070
commit
72ba53efbc
@ -2,6 +2,7 @@
|
|||||||
<Suspense @resolve="onResolve">
|
<Suspense @resolve="onResolve">
|
||||||
<ErrorComponent v-if="error" :error="error" />
|
<ErrorComponent v-if="error" :error="error" />
|
||||||
<IslandRenderer v-else-if="islandContext" :context="islandContext" />
|
<IslandRenderer v-else-if="islandContext" :context="islandContext" />
|
||||||
|
<component :is="SingleRenderer" v-else-if="SingleRenderer" />
|
||||||
<AppComponent v-else />
|
<AppComponent v-else />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</template>
|
</template>
|
||||||
@ -21,6 +22,10 @@ const IslandRenderer = process.server
|
|||||||
const nuxtApp = useNuxtApp()
|
const nuxtApp = useNuxtApp()
|
||||||
const onResolve = nuxtApp.deferHydration()
|
const onResolve = nuxtApp.deferHydration()
|
||||||
|
|
||||||
|
const url = process.server ? nuxtApp.ssrContext.url : window.location.pathname
|
||||||
|
const SingleRenderer = process.dev && process.server && url.startsWith('/__nuxt_component_test__/') && defineAsyncComponent(() => import('#build/test-component-wrapper.mjs')
|
||||||
|
.then(r => r.default(process.server ? url : window.location.href)))
|
||||||
|
|
||||||
// Inject default route (outside of pages) as active route
|
// Inject default route (outside of pages) as active route
|
||||||
provide('_route', useRoute())
|
provide('_route', useRoute())
|
||||||
|
|
||||||
|
19
packages/nuxt/src/app/components/test-component-wrapper.ts
Normal file
19
packages/nuxt/src/app/components/test-component-wrapper.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { parseURL } from 'ufo'
|
||||||
|
import { defineComponent, h } from 'vue'
|
||||||
|
import { parseQuery } from 'vue-router'
|
||||||
|
|
||||||
|
export default (url:string) => defineComponent({
|
||||||
|
name: 'NuxtTestComponentWrapper',
|
||||||
|
|
||||||
|
async setup (props, { attrs }) {
|
||||||
|
const query = parseQuery(parseURL(url).search)
|
||||||
|
const urlProps = query.props ? JSON.parse(query.props as string) : {}
|
||||||
|
const comp = await import(/* @vite-ignore */ query.path as string).then(r => r.default)
|
||||||
|
return () => [
|
||||||
|
h('div', 'Component Test Wrapper for ' + query.path),
|
||||||
|
h('div', { id: 'nuxt-component-root' }, [
|
||||||
|
h(comp, { ...attrs, ...props, ...urlProps })
|
||||||
|
])
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
@ -40,6 +40,11 @@ export const errorComponentTemplate: NuxtTemplate<TemplateContext> = {
|
|||||||
filename: 'error-component.mjs',
|
filename: 'error-component.mjs',
|
||||||
getContents: ctx => genExport(ctx.app.errorComponent!, ['default'])
|
getContents: ctx => genExport(ctx.app.errorComponent!, ['default'])
|
||||||
}
|
}
|
||||||
|
// TODO: Use an alias
|
||||||
|
export const testComponentWrapperTemplate = {
|
||||||
|
filename: 'test-component-wrapper.mjs',
|
||||||
|
getContents: (ctx: TemplateContext) => genExport(resolve(ctx.nuxt.options.appDir, 'components/test-component-wrapper'), ['default'])
|
||||||
|
}
|
||||||
|
|
||||||
export const cssTemplate: NuxtTemplate<TemplateContext> = {
|
export const cssTemplate: NuxtTemplate<TemplateContext> = {
|
||||||
filename: 'css.mjs',
|
filename: 'css.mjs',
|
||||||
|
@ -4,6 +4,7 @@ export default defineBuildConfig({
|
|||||||
declaration: true,
|
declaration: true,
|
||||||
entries: [
|
entries: [
|
||||||
'src/index',
|
'src/index',
|
||||||
|
'src/experimental',
|
||||||
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' }
|
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm' }
|
||||||
],
|
],
|
||||||
externals: [
|
externals: [
|
||||||
|
1
packages/test-utils/experimental.d.ts
vendored
Normal file
1
packages/test-utils/experimental.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './dist/experimental'
|
@ -9,6 +9,10 @@
|
|||||||
".": {
|
".": {
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"import": "./dist/index.mjs"
|
"import": "./dist/index.mjs"
|
||||||
|
},
|
||||||
|
"./experimental": {
|
||||||
|
"types": "./dist/experimental.d.ts",
|
||||||
|
"import": "./dist/experimental.mjs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
@ -26,7 +30,8 @@
|
|||||||
"get-port-please": "^3.0.1",
|
"get-port-please": "^3.0.1",
|
||||||
"jiti": "^1.18.2",
|
"jiti": "^1.18.2",
|
||||||
"ofetch": "^1.0.1",
|
"ofetch": "^1.0.1",
|
||||||
"pathe": "^1.1.0"
|
"pathe": "^1.1.0",
|
||||||
|
"ufo": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"playwright": "^1.32.2",
|
"playwright": "^1.32.2",
|
||||||
|
23
packages/test-utils/src/experimental.ts
Normal file
23
packages/test-utils/src/experimental.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { fetch as _fetch, $fetch as _$fetch } from 'ofetch'
|
||||||
|
import * as _kit from '@nuxt/kit'
|
||||||
|
import { resolve } from 'pathe'
|
||||||
|
import { stringifyQuery } from 'ufo'
|
||||||
|
import { useTestContext } from './context'
|
||||||
|
import { $fetch } from './server'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a function to render a component directly with the Nuxt server.
|
||||||
|
*/
|
||||||
|
export function $fetchComponent (filepath: string, props?: Record<string, any>) {
|
||||||
|
return $fetch(componentTestUrl(filepath, props))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function componentTestUrl (filepath: string, props?: Record<string, any>) {
|
||||||
|
const ctx = useTestContext()
|
||||||
|
filepath = resolve(ctx.options.rootDir, filepath)
|
||||||
|
const path = stringifyQuery({
|
||||||
|
path: filepath,
|
||||||
|
props: JSON.stringify(props)
|
||||||
|
})
|
||||||
|
return `/__nuxt_component_test__/?${path}`
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import { resolve } from 'node:path'
|
|
||||||
import { execa } from 'execa'
|
import { execa } from 'execa'
|
||||||
import { getRandomPort, waitForPort } from 'get-port-please'
|
import { getRandomPort, waitForPort } from 'get-port-please'
|
||||||
import type { FetchOptions } from 'ofetch'
|
import type { FetchOptions } from 'ofetch'
|
||||||
import { fetch as _fetch, $fetch as _$fetch } from 'ofetch'
|
import { fetch as _fetch, $fetch as _$fetch } from 'ofetch'
|
||||||
import * as _kit from '@nuxt/kit'
|
import * as _kit from '@nuxt/kit'
|
||||||
|
import { resolve } from 'pathe'
|
||||||
import { useTestContext } from './context'
|
import { useTestContext } from './context'
|
||||||
|
|
||||||
// @ts-ignore type cast
|
// @ts-ignore type cast
|
||||||
@ -75,5 +75,8 @@ export function url (path: string) {
|
|||||||
if (!ctx.url) {
|
if (!ctx.url) {
|
||||||
throw new Error('url is not available (is server option enabled?)')
|
throw new Error('url is not available (is server option enabled?)')
|
||||||
}
|
}
|
||||||
|
if (path.startsWith(ctx.url)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
return ctx.url + path
|
return ctx.url + path
|
||||||
}
|
}
|
||||||
|
@ -754,6 +754,9 @@ importers:
|
|||||||
pathe:
|
pathe:
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.0
|
||||||
version: 1.1.0
|
version: 1.1.0
|
||||||
|
ufo:
|
||||||
|
specifier: ^1.1.1
|
||||||
|
version: 1.1.1
|
||||||
vue:
|
vue:
|
||||||
specifier: ^3.2.47
|
specifier: ^3.2.47
|
||||||
version: 3.2.47
|
version: 3.2.47
|
||||||
|
@ -7,6 +7,7 @@ import { setup, fetch, $fetch, startServer, isDev, createPage, url } from '@nuxt
|
|||||||
|
|
||||||
import type { NuxtIslandResponse } from '../packages/nuxt/src/core/runtime/nitro/renderer'
|
import type { NuxtIslandResponse } from '../packages/nuxt/src/core/runtime/nitro/renderer'
|
||||||
import { expectNoClientErrors, expectWithPolling, renderPage, withLogs } from './utils'
|
import { expectNoClientErrors, expectWithPolling, renderPage, withLogs } from './utils'
|
||||||
|
import { $fetchComponent } from '@nuxt/test-utils/experimental'
|
||||||
|
|
||||||
const isWebpack = process.env.TEST_BUILDER === 'webpack'
|
const isWebpack = process.env.TEST_BUILDER === 'webpack'
|
||||||
|
|
||||||
@ -1277,3 +1278,13 @@ describe.skipIf(isWindows)('useAsyncData', () => {
|
|||||||
await expectNoClientErrors('/useAsyncData/promise-all')
|
await expectNoClientErrors('/useAsyncData/promise-all')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe.runIf(isDev())('component testing', () => {
|
||||||
|
it('should work', async () => {
|
||||||
|
const comp1 = await $fetchComponent('components/SugarCounter.vue', { multiplier: 2 })
|
||||||
|
expect(comp1).toContain('12 x 2 = 24')
|
||||||
|
|
||||||
|
const comp2 = await $fetchComponent('components/SugarCounter.vue', { multiplier: 4 })
|
||||||
|
expect(comp2).toContain('12 x 4 = 48')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -7,6 +7,7 @@ export default defineConfig({
|
|||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'#app': resolve('./packages/nuxt/dist/app/index'),
|
'#app': resolve('./packages/nuxt/dist/app/index'),
|
||||||
|
'@nuxt/test-utils/experimental': resolve('./packages/test-utils/src/experimental.ts'),
|
||||||
'@nuxt/test-utils': resolve('./packages/test-utils/src/index.ts')
|
'@nuxt/test-utils': resolve('./packages/test-utils/src/index.ts')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user