for.spec.js 12 KB

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