Skip to content

Commit

Permalink
feat: expose everything on wrapper.vm
Browse files Browse the repository at this point in the history
This commit changes `wrapper.vm` to actually point to `vm.$.proxy`.
This shouldn't change the behaviour of existing tests, but allows components written with `script setup` to be tested as well,
without the need to expose everything just for testing purposes.

For example a component like:

```vue
<script setup lang="ts">
import { ref } from 'vue'

const count = ref(0)
</script>
```

can now be tested like `expect(wrapper.vm.count).toBe(0)`, whereas you previously had to add `defineExpose({ count })` for this to work.

The downside is that you now can't test that something is _not_ exposed by a component, but I don't think this is a problem.

This also removes the previous hacks for script setup, as it looks like they are no longer necessary.
  • Loading branch information
cexbrayat committed Sep 24, 2021
1 parent a6aa2b7 commit e80b7b2
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 23 deletions.
23 changes: 3 additions & 20 deletions src/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,17 +337,6 @@ export function mount(
const Parent = defineComponent({
name: 'VTU_ROOT',
render() {
// https://github.com/vuejs/vue-test-utils-next/issues/651
// script setup components include an empty `expose` array as part of the
// code generated by the SFC compiler. Eg a component might look like
// { expose: [], setup: [Function], render: [Function] }
// not sure why (yet), but the empty expose array causes events to not be
// correctly captured.
// TODO: figure out why this is happening and understand the implications of
// the expose rfc for Test Utils.
if (isObjectComponent(component)) {
delete component.expose
}
return h(component, props, slots)
}
})
Expand Down Expand Up @@ -457,19 +446,13 @@ export function mount(
const warnSave = console.warn
console.warn = () => {}

// get `vm`.
// for some unknown reason, getting the `vm` for components using `<script setup>`
// as of Vue 3.0.3 works differently.
// if `appRef` has keys, use that (vm always has keys like $el, $props etc).
// if not, use the return value from app.mount.
const appRef = vm.$refs[MOUNT_COMPONENT_REF] as ComponentPublicInstance
const $vm = Reflect.ownKeys(appRef).length ? appRef : vm
// we add `hasOwnProperty` so jest can spy on the proxied vm without throwing
$vm.hasOwnProperty = (property) => {
return Reflect.has($vm, property)
appRef.hasOwnProperty = (property) => {
return Reflect.has(appRef, property)
}
console.warn = warnSave
return createWrapper(app, $vm, setProps)
return createWrapper(app, appRef, setProps)
}

export const shallowMount: typeof mount = (component: any, options?: any) => {
Expand Down
7 changes: 6 additions & 1 deletion src/vueWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ export class VueWrapper<T extends ComponentPublicInstance>
this.__app = app
// root is null on functional components
this.rootVM = vm?.$root
this.componentVM = vm as T
// vm.$.proxy is what the template has access to
// so even if the component is closed (as they are by default for `script setup`)
// a test will still be able to do something like
// `expect(wrapper.vm.count).toBe(1)`
// (note that vm can be null for functional components, hence the condition)
this.componentVM = vm ? (vm.$.proxy as T) : (vm as T)
this.__setProps = setProps

this.attachNativeEventListener()
Expand Down
23 changes: 23 additions & 0 deletions tests/components/DefineExpose.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<template>
<div id="root">
<div id="msg">{{ msg }}</div>
<div>{{ other }}</div>
</div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
name: 'Hello',
setup(props, { expose }) {
const other = ref('other')
expose({ other })
return {
msg: ref('Hello world'),
other
}
}
})
</script>
19 changes: 19 additions & 0 deletions tests/components/ScriptSetup_Expose.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script setup lang="ts">
// imported components are also directly usable in template
import { ref } from 'vue'
import Hello from './Hello.vue'
const count = ref(0)
const inc = () => {
count.value++
}
defineExpose({
count
})
</script>

<template>
<button @click="inc">{{ count }}</button>
<Hello />
</template>
40 changes: 40 additions & 0 deletions tests/expose.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { mount } from '../src'
import Hello from './components/Hello.vue'
import DefineExpose from './components/DefineExpose.vue'
import ScriptSetupExpose from './components/ScriptSetup_Expose.vue'
import ScriptSetup from './components/ScriptSetup.vue'

describe('expose', () => {
it('access vm on simple components', async () => {
const wrapper = mount(Hello)

expect(wrapper.vm.msg).toBe('Hello world')
})

it('access vm on simple components with custom `expose`', async () => {
const wrapper = mount(DefineExpose)

// other is exposed vie `expose`
expect(wrapper.vm.other).toBe('other')
// can access `msg` even if not exposed
expect(wrapper.vm.msg).toBe('Hello world')
})

it('access vm with <script setup> and defineExpose()', async () => {
const wrapper = mount(ScriptSetupExpose)

await wrapper.find('button').trigger('click')
expect(wrapper.html()).toContain('1')
// can access `count` as it is exposed via `defineExpose()`
expect(wrapper.vm.count).toBe(1)
})

it('access vm with <script setup> even without defineExpose()', async () => {
const wrapper = mount(ScriptSetup)

await wrapper.find('button').trigger('click')
expect(wrapper.html()).toContain('1')
// can access `count` even if it is _not_ exposed
expect(wrapper.vm.count).toBe(1)
})
})
1 change: 0 additions & 1 deletion tests/vm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { defineComponent, ref } from 'vue'

import { mount } from '../src'

describe('vm', () => {
Expand Down
3 changes: 2 additions & 1 deletion tsconfig.volar.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"compilerOptions": {
"lib": ["DOM", "ES2020"],
"skipLibCheck": true
}
},
"exclude": ["tests/expose.spec.ts"]
}

0 comments on commit e80b7b2

Please sign in to comment.