From: edison Date: Thu, 7 Jan 2021 09:07:23 +0000 (+0800) Subject: refactor(test): replace custom testing utils with VTU (#695) X-Git-Tag: v4.0.3~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9ab621ca1fef5a035dd5c1c72b76454564cd50dc;p=thirdparty%2Fvuejs%2Frouter.git refactor(test): replace custom testing utils with VTU (#695) Co-authored-by: Eduardo San Martin Morote --- diff --git a/__tests__/RouterLink.spec.ts b/__tests__/RouterLink.spec.ts index c2af1951..4aa877c9 100644 --- a/__tests__/RouterLink.spec.ts +++ b/__tests__/RouterLink.spec.ts @@ -9,11 +9,12 @@ import { RouteLocationNormalized, } from '../src/types' import { createMemoryHistory, RouterOptions } from '../src' -import { mount, createMockedRoute } from './mount' -import { defineComponent, nextTick, PropType } from 'vue' +import { createMockedRoute } from './mount' +import { defineComponent, PropType } from 'vue' import { RouteRecordNormalized } from '../src/matcher/types' import { routerKey } from '../src/injectionSymbols' import { tick } from './utils' +import { mount } from '@vue/test-utils' const records = { home: {} as RouteRecordNormalized, @@ -371,11 +372,13 @@ async function factory( } router.resolve.mockReturnValueOnce(resolvedLocation) - const wrapper = await mount(RouterLink, { + const wrapper = mount(RouterLink as any, { propsData, - provide: { - [routerKey as any]: router, - ...route.provides, + global: { + provide: { + [routerKey as any]: router, + ...route.provides, + }, }, slots: { default: slotTemplate }, }) @@ -390,7 +393,7 @@ describe('RouterLink', () => { { to: locations.basic.string }, locations.basic.normalized ) - expect(wrapper.find('a')!.getAttribute('href')).toBe('/home') + expect(wrapper.find('a')!.attributes('href')).toBe('/home') }) it('can change the value', async () => { @@ -399,10 +402,10 @@ describe('RouterLink', () => { { to: locations.basic.string }, locations.basic.normalized ) - expect(wrapper.find('a')!.getAttribute('href')).toBe('/home') + expect(wrapper.find('a')!.attributes('href')).toBe('/home') router.resolve.mockReturnValueOnce(locations.foo.normalized) await wrapper.setProps({ to: locations.foo.string }) - expect(wrapper.find('a')!.getAttribute('href')).toBe('/foo') + expect(wrapper.find('a')!.attributes('href')).toBe('/foo') }) it('displays a link with an object with path prop', async () => { @@ -411,7 +414,7 @@ describe('RouterLink', () => { { to: { path: locations.basic.string } }, locations.basic.normalized ) - expect(wrapper.find('a')!.getAttribute('href')).toBe('/home') + expect(wrapper.find('a')!.attributes('href')).toBe('/home') }) it('can be active', async () => { @@ -420,7 +423,7 @@ describe('RouterLink', () => { { to: locations.basic.string }, locations.basic.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') + expect(wrapper.find('a').classes()).toContain('router-link-active') }) it('sets aria-current to page by default when exact active', async () => { @@ -429,10 +432,10 @@ describe('RouterLink', () => { { to: locations.parent.string }, locations.parent.normalized ) - expect(wrapper.find('a')!.getAttribute('aria-current')).toBe('page') + expect(wrapper.find('a')!.attributes('aria-current')).toBe('page') route.set(locations.child.normalized) await tick() - expect(wrapper.find('a')!.getAttribute('aria-current')).not.toBe('page') + expect(wrapper.find('a')!.attributes('aria-current')).not.toBe('page') }) it('can customize aria-current value', async () => { @@ -441,7 +444,7 @@ describe('RouterLink', () => { { to: locations.basic.string, ariaCurrentValue: 'time' }, locations.basic.normalized ) - expect(wrapper.find('a')!.getAttribute('aria-current')).toBe('time') + expect(wrapper.find('a')!.attributes('aria-current')).toBe('time') }) it('can customize active class', async () => { @@ -450,8 +453,8 @@ describe('RouterLink', () => { { to: locations.basic.string, activeClass: 'is-active' }, locations.basic.normalized ) - expect(wrapper.find('a')!.className).not.toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('is-active') + expect(wrapper.find('a')!.classes()).not.toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('is-active') }) it('prop classes take over global', async () => { @@ -470,14 +473,14 @@ describe('RouterLink', () => { // force render because options is not reactive router.resolve.mockReturnValueOnce(locations.basic.normalized) await wrapper.setProps({ to: locations.basic.string }) - expect(wrapper.find('a')!.className).not.toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).not.toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) - expect(wrapper.find('a')!.className).not.toContain('custom') - expect(wrapper.find('a')!.className).not.toContain('custom-exact') - expect(wrapper.find('a')!.className).toContain('is-active') - expect(wrapper.find('a')!.className).toContain('is-exact') + expect(wrapper.find('a')!.classes()).not.toContain('custom') + expect(wrapper.find('a')!.classes()).not.toContain('custom-exact') + expect(wrapper.find('a')!.classes()).toContain('is-active') + expect(wrapper.find('a')!.classes()).toContain('is-exact') }) it('can globally customize active class', async () => { @@ -491,8 +494,8 @@ describe('RouterLink', () => { // force render because options is not reactive router.resolve.mockReturnValueOnce(locations.basic.normalized) await wrapper.setProps({ to: locations.basic.string }) - expect(wrapper.find('a')!.className).not.toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('custom') + expect(wrapper.find('a')!.classes()).not.toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('custom') }) it('can globally customize exact active class', async () => { @@ -506,10 +509,10 @@ describe('RouterLink', () => { // force render because options is not reactive router.resolve.mockReturnValueOnce(locations.basic.normalized) await wrapper.setProps({ to: locations.basic.string }) - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) - expect(wrapper.find('a')!.className).toContain('custom') + expect(wrapper.find('a')!.classes()).toContain('custom') }) it('can customize exact active class', async () => { @@ -518,10 +521,10 @@ describe('RouterLink', () => { { to: locations.basic.string, exactActiveClass: 'is-active' }, locations.basic.normalized ) - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) - expect(wrapper.find('a')!.className).toContain('is-active') + expect(wrapper.find('a')!.classes()).toContain('is-active') }) it('can be active with custom class', async () => { @@ -530,8 +533,8 @@ describe('RouterLink', () => { { to: locations.basic.string, class: 'nav-item' }, locations.basic.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('nav-item') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('nav-item') }) it('is not active on a non matched location', async () => { @@ -540,7 +543,7 @@ describe('RouterLink', () => { { to: locations.basic.string }, locations.basic.normalized ) - expect(wrapper.find('a')!.className).toBe('') + expect(wrapper.find('a')!.classes()).toHaveLength(0) }) it('is not active with different params type', async () => { @@ -549,7 +552,7 @@ describe('RouterLink', () => { { to: locations.singleStringParams.string }, locations.singleStringParams.normalized ) - expect(wrapper.find('a')!.className).toBe('') + expect(wrapper.find('a')!.classes()).toHaveLength(0) }) it('is not active with different repeated params', async () => { @@ -558,7 +561,7 @@ describe('RouterLink', () => { { to: locations.anotherRepeatedParams2.string }, locations.anotherRepeatedParams2.normalized ) - expect(wrapper.find('a')!.className).toBe('') + expect(wrapper.find('a')!.classes()).toHaveLength(0) }) it('is not active with more repeated params', async () => { @@ -567,7 +570,7 @@ describe('RouterLink', () => { { to: locations.repeatedParams3.string }, locations.repeatedParams3.normalized ) - expect(wrapper.find('a')!.className).toBe('') + expect(wrapper.find('a')!.classes()).toHaveLength(0) }) it('is not active with partial repeated params', async () => { @@ -576,7 +579,7 @@ describe('RouterLink', () => { { to: locations.repeatedParams2.string }, locations.repeatedParams2.normalized ) - expect(wrapper.find('a')!.className).toBe('') + expect(wrapper.find('a')!.classes()).toHaveLength(0) }) it('can be active as an alias', async () => { @@ -585,8 +588,8 @@ describe('RouterLink', () => { { to: locations.alias.string }, locations.alias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') wrapper = ( await factory( locations.alias.normalized, @@ -594,8 +597,8 @@ describe('RouterLink', () => { locations.basic.normalized ) ).wrapper - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') }) it('is active when a child is active', async () => { @@ -604,8 +607,8 @@ describe('RouterLink', () => { { to: locations.parent.string }, locations.parent.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -616,8 +619,8 @@ describe('RouterLink', () => { { to: locations.child.string }, locations.child.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') }) it('child is not active if the parent is active', async () => { @@ -626,8 +629,8 @@ describe('RouterLink', () => { { to: locations.child.string }, locations.child.normalized ) - expect(wrapper.find('a')!.className).not.toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).not.toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -638,8 +641,8 @@ describe('RouterLink', () => { { to: locations.parent.string }, locations.parent.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -650,8 +653,8 @@ describe('RouterLink', () => { { to: locations.childEmpty.string }, locations.childEmpty.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -662,8 +665,8 @@ describe('RouterLink', () => { { to: locations.childEmptyAlias.string }, locations.childEmptyAlias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -674,8 +677,8 @@ describe('RouterLink', () => { { to: locations.childEmpty.string }, locations.childEmpty.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -686,8 +689,8 @@ describe('RouterLink', () => { { to: locations.childEmptyAlias.string }, locations.childEmptyAlias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -698,8 +701,8 @@ describe('RouterLink', () => { { to: locations.parentAlias.string }, locations.parentAlias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -710,8 +713,8 @@ describe('RouterLink', () => { { to: locations.parentAlias.string }, locations.parentAlias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) wrapper = ( @@ -721,8 +724,8 @@ describe('RouterLink', () => { locations.parentAlias.normalized ) ).wrapper - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).not.toContain( + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).not.toContain( 'router-link-exact-active' ) }) @@ -733,8 +736,8 @@ describe('RouterLink', () => { { to: locations.parentAlias.string }, locations.parentAlias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') wrapper = ( await factory( @@ -743,8 +746,8 @@ describe('RouterLink', () => { locations.parent.normalized ) ).wrapper - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') }) it('child and parent with alias', async () => { @@ -753,8 +756,8 @@ describe('RouterLink', () => { { to: locations.childDoubleAlias.string }, locations.childDoubleAlias.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') wrapper = ( await factory( @@ -763,8 +766,8 @@ describe('RouterLink', () => { locations.childParentAlias.normalized ) ).wrapper - expect(wrapper.find('a')!.className).toContain('router-link-active') - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') }) it('can be exact-active', async () => { @@ -773,7 +776,7 @@ describe('RouterLink', () => { { to: locations.basic.string }, locations.basic.normalized ) - expect(wrapper.find('a')!.className).toContain('router-link-exact-active') + expect(wrapper.find('a')!.classes()).toContain('router-link-exact-active') }) it('calls ensureLocation', async () => { @@ -792,8 +795,7 @@ describe('RouterLink', () => { { to: locations.basic.string }, locations.basic.normalized ) - wrapper.find('a')!.click() - await nextTick() + wrapper.find('a')!.trigger('click') expect(router.push).toHaveBeenCalledTimes(1) }) @@ -803,8 +805,7 @@ describe('RouterLink', () => { { to: locations.alias.string }, locations.alias.normalized ) - wrapper.find('a')!.click() - await nextTick() + wrapper.find('a')!.trigger('click') expect(router.push).toHaveBeenCalledTimes(1) expect(router.push).not.toHaveBeenCalledWith( expect.objectContaining({ @@ -817,12 +818,14 @@ describe('RouterLink', () => { describe('v-slot', () => { const slotTemplate = ` + ` it('provides information on v-slot', async () => { @@ -844,8 +847,8 @@ describe('RouterLink', () => { slotTemplate ) - expect(wrapper.rootEl.children[0].tagName).toBe('A') - expect(wrapper.rootEl.children).toHaveLength(1) + expect(wrapper.element.tagName).toBe('A') + expect(wrapper.element.childElementCount).toBe(1) }) it('can customize the rendering and remove the wrapping `a`', async () => { @@ -911,11 +914,13 @@ describe('RouterLink', () => { } router.resolve.mockReturnValueOnce(resolvedLocation) - const wrapper = await mount(AppLink, { + const wrapper = await mount(AppLink as any, { propsData, - provide: { - [routerKey as any]: router, - ...route.provides, + global: { + provide: { + [routerKey as any]: router, + ...route.provides, + }, }, slots: { default: slotTemplate }, }) @@ -934,7 +939,7 @@ describe('RouterLink', () => { locations.foo.normalized ) - expect(wrapper.find('a')!.className).toEqual('inactive') + expect(wrapper.find('a')!.classes()).toEqual(['inactive']) }) it('can extend RouterLink with external link', async () => { @@ -946,8 +951,8 @@ describe('RouterLink', () => { locations.foo.normalized ) - expect(wrapper.find('a')!.className).toEqual('') - expect(wrapper.find('a')!.href).toEqual('https://esm.dev/') + expect(wrapper.find('a')!.classes()).toHaveLength(0) + expect(wrapper.find('a')!.attributes('href')).toEqual('https://esm.dev') }) }) }) diff --git a/__tests__/RouterView.spec.ts b/__tests__/RouterView.spec.ts index 83f3017b..f1ae2c98 100644 --- a/__tests__/RouterView.spec.ts +++ b/__tests__/RouterView.spec.ts @@ -8,8 +8,9 @@ import { RouteLocationNormalized, } from '../src/types' import { markRaw } from 'vue' -import { mount, createMockedRoute } from './mount' +import { createMockedRoute } from './mount' import { mockWarn } from 'jest-mock-warn' +import { mount } from '@vue/test-utils' // to have autocompletion function createRoutes>( @@ -211,10 +212,12 @@ describe('RouterView', () => { propsData: any = {} ) { const route = createMockedRoute(initialRoute) - const wrapper = await mount(RouterView, { + const wrapper = mount(RouterView as any, { propsData, - provide: route.provides, - components: { RouterView }, + global: { + provide: route.provides, + components: { RouterView }, + }, }) return { route, wrapper } @@ -234,7 +237,7 @@ describe('RouterView', () => { const { wrapper } = await factory(START_LOCATION_NORMALIZED as any) // NOTE: I wonder if this will stay stable in future releases expect('Router').not.toHaveBeenWarned() - expect(wrapper.rootEl.childElementCount).toBe(0) + expect(wrapper.element.childNodes).toHaveLength(0) }) it('displays nested views', async () => { @@ -308,9 +311,9 @@ describe('RouterView', () => { }) describe('warnings', () => { - it('does not warn RouterView is wrapped', async () => { + it('does not warn RouterView is wrapped', () => { const route = createMockedRoute(routes.root) - const wrapper = await mount( + const wrapper = mount( { template: `
@@ -320,17 +323,19 @@ describe('RouterView', () => { }, { propsData: {}, - provide: route.provides, - components: { RouterView }, + global: { + provide: route.provides, + components: { RouterView }, + }, } ) expect(wrapper.html()).toBe(`
Home
`) expect('can no longer be used directly inside').not.toHaveBeenWarned() }) - it('warns if KeepAlive wraps a RouterView', async () => { + it('warns if KeepAlive wraps a RouterView', () => { const route = createMockedRoute(routes.root) - const wrapper = await mount( + const wrapper = mount( { template: ` @@ -340,8 +345,10 @@ describe('RouterView', () => { }, { propsData: {}, - provide: route.provides, - components: { RouterView }, + global: { + provide: route.provides, + components: { RouterView }, + }, } ) expect(wrapper.html()).toBe(`
Home
`) @@ -350,7 +357,7 @@ describe('RouterView', () => { it('warns if KeepAlive and Transition wrap a RouterView', async () => { const route = createMockedRoute(routes.root) - const wrapper = await mount( + const wrapper = mount( { template: ` @@ -362,17 +369,22 @@ describe('RouterView', () => { }, { propsData: {}, - provide: route.provides, - components: { RouterView }, + global: { + stubs: { + transition: false, + }, + provide: route.provides, + components: { RouterView }, + }, } ) expect(wrapper.html()).toBe(`
Home
`) expect('can no longer be used directly inside').toHaveBeenWarned() }) - it('warns if Transition wraps a RouterView', async () => { + it('warns if Transition wraps a RouterView', () => { const route = createMockedRoute(routes.root) - const wrapper = await mount( + const wrapper = mount( { template: ` @@ -382,8 +394,13 @@ describe('RouterView', () => { }, { propsData: {}, - provide: route.provides, - components: { RouterView }, + global: { + stubs: { + transition: false, + }, + provide: route.provides, + components: { RouterView }, + }, } ) expect(wrapper.html()).toBe(`
Home
`) @@ -397,15 +414,18 @@ describe('RouterView', () => { propsData: any = {} ) { const route = createMockedRoute(initialRoute) - const wrapper = await mount(RouterView, { + const wrapper = await mount(RouterView as any, { propsData, - provide: route.provides, - components: { RouterView }, + global: { + provide: route.provides, + components: { RouterView }, + }, slots: { default: ` - {{ route.name }} - - `, + `, }, }) @@ -424,16 +444,19 @@ describe('RouterView', () => { propsData: any = {} ) { const route = createMockedRoute(initialRoute) - const wrapper = await mount(RouterView, { + const wrapper = await mount(RouterView as any, { propsData, - provide: route.provides, - components: { RouterView }, + global: { + provide: route.provides, + components: { RouterView }, + }, slots: { default: ` - - - - `, + `, }, }) diff --git a/__tests__/guards/beforeRouteEnterCallback.spec.ts b/__tests__/guards/beforeRouteEnterCallback.spec.ts index b223c770..6b66349d 100644 --- a/__tests__/guards/beforeRouteEnterCallback.spec.ts +++ b/__tests__/guards/beforeRouteEnterCallback.spec.ts @@ -2,7 +2,7 @@ * @jest-environment jsdom */ import { defineComponent, h } from 'vue' -import { mount } from '../mount' +import { mount } from '@vue/test-utils' import { createRouter, RouterView, @@ -56,7 +56,7 @@ describe('beforeRouteEnter next callback', () => { ...options, }) - const wrapper = await mount( + const wrapper = mount( { template: `
@@ -64,9 +64,13 @@ describe('beforeRouteEnter next callback', () => {
`, - components: { RouterView }, }, - { router } + { + global: { + plugins: [router], + components: { RouterView }, + }, + } ) return { wrapper, router } diff --git a/__tests__/mount.ts b/__tests__/mount.ts index 41976e43..47601250 100644 --- a/__tests__/mount.ts +++ b/__tests__/mount.ts @@ -1,162 +1,9 @@ -import { - createApp, - defineComponent, - h, - ComponentPublicInstance, - reactive, - nextTick, - ComponentObjectPropsOptions, - ComputedRef, - computed, - App, - VNode, - shallowRef, - ComponentOptions, -} from 'vue' -import { compile } from '@vue/compiler-dom' -import * as runtimeDom from '@vue/runtime-dom' +import { reactive, nextTick, ComputedRef, computed, shallowRef } from 'vue' import { RouteLocationNormalizedLoose } from './utils' import { routeLocationKey, routerViewLocationKey, } from '../src/injectionSymbols' -import { Router } from '../src' - -export interface MountOptions { - propsData: Record - provide: Record - components: ComponentOptions['components'] - slots: Record - router?: Router -} - -interface Wrapper { - app: App - vm: ComponentPublicInstance - rootEl: HTMLDivElement - setProps(props: MountOptions['propsData']): Promise - html(): string - find: typeof document['querySelector'] -} - -function initialProps

(propsOption: ComponentObjectPropsOptions

) { - let copy = {} as ComponentPublicInstance['$props'] - - for (let key in propsOption) { - const prop = propsOption[key]! - // @ts-ignore - if (!prop.required && prop.default) - // @ts-ignore - copy[key] = prop.default - } - - return copy -} - -// cleanup wrappers after a suite runs -let activeWrapperRemovers: Array<() => void> = [] -afterAll(() => { - activeWrapperRemovers.forEach(remove => remove()) - activeWrapperRemovers = [] -}) - -export function mount( - targetComponent: Parameters[0], - options: Partial = {} -): Promise { - const TargetComponent = targetComponent - return new Promise(resolve => { - // NOTE: only supports props as an object - const propsData = reactive( - Object.assign( - initialProps( - // @ts-ignore - TargetComponent.props || {} - ), - options.propsData - ) - ) - - function setProps(partialProps: Record) { - Object.assign(propsData, partialProps) - return nextTick() - } - - let slots: Record VNode> = {} - - const Wrapper = defineComponent({ - setup(_props, { emit }) { - const componentInstanceRef = shallowRef() - - return () => { - return h( - TargetComponent as any, - { - ref: componentInstanceRef, - onVnodeMounted() { - emit('ready', componentInstanceRef.value) - }, - ...propsData, - }, - slots - ) - } - }, - }) - - const app = createApp(Wrapper, { - onReady: (instance: ComponentPublicInstance) => { - resolve({ app, vm: instance!, rootEl, setProps, html, find }) - }, - }) - - if (options.provide) { - const keys = getKeys(options.provide) - - for (let key of keys) { - app.provide(key, options.provide[key as any]) - } - } - - if (options.components) { - for (let key in options.components) { - app.component(key, options.components[key]) - } - } - - if (options.slots) { - for (let key in options.slots) { - slots[key] = compileSlot(options.slots[key]) - } - } - - const rootEl = document.createElement('div') - document.body.appendChild(rootEl) - - function html() { - return rootEl.innerHTML - } - - function find(selector: string) { - return rootEl.querySelector(selector) - } - - if (options.router) app.use(options.router) - - app.mount(rootEl) - - activeWrapperRemovers.push(() => { - app.unmount(rootEl) - rootEl.remove() - }) - }) -} - -function getKeys(object: Record): Array { - return (Object.getOwnPropertyNames(object) as Array).concat( - Object.getOwnPropertySymbols(object) - ) -} export function createMockedRoute(initialValue: RouteLocationNormalizedLoose) { const route = {} as { @@ -190,24 +37,3 @@ export function createMockedRoute(initialValue: RouteLocationNormalizedLoose) { }, } } - -function compileSlot(template: string) { - const codegen = compile(template, { - mode: 'function', - hoistStatic: true, - prefixIdentifiers: true, - }) - - const render = new Function('Vue', codegen.code)(runtimeDom) - - const ToRender = defineComponent({ - render, - inheritAttrs: false, - - setup(props, { attrs }) { - return { ...attrs } - }, - }) - - return (propsData: any) => h(ToRender, { ...propsData }) -} diff --git a/package.json b/package.json index 98647d64..07819041 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "@vue/compiler-sfc": "^3.0.5", "@vue/devtools-api": "^6.0.0-beta.3", "@vue/server-renderer": "^3.0.5", + "@vue/test-utils": "^2.0.0-beta.13", "algoliasearch": "^4.8.3", "axios": "^0.21.1", "brotli": "^1.3.2", diff --git a/yarn.lock b/yarn.lock index 5660e05a..ceaf0080 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1126,6 +1126,11 @@ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.0.5.tgz#c131d88bd6713cc4d93b3bb1372edb1983225ff0" integrity sha512-gYsNoGkWejBxNO6SNRjOh/xKeZ0H0V+TFzaPzODfBjkAIb0aQgBuixC1brandC/CDJy1wYPwSoYrXpvul7m6yw== +"@vue/test-utils@^2.0.0-beta.13": + version "2.0.0-beta.13" + resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-2.0.0-beta.13.tgz#482aa29f4e80a713e03e22b736d7465d56909e66" + integrity sha512-Au+yGMBPvrtcmPP6W4ielTbNJd3equxVOq1iL0DvpAZJwkGOrdBfFTomMhANODiBVyIEE8HMxhr7YIwDHWO36w== + "@webassemblyjs/ast@1.9.1": version "1.9.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.1.tgz#76c6937716d68bf1484c15139f5ed30b9abc8bb4"