import Vue from 'vue' import { hasSymbol } from 'core/util/env' describe('Directive v-for', () => { it('should render array of primitive values', done => { const vm = new Vue({ template: `
{{item}}
`, data: { list: ['a', 'b', 'c'] } }).$mount() expect(vm.$el.innerHTML).toBe('abc') Vue.set(vm.list, 0, 'd') waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('dbc') vm.list.push('d') }).then(() => { expect(vm.$el.innerHTML).toBe('dbcd') vm.list.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('dd') vm.list = ['x', 'y'] }).then(() => { expect(vm.$el.innerHTML).toBe('xy') }).then(done) }) it('should render array of primitive values with index', done => { const vm = new Vue({ template: `
{{i}}-{{item}}
`, data: { list: ['a', 'b', 'c'] } }).$mount() expect(vm.$el.innerHTML).toBe('0-a1-b2-c') Vue.set(vm.list, 0, 'd') waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('0-d1-b2-c') vm.list.push('d') }).then(() => { expect(vm.$el.innerHTML).toBe('0-d1-b2-c3-d') vm.list.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('0-d1-d') vm.list = ['x', 'y'] }).then(() => { expect(vm.$el.innerHTML).toBe('0-x1-y') }).then(done) }) it('should render array of object values', done => { const vm = new Vue({ template: `
{{item.value}}
`, data: { list: [ { value: 'a' }, { value: 'b' }, { value: 'c' } ] } }).$mount() expect(vm.$el.innerHTML).toBe('abc') Vue.set(vm.list, 0, { value: 'd' }) waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('dbc') vm.list[0].value = 'e' }).then(() => { expect(vm.$el.innerHTML).toBe('ebc') vm.list.push({}) }).then(() => { expect(vm.$el.innerHTML).toBe('ebc') vm.list.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('e') vm.list = [{ value: 'x' }, { value: 'y' }] }).then(() => { expect(vm.$el.innerHTML).toBe('xy') }).then(done) }) it('should render array of object values with index', done => { const vm = new Vue({ template: `
{{i}}-{{item.value}}
`, data: { list: [ { value: 'a' }, { value: 'b' }, { value: 'c' } ] } }).$mount() expect(vm.$el.innerHTML).toBe('0-a1-b2-c') Vue.set(vm.list, 0, { value: 'd' }) waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('0-d1-b2-c') vm.list[0].value = 'e' }).then(() => { expect(vm.$el.innerHTML).toBe('0-e1-b2-c') vm.list.push({}) }).then(() => { expect(vm.$el.innerHTML).toBe('0-e1-b2-c3-') vm.list.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('0-e1-') vm.list = [{ value: 'x' }, { value: 'y' }] }).then(() => { expect(vm.$el.innerHTML).toBe('0-x1-y') }).then(done) }) if (hasSymbol) { it('should render native iterables (Map)', () => { const vm = new Vue({ template: `
{{key}},{{val}}
`, data: { list: new Map([[1, 'foo'], [2, 'bar']]) } }).$mount() expect(vm.$el.innerHTML).toBe(`1,foo2,bar`) }) it('should render native iterables (Set)', () => { const vm = new Vue({ template: `
{{val}}
`, data: { list: new Set([1, 2, 3]) } }).$mount() expect(vm.$el.innerHTML).toBe(`123`) }) it('should render iterable of primitive values', done => { const iterable = { models: ['a', 'b', 'c'], index: 0, [Symbol.iterator] () { const iterator = { index: 0, models: this.models, next () { if (this.index < this.models.length) { return { value: this.models[this.index++] } } else { return { done: true } } } } return iterator } } const vm = new Vue({ template: `
{{item}}
`, data: { list: iterable } }).$mount() expect(vm.$el.innerHTML).toBe('abc') Vue.set(vm.list.models, 0, 'd') waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('dbc') vm.list.models.push('d') }).then(() => { expect(vm.$el.innerHTML).toBe('dbcd') vm.list.models.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('dd') vm.list.models = ['x', 'y'] }).then(() => { expect(vm.$el.innerHTML).toBe('xy') }).then(done) }) it('should render iterable of primitive values with index', done => { const iterable = { models: ['a', 'b', 'c'], index: 0, [Symbol.iterator] () { const iterator = { index: 0, models: this.models, next () { if (this.index < this.models.length) { return { value: this.models[this.index++] } } else { return { done: true } } } } return iterator } } const vm = new Vue({ template: `
{{i}}-{{item}}
`, data: { list: iterable } }).$mount() expect(vm.$el.innerHTML).toBe('0-a1-b2-c') Vue.set(vm.list.models, 0, 'd') waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('0-d1-b2-c') vm.list.models.push('d') }).then(() => { expect(vm.$el.innerHTML).toBe('0-d1-b2-c3-d') vm.list.models.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('0-d1-d') vm.list.models = ['x', 'y'] }).then(() => { expect(vm.$el.innerHTML).toBe('0-x1-y') }).then(done) }) it('should render iterable of object values', done => { const iterable = { models: [ { value: 'a' }, { value: 'b' }, { value: 'c' } ], index: 0, [Symbol.iterator] () { const iterator = { index: 0, models: this.models, next () { if (this.index < this.models.length) { return { value: this.models[this.index++] } } else { return { done: true } } } } return iterator } } const vm = new Vue({ template: `
{{item.value}}
`, data: { list: iterable } }).$mount() expect(vm.$el.innerHTML).toBe('abc') Vue.set(vm.list.models, 0, { value: 'd' }) waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('dbc') vm.list.models[0].value = 'e' }).then(() => { expect(vm.$el.innerHTML).toBe('ebc') vm.list.models.push({}) }).then(() => { expect(vm.$el.innerHTML).toBe('ebc') vm.list.models.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('e') vm.list.models = [{ value: 'x' }, { value: 'y' }] }).then(() => { expect(vm.$el.innerHTML).toBe('xy') }).then(done) }) it('should render iterable of object values with index', done => { const iterable = { models: [ { value: 'a' }, { value: 'b' }, { value: 'c' } ], index: 0, [Symbol.iterator] () { const iterator = { index: 0, models: this.models, next () { if (this.index < this.models.length) { return { value: this.models[this.index++] } } else { return { done: true } } } } return iterator } } const vm = new Vue({ template: `
{{i}}-{{item.value}}
`, data: { list: iterable } }).$mount() expect(vm.$el.innerHTML).toBe('0-a1-b2-c') Vue.set(vm.list.models, 0, { value: 'd' }) waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('0-d1-b2-c') vm.list.models[0].value = 'e' }).then(() => { expect(vm.$el.innerHTML).toBe('0-e1-b2-c') vm.list.models.push({}) }).then(() => { expect(vm.$el.innerHTML).toBe('0-e1-b2-c3-') vm.list.models.splice(1, 2) }).then(() => { expect(vm.$el.innerHTML).toBe('0-e1-') vm.list.models = [{ value: 'x' }, { value: 'y' }] }).then(() => { expect(vm.$el.innerHTML).toBe('0-x1-y') }).then(done) }) } it('should render an Object', done => { const vm = new Vue({ template: `
{{val}}
`, data: { obj: { a: 0, b: 1, c: 2 } } }).$mount() expect(vm.$el.innerHTML).toBe('012') vm.obj.a = 3 waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('312') Vue.set(vm.obj, 'd', 4) }).then(() => { expect(vm.$el.innerHTML).toBe('3124') Vue.delete(vm.obj, 'a') }).then(() => { expect(vm.$el.innerHTML).toBe('124') }).then(done) }) it('should render an Object with key', done => { const vm = new Vue({ template: `
{{val}}-{{key}}
`, data: { obj: { a: 0, b: 1, c: 2 } } }).$mount() expect(vm.$el.innerHTML).toBe('0-a1-b2-c') vm.obj.a = 3 waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('3-a1-b2-c') Vue.set(vm.obj, 'd', 4) }).then(() => { expect(vm.$el.innerHTML).toBe('3-a1-b2-c4-d') Vue.delete(vm.obj, 'a') }).then(() => { expect(vm.$el.innerHTML).toBe('1-b2-c4-d') }).then(done) }) it('should render an Object with key and index', done => { const vm = new Vue({ template: `
{{val}}-{{key}}-{{i}}
`, data: { obj: { a: 0, b: 1, c: 2 } } }).$mount() expect(vm.$el.innerHTML).toBe('0-a-01-b-12-c-2') vm.obj.a = 3 waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('3-a-01-b-12-c-2') Vue.set(vm.obj, 'd', 4) }).then(() => { expect(vm.$el.innerHTML).toBe('3-a-01-b-12-c-24-d-3') Vue.delete(vm.obj, 'a') }).then(() => { expect(vm.$el.innerHTML).toBe('1-b-02-c-14-d-2') }).then(done) }) it('should render each key of data', done => { const vm = new Vue({ template: `
{{val}}-{{key}}
`, data: { a: 0, b: 1, c: 2 } }).$mount() expect(vm.$el.innerHTML).toBe('0-a1-b2-c') vm.a = 3 waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('3-a1-b2-c') }).then(done) }) it('check priorities: v-if before v-for', function () { const vm = new Vue({ data: { items: [1, 2, 3] }, template: '
{{item}}
' }).$mount() expect(vm.$el.textContent).toBe('12') }) it('check priorities: v-if after v-for', function () { const vm = new Vue({ data: { items: [1, 2, 3] }, template: '
{{item}}
' }).$mount() expect(vm.$el.textContent).toBe('12') }) it('range v-for', () => { const vm = new Vue({ template: '
{{n}}
' }).$mount() expect(vm.$el.textContent).toBe('123') }) it('without key', done => { const vm = new Vue({ data: { items: [ { id: 1, msg: 'a' }, { id: 2, msg: 'b' }, { id: 3, msg: 'c' } ] }, template: '
{{ item.msg }}
' }).$mount() expect(vm.$el.textContent).toBe('abc') const first = vm.$el.children[0] vm.items.reverse() waitForUpdate(() => { expect(vm.$el.textContent).toBe('cba') // assert reusing DOM element in place expect(vm.$el.children[0]).toBe(first) }).then(done) }) it('with key', done => { const vm = new Vue({ data: { items: [ { id: 1, msg: 'a' }, { id: 2, msg: 'b' }, { id: 3, msg: 'c' } ] }, template: '
{{ item.msg }}
' }).$mount() expect(vm.$el.textContent).toBe('abc') const first = vm.$el.children[0] vm.items.reverse() waitForUpdate(() => { expect(vm.$el.textContent).toBe('cba') // assert moving DOM element expect(vm.$el.children[0]).not.toBe(first) expect(vm.$el.children[2]).toBe(first) }).then(done) }) it('nested loops', () => { const vm = new Vue({ data: { items: [ { items: [{ a: 1 }, { a: 2 }], a: 1 }, { items: [{ a: 3 }, { a: 4 }], a: 2 } ] }, template: '
' + '
' + '

{{j}} {{subItem.a}} {{i}} {{item.a}}

' + '
' + '
' }).$mount() expect(vm.$el.innerHTML).toBe( '

0 1 0 1

1 2 0 1

' + '

0 3 1 2

1 4 1 2

' ) }) it('template v-for', done => { const vm = new Vue({ data: { list: [ { a: 1 }, { a: 2 }, { a: 3 } ] }, template: '
' + '' + '
' }).$mount() assertMarkup() vm.list.reverse() waitForUpdate(() => { assertMarkup() vm.list.splice(1, 1) }).then(() => { assertMarkup() vm.list.splice(1, 0, { a: 2 }) }).then(done) function assertMarkup () { const markup = vm.list.map(function (item) { return '

' + item.a + '

' + (item.a + 1) + '

' }).join('') expect(vm.$el.innerHTML).toBe(markup) } }) it('component v-for', done => { const vm = new Vue({ data: { list: [ { a: 1 }, { a: 2 }, { a: 3 } ] }, template: '
' + '' + '{{item.a}}' + '' + '
', components: { test: { props: ['msg'], template: '

{{msg}}

' } } }).$mount() assertMarkup() vm.list.reverse() waitForUpdate(() => { assertMarkup() vm.list.splice(1, 1) }).then(() => { assertMarkup() vm.list.splice(1, 0, { a: 2 }) }).then(done) function assertMarkup () { const markup = vm.list.map(function (item) { return `

${item.a}${item.a}

` }).join('') expect(vm.$el.innerHTML).toBe(markup) } }) it('dynamic component v-for', done => { const vm = new Vue({ data: { list: [ { type: 'one' }, { type: 'two' } ] }, template: '
' + '' + '
', components: { one: { template: '

One!

' }, two: { template: '
Two!
' } } }).$mount() expect(vm.$el.innerHTML).toContain('

One!

Two!
') vm.list.reverse() waitForUpdate(() => { expect(vm.$el.innerHTML).toContain('
Two!

One!

') }).then(done) }) it('should warn component v-for without keys', () => { const warn = console.warn console.warn = jasmine.createSpy() new Vue({ template: `
`, components: { test: { render () {} } } }).$mount() expect(console.warn.calls.argsFor(0)[0]).toContain( `: component lists rendered with v-for should have explicit keys` ) console.warn = warn }) it('multi nested array reactivity', done => { const vm = new Vue({ data: { list: [[['foo']]] }, template: `
{{ k }}
` }).$mount() expect(vm.$el.textContent).toMatch(/\s+foo\s+/) vm.list[0][0].push('bar') waitForUpdate(() => { expect(vm.$el.textContent).toMatch(/\s+foo\s+bar\s+/) }).then(done) }) it('should work with strings', done => { const vm = new Vue({ data: { text: 'foo' }, template: `
{{ letter }}.
` }).$mount() expect(vm.$el.textContent).toMatch('f.o.o.') vm.text += 'bar' waitForUpdate(() => { expect(vm.$el.textContent).toMatch('f.o.o.b.a.r.') }).then(done) }) // #7792 it('should work with multiline expressions', () => { const vm = new Vue({ data: { a: [1], b: [2] }, template: `
{{ n }}
` }).$mount() expect(vm.$el.textContent).toBe('12') }) // #9181 it('components with v-for and empty list', done => { const vm = new Vue({ template: '
' + '{{ item }}' + '
', data: { list: undefined }, components: { foo: { template: '
' }, } }).$mount() expect(vm.$el.innerHTML).toBe('') vm.list = [1, 2, 3] waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('
1
2
3
') }).then(done) }) it('elements with v-for and empty list', done => { const vm = new Vue({ template: '
' + '
{{ item }}
' + '
', data: { list: undefined } }).$mount() expect(vm.$el.innerHTML).toBe('') vm.list = [1, 2, 3] waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('
1
2
3
') }).then(done) }) const supportsDestructuring = (() => { try { new Function('var { foo } = bar') return true } catch (e) {} })() if (supportsDestructuring) { it('should support destructuring syntax in alias position (object)', () => { const vm = new Vue({ data: { list: [{ foo: 'hi', bar: 'ho' }] }, template: '
{{ foo }} {{ bar }} {{ i }}
' }).$mount() expect(vm.$el.textContent).toBe('hi ho 0') }) it('should support destructuring syntax in alias position (array)', () => { const vm = new Vue({ data: { list: [[1, 2], [3, 4]] }, template: '
{{ foo }} {{ bar }} {{ i }}
' }).$mount() expect(vm.$el.textContent).toBe('1 2 03 4 1') }) } })