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(
'' +
'' +
''
)
})
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: '{{a}}
{{a + 1}}
',
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 + '')
}
}