From b1e61fafbfded8e03c3b4e3cabe41860da1844cb Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Fri, 9 Dec 2022 22:45:06 +0100 Subject: [PATCH] feat: mock `` --- playground/app.vue | 2 +- playground/pages/index.vue | 6 ++++ playground/tests/index.spec.ts | 14 ++++++---- src/index.ts | 7 ++++- src/runtime/components/RouterLink.ts | 21 ++++++++++++++ src/runtime/mount.ts | 41 ++++++---------------------- src/vitest-environment-nuxt/index.ts | 19 ++++++------- 7 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 src/runtime/components/RouterLink.ts diff --git a/playground/app.vue b/playground/app.vue index d1016b40..5831a42b 100644 --- a/playground/app.vue +++ b/playground/app.vue @@ -1,7 +1,7 @@ diff --git a/playground/tests/index.spec.ts b/playground/tests/index.spec.ts index 4d674c54..e15244b0 100644 --- a/playground/tests/index.spec.ts +++ b/playground/tests/index.spec.ts @@ -25,12 +25,14 @@ describe('client-side nuxt features', () => { expect(app.$router).toBeDefined() }) - it('works with route composables', async () => { - expect(useRoute().matched[0].meta).toMatchInlineSnapshot('{}') + it('defaults to index page', async () => { + expect(useRoute().matched[0].meta).toMatchInlineSnapshot(` + { + "value": "set in index", + } + `) expect(useRoute().fullPath).toMatchInlineSnapshot('"/"') - // TODO: - // await useRouter().push('/test') - // expect(useRoute().fullPath).toMatchInlineSnapshot('"/test"') + // TODO: should it be possible to push to other routes? }) }) @@ -40,7 +42,7 @@ describe('test utils', () => { expect(component.html()).toMatchInlineSnapshot(` "
This is an auto-imported component
- " + Test link" `) }) diff --git a/src/index.ts b/src/index.ts index 59f7e744..c2c3ad34 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,7 +9,12 @@ async function getViteConfig (rootDir = process.cwd()) { const nuxt = await loadNuxt({ cwd: rootDir, dev: false, - overrides: { ssr: false }, + overrides: { + ssr: false, + app: { + rootId: 'nuxt-test' + }, + }, }) return new Promise((resolve, reject) => { nuxt.hook('vite:extendConfig', config => { diff --git a/src/runtime/components/RouterLink.ts b/src/runtime/components/RouterLink.ts new file mode 100644 index 00000000..12cdc2ed --- /dev/null +++ b/src/runtime/components/RouterLink.ts @@ -0,0 +1,21 @@ +export const RouterLink = defineComponent({ + functional: true, + props: { + to: String, + custom: Boolean, + replace: Boolean, + // Not implemented + activeClass: String, + exactActiveClass: String, + ariaCurrentValue: String + }, + setup: (props, { slots }) => { + const navigate = () => {} + return () => { + const route = props.to ? useRouter().resolve(props.to) : {} + return props.custom + ? slots.default?.({ href: props.to, navigate, route }) + : h('a', { href: props.to, onClick: (e: MouseEvent) => { e.preventDefault(); return navigate() } }, slots) + } + } +}) diff --git a/src/runtime/mount.ts b/src/runtime/mount.ts index dc613508..1b0cfdb9 100644 --- a/src/runtime/mount.ts +++ b/src/runtime/mount.ts @@ -1,6 +1,8 @@ import { mount, VueWrapper } from '@vue/test-utils' import { h, DefineComponent, Suspense } from 'vue' +import { RouterLink } from './components/RouterLink' + // @ts-expect-error virtual file import NuxtRoot from '#build/root-component.mjs' @@ -9,39 +11,12 @@ export async function mountSuspended h(Suspense, { onResolve: () => resolve(vm as any) }, { default: () => h(component) }) + }, { + global: { + components: { + RouterLink + } + } }) }) } - - -export async function mountSuspendedWithRoute> (component: T) { - return new Promise>>(async resolve => { - // const vueApp = createApp({ - // setup: NuxtRoot.setup, - // render: () => h(Suspense, { onResolve: () => resolve(vueApp as any) }, { default: () => h(component) }) - // }) - - // const nuxt = createNuxtApp({ vueApp }) - - // try { - // await applyPlugins(nuxt, plugins) - // } catch (err) { - // await nuxt.callHook('app:error', err) - // nuxt.payload.error = (nuxt.payload.error || err) as any - // } - - // try { - // await nuxt.hooks.callHook('app:created', vueApp) - // await nuxt.hooks.callHook('app:beforeMount', vueApp) - // const app = document.createElement('div') - // document.body.appendChild(app) - // app.id = 'something' - // vueApp.mount('#something') - // await nuxt.hooks.callHook('app:mounted', vueApp) - // await nextTick() - // } catch (err) { - // await nuxt.callHook('app:error', err) - // nuxt.payload.error = (nuxt.payload.error || err) as any - // } - }) -} diff --git a/src/vitest-environment-nuxt/index.ts b/src/vitest-environment-nuxt/index.ts index 5c6745ba..1b2fa04a 100644 --- a/src/vitest-environment-nuxt/index.ts +++ b/src/vitest-environment-nuxt/index.ts @@ -1,12 +1,14 @@ import type { Environment } from 'vitest' -import { builtinEnvironments, populateGlobal } from 'vitest/environments' import { Window, GlobalWindow } from 'happy-dom' import { createFetch } from 'ohmyfetch' -import { App, createApp, defineEventHandler, toNodeListener } from 'h3' +import { App, createApp, toNodeListener } from 'h3' import { createCall, createFetch as createLocalFetch, } from 'unenv/runtime/fetch/index' +// @ts-expect-error TODO: add subpath types +import * as viteEnvironments from 'vitest/environments' +const { populateGlobal } = viteEnvironments as typeof import('vitest/dist/environments') export default { name: 'nuxt', @@ -14,11 +16,11 @@ export default { const win = new (GlobalWindow || Window)() as any as (Window & { __app: App __registry: Set + __NUXT__: any $fetch: any fetch: any }) - // @ts-expect-error undeclared property on window win.__NUXT__ = { serverRendered: false, config: { @@ -30,16 +32,13 @@ export default { } const app = win.document.createElement('div') + app.id = 'nuxt-test' win.document.body.appendChild(app) - // Workaround for happy-dom bug - const { querySelector } = win.document - app.id = 'nuxt' - function qsWrapper (selectors) { - if (selectors === '#__nuxt') selectors = '#nuxt' - return querySelector(selectors) + // @ts-expect-error + win.IntersectionObserver = win.IntersectionObserver || class IntersectionObserver { + observe () {} } - win.document.querySelector = qsWrapper const h3App = createApp()