-
Notifications
You must be signed in to change notification settings - Fork 669
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
Changes from all commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
cecf8eb
feat: add scoped slots option
38elements e7da1f7
add scopedSlots to index.d.ts
38elements 37c5950
add test
38elements 3c841a1
use Object.assign()
38elements ea2d16f
fix types
38elements 8f3239f
change test
38elements d4e7bdf
fix document
38elements e6ad08d
refactor
38elements 8da1a4e
add test for slots
38elements 7665971
refactor
38elements 0953171
improve types
38elements f737344
use spread syntax
38elements d925dcf
Update options.md
38elements 58399c5
Update options.md
38elements 4678bfb
change message
38elements 032513c
fix props
38elements a1f008c
fix test condition
38elements cb000ea
add helpers
38elements 4690a6a
update packages/server-test-utils/types/index.d.ts
38elements 73a7e5f
document -> _document
38elements 125c39c
update message
38elements 303d1a6
fix message
38elements File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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) | ||
|
@@ -66,7 +67,33 @@ You can pass text to `slots`. | |
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). | ||
You can use [Puppeteer](https://github.com/karma-runner/karma-chrome-launcher#headless-chromium-with-puppeteer) as an alternative. | ||
|
||
### `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 are three limitations. | ||
|
||
* This option is only supported in [email protected]+. | ||
|
||
* You can not use `<template>` tag as the root element in the `scopedSlots` option. | ||
|
||
* This does not support PhantomJS. | ||
You can use [Puppeteer](https://github.com/karma-runner/karma-chrome-launcher#headless-chromium-with-puppeteer) as an alternative. | ||
|
||
Example: | ||
|
||
```js | ||
const wrapper = shallow(Component, { | ||
scopedSlots: { | ||
foo: '<p slot-scope="props">{{props.index}},{{props.text}}</p>' | ||
} | ||
}) | ||
expect(wrapper.find('#fooWrapper').html()).toBe('<div id="fooWrapper"><p>0,text1</p><p>1,text2</p><p>2,text3</p></div>') | ||
``` | ||
|
||
### `stubs` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// @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('the scopedSlots option does not support a template tag as the root element.') | ||
} | ||
const domParser = new window.DOMParser() | ||
const _document = domParser.parseFromString(template, 'text/html') | ||
vm.$_vueTestUtils_scopedSlots[key] = compileToFunctions(template).render | ||
vm.$_vueTestUtils_slotScopes[key] = _document.body.firstChild.getAttribute('slot-scope') | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' | ||
|
@@ -57,6 +59,40 @@ export default function createInstance ( | |
addAttrs(vm, options.attrs) | ||
addListeners(vm, options.listeners) | ||
|
||
if (options.scopedSlots) { | ||
if (window.navigator.userAgent.match(/PhantomJS/i)) { | ||
throwError('the scopedSlots option does not support PhantomJS. Please use Puppeteer, or pass a component.') | ||
} | ||
const vueVersion = Number(`${Vue.version.split('.')[0]}.${Vue.version.split('.')[1]}`) | ||
if (vueVersion >= 2.5) { | ||
vm.$_vueTestUtils_scopedSlots = {} | ||
vm.$_vueTestUtils_slotScopes = {} | ||
const renderSlot = vm._renderProxy._t | ||
|
||
vm._renderProxy._t = function (name, feedback, props, bindObject) { | ||
const scopedSlotFn = vm.$_vueTestUtils_scopedSlots[name] | ||
const slotScope = vm.$_vueTestUtils_slotScopes[name] | ||
if (scopedSlotFn) { | ||
props = { ...bindObject, ...props } | ||
const proxy = {} | ||
const helpers = ['_c', '_o', '_n', '_s', '_l', '_t', '_q', '_i', '_m', '_f', '_k', '_b', '_v', '_e', '_u', '_g'] | ||
helpers.forEach((key) => { | ||
proxy[key] = vm._renderProxy[key] | ||
}) | ||
proxy[slotScope] = props | ||
return scopedSlotFn.call(proxy) | ||
} else { | ||
return renderSlot.call(vm._renderProxy, name, feedback, props, bindObject) | ||
} | ||
} | ||
|
||
// $FlowIgnore | ||
addScopedSlots(vm, options.scopedSlots) | ||
} else { | ||
throwError('the scopedSlots option is only supported in [email protected]+.') | ||
} | ||
} | ||
|
||
if (options.slots) { | ||
addSlots(vm, options.slots) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<template> | ||
<div> | ||
<div id="foo"> | ||
<slot name="foo" | ||
v-for="(item, index) in foo" | ||
:text="item.text" | ||
:index="index"> | ||
</slot> | ||
</div> | ||
<div id="bar"> | ||
<slot name="bar" | ||
v-for="(item, index) in bar" | ||
: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 { | ||
foo: [{ text: 'a1' }, { text: 'a2' }, { text: 'a3' }], | ||
bar: [{ text: 'A1' }, { text: 'A2' }, { text: 'A3' }] | ||
} | ||
} | ||
} | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
import { describeWithShallowAndMount, vueVersion, itDoNotRunIf } from '~resources/utils' | ||
import ComponentWithScopedSlots from '~resources/components/component-with-scoped-slots.vue' | ||
|
||
describeWithShallowAndMount('scopedSlots', (mountingMethod) => { | ||
let _window | ||
|
||
beforeEach(() => { | ||
_window = window | ||
}) | ||
|
||
afterEach(() => { | ||
if (!window.navigator.userAgent.match(/Chrome/i)) { | ||
window = _window // eslint-disable-line no-native-reassign | ||
} | ||
}) | ||
|
||
itDoNotRunIf(vueVersion < 2.5, | ||
'mounts component scoped slots', () => { | ||
const wrapper = mountingMethod(ComponentWithScopedSlots, { | ||
slots: { default: '<span>123</span>' }, | ||
scopedSlots: { | ||
'foo': '<p slot-scope="foo">{{foo.index}},{{foo.text}}</p>', | ||
'bar': '<p slot-scope="bar">{{bar.text}},{{bar.index}}</p>' | ||
} | ||
}) | ||
expect(wrapper.find('#slots').html()).to.equal('<div id="slots"><span>123</span></div>') | ||
expect(wrapper.find('#foo').html()).to.equal('<div id="foo"><p>0,a1</p><p>1,a2</p><p>2,a3</p></div>') | ||
expect(wrapper.find('#bar').html()).to.equal('<div id="bar"><p>A1,0</p><p>A2,1</p><p>A3,2</p></div>') | ||
wrapper.vm.foo = [{ text: 'b1' }, { text: 'b2' }, { text: 'b3' }] | ||
wrapper.vm.bar = [{ text: 'B1' }, { text: 'B2' }, { text: 'B3' }] | ||
expect(wrapper.find('#foo').html()).to.equal('<div id="foo"><p>0,b1</p><p>1,b2</p><p>2,b3</p></div>') | ||
expect(wrapper.find('#bar').html()).to.equal('<div id="bar"><p>B1,0</p><p>B2,1</p><p>B3,2</p></div>') | ||
} | ||
) | ||
|
||
itDoNotRunIf(vueVersion < 2.5, | ||
'throws exception when it is seted to a template tag at top', () => { | ||
const fn = () => { | ||
mountingMethod(ComponentWithScopedSlots, { | ||
scopedSlots: { | ||
'foo': '<template></template>' | ||
} | ||
}) | ||
} | ||
const message = '[vue-test-utils]: the scopedSlots option does not support a template tag as the root element.' | ||
expect(fn).to.throw().with.property('message', message) | ||
} | ||
) | ||
|
||
itDoNotRunIf(vueVersion >= 2.5, | ||
'throws exception when vue version < 2.5', () => { | ||
const fn = () => { | ||
mountingMethod(ComponentWithScopedSlots, { | ||
scopedSlots: { | ||
'foo': '<p slot-scope="foo">{{foo.index}},{{foo.text}}</p>' | ||
} | ||
}) | ||
} | ||
const message = '[vue-test-utils]: the scopedSlots option is only supported in [email protected]+.' | ||
expect(fn).to.throw().with.property('message', message) | ||
} | ||
) | ||
|
||
itDoNotRunIf(vueVersion < 2.5, | ||
'throws exception when using PhantomJS', () => { | ||
if (window.navigator.userAgent.match(/Chrome/i)) { | ||
return | ||
} | ||
window = { navigator: { userAgent: 'PhantomJS' }} // eslint-disable-line no-native-reassign | ||
const fn = () => { | ||
mountingMethod(ComponentWithScopedSlots, { | ||
scopedSlots: { | ||
'foo': '<p slot-scope="foo">{{foo.index}},{{foo.text}}</p>' | ||
} | ||
}) | ||
} | ||
const message = '[vue-test-utils]: the scopedSlots option does not support PhantomJS. Please use Puppeteer, or pass a component.' | ||
expect(fn).to.throw().with.property('message', message) | ||
} | ||
) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does Vue throw an error if there are two conflicting slot scopes? If not, we should throw an error here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is impossible to set a word that starts with "_" in Vue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant if you mounted with this option:
The second scopedSlot would overwrite the first scopedSlots in
_vueTestUtils_slotScopes
. Although thinking about it, this is probably fine.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not possible to register the same key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤦♂️ Of course ;p