Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add scoped slots option #507

Merged
merged 22 commits into from
Apr 14, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Vue Test Utils is the official unit testing utility library for Vue.js.
* [Mounting Options](api/options.md)
- [context](api/options.md#context)
- [slots](api/options.md#slots)
- [scopedSlots](api/options.md#scopedslots)
- [stubs](api/options.md#stubs)
- [mocks](api/options.md#mocks)
- [localVue](api/options.md#localvue)
Expand Down
1 change: 1 addition & 0 deletions docs/en/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* [Mounting Options](api/options.md)
- [context](api/options.md#context)
- [slots](api/options.md#slots)
- [scopedSlots](api/options.md#scopedslots)
- [stubs](api/options.md#stubs)
- [mocks](api/options.md#mocks)
- [localVue](api/options.md#localvue)
Expand Down
1 change: 1 addition & 0 deletions docs/en/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [Mounting Options](./options.md)
- [context](./options.md#context)
- [slots](./options.md#slots)
- [scopedSlots](./options.md#scopedslots)
- [stubs](./options.md#stubs)
- [mocks](./options.md#mocks)
- [localVue](./options.md#localvue)
Expand Down
23 changes: 23 additions & 0 deletions docs/en/api/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Options for `mount` and `shallow`. The options object can contain both Vue Test

- [`context`](#context)
- [`slots`](#slots)
- [`scopedSlots`](#scopedslots)
- [`stubs`](#stubs)
- [`mocks`](#mocks)
- [`localVue`](#localvue)
Expand Down Expand Up @@ -68,6 +69,28 @@ There is a limitation to this.
This does not support PhantomJS.
Please use [Puppeteer](https://github.com/karma-runner/karma-chrome-launcher#headless-chromium-with-puppeteer).

### `scopedSlots`

- type: `{ [name: string]: string }`

Provide an object of scoped slots contents to the component. The key corresponds to the slot name. The value can be a template string.
There is two limitations.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is two limitations > There are two limitations.


* This supports vue@2.5+.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option is only supported in vue@2.5+


* You can not set a `template` tag to top of `scopedSlots` option.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot use <template> tag as the root element in thescopedSlots option.


Example:

```js
const wrapper = shallow(Component, {
scopedSlots: {
bar: '<p slot-scope="props">{{props.index}},{{props.text}}</p>'
}
})
expect(wrapper.html()).toBe('<div><p>0,text1</p><p>1,text2</p><p>2,text3</p></div>')
```

### `stubs`

- type: `{ [name: string]: Component | boolean } | Array<string>`
Expand Down
1 change: 1 addition & 0 deletions flow/options.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ declare type Options = { // eslint-disable-line no-undef
attachToDocument?: boolean,
mocks?: Object,
slots?: Object,
scopedSlots?: Object,
localVue?: Component,
provide?: Object,
stubs?: Object,
Expand Down
14 changes: 14 additions & 0 deletions packages/create-instance/add-scoped-slots.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @flow

import { compileToFunctions } from 'vue-template-compiler'
import { throwError } from 'shared/util'

export function addScopedSlots (vm: Component, scopedSlots: Object): void {
Object.keys(scopedSlots).forEach((key) => {
const template = scopedSlots[key].trim()
if (template.substr(0, 9) === '<template') {
throwError('scopedSlots option does not support template tag.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the error message read:

the scopedSlots option does not support a tag as the root element.

}
vm.$_vueTestUtils_scopedSlots[key] = compileToFunctions(template).render
})
}
24 changes: 24 additions & 0 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @flow

import Vue from 'vue'
import { addSlots } from './add-slots'
import { addScopedSlots } from './add-scoped-slots'
import addMocks from './add-mocks'
import addAttrs from './add-attrs'
import addListeners from './add-listeners'
Expand Down Expand Up @@ -57,6 +59,28 @@ export default function createInstance (
addAttrs(vm, options.attrs)
addListeners(vm, options.listeners)

if (options.scopedSlots) {
const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`)
if (vueVersion >= 2.5) {
vm.$_vueTestUtils_scopedSlots = {}
const renderSlot = vm._renderProxy._t
vm._renderProxy._t = function (name, feedback, props, bindObject) {
const scopedSlotFn = vm.$_vueTestUtils_scopedSlots[name]
if (scopedSlotFn) {
props = Object.assign({}, bindObject, props)
vm._renderProxy.props = props
return scopedSlotFn.call(vm._renderProxy)
} else {
return renderSlot.call(vm._renderProxy, name, feedback, props, bindObject)
}
}
// $FlowIgnore
addScopedSlots(vm, options.scopedSlots)
} else {
throwError('scopedSlots option supports vue@2.5+.')
}
}

if (options.slots) {
addSlots(vm, options.slots)
}
Expand Down
1 change: 1 addition & 0 deletions packages/test-utils/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ interface MountOptions<V extends Vue> extends ComponentOptions<V> {
localVue?: typeof Vue
mocks?: object
slots?: Slots
scopedSlots?: object
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can specify more detailed type Record<string, string>

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for pointing out.
I changed it.

stubs?: Stubs,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be an object. Can you add a test for this in types/test?

attrs?: object
listeners?: object
Expand Down
3 changes: 3 additions & 0 deletions packages/test-utils/types/test/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ mount(ClassComponent, {
foo: [normalOptions, functionalOptions],
bar: ClassComponent
},
scopedSlots: {
baz: `<div>Baz</div>`
},
stubs: {
foo: normalOptions,
bar: functionalOptions,
Expand Down
25 changes: 25 additions & 0 deletions test/resources/components/component-with-scoped-slots.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<div>
<div id="scopedSlots">
<slot name="item"
v-for="(item, index) in items"
:text="item.text"
:index="index">
</slot>
</div>
<div id="slots">
<slot></slot>
</div>
</div>
</template>

<script>
export default {
name: 'component-with-scoped-slots',
data () {
return {
items: [{ text: 'a1' }, { text: 'a2' }, { text: 'a3' }]
}
}
}
</script>
47 changes: 47 additions & 0 deletions test/specs/mounting-options/scopedSlots.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { describeWithShallowAndMount, vueVersion, itDoNotRunIf } from '~resources/utils'
import ComponentWithScopedSlots from '~resources/components/component-with-scoped-slots.vue'

describeWithShallowAndMount('scopedSlots', (mountingMethod) => {
itDoNotRunIf(vueVersion < 2.5,
'mounts component scoped slots', () => {
const wrapper = mountingMethod(ComponentWithScopedSlots, {
slots: { default: '<span>123</span>' },
scopedSlots: {
'item': '<p slot-scope="props">{{props.index}},{{props.text}}</p>'
}
})
expect(wrapper.find('#slots').html()).to.equal('<div id="slots"><span>123</span></div>')
expect(wrapper.find('#scopedSlots').html()).to.equal('<div id="scopedSlots"><p>0,a1</p><p>1,a2</p><p>2,a3</p></div>')
wrapper.vm.items = [{ text: 'b1' }, { text: 'b2' }, { text: 'b3' }]
expect(wrapper.find('#scopedSlots').html()).to.equal('<div id="scopedSlots"><p>0,b1</p><p>1,b2</p><p>2,b3</p></div>')
}
)

itDoNotRunIf(vueVersion < 2.5,
'throws exception when it is seted to template tag at top', () => {
const fn = () => {
mountingMethod(ComponentWithScopedSlots, {
scopedSlots: {
'item': '<template></template>'
}
})
}
const message = '[vue-test-utils]: scopedSlots option does not support template tag.'
expect(fn).to.throw().with.property('message', message)
}
)

itDoNotRunIf(vueVersion >= 2.5,
'throws exception when vue version < 2.5', () => {
const fn = () => {
mountingMethod(ComponentWithScopedSlots, {
scopedSlots: {
'item': '<p slot="item" slot-scope="props">{{props.index}},{{props.text}}</p>'
}
})
}
const message = '[vue-test-utils]: scopedSlots option supports vue@2.5+.'
expect(fn).to.throw().with.property('message', message)
}
)
})