transition-group.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. import Vue from 'vue'
  2. import { injectStyles, waitForUpdate, nextFrame } from './helpers'
  3. describe('Transition group', () => {
  4. const { duration, buffer } = injectStyles()
  5. let el
  6. beforeEach(() => {
  7. el = document.createElement('div')
  8. document.body.appendChild(el)
  9. })
  10. function createBasicVM(useIs?, appear = false) {
  11. const vm = new Vue({
  12. template: `
  13. <div>
  14. ${
  15. useIs
  16. ? `<span is="transition-group">`
  17. : `<transition-group${appear ? ` appear` : ``}>`
  18. }
  19. <div v-for="item in items" :key="item" class="test">{{ item }}</div>
  20. ${useIs ? `</span>` : `</transition-group>`}
  21. </div>
  22. `,
  23. data: {
  24. items: ['a', 'b', 'c']
  25. }
  26. }).$mount(el)
  27. if (!appear) {
  28. expect(vm.$el.innerHTML).toBe(
  29. `<span>` +
  30. vm.items.map(i => `<div class="test">${i}</div>`).join('') +
  31. `</span>`
  32. )
  33. }
  34. return vm
  35. }
  36. it('enter', done => {
  37. const vm = createBasicVM()
  38. vm.items.push('d', 'e')
  39. waitForUpdate(() => {
  40. expect(vm.$el.innerHTML).toBe(
  41. `<span>` +
  42. ['a', 'b', 'c'].map(i => `<div class="test">${i}</div>`).join('') +
  43. `<div class="test v-enter v-enter-active">d</div>` +
  44. `<div class="test v-enter v-enter-active">e</div>` +
  45. `</span>`
  46. )
  47. })
  48. .thenWaitFor(nextFrame)
  49. .then(() => {
  50. expect(vm.$el.innerHTML).toBe(
  51. `<span>` +
  52. ['a', 'b', 'c'].map(i => `<div class="test">${i}</div>`).join('') +
  53. `<div class="test v-enter-active v-enter-to">d</div>` +
  54. `<div class="test v-enter-active v-enter-to">e</div>` +
  55. `</span>`
  56. )
  57. })
  58. .thenWaitFor(duration + buffer)
  59. .then(() => {
  60. expect(vm.$el.innerHTML).toBe(
  61. `<span>` +
  62. vm.items.map(i => `<div class="test">${i}</div>`).join('') +
  63. `</span>`
  64. )
  65. })
  66. .then(done)
  67. })
  68. it('leave', done => {
  69. const vm = createBasicVM()
  70. vm.items = ['b']
  71. waitForUpdate(() => {
  72. expect(vm.$el.innerHTML).toBe(
  73. `<span>` +
  74. `<div class="test v-leave v-leave-active">a</div>` +
  75. `<div class="test">b</div>` +
  76. `<div class="test v-leave v-leave-active">c</div>` +
  77. `</span>`
  78. )
  79. })
  80. .thenWaitFor(nextFrame)
  81. .then(() => {
  82. expect(vm.$el.innerHTML).toBe(
  83. `<span>` +
  84. `<div class="test v-leave-active v-leave-to">a</div>` +
  85. `<div class="test">b</div>` +
  86. `<div class="test v-leave-active v-leave-to">c</div>` +
  87. `</span>`
  88. )
  89. })
  90. .thenWaitFor(duration + buffer)
  91. .then(() => {
  92. expect(vm.$el.innerHTML).toBe(
  93. `<span>` +
  94. vm.items.map(i => `<div class="test">${i}</div>`).join('') +
  95. `</span>`
  96. )
  97. })
  98. .then(done)
  99. })
  100. it('enter + leave', done => {
  101. const vm = createBasicVM()
  102. vm.items = ['b', 'c', 'd']
  103. waitForUpdate(() => {
  104. expect(vm.$el.innerHTML).toBe(
  105. `<span>` +
  106. `<div class="test v-leave v-leave-active">a</div>` +
  107. `<div class="test">b</div>` +
  108. `<div class="test">c</div>` +
  109. `<div class="test v-enter v-enter-active">d</div>` +
  110. `</span>`
  111. )
  112. })
  113. .thenWaitFor(nextFrame)
  114. .then(() => {
  115. expect(vm.$el.innerHTML).toBe(
  116. `<span>` +
  117. `<div class="test v-leave-active v-leave-to">a</div>` +
  118. `<div class="test">b</div>` +
  119. `<div class="test">c</div>` +
  120. `<div class="test v-enter-active v-enter-to">d</div>` +
  121. `</span>`
  122. )
  123. })
  124. .thenWaitFor(duration + buffer)
  125. .then(() => {
  126. expect(vm.$el.innerHTML).toBe(
  127. `<span>` +
  128. vm.items.map(i => `<div class="test">${i}</div>`).join('') +
  129. `</span>`
  130. )
  131. })
  132. .then(done)
  133. })
  134. it('use with "is" attribute', done => {
  135. const vm = createBasicVM(true)
  136. vm.items = ['b', 'c', 'd']
  137. waitForUpdate(() => {
  138. expect(vm.$el.innerHTML).toBe(
  139. `<span>` +
  140. `<div class="test v-leave v-leave-active">a</div>` +
  141. `<div class="test">b</div>` +
  142. `<div class="test">c</div>` +
  143. `<div class="test v-enter v-enter-active">d</div>` +
  144. `</span>`
  145. )
  146. })
  147. .thenWaitFor(nextFrame)
  148. .then(() => {
  149. expect(vm.$el.innerHTML).toBe(
  150. `<span>` +
  151. `<div class="test v-leave-active v-leave-to">a</div>` +
  152. `<div class="test">b</div>` +
  153. `<div class="test">c</div>` +
  154. `<div class="test v-enter-active v-enter-to">d</div>` +
  155. `</span>`
  156. )
  157. })
  158. .thenWaitFor(duration + buffer)
  159. .then(() => {
  160. expect(vm.$el.innerHTML).toBe(
  161. `<span>` +
  162. vm.items.map(i => `<div class="test">${i}</div>`).join('') +
  163. `</span>`
  164. )
  165. })
  166. .then(done)
  167. })
  168. it('appear', done => {
  169. const vm = createBasicVM(false, true /* appear */)
  170. waitForUpdate(() => {
  171. expect(vm.$el.innerHTML).toBe(
  172. `<span>` +
  173. vm.items
  174. .map(i => `<div class="test v-enter v-enter-active">${i}</div>`)
  175. .join('') +
  176. `</span>`
  177. )
  178. })
  179. .thenWaitFor(nextFrame)
  180. .then(() => {
  181. expect(vm.$el.innerHTML).toBe(
  182. `<span>` +
  183. vm.items
  184. .map(
  185. i => `<div class="test v-enter-active v-enter-to">${i}</div>`
  186. )
  187. .join('') +
  188. `</span>`
  189. )
  190. })
  191. .thenWaitFor(duration + buffer)
  192. .then(() => {
  193. expect(vm.$el.innerHTML).toBe(
  194. `<span>` +
  195. vm.items.map(i => `<div class="test">${i}</div>`).join('') +
  196. `</span>`
  197. )
  198. })
  199. .then(done)
  200. })
  201. it('events', done => {
  202. let next
  203. const beforeEnterSpy = jasmine.createSpy()
  204. const afterEnterSpy = jasmine.createSpy()
  205. const afterLeaveSpy = jasmine.createSpy()
  206. const vm = new Vue({
  207. template: `
  208. <div>
  209. <transition-group @before-enter="beforeEnter" @after-enter="afterEnter" @after-leave="afterLeave">
  210. <div v-for="item in items" :key="item" class="test">{{ item }}</div>
  211. </transition-group>
  212. </div>
  213. `,
  214. data: {
  215. items: ['a', 'b', 'c']
  216. },
  217. methods: {
  218. beforeEnter(el) {
  219. expect(el.textContent).toBe('d')
  220. beforeEnterSpy()
  221. },
  222. afterEnter(el) {
  223. expect(el.textContent).toBe('d')
  224. afterEnterSpy()
  225. next()
  226. },
  227. afterLeave(el) {
  228. expect(el.textContent).toBe('a')
  229. afterLeaveSpy()
  230. next()
  231. }
  232. }
  233. }).$mount(el)
  234. vm.items.push('d')
  235. waitForUpdate(() => {
  236. expect(vm.$el.innerHTML).toBe(
  237. `<span>` +
  238. `<div class="test">a</div>` +
  239. `<div class="test">b</div>` +
  240. `<div class="test">c</div>` +
  241. `<div class="test v-enter v-enter-active">d</div>` +
  242. `</span>`
  243. )
  244. expect(beforeEnterSpy.calls.count()).toBe(1)
  245. })
  246. .thenWaitFor(_next => {
  247. next = _next
  248. })
  249. .then(() => {
  250. expect(vm.$el.innerHTML).toBe(
  251. `<span>` +
  252. `<div class="test">a</div>` +
  253. `<div class="test">b</div>` +
  254. `<div class="test">c</div>` +
  255. `<div class="test">d</div>` +
  256. `</span>`
  257. )
  258. expect(afterEnterSpy.calls.count()).toBe(1)
  259. vm.items.shift()
  260. })
  261. .thenWaitFor(_next => {
  262. next = _next
  263. })
  264. .then(() => {
  265. expect(vm.$el.innerHTML).toBe(
  266. `<span>` +
  267. `<div class="test">b</div>` +
  268. `<div class="test">c</div>` +
  269. `<div class="test">d</div>` +
  270. `</span>`
  271. )
  272. expect(afterLeaveSpy.calls.count()).toBe(1)
  273. })
  274. .then(done)
  275. })
  276. it('move', done => {
  277. const vm = new Vue({
  278. template: `
  279. <div>
  280. <transition-group name="group">
  281. <div v-for="item in items" :key="item" class="test">{{ item }}</div>
  282. </transition-group>
  283. </div>
  284. `,
  285. data: {
  286. items: ['a', 'b', 'c']
  287. }
  288. }).$mount(el)
  289. vm.items = ['d', 'b', 'a']
  290. waitForUpdate(() => {
  291. expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
  292. `<span>` +
  293. `<div class="test group-enter group-enter-active">d</div>` +
  294. `<div class="test">b</div>` +
  295. `<div class="test group-move">a</div>` +
  296. `<div class="test group-leave group-leave-active group-move">c</div>` +
  297. `</span>`
  298. )
  299. })
  300. .thenWaitFor(nextFrame)
  301. .then(() => {
  302. expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
  303. `<span>` +
  304. `<div class="test group-enter-active group-enter-to">d</div>` +
  305. `<div class="test">b</div>` +
  306. `<div class="test group-move">a</div>` +
  307. `<div class="test group-leave-active group-move group-leave-to">c</div>` +
  308. `</span>`
  309. )
  310. })
  311. .thenWaitFor(duration * 2)
  312. .then(() => {
  313. expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
  314. `<span>` +
  315. `<div class="test">d</div>` +
  316. `<div class="test">b</div>` +
  317. `<div class="test">a</div>` +
  318. `</span>`
  319. )
  320. })
  321. .then(done)
  322. })
  323. it('warn unkeyed children', () => {
  324. new Vue({
  325. template: `<div><transition-group><div v-for="i in 3"></div></transition-group></div>`
  326. }).$mount()
  327. expect(
  328. '<transition-group> children must be keyed: <div>'
  329. ).toHaveBeenWarned()
  330. })
  331. // GitHub issue #6006
  332. it('should work with dynamic name', done => {
  333. const vm = new Vue({
  334. template: `
  335. <div>
  336. <transition-group :name="name">
  337. <div v-for="item in items" :key="item">{{ item }}</div>
  338. </transition-group>
  339. </div>
  340. `,
  341. data: {
  342. items: ['a', 'b', 'c'],
  343. name: 'group'
  344. }
  345. }).$mount(el)
  346. vm.name = 'invalid-name'
  347. vm.items = ['b', 'c', 'a']
  348. waitForUpdate(() => {
  349. expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
  350. `<span>` + `<div>b</div>` + `<div>c</div>` + `<div>a</div>` + `</span>`
  351. )
  352. vm.name = 'group'
  353. vm.items = ['a', 'b', 'c']
  354. })
  355. .thenWaitFor(nextFrame)
  356. .then(() => {
  357. expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
  358. `<span>` +
  359. `<div class="group-move">a</div>` +
  360. `<div class="group-move">b</div>` +
  361. `<div class="group-move">c</div>` +
  362. `</span>`
  363. )
  364. })
  365. .thenWaitFor(duration * 2 + buffer)
  366. .then(() => {
  367. expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
  368. `<span>` +
  369. `<div>a</div>` +
  370. `<div>b</div>` +
  371. `<div>c</div>` +
  372. `</span>`
  373. )
  374. })
  375. .then(done)
  376. })
  377. })