for.spec.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. import Vue from 'vue'
  2. import { hasSymbol } from 'core/util/env'
  3. describe('Directive v-for', () => {
  4. it('should render array of primitive values', done => {
  5. const vm = new Vue({
  6. template: `
  7. <div>
  8. <span v-for="item in list">{{item}}</span>
  9. </div>
  10. `,
  11. data: {
  12. list: ['a', 'b', 'c']
  13. }
  14. }).$mount()
  15. expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
  16. Vue.set(vm.list, 0, 'd')
  17. waitForUpdate(() => {
  18. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
  19. vm.list.push('d')
  20. }).then(() => {
  21. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span><span>d</span>')
  22. vm.list.splice(1, 2)
  23. }).then(() => {
  24. expect(vm.$el.innerHTML).toBe('<span>d</span><span>d</span>')
  25. vm.list = ['x', 'y']
  26. }).then(() => {
  27. expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
  28. }).then(done)
  29. })
  30. it('should render array of primitive values with index', done => {
  31. const vm = new Vue({
  32. template: `
  33. <div>
  34. <span v-for="(item, i) in list">{{i}}-{{item}}</span>
  35. </div>
  36. `,
  37. data: {
  38. list: ['a', 'b', 'c']
  39. }
  40. }).$mount()
  41. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  42. Vue.set(vm.list, 0, 'd')
  43. waitForUpdate(() => {
  44. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
  45. vm.list.push('d')
  46. }).then(() => {
  47. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span><span>3-d</span>')
  48. vm.list.splice(1, 2)
  49. }).then(() => {
  50. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-d</span>')
  51. vm.list = ['x', 'y']
  52. }).then(() => {
  53. expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
  54. }).then(done)
  55. })
  56. it('should render array of object values', done => {
  57. const vm = new Vue({
  58. template: `
  59. <div>
  60. <span v-for="item in list">{{item.value}}</span>
  61. </div>
  62. `,
  63. data: {
  64. list: [
  65. { value: 'a' },
  66. { value: 'b' },
  67. { value: 'c' }
  68. ]
  69. }
  70. }).$mount()
  71. expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
  72. Vue.set(vm.list, 0, { value: 'd' })
  73. waitForUpdate(() => {
  74. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
  75. vm.list[0].value = 'e'
  76. }).then(() => {
  77. expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span>')
  78. vm.list.push({})
  79. }).then(() => {
  80. expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span><span></span>')
  81. vm.list.splice(1, 2)
  82. }).then(() => {
  83. expect(vm.$el.innerHTML).toBe('<span>e</span><span></span>')
  84. vm.list = [{ value: 'x' }, { value: 'y' }]
  85. }).then(() => {
  86. expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
  87. }).then(done)
  88. })
  89. it('should render array of object values with index', done => {
  90. const vm = new Vue({
  91. template: `
  92. <div>
  93. <span v-for="(item, i) in list">{{i}}-{{item.value}}</span>
  94. </div>
  95. `,
  96. data: {
  97. list: [
  98. { value: 'a' },
  99. { value: 'b' },
  100. { value: 'c' }
  101. ]
  102. }
  103. }).$mount()
  104. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  105. Vue.set(vm.list, 0, { value: 'd' })
  106. waitForUpdate(() => {
  107. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
  108. vm.list[0].value = 'e'
  109. }).then(() => {
  110. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span>')
  111. vm.list.push({})
  112. }).then(() => {
  113. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span><span>3-</span>')
  114. vm.list.splice(1, 2)
  115. }).then(() => {
  116. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-</span>')
  117. vm.list = [{ value: 'x' }, { value: 'y' }]
  118. }).then(() => {
  119. expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
  120. }).then(done)
  121. })
  122. if (hasSymbol) {
  123. it('should render native iterables (Map)', () => {
  124. const vm = new Vue({
  125. template: `<div><span v-for="[key, val] in list">{{key}},{{val}}</span></div>`,
  126. data: {
  127. list: new Map([[1, 'foo'], [2, 'bar']])
  128. }
  129. }).$mount()
  130. expect(vm.$el.innerHTML).toBe(`<span>1,foo</span><span>2,bar</span>`)
  131. })
  132. it('should render native iterables (Set)', () => {
  133. const vm = new Vue({
  134. template: `<div><span v-for="val in list">{{val}}</span></div>`,
  135. data: {
  136. list: new Set([1, 2, 3])
  137. }
  138. }).$mount()
  139. expect(vm.$el.innerHTML).toBe(`<span>1</span><span>2</span><span>3</span>`)
  140. })
  141. it('should render iterable of primitive values', done => {
  142. const iterable = {
  143. models: ['a', 'b', 'c'],
  144. index: 0,
  145. [Symbol.iterator] () {
  146. const iterator = {
  147. index: 0,
  148. models: this.models,
  149. next () {
  150. if (this.index < this.models.length) {
  151. return { value: this.models[this.index++] }
  152. } else {
  153. return { done: true }
  154. }
  155. }
  156. }
  157. return iterator
  158. }
  159. }
  160. const vm = new Vue({
  161. template: `
  162. <div>
  163. <span v-for="item in list">{{item}}</span>
  164. </div>
  165. `,
  166. data: {
  167. list: iterable
  168. }
  169. }).$mount()
  170. expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
  171. Vue.set(vm.list.models, 0, 'd')
  172. waitForUpdate(() => {
  173. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
  174. vm.list.models.push('d')
  175. }).then(() => {
  176. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span><span>d</span>')
  177. vm.list.models.splice(1, 2)
  178. }).then(() => {
  179. expect(vm.$el.innerHTML).toBe('<span>d</span><span>d</span>')
  180. vm.list.models = ['x', 'y']
  181. }).then(() => {
  182. expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
  183. }).then(done)
  184. })
  185. it('should render iterable of primitive values with index', done => {
  186. const iterable = {
  187. models: ['a', 'b', 'c'],
  188. index: 0,
  189. [Symbol.iterator] () {
  190. const iterator = {
  191. index: 0,
  192. models: this.models,
  193. next () {
  194. if (this.index < this.models.length) {
  195. return { value: this.models[this.index++] }
  196. } else {
  197. return { done: true }
  198. }
  199. }
  200. }
  201. return iterator
  202. }
  203. }
  204. const vm = new Vue({
  205. template: `
  206. <div>
  207. <span v-for="(item, i) in list">{{i}}-{{item}}</span>
  208. </div>
  209. `,
  210. data: {
  211. list: iterable
  212. }
  213. }).$mount()
  214. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  215. Vue.set(vm.list.models, 0, 'd')
  216. waitForUpdate(() => {
  217. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
  218. vm.list.models.push('d')
  219. }).then(() => {
  220. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span><span>3-d</span>')
  221. vm.list.models.splice(1, 2)
  222. }).then(() => {
  223. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-d</span>')
  224. vm.list.models = ['x', 'y']
  225. }).then(() => {
  226. expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
  227. }).then(done)
  228. })
  229. it('should render iterable of object values', done => {
  230. const iterable = {
  231. models: [
  232. { value: 'a' },
  233. { value: 'b' },
  234. { value: 'c' }
  235. ],
  236. index: 0,
  237. [Symbol.iterator] () {
  238. const iterator = {
  239. index: 0,
  240. models: this.models,
  241. next () {
  242. if (this.index < this.models.length) {
  243. return { value: this.models[this.index++] }
  244. } else {
  245. return { done: true }
  246. }
  247. }
  248. }
  249. return iterator
  250. }
  251. }
  252. const vm = new Vue({
  253. template: `
  254. <div>
  255. <span v-for="item in list">{{item.value}}</span>
  256. </div>
  257. `,
  258. data: {
  259. list: iterable
  260. }
  261. }).$mount()
  262. expect(vm.$el.innerHTML).toBe('<span>a</span><span>b</span><span>c</span>')
  263. Vue.set(vm.list.models, 0, { value: 'd' })
  264. waitForUpdate(() => {
  265. expect(vm.$el.innerHTML).toBe('<span>d</span><span>b</span><span>c</span>')
  266. vm.list.models[0].value = 'e'
  267. }).then(() => {
  268. expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span>')
  269. vm.list.models.push({})
  270. }).then(() => {
  271. expect(vm.$el.innerHTML).toBe('<span>e</span><span>b</span><span>c</span><span></span>')
  272. vm.list.models.splice(1, 2)
  273. }).then(() => {
  274. expect(vm.$el.innerHTML).toBe('<span>e</span><span></span>')
  275. vm.list.models = [{ value: 'x' }, { value: 'y' }]
  276. }).then(() => {
  277. expect(vm.$el.innerHTML).toBe('<span>x</span><span>y</span>')
  278. }).then(done)
  279. })
  280. it('should render iterable of object values with index', done => {
  281. const iterable = {
  282. models: [
  283. { value: 'a' },
  284. { value: 'b' },
  285. { value: 'c' }
  286. ],
  287. index: 0,
  288. [Symbol.iterator] () {
  289. const iterator = {
  290. index: 0,
  291. models: this.models,
  292. next () {
  293. if (this.index < this.models.length) {
  294. return { value: this.models[this.index++] }
  295. } else {
  296. return { done: true }
  297. }
  298. }
  299. }
  300. return iterator
  301. }
  302. }
  303. const vm = new Vue({
  304. template: `
  305. <div>
  306. <span v-for="(item, i) in list">{{i}}-{{item.value}}</span>
  307. </div>
  308. `,
  309. data: {
  310. list: iterable
  311. }
  312. }).$mount()
  313. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  314. Vue.set(vm.list.models, 0, { value: 'd' })
  315. waitForUpdate(() => {
  316. expect(vm.$el.innerHTML).toBe('<span>0-d</span><span>1-b</span><span>2-c</span>')
  317. vm.list.models[0].value = 'e'
  318. }).then(() => {
  319. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span>')
  320. vm.list.models.push({})
  321. }).then(() => {
  322. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-b</span><span>2-c</span><span>3-</span>')
  323. vm.list.models.splice(1, 2)
  324. }).then(() => {
  325. expect(vm.$el.innerHTML).toBe('<span>0-e</span><span>1-</span>')
  326. vm.list.models = [{ value: 'x' }, { value: 'y' }]
  327. }).then(() => {
  328. expect(vm.$el.innerHTML).toBe('<span>0-x</span><span>1-y</span>')
  329. }).then(done)
  330. })
  331. }
  332. it('should render an Object', done => {
  333. const vm = new Vue({
  334. template: `
  335. <div>
  336. <span v-for="val in obj">{{val}}</span>
  337. </div>
  338. `,
  339. data: {
  340. obj: { a: 0, b: 1, c: 2 }
  341. }
  342. }).$mount()
  343. expect(vm.$el.innerHTML).toBe('<span>0</span><span>1</span><span>2</span>')
  344. vm.obj.a = 3
  345. waitForUpdate(() => {
  346. expect(vm.$el.innerHTML).toBe('<span>3</span><span>1</span><span>2</span>')
  347. Vue.set(vm.obj, 'd', 4)
  348. }).then(() => {
  349. expect(vm.$el.innerHTML).toBe('<span>3</span><span>1</span><span>2</span><span>4</span>')
  350. Vue.delete(vm.obj, 'a')
  351. }).then(() => {
  352. expect(vm.$el.innerHTML).toBe('<span>1</span><span>2</span><span>4</span>')
  353. }).then(done)
  354. })
  355. it('should render an Object with key', done => {
  356. const vm = new Vue({
  357. template: `
  358. <div>
  359. <span v-for="(val, key) in obj">{{val}}-{{key}}</span>
  360. </div>
  361. `,
  362. data: {
  363. obj: { a: 0, b: 1, c: 2 }
  364. }
  365. }).$mount()
  366. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  367. vm.obj.a = 3
  368. waitForUpdate(() => {
  369. expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span>')
  370. Vue.set(vm.obj, 'd', 4)
  371. }).then(() => {
  372. expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span><span>4-d</span>')
  373. Vue.delete(vm.obj, 'a')
  374. }).then(() => {
  375. expect(vm.$el.innerHTML).toBe('<span>1-b</span><span>2-c</span><span>4-d</span>')
  376. }).then(done)
  377. })
  378. it('should render an Object with key and index', done => {
  379. const vm = new Vue({
  380. template: `
  381. <div>
  382. <span v-for="(val, key, i) in obj">{{val}}-{{key}}-{{i}}</span>
  383. </div>
  384. `,
  385. data: {
  386. obj: { a: 0, b: 1, c: 2 }
  387. }
  388. }).$mount()
  389. expect(vm.$el.innerHTML).toBe('<span>0-a-0</span><span>1-b-1</span><span>2-c-2</span>')
  390. vm.obj.a = 3
  391. waitForUpdate(() => {
  392. expect(vm.$el.innerHTML).toBe('<span>3-a-0</span><span>1-b-1</span><span>2-c-2</span>')
  393. Vue.set(vm.obj, 'd', 4)
  394. }).then(() => {
  395. 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>')
  396. Vue.delete(vm.obj, 'a')
  397. }).then(() => {
  398. expect(vm.$el.innerHTML).toBe('<span>1-b-0</span><span>2-c-1</span><span>4-d-2</span>')
  399. }).then(done)
  400. })
  401. it('should render each key of data', done => {
  402. const vm = new Vue({
  403. template: `
  404. <div>
  405. <span v-for="(val, key) in $data">{{val}}-{{key}}</span>
  406. </div>
  407. `,
  408. data: { a: 0, b: 1, c: 2 }
  409. }).$mount()
  410. expect(vm.$el.innerHTML).toBe('<span>0-a</span><span>1-b</span><span>2-c</span>')
  411. vm.a = 3
  412. waitForUpdate(() => {
  413. expect(vm.$el.innerHTML).toBe('<span>3-a</span><span>1-b</span><span>2-c</span>')
  414. }).then(done)
  415. })
  416. it('check priorities: v-if before v-for', function () {
  417. const vm = new Vue({
  418. data: {
  419. items: [1, 2, 3]
  420. },
  421. template: '<div><div v-if="item < 3" v-for="item in items">{{item}}</div></div>'
  422. }).$mount()
  423. expect(vm.$el.textContent).toBe('12')
  424. })
  425. it('check priorities: v-if after v-for', function () {
  426. const vm = new Vue({
  427. data: {
  428. items: [1, 2, 3]
  429. },
  430. template: '<div><div v-for="item in items" v-if="item < 3">{{item}}</div></div>'
  431. }).$mount()
  432. expect(vm.$el.textContent).toBe('12')
  433. })
  434. it('range v-for', () => {
  435. const vm = new Vue({
  436. template: '<div><div v-for="n in 3">{{n}}</div></div>'
  437. }).$mount()
  438. expect(vm.$el.textContent).toBe('123')
  439. })
  440. it('without key', done => {
  441. const vm = new Vue({
  442. data: {
  443. items: [
  444. { id: 1, msg: 'a' },
  445. { id: 2, msg: 'b' },
  446. { id: 3, msg: 'c' }
  447. ]
  448. },
  449. template: '<div><div v-for="item in items">{{ item.msg }}</div></div>'
  450. }).$mount()
  451. expect(vm.$el.textContent).toBe('abc')
  452. const first = vm.$el.children[0]
  453. vm.items.reverse()
  454. waitForUpdate(() => {
  455. expect(vm.$el.textContent).toBe('cba')
  456. // assert reusing DOM element in place
  457. expect(vm.$el.children[0]).toBe(first)
  458. }).then(done)
  459. })
  460. it('with key', done => {
  461. const vm = new Vue({
  462. data: {
  463. items: [
  464. { id: 1, msg: 'a' },
  465. { id: 2, msg: 'b' },
  466. { id: 3, msg: 'c' }
  467. ]
  468. },
  469. template: '<div><div v-for="item in items" :key="item.id">{{ item.msg }}</div></div>'
  470. }).$mount()
  471. expect(vm.$el.textContent).toBe('abc')
  472. const first = vm.$el.children[0]
  473. vm.items.reverse()
  474. waitForUpdate(() => {
  475. expect(vm.$el.textContent).toBe('cba')
  476. // assert moving DOM element
  477. expect(vm.$el.children[0]).not.toBe(first)
  478. expect(vm.$el.children[2]).toBe(first)
  479. }).then(done)
  480. })
  481. it('nested loops', () => {
  482. const vm = new Vue({
  483. data: {
  484. items: [
  485. { items: [{ a: 1 }, { a: 2 }], a: 1 },
  486. { items: [{ a: 3 }, { a: 4 }], a: 2 }
  487. ]
  488. },
  489. template:
  490. '<div>' +
  491. '<div v-for="(item, i) in items">' +
  492. '<p v-for="(subItem, j) in item.items">{{j}} {{subItem.a}} {{i}} {{item.a}}</p>' +
  493. '</div>' +
  494. '</div>'
  495. }).$mount()
  496. expect(vm.$el.innerHTML).toBe(
  497. '<div><p>0 1 0 1</p><p>1 2 0 1</p></div>' +
  498. '<div><p>0 3 1 2</p><p>1 4 1 2</p></div>'
  499. )
  500. })
  501. it('template v-for', done => {
  502. const vm = new Vue({
  503. data: {
  504. list: [
  505. { a: 1 },
  506. { a: 2 },
  507. { a: 3 }
  508. ]
  509. },
  510. template:
  511. '<div>' +
  512. '<template v-for="item in list">' +
  513. '<p>{{item.a}}</p>' +
  514. '<p>{{item.a + 1}}</p>' +
  515. '</template>' +
  516. '</div>'
  517. }).$mount()
  518. assertMarkup()
  519. vm.list.reverse()
  520. waitForUpdate(() => {
  521. assertMarkup()
  522. vm.list.splice(1, 1)
  523. }).then(() => {
  524. assertMarkup()
  525. vm.list.splice(1, 0, { a: 2 })
  526. }).then(done)
  527. function assertMarkup () {
  528. const markup = vm.list.map(function (item) {
  529. return '<p>' + item.a + '</p><p>' + (item.a + 1) + '</p>'
  530. }).join('')
  531. expect(vm.$el.innerHTML).toBe(markup)
  532. }
  533. })
  534. it('component v-for', done => {
  535. const vm = new Vue({
  536. data: {
  537. list: [
  538. { a: 1 },
  539. { a: 2 },
  540. { a: 3 }
  541. ]
  542. },
  543. template:
  544. '<div>' +
  545. '<test v-for="item in list" :msg="item.a" :key="item.a">' +
  546. '<span>{{item.a}}</span>' +
  547. '</test>' +
  548. '</div>',
  549. components: {
  550. test: {
  551. props: ['msg'],
  552. template: '<p>{{msg}}<slot></slot></p>'
  553. }
  554. }
  555. }).$mount()
  556. assertMarkup()
  557. vm.list.reverse()
  558. waitForUpdate(() => {
  559. assertMarkup()
  560. vm.list.splice(1, 1)
  561. }).then(() => {
  562. assertMarkup()
  563. vm.list.splice(1, 0, { a: 2 })
  564. }).then(done)
  565. function assertMarkup () {
  566. const markup = vm.list.map(function (item) {
  567. return `<p>${item.a}<span>${item.a}</span></p>`
  568. }).join('')
  569. expect(vm.$el.innerHTML).toBe(markup)
  570. }
  571. })
  572. it('dynamic component v-for', done => {
  573. const vm = new Vue({
  574. data: {
  575. list: [
  576. { type: 'one' },
  577. { type: 'two' }
  578. ]
  579. },
  580. template:
  581. '<div>' +
  582. '<component v-for="item in list" :key="item.type" :is="item.type"></component>' +
  583. '</div>',
  584. components: {
  585. one: {
  586. template: '<p>One!</p>'
  587. },
  588. two: {
  589. template: '<div>Two!</div>'
  590. }
  591. }
  592. }).$mount()
  593. expect(vm.$el.innerHTML).toContain('<p>One!</p><div>Two!</div>')
  594. vm.list.reverse()
  595. waitForUpdate(() => {
  596. expect(vm.$el.innerHTML).toContain('<div>Two!</div><p>One!</p>')
  597. }).then(done)
  598. })
  599. it('should warn component v-for without keys', () => {
  600. const warn = console.warn
  601. console.warn = jasmine.createSpy()
  602. new Vue({
  603. template: `<div><test v-for="i in 3"></test></div>`,
  604. components: {
  605. test: {
  606. render () {}
  607. }
  608. }
  609. }).$mount()
  610. expect(console.warn.calls.argsFor(0)[0]).toContain(
  611. `<test v-for="i in 3">: component lists rendered with v-for should have explicit keys`
  612. )
  613. console.warn = warn
  614. })
  615. it('multi nested array reactivity', done => {
  616. const vm = new Vue({
  617. data: {
  618. list: [[['foo']]]
  619. },
  620. template: `
  621. <div>
  622. <div v-for="i in list">
  623. <div v-for="j in i">
  624. <div v-for="k in j">
  625. {{ k }}
  626. </div>
  627. </div>
  628. </div>
  629. </div>
  630. `
  631. }).$mount()
  632. expect(vm.$el.textContent).toMatch(/\s+foo\s+/)
  633. vm.list[0][0].push('bar')
  634. waitForUpdate(() => {
  635. expect(vm.$el.textContent).toMatch(/\s+foo\s+bar\s+/)
  636. }).then(done)
  637. })
  638. it('should work with strings', done => {
  639. const vm = new Vue({
  640. data: {
  641. text: 'foo'
  642. },
  643. template: `
  644. <div>
  645. <span v-for="letter in text">{{ letter }}.</span>
  646. </div>
  647. `
  648. }).$mount()
  649. expect(vm.$el.textContent).toMatch('f.o.o.')
  650. vm.text += 'bar'
  651. waitForUpdate(() => {
  652. expect(vm.$el.textContent).toMatch('f.o.o.b.a.r.')
  653. }).then(done)
  654. })
  655. // #7792
  656. it('should work with multiline expressions', () => {
  657. const vm = new Vue({
  658. data: {
  659. a: [1],
  660. b: [2]
  661. },
  662. template: `
  663. <div>
  664. <span v-for="n in (
  665. a.concat(
  666. b
  667. )
  668. )">{{ n }}</span>
  669. </div>
  670. `
  671. }).$mount()
  672. expect(vm.$el.textContent).toBe('12')
  673. })
  674. // #9181
  675. it('components with v-for and empty list', done => {
  676. const vm = new Vue({
  677. template:
  678. '<div attr>' +
  679. '<foo v-for="item in list" :key="item">{{ item }}</foo>' +
  680. '</div>',
  681. data: {
  682. list: undefined
  683. },
  684. components: {
  685. foo: {
  686. template: '<div><slot></slot></div>'
  687. },
  688. }
  689. }).$mount()
  690. expect(vm.$el.innerHTML).toBe('')
  691. vm.list = [1, 2, 3]
  692. waitForUpdate(() => {
  693. expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
  694. }).then(done)
  695. })
  696. it('elements with v-for and empty list', done => {
  697. const vm = new Vue({
  698. template:
  699. '<div attr>' +
  700. '<div v-for="item in list">{{ item }}</div>' +
  701. '</div>',
  702. data: {
  703. list: undefined
  704. }
  705. }).$mount()
  706. expect(vm.$el.innerHTML).toBe('')
  707. vm.list = [1, 2, 3]
  708. waitForUpdate(() => {
  709. expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
  710. }).then(done)
  711. })
  712. const supportsDestructuring = (() => {
  713. try {
  714. new Function('var { foo } = bar')
  715. return true
  716. } catch (e) {}
  717. })()
  718. if (supportsDestructuring) {
  719. it('should support destructuring syntax in alias position (object)', () => {
  720. const vm = new Vue({
  721. data: { list: [{ foo: 'hi', bar: 'ho' }] },
  722. template: '<div><div v-for="({ foo, bar }, i) in list">{{ foo }} {{ bar }} {{ i }}</div></div>'
  723. }).$mount()
  724. expect(vm.$el.textContent).toBe('hi ho 0')
  725. })
  726. it('should support destructuring syntax in alias position (array)', () => {
  727. const vm = new Vue({
  728. data: { list: [[1, 2], [3, 4]] },
  729. template: '<div><div v-for="([ foo, bar ], i) in list">{{ foo }} {{ bar }} {{ i }}</div></div>'
  730. }).$mount()
  731. expect(vm.$el.textContent).toBe('1 2 03 4 1')
  732. })
  733. }
  734. })