| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- import Vue from 'vue'
- import { createEmptyVNode } from 'core/vdom/vnode'
- describe('Options functional', () => {
- it('should work', done => {
- const vm = new Vue({
- data: { test: 'foo' },
- template: '<div><wrap :msg="test">bar</wrap></div>',
- components: {
- wrap: {
- functional: true,
- props: ['msg'],
- render (h, { props, children }) {
- return h('div', null, [props.msg, ' '].concat(children))
- }
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div>foo bar</div>')
- vm.test = 'qux'
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe('<div>qux bar</div>')
- }).then(done)
- })
- it('should expose all props when not declared', done => {
- const fn = {
- functional: true,
- render (h, { props }) {
- return h('div', `${props.msg} ${props.kebabMsg}`)
- }
- }
- const vm = new Vue({
- data: { test: 'foo' },
- render (h) {
- return h('div', [
- h(fn, {
- props: { msg: this.test },
- attrs: { 'kebab-msg': 'bar' }
- })
- ])
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div>foo bar</div>')
- vm.test = 'qux'
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe('<div>qux bar</div>')
- }).then(done)
- })
- it('should expose data.on as listeners', () => {
- const foo = jasmine.createSpy('foo')
- const bar = jasmine.createSpy('bar')
- const vm = new Vue({
- template: '<div><wrap @click="foo" @test="bar"/></div>',
- methods: { foo, bar },
- components: {
- wrap: {
- functional: true,
- render (h, { listeners }) {
- return h('div', {
- on: {
- click: [listeners.click, () => listeners.test('bar')]
- }
- })
- }
- }
- }
- }).$mount()
- document.body.appendChild(vm.$el)
- triggerEvent(vm.$el.children[0], 'click')
- expect(foo).toHaveBeenCalled()
- expect(foo.calls.argsFor(0)[0].type).toBe('click') // should have click event
- triggerEvent(vm.$el.children[0], 'mousedown')
- expect(bar).toHaveBeenCalledWith('bar')
- document.body.removeChild(vm.$el)
- })
- it('should expose scopedSlots on render context', () => {
- const vm = new Vue({
- template: '<div><wrap>foo<p slot="p" slot-scope="a">{{ a }}</p></wrap></div>',
- components: {
- wrap: {
- functional: true,
- render (h, { scopedSlots }) {
- return [
- // scoped
- scopedSlots.p('a'),
- // normal slot content should be exposed as well
- scopedSlots.default()
- ]
- }
- }
- }
- }).$mount()
- expect(vm.$el.textContent).toBe('afoo')
- })
- it('should support returning more than one root node', () => {
- const vm = new Vue({
- template: `<div><test></test></div>`,
- components: {
- test: {
- functional: true,
- render (h) {
- return [h('span', 'foo'), h('span', 'bar')]
- }
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<span>foo</span><span>bar</span>')
- })
- it('should support slots', () => {
- const vm = new Vue({
- data: { test: 'foo' },
- template: '<div><wrap><div slot="a">foo</div><div slot="b">bar</div></wrap></div>',
- components: {
- wrap: {
- functional: true,
- props: ['msg'],
- render (h, { slots }) {
- slots = slots()
- return h('div', null, [slots.b, slots.a])
- }
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div><div>bar</div><div>foo</div></div>')
- })
- it('should let vnode raw data pass through', done => {
- const onValid = jasmine.createSpy('valid')
- const vm = new Vue({
- data: { msg: 'hello' },
- template: `<div>
- <validate field="field1" @valid="onValid">
- <input type="text" v-model="msg">
- </validate>
- </div>`,
- components: {
- validate: {
- functional: true,
- props: ['field'],
- render (h, { props, children, data: { on }}) {
- props.child = children[0]
- return h('validate-control', { props, on })
- }
- },
- 'validate-control': {
- props: ['field', 'child'],
- render () {
- return this.child
- },
- mounted () {
- this.$el.addEventListener('input', this.onInput)
- },
- destroyed () {
- this.$el.removeEventListener('input', this.onInput)
- },
- methods: {
- onInput (e) {
- const value = e.target.value
- if (this.validate(value)) {
- this.$emit('valid', this)
- }
- },
- // something validation logic here
- validate (val) {
- return val.length > 0
- }
- }
- }
- },
- methods: { onValid }
- }).$mount()
- document.body.appendChild(vm.$el)
- const input = vm.$el.querySelector('input')
- expect(onValid).not.toHaveBeenCalled()
- waitForUpdate(() => {
- input.value = 'foo'
- triggerEvent(input, 'input')
- }).then(() => {
- expect(onValid).toHaveBeenCalled()
- }).then(() => {
- document.body.removeChild(vm.$el)
- vm.$destroy()
- }).then(done)
- })
- it('create empty vnode when render return null', () => {
- const child = {
- functional: true,
- render () {
- return null
- }
- }
- const vm = new Vue({
- components: {
- child
- }
- })
- const h = vm.$createElement
- const vnode = h('child')
- expect(vnode).toEqual(createEmptyVNode())
- })
- // #7282
- it('should normalize top-level arrays', () => {
- const Foo = {
- functional: true,
- render (h) {
- return [h('span', 'hi'), null]
- }
- }
- const vm = new Vue({
- template: `<div><foo/></div>`,
- components: { Foo }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<span>hi</span>')
- })
- it('should work when used as named slot and returning array', () => {
- const Foo = {
- template: `<div><slot name="test"/></div>`
- }
- const Bar = {
- functional: true,
- render: h => ([
- h('div', 'one'),
- h('div', 'two'),
- h(Baz)
- ])
- }
- const Baz = {
- functional: true,
- render: h => h('div', 'three')
- }
- const vm = new Vue({
- template: `<foo><bar slot="test"/></foo>`,
- components: { Foo, Bar }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div>one</div><div>two</div><div>three</div>')
- })
- it('should apply namespace when returning arrays', () => {
- const Child = {
- functional: true,
- render: h => ([h('foo'), h('bar')])
- }
- const vm = new Vue({
- template: `<svg><child/></svg>`,
- components: { Child }
- }).$mount()
- expect(vm.$el.childNodes[0].namespaceURI).toContain('svg')
- expect(vm.$el.childNodes[1].namespaceURI).toContain('svg')
- })
- it('should work with render fns compiled from template', done => {
- // code generated via vue-template-es2015-compiler
- const render = function (_h, _vm) {
- const _c = _vm._c
- return _c(
- 'div',
- [
- _c('h2', { staticClass: 'red' }, [_vm._v(_vm._s(_vm.props.msg))]),
- _vm._t('default'),
- _vm._t('slot2'),
- _vm._t('scoped', null, { msg: _vm.props.msg }),
- _vm._m(0),
- _c('div', { staticClass: 'clickable', on: { click: _vm.parent.fn }}, [
- _vm._v('click me')
- ])
- ],
- 2
- )
- }
- const staticRenderFns = [
- function (_h, _vm) {
- const _c = _vm._c
- return _c('div', [_vm._v('Some '), _c('span', [_vm._v('text')])])
- }
- ]
- const child = {
- functional: true,
- _compiled: true,
- render,
- staticRenderFns
- }
- const parent = new Vue({
- components: {
- child
- },
- data: {
- msg: 'hello'
- },
- template: `
- <div>
- <child :msg="msg">
- <span>{{ msg }}</span>
- <div slot="slot2">Second slot</div>
- <template slot="scoped" slot-scope="scope">{{ scope.msg }}</template>
- </child>
- </div>
- `,
- methods: {
- fn () {
- this.msg = 'bye'
- }
- }
- }).$mount()
- function assertMarkup () {
- expect(parent.$el.innerHTML).toBe(
- `<div>` +
- `<h2 class="red">${parent.msg}</h2>` +
- `<span>${parent.msg}</span> ` +
- `<div>Second slot</div>` +
- parent.msg +
- // static
- `<div>Some <span>text</span></div>` +
- `<div class="clickable">click me</div>` +
- `</div>`
- )
- }
- assertMarkup()
- triggerEvent(parent.$el.querySelector('.clickable'), 'click')
- waitForUpdate(assertMarkup).then(done)
- })
- // #8468
- it('should normalize nested arrays when use functional components with v-for', () => {
- const Foo = {
- functional: true,
- props: {
- name: {}
- },
- render (h, context) {
- return [h('span', 'hi'), h('span', context.props.name)]
- }
- }
- const vm = new Vue({
- template: `<div><foo v-for="name in names" :name="name" /></div>`,
- data: {
- names: ['foo', 'bar']
- },
- components: { Foo }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<span>hi</span><span>foo</span><span>hi</span><span>bar</span>')
- })
- })
|