[READ-ONLY] Mirror of https://github.com/danielroe/nuxt-vitest. An vitest environment with support for testing code that needs a Nuxt runtime environment
nuxt nuxt-module testing unit-testing vitest
0

Configure Feed

Select the types of activity you want to include in your feed.

feat: allow passing mount options in `mountSuspended` (#159)

Co-authored-by: Ghazi <ghazialhouwari@gmail.com>

authored by

Maarten van Hunsel
Ghazi
and committed by
GitHub
(May 7, 2023, 9:58 PM +0100) b0c478a9 bf09b12a

+78 -17
+2
.gitignore
··· 1 1 node_modules 2 2 *.log* 3 3 dist 4 + 5 + .idea
+13 -7
pnpm-lock.yaml
··· 55 55 version: 1.2.1 56 56 vitest: 57 57 specifier: 0.30.1 58 - version: 0.30.1(happy-dom@9.10.9) 58 + version: 0.30.1(@vitest/ui@0.30.0) 59 59 vue: 60 60 specifier: 3.2.47 61 61 version: 3.2.47 ··· 111 111 '@vue/test-utils': 112 112 specifier: ^2.3.2 113 113 version: 2.3.2(vue@3.2.47) 114 + defu: 115 + specifier: ^6.1.2 116 + version: 6.1.2 114 117 estree-walker: 115 118 specifier: ^3.0.3 116 119 version: 3.0.3 ··· 152 155 version: link:../packages/nuxt-vitest 153 156 vitest: 154 157 specifier: 0.30.1 155 - version: 0.30.1(happy-dom@9.10.9) 158 + version: 0.30.1(@vitest/ui@0.30.0) 156 159 vitest-environment-nuxt: 157 160 specifier: workspace:* 158 161 version: link:../packages/vitest-environment-nuxt ··· 1808 1811 c8: 7.13.0 1809 1812 picocolors: 1.0.0 1810 1813 std-env: 3.3.3 1811 - vitest: 0.30.1(happy-dom@9.10.9) 1814 + vitest: 0.30.1(@vitest/ui@0.30.0) 1812 1815 dev: true 1813 1816 1814 1817 /@vitest/expect@0.30.1: ··· 1848 1851 pathe: 1.1.0 1849 1852 picocolors: 1.0.0 1850 1853 sirv: 2.0.2 1851 - dev: false 1852 1854 1853 1855 /@vitest/utils@0.30.0: 1854 1856 resolution: {integrity: sha512-qFZgoOKQ+rJV9xG4BBxgOSilnLQ2gkfG4I+z1wBuuQ3AD33zQrnB88kMFfzsot1E1AbF3dNK1e4CU7q3ojahRA==} ··· 1856 1858 concordance: 5.0.4 1857 1859 loupe: 2.3.6 1858 1860 pretty-format: 27.5.1 1859 - dev: false 1860 1861 1861 1862 /@vitest/utils@0.30.1: 1862 1863 resolution: {integrity: sha512-/c8Xv2zUVc+rnNt84QF0Y0zkfxnaGhp87K2dYJMLtLOIckPzuxLVzAtFCicGFdB4NeBHNzTRr1tNn7rCtQcWFA==} ··· 3108 3109 3109 3110 /css.escape@1.5.1: 3110 3111 resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} 3112 + dev: false 3111 3113 3112 3114 /cssesc@3.0.0: 3113 3115 resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} ··· 3983 3985 3984 3986 /fflate@0.7.4: 3985 3987 resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} 3986 - dev: false 3987 3988 3988 3989 /figures@5.0.0: 3989 3990 resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} ··· 4513 4514 webidl-conversions: 7.0.0 4514 4515 whatwg-encoding: 2.0.0 4515 4516 whatwg-mimetype: 3.0.0 4517 + dev: false 4516 4518 4517 4519 /hard-rejection@2.1.0: 4518 4520 resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} ··· 4577 4579 /he@1.2.0: 4578 4580 resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 4579 4581 hasBin: true 4582 + dev: false 4580 4583 4581 4584 /hookable@5.5.3: 4582 4585 resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} ··· 9044 9047 - sugarss 9045 9048 - supports-color 9046 9049 - terser 9047 - dev: false 9048 9050 9049 9051 /vitest@0.30.1(happy-dom@9.10.9): 9050 9052 resolution: {integrity: sha512-y35WTrSTlTxfMLttgQk4rHcaDkbHQwDP++SNwPb+7H8yb13Q3cu2EixrtHzF27iZ8v0XCciSsLg00RkPAzB/aA==} ··· 9111 9113 - sugarss 9112 9114 - supports-color 9113 9115 - terser 9116 + dev: false 9114 9117 9115 9118 /vm2@3.9.14: 9116 9119 resolution: {integrity: sha512-HgvPHYHeQy8+QhzlFryvSteA4uQLBCOub02mgqdR+0bN/akRZ48TGB1v0aCv7ksyc0HXx16AZtMHKS38alc6TA==} ··· 9248 9251 /webidl-conversions@7.0.0: 9249 9252 resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} 9250 9253 engines: {node: '>=12'} 9254 + dev: false 9251 9255 9252 9256 /webpack-sources@3.2.3: 9253 9257 resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} ··· 9265 9269 engines: {node: '>=12'} 9266 9270 dependencies: 9267 9271 iconv-lite: 0.6.3 9272 + dev: false 9268 9273 9269 9274 /whatwg-mimetype@3.0.0: 9270 9275 resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} 9271 9276 engines: {node: '>=12'} 9277 + dev: false 9272 9278 9273 9279 /whatwg-url@5.0.0: 9274 9280 resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+1
packages/vitest-environment-nuxt/package.json
··· 44 44 "dependencies": { 45 45 "@nuxt/kit": "^3.2.0", 46 46 "@vue/test-utils": "^2.3.2", 47 + "defu": "^6.1.2", 47 48 "estree-walker": "^3.0.3", 48 49 "h3": "^1.6.5", 49 50 "happy-dom": "^9.10.9",
+15
playground/components/OptionsComponent.vue
··· 1 + <template> 2 + <h2>{{ title }}</h2> 3 + <div> 4 + <slot /> 5 + </div> 6 + </template> 7 + 8 + <script setup lang="ts"> 9 + withDefaults( 10 + defineProps<{ title?: string }>(), 11 + { title: 'The original' } 12 + ) 13 + 14 + await new Promise(resolve => setTimeout(resolve, 1)) 15 + </script>
+30
playground/tests/nuxt/index.spec.ts
··· 4 4 5 5 import App from '~/app.vue' 6 6 import FetchComponent from '~/components/FetchComponent.vue' 7 + import OptionsComponent from '~/components/OptionsComponent.vue' 7 8 8 9 describe('client-side nuxt features', () => { 9 10 it('can use core nuxt composables within test file', () => { ··· 48 49 <div>Index page</div> 49 50 <a href=\\"/test\\"> Test link </a>" 50 51 `) 52 + }) 53 + 54 + it('should render default props within nuxt suspense', async () => { 55 + const component = await mountSuspended(OptionsComponent) 56 + expect(component.find('h2').html()).toMatchInlineSnapshot( 57 + '"<h2>The original</h2>"' 58 + ) 59 + }) 60 + 61 + it('should render passed props within nuxt suspense', async () => { 62 + const component = await mountSuspended(OptionsComponent, { 63 + props: { 64 + title: 'title from mount suspense props', 65 + }, 66 + }) 67 + expect(component.find('h2').html()).toMatchInlineSnapshot( 68 + '"<h2>title from mount suspense props</h2>"' 69 + ) 70 + }) 71 + 72 + it('can pass slots to mounted components within nuxt suspense', async () => { 73 + const component = await mountSuspended(OptionsComponent, { 74 + slots: { 75 + default: () => 'slot from mount suspense', 76 + }, 77 + }) 78 + expect(component.find('div').html()).toMatchInlineSnapshot( 79 + '"<div>slot from mount suspense</div>"' 80 + ) 51 81 }) 52 82 53 83 it('can mock fetch requests', async () => {
+17 -10
packages/vitest-environment-nuxt/src/runtime/mount.ts
··· 1 - import { mount, VueWrapper } from '@vue/test-utils' 1 + import { mount, VueWrapper, MountingOptions } from '@vue/test-utils' 2 2 import { h, DefineComponent, Suspense, nextTick } from 'vue' 3 + import { defu } from 'defu' 3 4 import type { RouteLocationRaw } from 'vue-router' 4 5 5 6 import { RouterLink } from './components/RouterLink' ··· 8 9 import NuxtRoot from '#build/root-component.mjs' 9 10 import { useRouter } from '#imports' 10 11 11 - interface MountSuspendedOptions { 12 + interface MountSuspendedOptions extends MountingOptions<any, any> { 12 13 route?: RouteLocationRaw 13 14 } 14 15 15 16 export async function mountSuspended< 16 17 T extends DefineComponent<any, any, any, any> 17 - >(component: T, options?: MountSuspendedOptions) { 18 + > (component: T, options?: MountSuspendedOptions) { 19 + const { 20 + props = {}, 21 + attrs = {}, 22 + slots = {}, 23 + route = '/', 24 + ..._options 25 + } = options || {} 26 + 18 27 // @ts-expect-error untyped global __unctx__ 19 28 const vueApp = globalThis.__unctx__.get('nuxt-app').tryUse().vueApp 20 29 return new Promise<VueWrapper<InstanceType<T>>>(resolve => { ··· 30 39 h({ 31 40 async setup() { 32 41 const router = useRouter() 33 - await router.replace(options?.route || '/') 34 - return () => h(component) 42 + await router.replace(route) 43 + return () => h(component, { ...props, ...attrs }, slots) 35 44 }, 36 45 }), 37 46 } 38 47 ), 39 48 }, 40 - { 49 + defu(_options, { 41 50 global: { 42 51 config: { 43 52 globalProperties: vueApp.config.globalProperties, 44 53 }, 45 54 provide: vueApp._context.provides, 46 - components: { 47 - RouterLink, 48 - }, 55 + components: { RouterLink }, 49 56 }, 50 - } 57 + }) 51 58 ) 52 59 }) 53 60 }