repeat_spec.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869
  1. // patch inDoc
  2. require('../../lib/indoc_patch')
  3. var _ = require('../../../../src/util')
  4. var Vue = require('../../../../src/vue')
  5. if (_.inBrowser) {
  6. describe('v-repeat', function () {
  7. var el
  8. beforeEach(function () {
  9. el = document.createElement('div')
  10. spyOn(_, 'warn')
  11. })
  12. it('objects', function (done) {
  13. var vm = new Vue({
  14. el: el,
  15. data: {
  16. items: [{a:1}, {a:2}]
  17. },
  18. template: '<div v-repeat="items">{{$index}} {{a}}</div>'
  19. })
  20. assertMutations(vm, el, done)
  21. })
  22. it('primitive values', function (done) {
  23. var vm = new Vue({
  24. el: el,
  25. data: {
  26. items: [2, 1, 2]
  27. },
  28. template: '<div v-repeat="items">{{$index}} {{$value}}</div>'
  29. })
  30. assertPrimitiveMutations(vm, el, done)
  31. })
  32. it('objects with identifier', function (done) {
  33. var vm = new Vue({
  34. el: el,
  35. data: {
  36. items: [{a:1}, {a:2}]
  37. },
  38. template: '<div v-repeat="item:items">{{$index}} {{item.a}}</div>'
  39. })
  40. assertMutations(vm, el, done)
  41. })
  42. it('primitive with identifier', function (done) {
  43. var vm = new Vue({
  44. el: el,
  45. data: {
  46. items: [2, 1, 2]
  47. },
  48. template: '<div v-repeat="item:items">{{$index}} {{item}}</div>'
  49. })
  50. assertPrimitiveMutations(vm, el, done)
  51. })
  52. it('repeating an object of objects', function (done) {
  53. var vm = new Vue({
  54. el: el,
  55. data: {
  56. items: {
  57. a: {a:1},
  58. b: {a:2}
  59. }
  60. },
  61. template: '<div v-repeat="items">{{$index}} {{$key}} {{a}}</div>'
  62. })
  63. assertObjectMutations(vm, el, done)
  64. })
  65. it('repeating an object of primitives', function (done) {
  66. var vm = new Vue({
  67. el: el,
  68. data: {
  69. items: {
  70. a: 1,
  71. b: 2
  72. }
  73. },
  74. template: '<div v-repeat="items">{{$index}} {{$key}} {{$value}}</div>'
  75. })
  76. assertObjectPrimitiveMutations(vm, el, done)
  77. })
  78. it('repeating an object of objects with identifier', function (done) {
  79. var vm = new Vue({
  80. el: el,
  81. data: {
  82. items: {
  83. a: {a:1},
  84. b: {a:2}
  85. }
  86. },
  87. template: '<div v-repeat="item:items">{{$index}} {{$key}} {{item.a}}</div>'
  88. })
  89. assertObjectMutations(vm, el, done)
  90. })
  91. it('repeating an object of primitives with identifier', function (done) {
  92. var vm = new Vue({
  93. el: el,
  94. data: {
  95. items: {
  96. a: 1,
  97. b: 2
  98. }
  99. },
  100. template: '<div v-repeat="item:items">{{$index}} {{$key}} {{item}}</div>'
  101. })
  102. assertObjectPrimitiveMutations(vm, el, done)
  103. })
  104. it('array of arrays', function () {
  105. var vm = new Vue({
  106. el: el,
  107. data: {
  108. items: [[1,1], [2,2], [3,3]]
  109. },
  110. template: '<div v-repeat="items">{{$index}} {{$value}}</div>'
  111. })
  112. var markup = vm.items.map(function (item, i) {
  113. return '<div>' + i + ' ' + item.toString() + '</div>'
  114. }).join('') + '<!--v-repeat-->'
  115. expect(el.innerHTML).toBe(markup)
  116. })
  117. it('repeating object with filter', function () {
  118. var vm = new Vue({
  119. el: el,
  120. data: {
  121. items: {
  122. a: { msg: 'aaa' },
  123. b: { msg: 'bbb' }
  124. }
  125. },
  126. template: '<div v-repeat="items | filterBy \'aaa\'">{{msg}}</div>'
  127. })
  128. expect(el.innerHTML).toBe('<div>aaa</div><!--v-repeat-->')
  129. })
  130. it('v-component', function (done) {
  131. var vm = new Vue({
  132. el: el,
  133. data: {
  134. items: [{a:1}, {a:2}]
  135. },
  136. template: '<p v-repeat="items" v-component="test"></p>',
  137. components: {
  138. test: {
  139. template: '<div>{{$index}} {{a}}</div>',
  140. replace: true
  141. }
  142. }
  143. })
  144. assertMutations(vm, el, done)
  145. })
  146. it('v-component with inline-template', function (done) {
  147. var vm = new Vue({
  148. el: el,
  149. data: {
  150. items: [{a:1}, {a:2}]
  151. },
  152. template:
  153. '<div v-repeat="items" v-component="test" inline-template>' +
  154. '{{$index}} {{a}}' +
  155. '</div>',
  156. components: {
  157. test: {}
  158. }
  159. })
  160. assertMutations(vm, el, done)
  161. })
  162. it('v-component with primitive values', function (done) {
  163. var vm = new Vue({
  164. el: el,
  165. data: {
  166. items: [2, 1, 2]
  167. },
  168. template: '<p v-repeat="items" v-component="test"></p>',
  169. components: {
  170. test: {
  171. template: '<div>{{$index}} {{$value}}</div>',
  172. replace: true
  173. }
  174. }
  175. })
  176. assertPrimitiveMutations(vm, el, done)
  177. })
  178. it('v-component with object of objects', function (done) {
  179. var vm = new Vue({
  180. el: el,
  181. data: {
  182. items: {
  183. a: {a:1},
  184. b: {a:2}
  185. }
  186. },
  187. template: '<p v-repeat="items" v-component="test"></p>',
  188. components: {
  189. test: {
  190. template: '<div>{{$index}} {{$key}} {{a}}</div>',
  191. replace: true
  192. }
  193. }
  194. })
  195. assertObjectMutations(vm, el, done)
  196. })
  197. it('custom element component', function () {
  198. var vm = new Vue({
  199. el: el,
  200. data: {
  201. items: [{a:1}, {a:2}, {a:3}]
  202. },
  203. template: '<test-component v-repeat="items"></test-component>',
  204. components: {
  205. 'test-component': {
  206. template: '<p>{{$index}} {{a}}</p>',
  207. replace: true
  208. }
  209. }
  210. })
  211. expect(el.innerHTML).toBe('<p>0 1</p><p>1 2</p><p>2 3</p><!--v-repeat-->')
  212. })
  213. it('nested repeats', function () {
  214. var vm = new Vue({
  215. el: el,
  216. data: {
  217. items: [
  218. { items: [{a:1}, {a:2}], a: 1 },
  219. { items: [{a:3}, {a:4}], a: 2 }
  220. ]
  221. },
  222. template: '<div v-repeat="items">' +
  223. '<p v-repeat="items">{{$index}} {{a}} {{$parent.$index}} {{$parent.a}}</p>' +
  224. '</div>'
  225. })
  226. expect(el.innerHTML).toBe(
  227. '<div><p>0 1 0 1</p><p>1 2 0 1</p><!--v-repeat--></div>' +
  228. '<div><p>0 3 1 2</p><p>1 4 1 2</p><!--v-repeat--></div>' +
  229. '<!--v-repeat-->'
  230. )
  231. })
  232. it('nested repeats on object', function(){
  233. var vm = new Vue({
  234. el: el,
  235. data: {
  236. listHash: {
  237. listA: [{a: 1},{a: 2}],
  238. listB: [{a: 1},{a: 2}]
  239. }
  240. },
  241. template: '<div v-repeat="listHash">{{$key}}' +
  242. '<p v-repeat="$data">{{a}}</p>' +
  243. '</div>'
  244. })
  245. function output(key){
  246. var key1 = key === 'listA' ? 'listB' : 'listA'
  247. return '<div>'+ key +'<p>1</p><p>2</p><!--v-repeat--></div>' +
  248. '<div>'+ key1 +'<p>1</p><p>2</p><!--v-repeat--></div>' +
  249. '<!--v-repeat-->'
  250. }
  251. expect(el.innerHTML === output('listA') || el.innerHTML === output('listB')).toBeTruthy()
  252. })
  253. it('dynamic component type based on instance data', function () {
  254. var vm = new Vue({
  255. el: el,
  256. template: '<div v-repeat="list" v-component="view-{{type}}"></div>',
  257. data: {
  258. list: [
  259. { type: 'a' },
  260. { type: 'b' },
  261. { type: 'c' }
  262. ]
  263. },
  264. components: {
  265. 'view-a': {
  266. template: 'AAA'
  267. },
  268. 'view-b': {
  269. template: 'BBB'
  270. },
  271. 'view-c': {
  272. template: 'CCC'
  273. }
  274. }
  275. })
  276. expect(el.innerHTML).toBe('<div>AAA</div><div>BBB</div><div>CCC</div><!--v-repeat-->')
  277. // #458 meta properties
  278. vm = new Vue({
  279. el: el,
  280. template: '<div v-repeat="list" v-component="view-{{$value}}"></div>',
  281. data: {
  282. list: ['a', 'b', 'c']
  283. },
  284. components: {
  285. 'view-a': {
  286. template: 'AAA'
  287. },
  288. 'view-b': {
  289. template: 'BBB'
  290. },
  291. 'view-c': {
  292. template: 'CCC'
  293. }
  294. }
  295. })
  296. expect(el.innerHTML).toBe('<div>AAA</div><div>BBB</div><div>CCC</div><!--v-repeat-->')
  297. })
  298. it('block repeat', function () {
  299. var vm = new Vue({
  300. el: el,
  301. template: '<template v-repeat="list"><p>{{a}}</p><p>{{a + 1}}</p></template>',
  302. data: {
  303. list: [
  304. { a: 1 },
  305. { a: 2 },
  306. { a: 3 }
  307. ]
  308. }
  309. })
  310. var markup = vm.list.map(function (item) {
  311. return '<!--v-start--><p>' + item.a + '</p><p>' + (item.a + 1) + '</p><!--v-end-->'
  312. }).join('')
  313. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  314. })
  315. it('component + parent directive + transclusion', function (done) {
  316. var vm = new Vue({
  317. el: el,
  318. template: '<div v-repeat="list" v-component="test" v-class="cls">{{msg}}</div>',
  319. data: {
  320. cls: 'parent',
  321. msg: 'hi',
  322. list: [{a:1},{a:2},{a:3}]
  323. },
  324. components: {
  325. test: {
  326. replace: true,
  327. template: '<div class="child">{{a}} <content></content></div>'
  328. }
  329. }
  330. })
  331. var markup = vm.list.map(function (item) {
  332. return '<div class="child parent">' + item.a + ' hi</div>'
  333. }).join('')
  334. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  335. vm.msg = 'ho'
  336. markup = vm.list.map(function (item) {
  337. return '<div class="child parent">' + item.a + ' ho</div>'
  338. }).join('')
  339. _.nextTick(function () {
  340. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  341. done()
  342. })
  343. })
  344. it('array filters', function (done) {
  345. var vm = new Vue({
  346. el: el,
  347. template: '<div v-repeat="list | filterBy filterKey | orderBy sortKey -1">{{id}}</div>',
  348. data: {
  349. filterKey: 'hi!',
  350. sortKey: 'id',
  351. list: [
  352. { id: 1, id2: 4, msg: 'hi!' },
  353. { id: 2, id2: 3, msg: 'na' },
  354. { id: 3, id2: 2, msg: 'hi!' },
  355. { id: 4, id2: 1, msg: 'na' }
  356. ]
  357. }
  358. })
  359. assertMarkup()
  360. go(
  361. function () {
  362. vm.filterKey = 'na'
  363. }, assertMarkup
  364. )
  365. .then(
  366. function () {
  367. vm.sortKey = 'id2'
  368. }, assertMarkup
  369. )
  370. .then(
  371. function () {
  372. vm.list[0].id2 = 0
  373. }, assertMarkup
  374. )
  375. .then(
  376. function () {
  377. vm.list.push({ id: 0, id2: 4, msg: 'na' })
  378. }, assertMarkup
  379. )
  380. .then(
  381. function () {
  382. vm.list = [
  383. { id: 33, id2: 4, msg: 'hi!' },
  384. { id: 44, id2: 3, msg: 'na' }
  385. ]
  386. }, assertMarkup
  387. )
  388. .run(done)
  389. function assertMarkup () {
  390. var markup = vm.list
  391. .filter(function (item) {
  392. return item.msg === vm.filterKey
  393. })
  394. .sort(function (a, b) {
  395. return a[vm.sortKey] > b[vm.sortKey] ? -1 : 1
  396. })
  397. .map(function (item) {
  398. return '<div>' + item.id + '</div>'
  399. }).join('')
  400. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  401. }
  402. })
  403. it('orderBy supporting $key for object repeaters', function (done) {
  404. var vm = new Vue({
  405. el: el,
  406. template: '<div v-repeat="obj | orderBy sortKey">{{$value}}</div>',
  407. data: {
  408. sortKey: '$key',
  409. obj: {
  410. c: 1,
  411. a: 3,
  412. b: 2
  413. }
  414. }
  415. })
  416. expect(el.innerHTML).toBe('<div>3</div><div>2</div><div>1</div><!--v-repeat-->')
  417. vm.sortKey = '$value'
  418. _.nextTick(function () {
  419. expect(el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div><!--v-repeat-->')
  420. done()
  421. })
  422. })
  423. it('orderBy supporting $value for primitive arrays', function () {
  424. var vm = new Vue({
  425. el: el,
  426. template: '<div v-repeat="list | orderBy \'$value\'">{{$value}}</div>',
  427. data: {
  428. list: [3, 2, 1]
  429. }
  430. })
  431. expect(el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div><!--v-repeat-->')
  432. })
  433. it('track by id', function (done) {
  434. assertTrackBy('<div v-repeat="list" v-component="test" track-by="id"></div>', '{{msg}}', function () {
  435. assertTrackBy('<div v-repeat="item:list" v-component="test" track-by="id"></div>', '{{item.msg}}', done)
  436. })
  437. function assertTrackBy (template, componentTemplate, next) {
  438. var vm = new Vue({
  439. el: el,
  440. template: template,
  441. data: {
  442. list: [
  443. { id: 1, msg: 'hi' },
  444. { id: 2, msg: 'ha' },
  445. { id: 3, msg: 'ho' }
  446. ]
  447. },
  448. components: {
  449. test: {
  450. template: componentTemplate
  451. }
  452. }
  453. })
  454. assertMarkup()
  455. var oldVms = vm._children.slice()
  456. // swap the data with different objects, but with
  457. // the same ID!
  458. vm.list = [
  459. { id: 1, msg: 'wa' },
  460. { id: 2, msg: 'wo' }
  461. ]
  462. _.nextTick(function () {
  463. assertMarkup()
  464. // should reuse old vms!
  465. var i = 2
  466. while (i--) {
  467. expect(vm._children[i]).toBe(oldVms[i])
  468. }
  469. next()
  470. })
  471. function assertMarkup () {
  472. var markup = vm.list.map(function (item) {
  473. return '<div>' + item.msg + '</div>'
  474. }).join('')
  475. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  476. }
  477. }
  478. })
  479. it('warn duplicate objects', function () {
  480. var obj = {}
  481. var vm = new Vue({
  482. el: el,
  483. template: '<div v-repeat="items" v-component="test"></div>',
  484. data: {
  485. items: [obj, obj]
  486. },
  487. components: {
  488. test: {}
  489. }
  490. })
  491. expect(_.warn).toHaveBeenCalled()
  492. })
  493. it('warn duplicate trackby id', function () {
  494. var vm = new Vue({
  495. el: el,
  496. template: '<div v-repeat="items" v-component="test" track-by="id"></div>',
  497. data: {
  498. items: [{id:1}, {id:1}]
  499. },
  500. components: {
  501. test: {}
  502. }
  503. })
  504. expect(_.warn).toHaveBeenCalled()
  505. })
  506. it('warn v-if', function () {
  507. var vm = new Vue({
  508. el: el,
  509. template: '<div v-repeat="items" v-if="aaa"></div>',
  510. data: {
  511. items: []
  512. }
  513. })
  514. expect(_.warn).toHaveBeenCalled()
  515. })
  516. it('repeat number', function () {
  517. var vm = new Vue({
  518. el: el,
  519. template: '<div v-repeat="3">{{$index}} {{$value}}</div>'
  520. })
  521. expect(el.innerHTML).toBe('<div>0 0</div><div>1 1</div><div>2 2</div><!--v-repeat-->')
  522. })
  523. it('repeat string', function () {
  524. var vm = new Vue({
  525. el: el,
  526. template: '<div v-repeat="\'vue\'">{{$index}} {{$value}}</div>'
  527. })
  528. expect(el.innerHTML).toBe('<div>0 v</div><div>1 u</div><div>2 e</div><!--v-repeat-->')
  529. })
  530. it('teardown', function () {
  531. var vm = new Vue({
  532. el: el,
  533. template: '<div v-repeat="items" v-component="test"></div>',
  534. data: {
  535. items: [{a:1}, {a:2}]
  536. },
  537. components: {
  538. test: {}
  539. }
  540. })
  541. vm._directives[0].unbind()
  542. expect(vm._children.length).toBe(0)
  543. })
  544. it('with transition', function (done) {
  545. document.body.appendChild(el)
  546. var vm = new Vue({
  547. el: el,
  548. template: '<div v-repeat="items" v-transition="test">{{a}}</div>',
  549. data: {
  550. items: [{a:1}, {a:2}, {a:3}]
  551. },
  552. transitions: {
  553. test: {
  554. leave: function (el, done) {
  555. setTimeout(done, 1)
  556. }
  557. }
  558. }
  559. })
  560. vm.items.splice(1, 1, {a:4})
  561. setTimeout(function () {
  562. expect(el.innerHTML).toBe('<div>1</div><div>4</div><div>3</div><!--v-repeat-->')
  563. document.body.removeChild(el)
  564. done()
  565. }, 30)
  566. })
  567. it('sync $value changes back to original array/object', function (done) {
  568. var vm = new Vue({
  569. el: el,
  570. template:
  571. '<div v-repeat="items">{{$value}}</div>' +
  572. '<div v-repeat="obj">{{$value}}</div>',
  573. data: {
  574. items: ['a', 'b'],
  575. obj: { foo: 'a', bar: 'b' }
  576. }
  577. })
  578. vm._children[0].$value = 'c'
  579. var key = vm._children[2].$key
  580. vm._children[2].$value = 'd'
  581. _.nextTick(function () {
  582. expect(vm.items[0]).toBe('c')
  583. expect(vm.obj[key]).toBe('d')
  584. done()
  585. })
  586. })
  587. })
  588. }
  589. /**
  590. * Simple helper for chained async asssertions
  591. *
  592. * @param {Function} fn - the data manipulation function
  593. * @param {Function} cb - the assertion fn to be called on nextTick
  594. */
  595. function go (fn, cb) {
  596. return {
  597. stack: [{fn:fn, cb:cb}],
  598. then: function (fn, cb) {
  599. this.stack.push({fn:fn, cb:cb})
  600. return this
  601. },
  602. run: function (done) {
  603. var self = this
  604. var step = this.stack.shift()
  605. if (!step) return done()
  606. step.fn()
  607. _.nextTick(function () {
  608. step.cb()
  609. self.run(done)
  610. })
  611. }
  612. }
  613. }
  614. /**
  615. * Assert mutation and markup correctness for v-repeat on
  616. * an Array of Objects
  617. */
  618. function assertMutations (vm, el, done) {
  619. assertMarkup()
  620. var poppedItem
  621. go(
  622. function () {
  623. vm.items.push({a:3})
  624. },
  625. assertMarkup
  626. )
  627. .then(
  628. function () {
  629. vm.items.shift()
  630. },
  631. assertMarkup
  632. )
  633. .then(
  634. function () {
  635. vm.items.reverse()
  636. },
  637. assertMarkup
  638. )
  639. .then(
  640. function () {
  641. poppedItem = vm.items.pop()
  642. },
  643. assertMarkup
  644. )
  645. .then(
  646. function () {
  647. vm.items.unshift(poppedItem)
  648. },
  649. assertMarkup
  650. )
  651. .then(
  652. function () {
  653. vm.items.sort(function (a, b) {
  654. return a.a > b.a ? 1 : -1
  655. })
  656. },
  657. assertMarkup
  658. )
  659. .then(
  660. function () {
  661. vm.items.splice(1, 1, {a:5})
  662. },
  663. assertMarkup
  664. )
  665. // test swapping the array
  666. .then(
  667. function () {
  668. vm.items = [{a:0}, {a:1}, {a:2}]
  669. },
  670. assertMarkup
  671. )
  672. .run(done)
  673. function assertMarkup () {
  674. var markup = vm.items.map(function (item, i) {
  675. return '<div>' + i + ' ' + item.a + '</div>'
  676. }).join('')
  677. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  678. }
  679. }
  680. /**
  681. * Assert mutation and markup correctness for v-repeat on
  682. * an Array of primitive values
  683. */
  684. function assertPrimitiveMutations (vm, el, done) {
  685. assertMarkup()
  686. go(
  687. function () {
  688. // check duplicate
  689. vm.items.push(2, 2, 3)
  690. },
  691. assertMarkup
  692. )
  693. .then(
  694. function () {
  695. vm.items.shift()
  696. },
  697. assertMarkup
  698. )
  699. .then(
  700. function () {
  701. vm.items.reverse()
  702. },
  703. assertMarkup
  704. )
  705. .then(
  706. function () {
  707. vm.items.pop()
  708. },
  709. assertMarkup
  710. )
  711. .then(
  712. function () {
  713. vm.items.unshift(3)
  714. },
  715. assertMarkup
  716. )
  717. .then(
  718. function () {
  719. vm.items.sort(function (a, b) {
  720. return a > b ? 1 : -1
  721. })
  722. },
  723. assertMarkup
  724. )
  725. .then(
  726. function () {
  727. vm.items.splice(1, 1, 5)
  728. },
  729. assertMarkup
  730. )
  731. // test swapping the array
  732. .then(
  733. function () {
  734. vm.items = [1, 2, 2]
  735. },
  736. assertMarkup
  737. )
  738. .run(done)
  739. function assertMarkup () {
  740. var markup = vm.items.map(function (item, i) {
  741. return '<div>' + i + ' ' + item + '</div>'
  742. }).join('')
  743. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  744. }
  745. }
  746. /**
  747. * Assert mutation and markup correctness for v-repeat on
  748. * an Object of Objects
  749. */
  750. function assertObjectMutations (vm, el, done) {
  751. assertMarkup()
  752. go(
  753. function () {
  754. vm.items.a = {a:3}
  755. },
  756. assertMarkup
  757. )
  758. .then(
  759. function () {
  760. vm.items = {
  761. c: {a:1},
  762. d: {a:2}
  763. }
  764. },
  765. assertMarkup
  766. )
  767. .then(
  768. function () {
  769. vm.items.$add('a', {a:3})
  770. },
  771. assertMarkup
  772. )
  773. .run(done)
  774. function assertMarkup () {
  775. var markup = Object.keys(vm.items).map(function (key, i) {
  776. return '<div>' + i + ' ' + key + ' ' + vm.items[key].a + '</div>'
  777. }).join('')
  778. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  779. }
  780. }
  781. /**
  782. * Assert mutation and markup correctness for v-repeat on
  783. * an Object of primitive values
  784. */
  785. function assertObjectPrimitiveMutations (vm, el, done) {
  786. assertMarkup()
  787. go(
  788. function () {
  789. vm.items.a = 3
  790. },
  791. assertMarkup
  792. )
  793. .then(
  794. function () {
  795. vm.items = {
  796. c: 1,
  797. d: 2
  798. }
  799. },
  800. assertMarkup
  801. )
  802. .then(
  803. function () {
  804. vm.items.$add('a', 3)
  805. },
  806. assertMarkup
  807. )
  808. .run(done)
  809. function assertMarkup () {
  810. var markup = Object.keys(vm.items).map(function (key, i) {
  811. return '<div>' + i + ' ' + key + ' ' + vm.items[key] + '</div>'
  812. }).join('')
  813. expect(el.innerHTML).toBe(markup + '<!--v-repeat-->')
  814. }
  815. }