for.spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. import Vue from 'vue'
  2. describe('Directive v-for', () => {
  3. it('should render array of primitive values', done => {
  4. const vm = new Vue({
  5. template: `
  6. <div>
  7. <span v-for="item in list">{{item}}</span>
  8. </div>
  9. `,
  10. data: {
  11. list: ['a', 'b', 'c']
  12. }
  13. }).$mount()
  14. expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
  15. Vue.set(vm.list, 0, 'd')
  16. waitForUpdate(() => {
  17. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
  18. vm.list.push('d')
  19. }).then(() => {
  20. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span><span>d</span>')
  21. vm.list.splice(1, 2)
  22. }).then(() => {
  23. expect(vm.$el.innerHTML).toBe('<span>d</span><span>d</span>')
  24. vm.list = ['x', 'y']
  25. }).then(() => {
  26. expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
  27. }).then(done)
  28. })
  29. it('should render array of primitive values with index', done => {
  30. const vm = new Vue({
  31. template: `
  32. <div>
  33. <span v-for="(item, i) in list">{{i}}-{{item}}</span>
  34. </div>
  35. `,
  36. data: {
  37. list: ['a', 'b', 'c']
  38. }
  39. }).$mount()
  40. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  41. Vue.set(vm.list, 0, 'd')
  42. waitForUpdate(() => {
  43. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
  44. vm.list.push('d')
  45. }).then(() => {
  46. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span><span>3-d</span>')
  47. vm.list.splice(1, 2)
  48. }).then(() => {
  49. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-d</span>')
  50. vm.list = ['x', 'y']
  51. }).then(() => {
  52. expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
  53. }).then(done)
  54. })
  55. it('should render array of object values', done => {
  56. const vm = new Vue({
  57. template: `
  58. <div>
  59. <span v-for="item in list">{{item.value}}</span>
  60. </div>
  61. `,
  62. data: {
  63. list: [
  64. { value: 'a' },
  65. { value: 'b' },
  66. { value: 'c' }
  67. ]
  68. }
  69. }).$mount()
  70. expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
  71. Vue.set(vm.list, 0, { value: 'd' })
  72. waitForUpdate(() => {
  73. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
  74. vm.list[0].value = 'e'
  75. }).then(() => {
  76. expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span>')
  77. vm.list.push({})
  78. }).then(() => {
  79. expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span><span></span>')
  80. vm.list.splice(1, 2)
  81. }).then(() => {
  82. expect(vm.$el.innerHTML).toBe('<span>e</span><span></span>')
  83. vm.list = [{ value: 'x' }, { value: 'y' }]
  84. }).then(() => {
  85. expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
  86. }).then(done)
  87. })
  88. it('should render array of object values with index', done => {
  89. const vm = new Vue({
  90. template: `
  91. <div>
  92. <span v-for="(item, i) in list">{{i}}-{{item.value}}</span>
  93. </div>
  94. `,
  95. data: {
  96. list: [
  97. { value: 'a' },
  98. { value: 'b' },
  99. { value: 'c' }
  100. ]
  101. }
  102. }).$mount()
  103. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  104. Vue.set(vm.list, 0, { value: 'd' })
  105. waitForUpdate(() => {
  106. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
  107. vm.list[0].value = 'e'
  108. }).then(() => {
  109. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span>')
  110. vm.list.push({})
  111. }).then(() => {
  112. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span><span>3-</span>')
  113. vm.list.splice(1, 2)
  114. }).then(() => {
  115. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-</span>')
  116. vm.list = [{ value: 'x' }, { value: 'y' }]
  117. }).then(() => {
  118. expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
  119. }).then(done)
  120. })
  121. it('should render an Object', done => {
  122. const vm = new Vue({
  123. template: `
  124. <div>
  125. <span v-for="val in obj">{{val}}</span>
  126. </div>
  127. `,
  128. data: {
  129. obj: { a: 0, b: 1, c: 2 }
  130. }
  131. }).$mount()
  132. expect(vm.$el.innerHTML).toBe('<span>0</span><span>1</span><span>2</span>')
  133. vm.obj.a = 3
  134. waitForUpdate(() => {
  135. expect(vm.$el.innerHTML).toBe('<span>3</span><span>1</span><span>2</span>')
  136. Vue.set(vm.obj, 'd', 4)
  137. }).then(() => {
  138. expect(vm.$el.innerHTML).toBe('<span>3</span><span>1</span><span>2</span><span>4</span>')
  139. Vue.delete(vm.obj, 'a')
  140. }).then(() => {
  141. expect(vm.$el.innerHTML).toBe('<span>1</span><span>2</span><span>4</span>')
  142. }).then(done)
  143. })
  144. it('should render an Object with key', done => {
  145. const vm = new Vue({
  146. template: `
  147. <div>
  148. <span v-for="(val, key) in obj">{{val}}-{{key}}</span>
  149. </div>
  150. `,
  151. data: {
  152. obj: { a: 0, b: 1, c: 2 }
  153. }
  154. }).$mount()
  155. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  156. vm.obj.a = 3
  157. waitForUpdate(() => {
  158. expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span>')
  159. Vue.set(vm.obj, 'd', 4)
  160. }).then(() => {
  161. expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span><span>4-d</span>')
  162. Vue.delete(vm.obj, 'a')
  163. }).then(() => {
  164. expect(vm.$el.innerHTML).toBe('<span>1-b</span><span>2-c</span><span>4-d</span>')
  165. }).then(done)
  166. })
  167. it('should render an Object with key and index', done => {
  168. const vm = new Vue({
  169. template: `
  170. <div>
  171. <span v-for="(val, key, i) in obj">{{val}}-{{key}}-{{i}}</span>
  172. </div>
  173. `,
  174. data: {
  175. obj: { a: 0, b: 1, c: 2 }
  176. }
  177. }).$mount()
  178. expect(vm.$el.innerHTML).toBe('<span>0-a-0</span><span>1-b-1</span><span>2-c-2</span>')
  179. vm.obj.a = 3
  180. waitForUpdate(() => {
  181. expect(vm.$el.innerHTML).toBe('<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span>')
  182. Vue.set(vm.obj, 'd', 4)
  183. }).then(() => {
  184. expect(vm.$el.innerHTML).toBe('<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span><span>4-d-3</span>')
  185. Vue.delete(vm.obj, 'a')
  186. }).then(() => {
  187. expect(vm.$el.innerHTML).toBe('<span>1-b-0</span><span>2-c-1</span><span>4-d-2</span>')
  188. }).then(done)
  189. })
  190. it('should render each key of data', done => {
  191. const vm = new Vue({
  192. template: `
  193. <div>
  194. <span v-for="(val, key) in $data">{{val}}-{{key}}</span>
  195. </div>
  196. `,
  197. data: { a: 0, b: 1, c: 2 }
  198. }).$mount()
  199. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  200. vm.a = 3
  201. waitForUpdate(() => {
  202. expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span>')
  203. }).then(done)
  204. })
  205. it('check priorities: v-if before v-for', function () {
  206. const vm = new Vue({
  207. data: {
  208. items: [1, 2, 3]
  209. },
  210. template: '<div><div v-if="item < 3" v-for="item in items">{{item}}</div></div>'
  211. }).$mount()
  212. expect(vm.$el.textContent).toBe('12')
  213. })
  214. it('check priorities: v-if after v-for', function () {
  215. const vm = new Vue({
  216. data: {
  217. items: [1, 2, 3]
  218. },
  219. template: '<div><div v-for="item in items" v-if="item < 3">{{item}}</div></div>'
  220. }).$mount()
  221. expect(vm.$el.textContent).toBe('12')
  222. })
  223. it('range v-for', () => {
  224. const vm = new Vue({
  225. template: '<div><div v-for="n in 3">{{n}}</div></div>'
  226. }).$mount()
  227. expect(vm.$el.textContent).toBe('123')
  228. })
  229. it('without key', done => {
  230. const vm = new Vue({
  231. data: {
  232. items: [
  233. { id: 1, msg: 'a' },
  234. { id: 2, msg: 'b' },
  235. { id: 3, msg: 'c' }
  236. ]
  237. },
  238. template: '<div><div v-for="item in items">{{ item.msg }}</div></div>'
  239. }).$mount()
  240. expect(vm.$el.textContent).toBe('abc')
  241. const first = vm.$el.children[0]
  242. vm.items.reverse()
  243. waitForUpdate(() => {
  244. expect(vm.$el.textContent).toBe('cba')
  245. // assert reusing DOM element in place
  246. expect(vm.$el.children[0]).toBe(first)
  247. }).then(done)
  248. })
  249. it('with key', done => {
  250. const vm = new Vue({
  251. data: {
  252. items: [
  253. { id: 1, msg: 'a' },
  254. { id: 2, msg: 'b' },
  255. { id: 3, msg: 'c' }
  256. ]
  257. },
  258. template: '<div><div v-for="item in items" :key="item.id">{{ item.msg }}</div></div>'
  259. }).$mount()
  260. expect(vm.$el.textContent).toBe('abc')
  261. const first = vm.$el.children[0]
  262. vm.items.reverse()
  263. waitForUpdate(() => {
  264. expect(vm.$el.textContent).toBe('cba')
  265. // assert moving DOM element
  266. expect(vm.$el.children[0]).not.toBe(first)
  267. expect(vm.$el.children[2]).toBe(first)
  268. }).then(done)
  269. })
  270. it('nested loops', () => {
  271. const vm = new Vue({
  272. data: {
  273. items: [
  274. { items: [{ a: 1 }, { a: 2 }], a: 1 },
  275. { items: [{ a: 3 }, { a: 4 }], a: 2 }
  276. ]
  277. },
  278. template:
  279. '<div>' +
  280. '<div v-for="(item, i) in items">' +
  281. '<p v-for="(subItem, j) in item.items">{{j}} {{subItem.a}} {{i}} {{item.a}}</p>' +
  282. '</div>' +
  283. '</div>'
  284. }).$mount()
  285. expect(vm.$el.innerHTML).toBe(
  286. '<div><p>0 1 0 1</p><p>1 2 0 1</p></div>' +
  287. '<div><p>0 3 1 2</p><p>1 4 1 2</p></div>'
  288. )
  289. })
  290. it('template v-for', done => {
  291. const vm = new Vue({
  292. data: {
  293. list: [
  294. { a: 1 },
  295. { a: 2 },
  296. { a: 3 }
  297. ]
  298. },
  299. template:
  300. '<div>' +
  301. '<template v-for="item in list">' +
  302. '<p>{{item.a}}</p>' +
  303. '<p>{{item.a + 1}}</p>' +
  304. '</template>' +
  305. '</div>'
  306. }).$mount()
  307. assertMarkup()
  308. vm.list.reverse()
  309. waitForUpdate(() => {
  310. assertMarkup()
  311. vm.list.splice(1, 1)
  312. }).then(() => {
  313. assertMarkup()
  314. vm.list.splice(1, 0, { a: 2 })
  315. }).then(done)
  316. function assertMarkup () {
  317. var markup = vm.list.map(function (item) {
  318. return '<p>' + item.a + '</p><p>' + (item.a + 1) + '</p>'
  319. }).join('')
  320. expect(vm.$el.innerHTML).toBe(markup)
  321. }
  322. })
  323. it('component v-for', done => {
  324. const vm = new Vue({
  325. data: {
  326. list: [
  327. { a: 1 },
  328. { a: 2 },
  329. { a: 3 }
  330. ]
  331. },
  332. template:
  333. '<div>' +
  334. '<test v-for="item in list" :msg="item.a" :key="item.a">' +
  335. '<span>{{item.a}}</span>' +
  336. '</test>' +
  337. '</div>',
  338. components: {
  339. test: {
  340. props: ['msg'],
  341. template: '<p>{{msg}}<slot></slot></p>'
  342. }
  343. }
  344. }).$mount()
  345. assertMarkup()
  346. vm.list.reverse()
  347. waitForUpdate(() => {
  348. assertMarkup()
  349. vm.list.splice(1, 1)
  350. }).then(() => {
  351. assertMarkup()
  352. vm.list.splice(1, 0, { a: 2 })
  353. }).then(done)
  354. function assertMarkup () {
  355. var markup = vm.list.map(function (item) {
  356. return `<p>${item.a}<span>${item.a}</span></p>`
  357. }).join('')
  358. expect(vm.$el.innerHTML).toBe(markup)
  359. }
  360. })
  361. it('dynamic component v-for', done => {
  362. const vm = new Vue({
  363. data: {
  364. list: [
  365. { type: 'one' },
  366. { type: 'two' }
  367. ]
  368. },
  369. template:
  370. '<div>' +
  371. '<component v-for="item in list" :key="item.type" :is="item.type"></component>' +
  372. '</div>',
  373. components: {
  374. one: {
  375. template: '<p>One!</p>'
  376. },
  377. two: {
  378. template: '<div>Two!</div>'
  379. }
  380. }
  381. }).$mount()
  382. expect(vm.$el.innerHTML).toContain('<p>One!</p><div>Two!</div>')
  383. vm.list.reverse()
  384. waitForUpdate(() => {
  385. expect(vm.$el.innerHTML).toContain('<div>Two!</div><p>One!</p>')
  386. }).then(done)
  387. })
  388. it('should warn component v-for without keys', () => {
  389. const warn = console.warn
  390. console.warn = jasmine.createSpy()
  391. new Vue({
  392. template: `<div><test v-for="i in 3"></test></div>`,
  393. components: {
  394. test: {
  395. render () {}
  396. }
  397. }
  398. }).$mount()
  399. expect(console.warn.calls.argsFor(0)[0]).toContain(
  400. `<test v-for="i in 3">: component lists rendered with v-for should have explicit keys`
  401. )
  402. console.warn = warn
  403. })
  404. it('multi nested array reactivity', done => {
  405. const vm = new Vue({
  406. data: {
  407. list: [[['foo']]]
  408. },
  409. template: `
  410. <div>
  411. <div v-for="i in list">
  412. <div v-for="j in i">
  413. <div v-for="k in j">
  414. {{ k }}
  415. </div>
  416. </div>
  417. </div>
  418. </div>
  419. `
  420. }).$mount()
  421. expect(vm.$el.textContent).toMatch(/\s+foo\s+/)
  422. vm.list[0][0].push('bar')
  423. waitForUpdate(() => {
  424. expect(vm.$el.textContent).toMatch(/\s+foo\s+bar\s+/)
  425. }).then(done)
  426. })
  427. it('strings', done => {
  428. const vm = new Vue({
  429. data: {
  430. text: 'foo'
  431. },
  432. template: `
  433. <div>
  434. <span v-for="letter in text">{{ letter }}.</span>
  435. </div>
  436. `
  437. }).$mount()
  438. expect(vm.$el.textContent).toMatch('f.o.o.')
  439. vm.text += 'bar'
  440. waitForUpdate(() => {
  441. expect(vm.$el.textContent).toMatch('f.o.o.b.a.r.')
  442. }).then(done)
  443. })
  444. })