var _ = require('../../../../src/util') var Vue = require('../../../../src/vue') if (_.inBrowser) { describe('v-repeat', function () { var el beforeEach(function () { el = document.createElement('div') spyOn(_, 'warn') }) it('objects', function (done) { var vm = new Vue({ el: el, data: { items: [{a:1}, {a:2}] }, template: '
{{$index}} {{a}}
' }) assertMutations(vm, el, done) }) it('primitive values', function (done) { var vm = new Vue({ el: el, data: { items: [2, 1, 2] }, template: '
{{$index}} {{$value}}
' }) assertPrimitiveMutations(vm, el, done) }) it('objects with identifier', function (done) { var vm = new Vue({ el: el, data: { items: [{a:1}, {a:2}] }, template: '
{{$index}} {{item.a}}
' }) assertMutations(vm, el, done) }) it('primitive with identifier', function (done) { var vm = new Vue({ el: el, data: { items: [2, 1, 2] }, template: '
{{$index}} {{item}}
' }) assertPrimitiveMutations(vm, el, done) }) it('repeating an object of objects', function (done) { var vm = new Vue({ el: el, data: { items: { a: {a:1}, b: {a:2} } }, template: '
{{$index}} {{$key}} {{a}}
' }) assertObjectMutations(vm, el, done) }) it('repeating an object of primitives', function (done) { var vm = new Vue({ el: el, data: { items: { a: 1, b: 2 } }, template: '
{{$index}} {{$key}} {{$value}}
' }) assertObjectPrimitiveMutations(vm, el, done) }) it('repeating an object of objects with identifier', function (done) { var vm = new Vue({ el: el, data: { items: { a: {a:1}, b: {a:2} } }, template: '
{{$index}} {{$key}} {{item.a}}
' }) assertObjectMutations(vm, el, done) }) it('repeating an object of primitives with identifier', function (done) { var vm = new Vue({ el: el, data: { items: { a: 1, b: 2 } }, template: '
{{$index}} {{$key}} {{item}}
' }) assertObjectPrimitiveMutations(vm, el, done) }) it('v-component', function () { var vm = new Vue({ el: el, data: { items: [{a:1}, {a:2}, {a:3}] }, template: '
', components: { test: { template: '

{{$index}} {{a}}

', replace: true } } }) expect(el.innerHTML).toBe('

0 1

1 2

2 3

') }) it('nested repeats', function () { var vm = new Vue({ el: el, data: { items: [ { items: [{a:1}, {a:2}], a: 1 }, { items: [{a:3}, {a:4}], a: 2 } ] }, template: '
' + '

{{$index}} {{a}} {{$parent.$index}} {{$parent.a}}

' + '
' }) expect(el.innerHTML).toBe( '

0 1 0 1

1 2 0 1

' + '

0 3 1 2

1 4 1 2

' + '' ) }) it('dynamic component type based on instance data', function () { var vm = new Vue({ el: el, template: '
', data: { list: [ { type: 'a' }, { type: 'b' }, { type: 'c' } ] }, components: { 'view-a': { template: 'AAA' }, 'view-b': { template: 'BBB' }, 'view-c': { template: 'CCC' } } }) expect(el.innerHTML).toBe('
AAA
BBB
CCC
') // #458 meta properties vm = new Vue({ el: el, template: '
', data: { list: ['a', 'b', 'c'] }, components: { 'view-a': { template: 'AAA' }, 'view-b': { template: 'BBB' }, 'view-c': { template: 'CCC' } } }) expect(el.innerHTML).toBe('
AAA
BBB
CCC
') }) it('block repeat', function () { var vm = new Vue({ el: el, template: '', data: { list: [ { a: 1 }, { a: 2 }, { a: 3 } ] } }) var markup = vm.list.map(function (item) { return '

' + item.a + '

' + (item.a + 1) + '

' }).join('') expect(el.innerHTML).toBe(markup + '') }) it('array filters', function (done) { var vm = new Vue({ el: el, template: '
{{id}}
', data: { filterKey: 'hi!', sortKey: 'id', list: [ { id: 1, id2: 4, msg: 'hi!' }, { id: 2, id2: 3, msg: 'na' }, { id: 3, id2: 2, msg: 'hi!' }, { id: 4, id2: 1, msg: 'na' } ] } }) assertMarkup() go( function () { vm.filterKey = 'na' }, assertMarkup ) .then( function () { vm.sortKey = 'id2' }, assertMarkup ) .then( function () { vm.list[0].id2 = 0 }, assertMarkup ) .then( function () { vm.list.push({ id: 0, id2: 4, msg: 'na' }) }, assertMarkup ) .then( function () { vm.list = [ { id: 33, id2: 4, msg: 'hi!' }, { id: 44, id2: 3, msg: 'na' } ] }, assertMarkup ) .run(done) function assertMarkup () { var markup = vm.list .filter(function (item) { return item.msg === vm.filterKey }) .sort(function (a, b) { return a[vm.sortKey] > b[vm.sortKey] ? -1 : 1 }) .map(function (item) { return '
' + item.id + '
' }).join('') expect(el.innerHTML).toBe(markup + '') } }) it('trackby id', function (done) { assertTrackBy('
{{msg}}
', function () { assertTrackBy('
{{item.msg}}
', done) }) function assertTrackBy (template, next) { var vm = new Vue({ el: el, template: template, data: { list: [ { id: 1, msg: 'hi' }, { id: 2, msg: 'ha' }, { id: 3, msg: 'ho' } ] } }) assertMarkup() var oldVms = vm._children.slice() // swap the data with different objects, but with // the same ID! vm.list = [ { id: 1, msg: 'wa' }, { id: 2, msg: 'wo' } ] _.nextTick(function () { assertMarkup() // should reuse old vms! var i = 2 while (i--) { expect(vm._children[i]).toBe(oldVms[i]) } next() }) function assertMarkup () { var markup = vm.list.map(function (item) { return '
' + item.msg + '
' }).join('') expect(el.innerHTML).toBe(markup + '') } } }) it('warn duplicate objects', function () { var obj = {} var vm = new Vue({ el: el, template: '
', data: { items: [obj, obj] } }) expect(_.warn).toHaveBeenCalled() }) it('warn duplicate trackby id', function () { var vm = new Vue({ el: el, template: '
', data: { items: [{id:1}, {id:1}] } }) expect(_.warn).toHaveBeenCalled() }) it('warn v-if', function () { var vm = new Vue({ el: el, template: '
', data: { items: [] } }) expect(_.warn).toHaveBeenCalled() }) it('repeat number', function () { var vm = new Vue({ el: el, template: '
{{$index}} {{$value}}
' }) expect(el.innerHTML).toBe('
0 0
1 1
2 2
') }) it('repeat string', function () { var vm = new Vue({ el: el, template: '
{{$index}} {{$value}}
' }) expect(el.innerHTML).toBe('
0 v
1 u
2 e
') }) it('teardown', function () { var vm = new Vue({ el: el, template: '
{{a}}
', data: { items: [{a:1}, {a:2}] } }) vm._directives[0].unbind() expect(vm._children.length).toBe(0) }) it('with transition', function (done) { // === IMPORTANT === // PhantomJS always returns false when calling // Element.contains() on a comment node. This causes // transitions to be skipped. Monkey patching here // isn't ideal but does the job... var inDoc = _.inDoc _.inDoc = function () { return true } var vm = new Vue({ el: el, template: '
{{a}}
', data: { items: [{a:1}, {a:2}, {a:3}] }, transitions: { test: { leave: function (el, done) { setTimeout(done, 1) } } } }) vm.items.splice(1, 1, {a:4}) setTimeout(function () { expect(el.innerHTML).toBe('
1
4
3
') // clean up _.inDoc = inDoc done() }, 30) }) }) } /** * Simple helper for chained async asssertions * * @param {Function} fn - the data manipulation function * @param {Function} cb - the assertion fn to be called on nextTick */ function go (fn, cb) { return { stack: [{fn:fn, cb:cb}], then: function (fn, cb) { this.stack.push({fn:fn, cb:cb}) return this }, run: function (done) { var self = this var step = this.stack.shift() if (!step) return done() step.fn() _.nextTick(function () { step.cb() self.run(done) }) } } } /** * Assert mutation and markup correctness for v-repeat on * an Array of Objects */ function assertMutations (vm, el, done) { assertMarkup() var poppedItem go( function () { vm.items.push({a:3}) }, assertMarkup ) .then( function () { vm.items.shift() }, assertMarkup ) .then( function () { vm.items.reverse() }, assertMarkup ) .then( function () { poppedItem = vm.items.pop() }, assertMarkup ) .then( function () { vm.items.unshift(poppedItem) }, assertMarkup ) .then( function () { vm.items.sort(function (a, b) { return a.a > b.a ? 1 : -1 }) }, assertMarkup ) .then( function () { vm.items.splice(1, 1, {a:5}) }, assertMarkup ) // test swapping the array .then( function () { vm.items = [{a:0}, {a:1}, {a:2}] }, assertMarkup ) .run(done) function assertMarkup () { var markup = vm.items.map(function (item, i) { return '
' + i + ' ' + item.a + '
' }).join('') expect(el.innerHTML).toBe(markup + '') } } /** * Assert mutation and markup correctness for v-repeat on * an Array of primitive values */ function assertPrimitiveMutations (vm, el, done) { assertMarkup() go( function () { // check duplicate vm.items.push(2, 2, 3) }, assertMarkup ) .then( function () { vm.items.shift() }, assertMarkup ) .then( function () { vm.items.reverse() }, assertMarkup ) .then( function () { vm.items.pop() }, assertMarkup ) .then( function () { vm.items.unshift(3) }, assertMarkup ) .then( function () { vm.items.sort(function (a, b) { return a > b ? 1 : -1 }) }, assertMarkup ) .then( function () { vm.items.splice(1, 1, 5) }, assertMarkup ) // test swapping the array .then( function () { vm.items = [1, 2, 2] }, assertMarkup ) .run(done) function assertMarkup () { var markup = vm.items.map(function (item, i) { return '
' + i + ' ' + item + '
' }).join('') expect(el.innerHTML).toBe(markup + '') } } /** * Assert mutation and markup correctness for v-repeat on * an Object of Objects */ function assertObjectMutations (vm, el, done) { assertMarkup() go( function () { vm.items.a = {a:3} }, assertMarkup ) .then( function () { vm.items = { c: {a:1}, d: {a:2} } }, assertMarkup ) .then( function () { vm.items.$add('a', {a:3}) }, assertMarkup ) .run(done) function assertMarkup () { var markup = Object.keys(vm.items).map(function (key, i) { return '
' + i + ' ' + key + ' ' + vm.items[key].a + '
' }).join('') expect(el.innerHTML).toBe(markup + '') } } /** * Assert mutation and markup correctness for v-repeat on * an Object of primitive values */ function assertObjectPrimitiveMutations (vm, el, done) { assertMarkup() go( function () { vm.items.a = 3 }, assertMarkup ) .then( function () { vm.items = { c: 1, d: 2 } }, assertMarkup ) .then( function () { vm.items.$add('a', 3) }, assertMarkup ) .run(done) function assertMarkup () { var markup = Object.keys(vm.items).map(function (key, i) { return '
' + i + ' ' + key + ' ' + vm.items[key] + '
' }).join('') expect(el.innerHTML).toBe(markup + '') } }