transition.spec.ts 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843
  1. import Vue from 'vue'
  2. import injectStyles from './inject-styles'
  3. import { nextFrame } from 'web/runtime/transition-util'
  4. describe.skip('Transition basic', () => {
  5. const { duration, buffer } = injectStyles() as {
  6. duration: number
  7. buffer: number
  8. }
  9. const explicitDuration = duration * 2
  10. let el
  11. beforeEach(() => {
  12. el = document.createElement('div')
  13. document.body.appendChild(el)
  14. })
  15. it('basic transition', (done) => {
  16. const vm = new Vue({
  17. template:
  18. '<div><transition><div v-if="ok" class="test">foo</div></transition></div>',
  19. data: { ok: true }
  20. }).$mount(el)
  21. // should not apply transition on initial render by default
  22. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  23. vm.ok = false
  24. waitForUpdate(() => {
  25. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  26. })
  27. .thenWaitFor(nextFrame)
  28. .then(() => {
  29. expect(vm.$el.children[0].className).toBe(
  30. 'test v-leave-active v-leave-to'
  31. )
  32. })
  33. .thenWaitFor(duration + buffer)
  34. .then(() => {
  35. expect(vm.$el.children.length).toBe(0)
  36. vm.ok = true
  37. })
  38. .then(() => {
  39. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  40. })
  41. .thenWaitFor(nextFrame)
  42. .then(() => {
  43. expect(vm.$el.children[0].className).toBe(
  44. 'test v-enter-active v-enter-to'
  45. )
  46. })
  47. .thenWaitFor(duration + buffer)
  48. .then(() => {
  49. expect(vm.$el.children[0].className).toBe('test')
  50. })
  51. .then(done)
  52. })
  53. it('named transition', (done) => {
  54. const vm = new Vue({
  55. template:
  56. '<div><transition name="test"><div v-if="ok" class="test">foo</div></transition></div>',
  57. data: { ok: true }
  58. }).$mount(el)
  59. // should not apply transition on initial render by default
  60. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  61. vm.ok = false
  62. waitForUpdate(() => {
  63. expect(vm.$el.children[0].className).toBe(
  64. 'test test-leave test-leave-active'
  65. )
  66. })
  67. .thenWaitFor(nextFrame)
  68. .then(() => {
  69. expect(vm.$el.children[0].className).toBe(
  70. 'test test-leave-active test-leave-to'
  71. )
  72. })
  73. .thenWaitFor(duration + buffer)
  74. .then(() => {
  75. expect(vm.$el.children.length).toBe(0)
  76. vm.ok = true
  77. })
  78. .then(() => {
  79. expect(vm.$el.children[0].className).toBe(
  80. 'test test-enter test-enter-active'
  81. )
  82. })
  83. .thenWaitFor(nextFrame)
  84. .then(() => {
  85. expect(vm.$el.children[0].className).toBe(
  86. 'test test-enter-active test-enter-to'
  87. )
  88. })
  89. .thenWaitFor(duration + buffer)
  90. .then(() => {
  91. expect(vm.$el.children[0].className).toBe('test')
  92. })
  93. .then(done)
  94. })
  95. it('custom transition classes', (done) => {
  96. const vm = new Vue({
  97. template: `
  98. <div>
  99. <transition
  100. enter-class="hello"
  101. enter-active-class="hello-active"
  102. enter-to-class="hello-to"
  103. leave-class="bye"
  104. leave-to-class="bye-to"
  105. leave-active-class="byebye active more ">
  106. <div v-if="ok" class="test">foo</div>
  107. </transition>
  108. </div>
  109. `,
  110. data: { ok: true }
  111. }).$mount(el)
  112. // should not apply transition on initial render by default
  113. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  114. vm.ok = false
  115. waitForUpdate(() => {
  116. expect(vm.$el.children[0].className).toBe('test bye byebye active more')
  117. })
  118. .thenWaitFor(nextFrame)
  119. .then(() => {
  120. expect(vm.$el.children[0].className).toBe(
  121. 'test byebye active more bye-to'
  122. )
  123. })
  124. .thenWaitFor(duration + buffer)
  125. .then(() => {
  126. expect(vm.$el.children.length).toBe(0)
  127. vm.ok = true
  128. })
  129. .then(() => {
  130. expect(vm.$el.children[0].className).toBe('test hello hello-active')
  131. })
  132. .thenWaitFor(nextFrame)
  133. .then(() => {
  134. expect(vm.$el.children[0].className).toBe('test hello-active hello-to')
  135. })
  136. .thenWaitFor(duration + buffer)
  137. .then(() => {
  138. expect(vm.$el.children[0].className).toBe('test')
  139. })
  140. .then(done)
  141. })
  142. it('dynamic transition', (done) => {
  143. const vm = new Vue({
  144. template: `
  145. <div>
  146. <transition :name="trans">
  147. <div v-if="ok" class="test">foo</div>
  148. </transition>
  149. </div>
  150. `,
  151. data: {
  152. ok: true,
  153. trans: 'test'
  154. }
  155. }).$mount(el)
  156. // should not apply transition on initial render by default
  157. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  158. vm.ok = false
  159. waitForUpdate(() => {
  160. expect(vm.$el.children[0].className).toBe(
  161. 'test test-leave test-leave-active'
  162. )
  163. })
  164. .thenWaitFor(nextFrame)
  165. .then(() => {
  166. expect(vm.$el.children[0].className).toBe(
  167. 'test test-leave-active test-leave-to'
  168. )
  169. })
  170. .thenWaitFor(duration + buffer)
  171. .then(() => {
  172. expect(vm.$el.children.length).toBe(0)
  173. vm.ok = true
  174. vm.trans = 'changed'
  175. })
  176. .then(() => {
  177. expect(vm.$el.children[0].className).toBe(
  178. 'test changed-enter changed-enter-active'
  179. )
  180. })
  181. .thenWaitFor(nextFrame)
  182. .then(() => {
  183. expect(vm.$el.children[0].className).toBe(
  184. 'test changed-enter-active changed-enter-to'
  185. )
  186. })
  187. .thenWaitFor(duration + buffer)
  188. .then(() => {
  189. expect(vm.$el.children[0].className).toBe('test')
  190. })
  191. .then(done)
  192. })
  193. it('inline transition object', (done) => {
  194. const enter = vi.fn()
  195. const leave = vi.fn()
  196. const vm = new Vue({
  197. render(h) {
  198. return h('div', null, [
  199. h(
  200. 'transition',
  201. {
  202. props: {
  203. name: 'inline',
  204. enterClass: 'hello',
  205. enterToClass: 'hello-to',
  206. enterActiveClass: 'hello-active',
  207. leaveClass: 'bye',
  208. leaveToClass: 'bye-to',
  209. leaveActiveClass: 'byebye active'
  210. },
  211. on: {
  212. enter,
  213. leave
  214. }
  215. },
  216. this.ok ? [h('div', { class: 'test' }, 'foo')] : undefined
  217. )
  218. ])
  219. },
  220. data: { ok: true }
  221. }).$mount(el)
  222. // should not apply transition on initial render by default
  223. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  224. vm.ok = false
  225. waitForUpdate(() => {
  226. expect(vm.$el.children[0].className).toBe('test bye byebye active')
  227. expect(leave).toHaveBeenCalled()
  228. })
  229. .thenWaitFor(nextFrame)
  230. .then(() => {
  231. expect(vm.$el.children[0].className).toBe('test byebye active bye-to')
  232. })
  233. .thenWaitFor(duration + buffer)
  234. .then(() => {
  235. expect(vm.$el.children.length).toBe(0)
  236. vm.ok = true
  237. })
  238. .then(() => {
  239. expect(vm.$el.children[0].className).toBe('test hello hello-active')
  240. expect(enter).toHaveBeenCalled()
  241. })
  242. .thenWaitFor(nextFrame)
  243. .then(() => {
  244. expect(vm.$el.children[0].className).toBe('test hello-active hello-to')
  245. })
  246. .thenWaitFor(duration + buffer)
  247. .then(() => {
  248. expect(vm.$el.children[0].className).toBe('test')
  249. })
  250. .then(done)
  251. })
  252. it('transition events', (done) => {
  253. const onLeaveSpy = vi.fn()
  254. const onEnterSpy = vi.fn()
  255. const beforeLeaveSpy = vi.fn()
  256. const beforeEnterSpy = vi.fn()
  257. const afterLeaveSpy = vi.fn()
  258. const afterEnterSpy = vi.fn()
  259. const vm = new Vue({
  260. template: `
  261. <div>
  262. <transition
  263. name="test"
  264. @before-enter="beforeEnter"
  265. @enter="enter"
  266. @after-enter="afterEnter"
  267. @before-leave="beforeLeave"
  268. @leave="leave"
  269. @after-leave="afterLeave">
  270. <div v-if="ok" class="test">foo</div>
  271. </transition>
  272. </div>
  273. `,
  274. data: { ok: true },
  275. methods: {
  276. beforeLeave: (el) => {
  277. expect(el).toBe(vm.$el.children[0])
  278. expect(el.className).toBe('test')
  279. beforeLeaveSpy(el)
  280. },
  281. leave: (el) => onLeaveSpy(el),
  282. afterLeave: (el) => afterLeaveSpy(el),
  283. beforeEnter: (el) => {
  284. expect(vm.$el.contains(el)).toBe(false)
  285. expect(el.className).toBe('test')
  286. beforeEnterSpy(el)
  287. },
  288. enter: (el) => {
  289. expect(vm.$el.contains(el)).toBe(true)
  290. onEnterSpy(el)
  291. },
  292. afterEnter: (el) => afterEnterSpy(el)
  293. }
  294. }).$mount(el)
  295. // should not apply transition on initial render by default
  296. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  297. let _el = vm.$el.children[0]
  298. vm.ok = false
  299. waitForUpdate(() => {
  300. expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
  301. expect(onLeaveSpy).toHaveBeenCalledWith(_el)
  302. expect(vm.$el.children[0].className).toBe(
  303. 'test test-leave test-leave-active'
  304. )
  305. })
  306. .thenWaitFor(nextFrame)
  307. .then(() => {
  308. expect(afterLeaveSpy).not.toHaveBeenCalled()
  309. expect(vm.$el.children[0].className).toBe(
  310. 'test test-leave-active test-leave-to'
  311. )
  312. })
  313. .thenWaitFor(duration + buffer)
  314. .then(() => {
  315. expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
  316. expect(vm.$el.children.length).toBe(0)
  317. vm.ok = true
  318. })
  319. .then(() => {
  320. _el = vm.$el.children[0]
  321. expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
  322. expect(onEnterSpy).toHaveBeenCalledWith(_el)
  323. expect(vm.$el.children[0].className).toBe(
  324. 'test test-enter test-enter-active'
  325. )
  326. })
  327. .thenWaitFor(nextFrame)
  328. .then(() => {
  329. expect(afterEnterSpy).not.toHaveBeenCalled()
  330. expect(vm.$el.children[0].className).toBe(
  331. 'test test-enter-active test-enter-to'
  332. )
  333. })
  334. .thenWaitFor(duration + buffer)
  335. .then(() => {
  336. expect(afterEnterSpy).toHaveBeenCalledWith(_el)
  337. expect(vm.$el.children[0].className).toBe('test')
  338. })
  339. .then(done)
  340. })
  341. it('transition events (v-show)', (done) => {
  342. const onLeaveSpy = vi.fn()
  343. const onEnterSpy = vi.fn()
  344. const beforeLeaveSpy = vi.fn()
  345. const beforeEnterSpy = vi.fn()
  346. const afterLeaveSpy = vi.fn()
  347. const afterEnterSpy = vi.fn()
  348. const vm = new Vue({
  349. template: `
  350. <div>
  351. <transition
  352. name="test"
  353. @before-enter="beforeEnter"
  354. @enter="enter"
  355. @after-enter="afterEnter"
  356. @before-leave="beforeLeave"
  357. @leave="leave"
  358. @after-leave="afterLeave">
  359. <div v-show="ok" class="test">foo</div>
  360. </transition>
  361. </div>
  362. `,
  363. data: { ok: true },
  364. methods: {
  365. beforeLeave: (el) => {
  366. expect(el.style.display).toBe('')
  367. expect(el).toBe(vm.$el.children[0])
  368. expect(el.className).toBe('test')
  369. beforeLeaveSpy(el)
  370. },
  371. leave: (el) => {
  372. expect(el.style.display).toBe('')
  373. onLeaveSpy(el)
  374. },
  375. afterLeave: (el) => {
  376. expect(el.style.display).toBe('none')
  377. afterLeaveSpy(el)
  378. },
  379. beforeEnter: (el) => {
  380. expect(el.className).toBe('test')
  381. expect(el.style.display).toBe('none')
  382. beforeEnterSpy(el)
  383. },
  384. enter: (el) => {
  385. expect(el.style.display).toBe('')
  386. onEnterSpy(el)
  387. },
  388. afterEnter: (el) => {
  389. expect(el.style.display).toBe('')
  390. afterEnterSpy(el)
  391. }
  392. }
  393. }).$mount(el)
  394. // should not apply transition on initial render by default
  395. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  396. let _el = vm.$el.children[0]
  397. vm.ok = false
  398. waitForUpdate(() => {
  399. expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
  400. expect(onLeaveSpy).toHaveBeenCalledWith(_el)
  401. expect(vm.$el.children[0].className).toBe(
  402. 'test test-leave test-leave-active'
  403. )
  404. })
  405. .thenWaitFor(nextFrame)
  406. .then(() => {
  407. expect(afterLeaveSpy).not.toHaveBeenCalled()
  408. expect(vm.$el.children[0].className).toBe(
  409. 'test test-leave-active test-leave-to'
  410. )
  411. })
  412. .thenWaitFor(duration + buffer)
  413. .then(() => {
  414. expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
  415. expect(vm.$el.children[0].style.display).toBe('none')
  416. vm.ok = true
  417. })
  418. .then(() => {
  419. _el = vm.$el.children[0]
  420. expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
  421. expect(onEnterSpy).toHaveBeenCalledWith(_el)
  422. expect(vm.$el.children[0].className).toBe(
  423. 'test test-enter test-enter-active'
  424. )
  425. })
  426. .thenWaitFor(nextFrame)
  427. .then(() => {
  428. expect(afterEnterSpy).not.toHaveBeenCalled()
  429. expect(vm.$el.children[0].className).toBe(
  430. 'test test-enter-active test-enter-to'
  431. )
  432. })
  433. .thenWaitFor(duration + buffer)
  434. .then(() => {
  435. expect(afterEnterSpy).toHaveBeenCalledWith(_el)
  436. expect(vm.$el.children[0].className).toBe('test')
  437. })
  438. .then(done)
  439. })
  440. it('explicit user callback in JavaScript hooks', (done) => {
  441. let next
  442. const vm = new Vue({
  443. template: `<div>
  444. <transition name="test" @enter="enter" @leave="leave">
  445. <div v-if="ok" class="test">foo</div>
  446. </transition>
  447. </div>`,
  448. data: { ok: true },
  449. methods: {
  450. enter: (el, cb) => {
  451. next = cb
  452. },
  453. leave: (el, cb) => {
  454. next = cb
  455. }
  456. }
  457. }).$mount(el)
  458. vm.ok = false
  459. waitForUpdate(() => {
  460. expect(vm.$el.children[0].className).toBe(
  461. 'test test-leave test-leave-active'
  462. )
  463. })
  464. .thenWaitFor(nextFrame)
  465. .then(() => {
  466. expect(vm.$el.children[0].className).toBe(
  467. 'test test-leave-active test-leave-to'
  468. )
  469. })
  470. .thenWaitFor(duration + buffer)
  471. .then(() => {
  472. expect(vm.$el.children[0].className).toBe(
  473. 'test test-leave-active test-leave-to'
  474. )
  475. expect(next).toBeTruthy()
  476. next()
  477. expect(vm.$el.children.length).toBe(0)
  478. })
  479. .then(() => {
  480. vm.ok = true
  481. })
  482. .then(() => {
  483. expect(vm.$el.children[0].className).toBe(
  484. 'test test-enter test-enter-active'
  485. )
  486. })
  487. .thenWaitFor(nextFrame)
  488. .then(() => {
  489. expect(vm.$el.children[0].className).toBe(
  490. 'test test-enter-active test-enter-to'
  491. )
  492. })
  493. .thenWaitFor(duration + buffer)
  494. .then(() => {
  495. expect(vm.$el.children[0].className).toBe(
  496. 'test test-enter-active test-enter-to'
  497. )
  498. expect(next).toBeTruthy()
  499. next()
  500. expect(vm.$el.children[0].className).toBe('test')
  501. })
  502. .then(done)
  503. })
  504. it('css: false', (done) => {
  505. const enterSpy = vi.fn()
  506. const leaveSpy = vi.fn()
  507. const vm = new Vue({
  508. template: `
  509. <div>
  510. <transition :css="false" name="test" @enter="enter" @leave="leave">
  511. <div v-if="ok" class="test">foo</div>
  512. </transition>
  513. </div>
  514. `,
  515. data: { ok: true },
  516. methods: {
  517. enter: enterSpy,
  518. leave: leaveSpy
  519. }
  520. }).$mount(el)
  521. vm.ok = false
  522. waitForUpdate(() => {
  523. expect(leaveSpy).toHaveBeenCalled()
  524. expect(vm.$el.innerHTML).toBe('<!---->')
  525. vm.ok = true
  526. })
  527. .then(() => {
  528. expect(enterSpy).toHaveBeenCalled()
  529. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  530. })
  531. .then(done)
  532. })
  533. it('no transition detected', (done) => {
  534. const enterSpy = vi.fn()
  535. const leaveSpy = vi.fn()
  536. const vm = new Vue({
  537. template:
  538. '<div><transition name="nope" @enter="enter" @leave="leave"><div v-if="ok">foo</div></transition></div>',
  539. data: { ok: true },
  540. methods: {
  541. enter: enterSpy,
  542. leave: leaveSpy
  543. }
  544. }).$mount(el)
  545. vm.ok = false
  546. waitForUpdate(() => {
  547. expect(leaveSpy).toHaveBeenCalled()
  548. expect(vm.$el.innerHTML).toBe(
  549. '<div class="nope-leave nope-leave-active">foo</div><!---->'
  550. )
  551. })
  552. .thenWaitFor(nextFrame)
  553. .then(() => {
  554. expect(vm.$el.innerHTML).toBe('<!---->')
  555. vm.ok = true
  556. })
  557. .then(() => {
  558. expect(enterSpy).toHaveBeenCalled()
  559. expect(vm.$el.innerHTML).toBe(
  560. '<div class="nope-enter nope-enter-active">foo</div>'
  561. )
  562. })
  563. .thenWaitFor(nextFrame)
  564. .then(() => {
  565. expect(vm.$el.innerHTML).toBe('<div>foo</div>')
  566. })
  567. .then(done)
  568. })
  569. it('enterCancelled', (done) => {
  570. const spy = vi.fn()
  571. const vm = new Vue({
  572. template: `
  573. <div>
  574. <transition name="test" @enter-cancelled="enterCancelled">
  575. <div v-if="ok" class="test">foo</div>
  576. </transition>
  577. </div>
  578. `,
  579. data: { ok: false },
  580. methods: {
  581. enterCancelled: spy
  582. }
  583. }).$mount(el)
  584. expect(vm.$el.innerHTML).toBe('<!---->')
  585. vm.ok = true
  586. waitForUpdate(() => {
  587. expect(vm.$el.children[0].className).toBe(
  588. 'test test-enter test-enter-active'
  589. )
  590. })
  591. .thenWaitFor(nextFrame)
  592. .then(() => {
  593. expect(vm.$el.children[0].className).toBe(
  594. 'test test-enter-active test-enter-to'
  595. )
  596. })
  597. .thenWaitFor(duration / 2)
  598. .then(() => {
  599. vm.ok = false
  600. })
  601. .then(() => {
  602. expect(spy).toHaveBeenCalled()
  603. expect(vm.$el.children[0].className).toBe(
  604. 'test test-leave test-leave-active'
  605. )
  606. })
  607. .thenWaitFor(nextFrame)
  608. .then(() => {
  609. expect(vm.$el.children[0].className).toBe(
  610. 'test test-leave-active test-leave-to'
  611. )
  612. })
  613. .thenWaitFor(duration + buffer)
  614. .then(() => {
  615. expect(vm.$el.children.length).toBe(0)
  616. })
  617. .then(done)
  618. })
  619. it('should remove stale leaving elements', (done) => {
  620. const spy = vi.fn()
  621. const vm = new Vue({
  622. template: `
  623. <div>
  624. <transition name="test" @after-leave="afterLeave">
  625. <div v-if="ok" class="test">foo</div>
  626. </transition>
  627. </div>
  628. `,
  629. data: { ok: true },
  630. methods: {
  631. afterLeave: spy
  632. }
  633. }).$mount(el)
  634. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  635. vm.ok = false
  636. waitForUpdate(() => {
  637. expect(vm.$el.children[0].className).toBe(
  638. 'test test-leave test-leave-active'
  639. )
  640. })
  641. .thenWaitFor(duration / 2)
  642. .then(() => {
  643. vm.ok = true
  644. })
  645. .then(() => {
  646. expect(spy).toHaveBeenCalled()
  647. expect(vm.$el.children.length).toBe(1) // should have removed leaving element
  648. expect(vm.$el.children[0].className).toBe(
  649. 'test test-enter test-enter-active'
  650. )
  651. })
  652. .thenWaitFor(nextFrame)
  653. .then(() => {
  654. expect(vm.$el.children[0].className).toBe(
  655. 'test test-enter-active test-enter-to'
  656. )
  657. })
  658. .thenWaitFor(duration + buffer)
  659. .then(() => {
  660. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  661. })
  662. .then(done)
  663. })
  664. it('transition with v-show', (done) => {
  665. const vm = new Vue({
  666. template: `
  667. <div>
  668. <transition name="test">
  669. <div v-show="ok" class="test">foo</div>
  670. </transition>
  671. </div>
  672. `,
  673. data: { ok: true }
  674. }).$mount(el)
  675. // should not apply transition on initial render by default
  676. expect(vm.$el.textContent).toBe('foo')
  677. expect(vm.$el.children[0].style.display).toBe('')
  678. expect(vm.$el.children[0].className).toBe('test')
  679. vm.ok = false
  680. waitForUpdate(() => {
  681. expect(vm.$el.children[0].className).toBe(
  682. 'test test-leave test-leave-active'
  683. )
  684. })
  685. .thenWaitFor(nextFrame)
  686. .then(() => {
  687. expect(vm.$el.children[0].className).toBe(
  688. 'test test-leave-active test-leave-to'
  689. )
  690. })
  691. .thenWaitFor(duration + buffer)
  692. .then(() => {
  693. expect(vm.$el.children[0].style.display).toBe('none')
  694. vm.ok = true
  695. })
  696. .then(() => {
  697. expect(vm.$el.children[0].style.display).toBe('')
  698. expect(vm.$el.children[0].className).toBe(
  699. 'test test-enter test-enter-active'
  700. )
  701. })
  702. .thenWaitFor(nextFrame)
  703. .then(() => {
  704. expect(vm.$el.children[0].className).toBe(
  705. 'test test-enter-active test-enter-to'
  706. )
  707. })
  708. .thenWaitFor(duration + buffer)
  709. .then(() => {
  710. expect(vm.$el.children[0].className).toBe('test')
  711. })
  712. .then(done)
  713. })
  714. it('transition with v-show, inside child component', (done) => {
  715. const vm = new Vue({
  716. template: `
  717. <div>
  718. <test v-show="ok"></test>
  719. </div>
  720. `,
  721. data: { ok: true },
  722. components: {
  723. test: {
  724. template: `<transition name="test"><div class="test">foo</div></transition>`
  725. }
  726. }
  727. }).$mount(el)
  728. // should not apply transition on initial render by default
  729. expect(vm.$el.textContent).toBe('foo')
  730. expect(vm.$el.children[0].style.display).toBe('')
  731. vm.ok = false
  732. waitForUpdate(() => {
  733. expect(vm.$el.children[0].className).toBe(
  734. 'test test-leave test-leave-active'
  735. )
  736. })
  737. .thenWaitFor(nextFrame)
  738. .then(() => {
  739. expect(vm.$el.children[0].className).toBe(
  740. 'test test-leave-active test-leave-to'
  741. )
  742. })
  743. .thenWaitFor(duration + buffer)
  744. .then(() => {
  745. expect(vm.$el.children[0].style.display).toBe('none')
  746. vm.ok = true
  747. })
  748. .then(() => {
  749. expect(vm.$el.children[0].style.display).toBe('')
  750. expect(vm.$el.children[0].className).toBe(
  751. 'test test-enter test-enter-active'
  752. )
  753. })
  754. .thenWaitFor(nextFrame)
  755. .then(() => {
  756. expect(vm.$el.children[0].className).toBe(
  757. 'test test-enter-active test-enter-to'
  758. )
  759. })
  760. .thenWaitFor(duration + buffer)
  761. .then(() => {
  762. expect(vm.$el.children[0].className).toBe('test')
  763. })
  764. .then(done)
  765. })
  766. it('leaveCancelled (v-show only)', (done) => {
  767. const spy = vi.fn()
  768. const vm = new Vue({
  769. template: `
  770. <div>
  771. <transition name="test" @leave-cancelled="leaveCancelled">
  772. <div v-show="ok" class="test">foo</div>
  773. </transition>
  774. </div>
  775. `,
  776. data: { ok: true },
  777. methods: {
  778. leaveCancelled: spy
  779. }
  780. }).$mount(el)
  781. expect(vm.$el.children[0].style.display).toBe('')
  782. vm.ok = false
  783. waitForUpdate(() => {
  784. expect(vm.$el.children[0].className).toBe(
  785. 'test test-leave test-leave-active'
  786. )
  787. })
  788. .thenWaitFor(nextFrame)
  789. .then(() => {
  790. expect(vm.$el.children[0].className).toBe(
  791. 'test test-leave-active test-leave-to'
  792. )
  793. })
  794. .thenWaitFor(10)
  795. .then(() => {
  796. vm.ok = true
  797. })
  798. .then(() => {
  799. expect(spy).toHaveBeenCalled()
  800. expect(vm.$el.children[0].className).toBe(
  801. 'test test-enter test-enter-active'
  802. )
  803. })
  804. .thenWaitFor(nextFrame)
  805. .then(() => {
  806. expect(vm.$el.children[0].className).toBe(
  807. 'test test-enter-active test-enter-to'
  808. )
  809. })
  810. .thenWaitFor(duration + buffer)
  811. .then(() => {
  812. expect(vm.$el.children[0].style.display).toBe('')
  813. })
  814. .then(done)
  815. })
  816. it('leave transition with v-show: cancelled on next frame', (done) => {
  817. const vm = new Vue({
  818. template: `
  819. <div>
  820. <transition name="test">
  821. <div v-show="ok" class="test">foo</div>
  822. </transition>
  823. </div>
  824. `,
  825. data: { ok: true }
  826. }).$mount(el)
  827. vm.ok = false
  828. waitForUpdate(() => {
  829. vm.ok = true
  830. })
  831. .thenWaitFor(nextFrame)
  832. .then(() => {
  833. expect(vm.$el.children[0].className).toBe(
  834. 'test test-enter-active test-enter-to'
  835. )
  836. })
  837. .thenWaitFor(duration + buffer)
  838. .then(() => {
  839. expect(vm.$el.children[0].className).toBe('test')
  840. })
  841. .then(done)
  842. })
  843. it('enter transition with v-show: cancelled on next frame', (done) => {
  844. const vm = new Vue({
  845. template: `
  846. <div>
  847. <transition name="test">
  848. <div v-show="ok" class="test">foo</div>
  849. </transition>
  850. </div>
  851. `,
  852. data: { ok: false }
  853. }).$mount(el)
  854. vm.ok = true
  855. waitForUpdate(() => {
  856. vm.ok = false
  857. })
  858. .thenWaitFor(nextFrame)
  859. .then(() => {
  860. expect(vm.$el.children[0].className).toBe(
  861. 'test test-leave-active test-leave-to'
  862. )
  863. })
  864. .thenWaitFor(duration + buffer)
  865. .then(() => {
  866. expect(vm.$el.children[0].className).toBe('test')
  867. })
  868. .then(done)
  869. })
  870. it('animations', (done) => {
  871. const vm = new Vue({
  872. template: `
  873. <div>
  874. <transition name="test-anim">
  875. <div v-if="ok">foo</div>
  876. </transition>
  877. </div>
  878. `,
  879. data: { ok: true }
  880. }).$mount(el)
  881. // should not apply transition on initial render by default
  882. expect(vm.$el.innerHTML).toBe('<div>foo</div>')
  883. vm.ok = false
  884. waitForUpdate(() => {
  885. expect(vm.$el.children[0].className).toBe(
  886. 'test-anim-leave test-anim-leave-active'
  887. )
  888. })
  889. .thenWaitFor(nextFrame)
  890. .then(() => {
  891. expect(vm.$el.children[0].className).toBe(
  892. 'test-anim-leave-active test-anim-leave-to'
  893. )
  894. })
  895. .thenWaitFor(duration + buffer)
  896. .then(() => {
  897. expect(vm.$el.children.length).toBe(0)
  898. vm.ok = true
  899. })
  900. .then(() => {
  901. expect(vm.$el.children[0].className).toBe(
  902. 'test-anim-enter test-anim-enter-active'
  903. )
  904. })
  905. .thenWaitFor(nextFrame)
  906. .then(() => {
  907. expect(vm.$el.children[0].className).toBe(
  908. 'test-anim-enter-active test-anim-enter-to'
  909. )
  910. })
  911. .thenWaitFor(duration + buffer)
  912. .then(() => {
  913. expect(vm.$el.children[0].className).toBe('')
  914. })
  915. .then(done)
  916. })
  917. it('explicit transition type', (done) => {
  918. const vm = new Vue({
  919. template: `
  920. <div>
  921. <transition name="test-anim-long" type="animation">
  922. <div v-if="ok" class="test">foo</div>
  923. </transition>
  924. </div>
  925. `,
  926. data: { ok: true }
  927. }).$mount(el)
  928. // should not apply transition on initial render by default
  929. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  930. vm.ok = false
  931. waitForUpdate(() => {
  932. expect(vm.$el.children[0].className).toBe(
  933. 'test test-anim-long-leave test-anim-long-leave-active'
  934. )
  935. })
  936. .thenWaitFor(nextFrame)
  937. .then(() => {
  938. expect(vm.$el.children[0].className).toBe(
  939. 'test test-anim-long-leave-active test-anim-long-leave-to'
  940. )
  941. })
  942. .thenWaitFor(duration + 5)
  943. .then(() => {
  944. // should not end early due to transition presence
  945. expect(vm.$el.children[0].className).toBe(
  946. 'test test-anim-long-leave-active test-anim-long-leave-to'
  947. )
  948. })
  949. .thenWaitFor(duration + 5)
  950. .then(() => {
  951. expect(vm.$el.children.length).toBe(0)
  952. vm.ok = true
  953. })
  954. .then(() => {
  955. expect(vm.$el.children[0].className).toBe(
  956. 'test test-anim-long-enter test-anim-long-enter-active'
  957. )
  958. })
  959. .thenWaitFor(nextFrame)
  960. .then(() => {
  961. expect(vm.$el.children[0].className).toBe(
  962. 'test test-anim-long-enter-active test-anim-long-enter-to'
  963. )
  964. })
  965. .thenWaitFor(duration + 5)
  966. .then(() => {
  967. expect(vm.$el.children[0].className).toBe(
  968. 'test test-anim-long-enter-active test-anim-long-enter-to'
  969. )
  970. })
  971. .thenWaitFor(duration + 5)
  972. .then(() => {
  973. expect(vm.$el.children[0].className).toBe('test')
  974. })
  975. .then(done)
  976. })
  977. it('transition on appear', (done) => {
  978. const vm = new Vue({
  979. template: `
  980. <div>
  981. <transition name="test"
  982. appear
  983. appear-class="test-appear"
  984. appear-to-class="test-appear-to"
  985. appear-active-class="test-appear-active">
  986. <div v-if="ok" class="test">foo</div>
  987. </transition>
  988. </div>
  989. `,
  990. data: { ok: true }
  991. }).$mount(el)
  992. waitForUpdate(() => {
  993. expect(vm.$el.children[0].className).toBe(
  994. 'test test-appear test-appear-active'
  995. )
  996. })
  997. .thenWaitFor(nextFrame)
  998. .then(() => {
  999. expect(vm.$el.children[0].className).toBe(
  1000. 'test test-appear-active test-appear-to'
  1001. )
  1002. })
  1003. .thenWaitFor(duration + buffer)
  1004. .then(() => {
  1005. expect(vm.$el.children[0].className).toBe('test')
  1006. })
  1007. .then(done)
  1008. })
  1009. it('transition on appear with v-show', (done) => {
  1010. const vm = new Vue({
  1011. template: `
  1012. <div>
  1013. <transition name="test" appear>
  1014. <div v-show="ok" class="test">foo</div>
  1015. </transition>
  1016. </div>
  1017. `,
  1018. data: { ok: true }
  1019. }).$mount(el)
  1020. waitForUpdate(() => {
  1021. expect(vm.$el.children[0].className).toBe(
  1022. 'test test-enter test-enter-active'
  1023. )
  1024. })
  1025. .thenWaitFor(nextFrame)
  1026. .then(() => {
  1027. expect(vm.$el.children[0].className).toBe(
  1028. 'test test-enter-active test-enter-to'
  1029. )
  1030. })
  1031. .thenWaitFor(duration + buffer)
  1032. .then(() => {
  1033. expect(vm.$el.children[0].className).toBe('test')
  1034. })
  1035. .then(done)
  1036. })
  1037. it('transition on SVG elements', (done) => {
  1038. const vm = new Vue({
  1039. template: `
  1040. <svg>
  1041. <transition>
  1042. <circle cx="0" cy="0" r="10" v-if="ok" class="test"></circle>
  1043. </transition>
  1044. </svg>
  1045. `,
  1046. data: { ok: true }
  1047. }).$mount(el)
  1048. // should not apply transition on initial render by default
  1049. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  1050. vm.ok = false
  1051. waitForUpdate(() => {
  1052. expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
  1053. 'test v-leave v-leave-active'
  1054. )
  1055. })
  1056. .thenWaitFor(nextFrame)
  1057. .then(() => {
  1058. expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
  1059. 'test v-leave-active v-leave-to'
  1060. )
  1061. })
  1062. .thenWaitFor(duration + buffer)
  1063. .then(() => {
  1064. expect(vm.$el.childNodes.length).toBe(1)
  1065. expect(vm.$el.childNodes[0].nodeType).toBe(8) // should be an empty comment node
  1066. expect(vm.$el.childNodes[0].textContent).toBe('')
  1067. vm.ok = true
  1068. })
  1069. .then(() => {
  1070. expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
  1071. 'test v-enter v-enter-active'
  1072. )
  1073. })
  1074. .thenWaitFor(nextFrame)
  1075. .then(() => {
  1076. expect(vm.$el.childNodes[0].getAttribute('class')).toBe(
  1077. 'test v-enter-active v-enter-to'
  1078. )
  1079. })
  1080. .thenWaitFor(duration + buffer)
  1081. .then(() => {
  1082. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  1083. })
  1084. .then(done)
  1085. })
  1086. it('transition on child components', (done) => {
  1087. const vm = new Vue({
  1088. template: `
  1089. <div>
  1090. <transition>
  1091. <test v-if="ok" class="test"></test>
  1092. </transition>
  1093. </div>
  1094. `,
  1095. data: { ok: true },
  1096. components: {
  1097. test: {
  1098. template: `
  1099. <transition name="test">
  1100. <div>foo</div>
  1101. </transition>
  1102. ` // test transition override from parent
  1103. }
  1104. }
  1105. }).$mount(el)
  1106. // should not apply transition on initial render by default
  1107. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  1108. vm.ok = false
  1109. waitForUpdate(() => {
  1110. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1111. })
  1112. .thenWaitFor(nextFrame)
  1113. .then(() => {
  1114. expect(vm.$el.children[0].className).toBe(
  1115. 'test v-leave-active v-leave-to'
  1116. )
  1117. })
  1118. .thenWaitFor(duration + buffer)
  1119. .then(() => {
  1120. expect(vm.$el.children.length).toBe(0)
  1121. vm.ok = true
  1122. })
  1123. .then(() => {
  1124. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  1125. })
  1126. .thenWaitFor(nextFrame)
  1127. .then(() => {
  1128. expect(vm.$el.children[0].className).toBe(
  1129. 'test v-enter-active v-enter-to'
  1130. )
  1131. })
  1132. .thenWaitFor(duration + buffer)
  1133. .then(() => {
  1134. expect(vm.$el.children[0].className).toBe('test')
  1135. })
  1136. .then(done)
  1137. })
  1138. it('transition inside child component with v-if', (done) => {
  1139. const vm = new Vue({
  1140. template: `
  1141. <div>
  1142. <test v-if="ok" class="test"></test>
  1143. </div>
  1144. `,
  1145. data: { ok: true },
  1146. components: {
  1147. test: {
  1148. template: `
  1149. <transition>
  1150. <div>foo</div>
  1151. </transition>
  1152. `
  1153. }
  1154. }
  1155. }).$mount(el)
  1156. // should not apply transition on initial render by default
  1157. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  1158. vm.ok = false
  1159. waitForUpdate(() => {
  1160. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1161. })
  1162. .thenWaitFor(nextFrame)
  1163. .then(() => {
  1164. expect(vm.$el.children[0].className).toBe(
  1165. 'test v-leave-active v-leave-to'
  1166. )
  1167. })
  1168. .thenWaitFor(duration + buffer)
  1169. .then(() => {
  1170. expect(vm.$el.children.length).toBe(0)
  1171. vm.ok = true
  1172. })
  1173. .then(() => {
  1174. expect(vm.$el.children[0].className).toBe('test')
  1175. })
  1176. .then(done)
  1177. })
  1178. it('transition with appear inside child component with v-if', (done) => {
  1179. const vm = new Vue({
  1180. template: `
  1181. <div>
  1182. <test v-if="ok" class="test"></test>
  1183. </div>
  1184. `,
  1185. data: { ok: true },
  1186. components: {
  1187. test: {
  1188. template: `
  1189. <transition appear
  1190. appear-class="test-appear"
  1191. appear-to-class="test-appear-to"
  1192. appear-active-class="test-appear-active">
  1193. <div>foo</div>
  1194. </transition>
  1195. `
  1196. }
  1197. }
  1198. }).$mount(el)
  1199. waitForUpdate(() => {
  1200. expect(vm.$el.children[0].className).toBe(
  1201. 'test test-appear test-appear-active'
  1202. )
  1203. })
  1204. .thenWaitFor(nextFrame)
  1205. .then(() => {
  1206. expect(vm.$el.children[0].className).toBe(
  1207. 'test test-appear-active test-appear-to'
  1208. )
  1209. })
  1210. .thenWaitFor(duration + buffer)
  1211. .then(() => {
  1212. expect(vm.$el.children[0].className).toBe('test')
  1213. vm.ok = false
  1214. })
  1215. .then(() => {
  1216. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1217. })
  1218. .thenWaitFor(nextFrame)
  1219. .then(() => {
  1220. expect(vm.$el.children[0].className).toBe(
  1221. 'test v-leave-active v-leave-to'
  1222. )
  1223. })
  1224. .thenWaitFor(duration + buffer)
  1225. .then(() => {
  1226. expect(vm.$el.children.length).toBe(0)
  1227. })
  1228. .then(done)
  1229. })
  1230. it('transition inside nested child component with v-if', (done) => {
  1231. const vm = new Vue({
  1232. template: `
  1233. <div>
  1234. <foo v-if="ok" class="test"></foo>
  1235. </div>
  1236. `,
  1237. data: { ok: true },
  1238. components: {
  1239. foo: {
  1240. template: '<bar></bar>',
  1241. components: {
  1242. bar: {
  1243. template: '<transition><div>foo</div></transition>'
  1244. }
  1245. }
  1246. }
  1247. }
  1248. }).$mount(el)
  1249. // should not apply transition on initial render by default
  1250. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  1251. vm.ok = false
  1252. waitForUpdate(() => {
  1253. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1254. })
  1255. .thenWaitFor(nextFrame)
  1256. .then(() => {
  1257. expect(vm.$el.children[0].className).toBe(
  1258. 'test v-leave-active v-leave-to'
  1259. )
  1260. })
  1261. .thenWaitFor(duration + buffer)
  1262. .then(() => {
  1263. expect(vm.$el.children.length).toBe(0)
  1264. vm.ok = true
  1265. })
  1266. .then(() => {
  1267. expect(vm.$el.children[0].className).toBe('test')
  1268. })
  1269. .then(done)
  1270. })
  1271. it('transition with appear inside nested child component with v-if', (done) => {
  1272. const vm = new Vue({
  1273. template: `
  1274. <div>
  1275. <foo v-if="ok" class="test"></foo>
  1276. </div>
  1277. `,
  1278. data: { ok: true },
  1279. components: {
  1280. foo: {
  1281. template: '<bar></bar>',
  1282. components: {
  1283. bar: {
  1284. template: `
  1285. <transition appear
  1286. appear-class="test-appear"
  1287. appear-to-class="test-appear-to"
  1288. appear-active-class="test-appear-active">
  1289. <div>foo</div>
  1290. </transition>
  1291. `
  1292. }
  1293. }
  1294. }
  1295. }
  1296. }).$mount(el)
  1297. waitForUpdate(() => {
  1298. expect(vm.$el.children[0].className).toBe(
  1299. 'test test-appear test-appear-active'
  1300. )
  1301. })
  1302. .thenWaitFor(nextFrame)
  1303. .then(() => {
  1304. expect(vm.$el.children[0].className).toBe(
  1305. 'test test-appear-active test-appear-to'
  1306. )
  1307. })
  1308. .thenWaitFor(duration + buffer)
  1309. .then(() => {
  1310. expect(vm.$el.children[0].className).toBe('test')
  1311. vm.ok = false
  1312. })
  1313. .then(() => {
  1314. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1315. })
  1316. .thenWaitFor(nextFrame)
  1317. .then(() => {
  1318. expect(vm.$el.children[0].className).toBe(
  1319. 'test v-leave-active v-leave-to'
  1320. )
  1321. })
  1322. .thenWaitFor(duration + buffer)
  1323. .then(() => {
  1324. expect(vm.$el.children.length).toBe(0)
  1325. })
  1326. .then(done)
  1327. })
  1328. it('custom transition higher-order component', (done) => {
  1329. const vm = new Vue({
  1330. template:
  1331. '<div><my-transition><div v-if="ok" class="test">foo</div></my-transition></div>',
  1332. data: { ok: true },
  1333. components: {
  1334. 'my-transition': {
  1335. functional: true,
  1336. render(h, { data, children }) {
  1337. ;(data.props || (data.props = {})).name = 'test'
  1338. return h('transition', data, children)
  1339. }
  1340. }
  1341. }
  1342. }).$mount(el)
  1343. // should not apply transition on initial render by default
  1344. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  1345. vm.ok = false
  1346. waitForUpdate(() => {
  1347. expect(vm.$el.children[0].className).toBe(
  1348. 'test test-leave test-leave-active'
  1349. )
  1350. })
  1351. .thenWaitFor(nextFrame)
  1352. .then(() => {
  1353. expect(vm.$el.children[0].className).toBe(
  1354. 'test test-leave-active test-leave-to'
  1355. )
  1356. })
  1357. .thenWaitFor(duration + buffer)
  1358. .then(() => {
  1359. expect(vm.$el.children.length).toBe(0)
  1360. vm.ok = true
  1361. })
  1362. .then(() => {
  1363. expect(vm.$el.children[0].className).toBe(
  1364. 'test test-enter test-enter-active'
  1365. )
  1366. })
  1367. .thenWaitFor(nextFrame)
  1368. .then(() => {
  1369. expect(vm.$el.children[0].className).toBe(
  1370. 'test test-enter-active test-enter-to'
  1371. )
  1372. })
  1373. .thenWaitFor(duration + buffer)
  1374. .then(() => {
  1375. expect(vm.$el.children[0].className).toBe('test')
  1376. })
  1377. .then(done)
  1378. })
  1379. it('warn when used on multiple elements', () => {
  1380. new Vue({
  1381. template: `<transition><p>1</p><p>2</p></transition>`
  1382. }).$mount()
  1383. expect(
  1384. `<transition> can only be used on a single element`
  1385. ).toHaveBeenWarned()
  1386. })
  1387. describe('explicit durations -', () => {
  1388. it('single value', (done) => {
  1389. const vm = new Vue({
  1390. template: `
  1391. <div>
  1392. <transition duration="${explicitDuration}">
  1393. <div v-if="ok" class="test">foo</div>
  1394. </transition>
  1395. </div>
  1396. `,
  1397. data: { ok: true }
  1398. }).$mount(el)
  1399. vm.ok = false
  1400. waitForUpdate(() => {
  1401. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1402. })
  1403. .thenWaitFor(nextFrame)
  1404. .then(() => {
  1405. expect(vm.$el.children[0].className).toBe(
  1406. 'test v-leave-active v-leave-to'
  1407. )
  1408. })
  1409. .thenWaitFor(explicitDuration + buffer)
  1410. .then(() => {
  1411. expect(vm.$el.children.length).toBe(0)
  1412. vm.ok = true
  1413. })
  1414. .then(() => {
  1415. expect(vm.$el.children[0].className).toBe(
  1416. 'test v-enter v-enter-active'
  1417. )
  1418. })
  1419. .thenWaitFor(nextFrame)
  1420. .then(() => {
  1421. expect(vm.$el.children[0].className).toBe(
  1422. 'test v-enter-active v-enter-to'
  1423. )
  1424. })
  1425. .thenWaitFor(explicitDuration + buffer)
  1426. .then(() => {
  1427. expect(vm.$el.children[0].className).toBe('test')
  1428. })
  1429. .then(done)
  1430. })
  1431. it('enter and auto leave', (done) => {
  1432. const vm = new Vue({
  1433. template: `
  1434. <div>
  1435. <transition :duration="{ enter: ${explicitDuration} }">
  1436. <div v-if="ok" class="test">foo</div>
  1437. </transition>
  1438. </div>
  1439. `,
  1440. data: { ok: true }
  1441. }).$mount(el)
  1442. vm.ok = false
  1443. waitForUpdate(() => {
  1444. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1445. })
  1446. .thenWaitFor(nextFrame)
  1447. .then(() => {
  1448. expect(vm.$el.children[0].className).toBe(
  1449. 'test v-leave-active v-leave-to'
  1450. )
  1451. })
  1452. .thenWaitFor(duration + buffer)
  1453. .then(() => {
  1454. expect(vm.$el.children.length).toBe(0)
  1455. vm.ok = true
  1456. })
  1457. .then(() => {
  1458. expect(vm.$el.children[0].className).toBe(
  1459. 'test v-enter v-enter-active'
  1460. )
  1461. })
  1462. .thenWaitFor(nextFrame)
  1463. .then(() => {
  1464. expect(vm.$el.children[0].className).toBe(
  1465. 'test v-enter-active v-enter-to'
  1466. )
  1467. })
  1468. .thenWaitFor(explicitDuration + buffer)
  1469. .then(() => {
  1470. expect(vm.$el.children[0].className).toBe('test')
  1471. })
  1472. .then(done)
  1473. })
  1474. it('leave and auto enter', (done) => {
  1475. const vm = new Vue({
  1476. template: `
  1477. <div>
  1478. <transition :duration="{ leave: ${explicitDuration} }">
  1479. <div v-if="ok" class="test">foo</div>
  1480. </transition>
  1481. </div>
  1482. `,
  1483. data: { ok: true }
  1484. }).$mount(el)
  1485. vm.ok = false
  1486. waitForUpdate(() => {
  1487. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1488. })
  1489. .thenWaitFor(nextFrame)
  1490. .then(() => {
  1491. expect(vm.$el.children[0].className).toBe(
  1492. 'test v-leave-active v-leave-to'
  1493. )
  1494. })
  1495. .thenWaitFor(explicitDuration + buffer)
  1496. .then(() => {
  1497. expect(vm.$el.children.length).toBe(0)
  1498. vm.ok = true
  1499. })
  1500. .then(() => {
  1501. expect(vm.$el.children[0].className).toBe(
  1502. 'test v-enter v-enter-active'
  1503. )
  1504. })
  1505. .thenWaitFor(nextFrame)
  1506. .then(() => {
  1507. expect(vm.$el.children[0].className).toBe(
  1508. 'test v-enter-active v-enter-to'
  1509. )
  1510. })
  1511. .thenWaitFor(duration + buffer)
  1512. .then(() => {
  1513. expect(vm.$el.children[0].className).toBe('test')
  1514. })
  1515. .then(done)
  1516. })
  1517. it('separate enter and leave', (done) => {
  1518. const enter = explicitDuration
  1519. const leave = explicitDuration * 2
  1520. const vm = new Vue({
  1521. template: `
  1522. <div>
  1523. <transition :duration="{ enter: ${enter}, leave: ${leave} }">
  1524. <div v-if="ok" class="test">foo</div>
  1525. </transition>
  1526. </div>
  1527. `,
  1528. data: { ok: true }
  1529. }).$mount(el)
  1530. vm.ok = false
  1531. waitForUpdate(() => {
  1532. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1533. })
  1534. .thenWaitFor(nextFrame)
  1535. .then(() => {
  1536. expect(vm.$el.children[0].className).toBe(
  1537. 'test v-leave-active v-leave-to'
  1538. )
  1539. })
  1540. .thenWaitFor(leave + buffer)
  1541. .then(() => {
  1542. expect(vm.$el.children.length).toBe(0)
  1543. vm.ok = true
  1544. })
  1545. .then(() => {
  1546. expect(vm.$el.children[0].className).toBe(
  1547. 'test v-enter v-enter-active'
  1548. )
  1549. })
  1550. .thenWaitFor(nextFrame)
  1551. .then(() => {
  1552. expect(vm.$el.children[0].className).toBe(
  1553. 'test v-enter-active v-enter-to'
  1554. )
  1555. })
  1556. .thenWaitFor(enter + buffer)
  1557. .then(() => {
  1558. expect(vm.$el.children[0].className).toBe('test')
  1559. })
  1560. .then(done)
  1561. })
  1562. it('enter and leave + duration change', (done) => {
  1563. const enter1 = explicitDuration * 2
  1564. const enter2 = explicitDuration
  1565. const leave1 = explicitDuration * 0.5
  1566. const leave2 = explicitDuration * 3
  1567. const vm = new Vue({
  1568. template: `
  1569. <div>
  1570. <transition :duration="{ enter: enter, leave: leave }">
  1571. <div v-if="ok" class="test">foo</div>
  1572. </transition>
  1573. </div>
  1574. `,
  1575. data: {
  1576. ok: true,
  1577. enter: enter1,
  1578. leave: leave1
  1579. }
  1580. }).$mount(el)
  1581. vm.ok = false
  1582. waitForUpdate(() => {
  1583. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1584. })
  1585. .thenWaitFor(nextFrame)
  1586. .then(() => {
  1587. expect(vm.$el.children[0].className).toBe(
  1588. 'test v-leave-active v-leave-to'
  1589. )
  1590. })
  1591. .thenWaitFor(leave1 + buffer)
  1592. .then(() => {
  1593. expect(vm.$el.children.length).toBe(0)
  1594. vm.ok = true
  1595. })
  1596. .then(() => {
  1597. expect(vm.$el.children[0].className).toBe(
  1598. 'test v-enter v-enter-active'
  1599. )
  1600. })
  1601. .thenWaitFor(nextFrame)
  1602. .then(() => {
  1603. expect(vm.$el.children[0].className).toBe(
  1604. 'test v-enter-active v-enter-to'
  1605. )
  1606. })
  1607. .thenWaitFor(enter1 + buffer)
  1608. .then(() => {
  1609. expect(vm.$el.children[0].className).toBe('test')
  1610. vm.enter = enter2
  1611. vm.leave = leave2
  1612. })
  1613. .then(() => {
  1614. vm.ok = false
  1615. })
  1616. .then(() => {
  1617. expect(vm.$el.children[0].className).toBe(
  1618. 'test v-leave v-leave-active'
  1619. )
  1620. })
  1621. .thenWaitFor(nextFrame)
  1622. .then(() => {
  1623. expect(vm.$el.children[0].className).toBe(
  1624. 'test v-leave-active v-leave-to'
  1625. )
  1626. })
  1627. .thenWaitFor(leave2 + buffer)
  1628. .then(() => {
  1629. expect(vm.$el.children.length).toBe(0)
  1630. vm.ok = true
  1631. })
  1632. .then(() => {
  1633. expect(vm.$el.children[0].className).toBe(
  1634. 'test v-enter v-enter-active'
  1635. )
  1636. })
  1637. .thenWaitFor(nextFrame)
  1638. .then(() => {
  1639. expect(vm.$el.children[0].className).toBe(
  1640. 'test v-enter-active v-enter-to'
  1641. )
  1642. })
  1643. .thenWaitFor(enter2 + buffer)
  1644. .then(() => {
  1645. expect(vm.$el.children[0].className).toBe('test')
  1646. })
  1647. .then(done)
  1648. }, 10000)
  1649. it('warn invalid durations', (done) => {
  1650. const vm = new Vue({
  1651. template: `
  1652. <div>
  1653. <transition :duration="{ enter: NaN, leave: 'foo' }">
  1654. <div v-if="ok" class="test">foo</div>
  1655. </transition>
  1656. </div>
  1657. `,
  1658. data: {
  1659. ok: true
  1660. }
  1661. }).$mount(el)
  1662. vm.ok = false
  1663. waitForUpdate(() => {
  1664. expect(
  1665. `<transition> explicit leave duration is not a valid number - got "foo"`
  1666. ).toHaveBeenWarned()
  1667. })
  1668. .thenWaitFor(duration + buffer)
  1669. .then(() => {
  1670. vm.ok = true
  1671. })
  1672. .then(() => {
  1673. expect(
  1674. `<transition> explicit enter duration is NaN`
  1675. ).toHaveBeenWarned()
  1676. })
  1677. .then(done)
  1678. })
  1679. })
  1680. // #6687
  1681. it('transition on child components with empty root node', (done) => {
  1682. const vm = new Vue({
  1683. template: `
  1684. <div>
  1685. <transition mode="out-in">
  1686. <component class="test" :is="view"></component>
  1687. </transition>
  1688. </div>
  1689. `,
  1690. data: { view: 'one' },
  1691. components: {
  1692. one: {
  1693. template: '<div v-if="false">one</div>'
  1694. },
  1695. two: {
  1696. template: '<div>two</div>'
  1697. }
  1698. }
  1699. }).$mount(el)
  1700. // should not apply transition on initial render by default
  1701. expect(vm.$el.innerHTML).toBe('<!---->')
  1702. vm.view = 'two'
  1703. waitForUpdate(() => {
  1704. expect(vm.$el.innerHTML).toBe(
  1705. '<div class="test v-enter v-enter-active">two</div>'
  1706. )
  1707. })
  1708. .thenWaitFor(nextFrame)
  1709. .then(() => {
  1710. expect(vm.$el.children[0].className).toBe(
  1711. 'test v-enter-active v-enter-to'
  1712. )
  1713. })
  1714. .thenWaitFor(duration + buffer)
  1715. .then(() => {
  1716. expect(vm.$el.children[0].className).toBe('test')
  1717. vm.view = 'one'
  1718. })
  1719. .then(() => {
  1720. // incoming comment node is appended instantly because it doesn't have
  1721. // data and therefore doesn't go through the transition module.
  1722. expect(vm.$el.innerHTML).toBe(
  1723. '<div class="test v-leave v-leave-active">two</div><!---->'
  1724. )
  1725. })
  1726. .thenWaitFor(nextFrame)
  1727. .then(() => {
  1728. expect(vm.$el.children[0].className).toBe(
  1729. 'test v-leave-active v-leave-to'
  1730. )
  1731. })
  1732. .thenWaitFor(duration + buffer)
  1733. .then(() => {
  1734. expect(vm.$el.innerHTML).toBe('<!---->')
  1735. })
  1736. .then(done)
  1737. })
  1738. // #8199
  1739. it('should not throw error when replaced by v-html contents', (done) => {
  1740. const vm = new Vue({
  1741. template: `
  1742. <div>
  1743. <div v-if="ok" :class="ok">
  1744. <transition>
  1745. <span>a</span>
  1746. </transition>
  1747. </div>
  1748. <div v-else v-html="ok"></div>
  1749. </div>
  1750. `,
  1751. data: { ok: true }
  1752. }).$mount(el)
  1753. vm.ok = false
  1754. waitForUpdate(() => {
  1755. expect(vm.$el.children[0].innerHTML).toBe('false')
  1756. }).then(done)
  1757. })
  1758. })