transition.spec.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779
  1. import Vue from 'vue'
  2. import injectStyles from './inject-styles'
  3. import { isIE9 } from 'web/util/index'
  4. import { nextFrame } from 'web/runtime/transition-util'
  5. if (!isIE9) {
  6. describe('Transition basic', () => {
  7. const duration = injectStyles()
  8. let el
  9. beforeEach(() => {
  10. el = document.createElement('div')
  11. document.body.appendChild(el)
  12. })
  13. it('basic transition', done => {
  14. const vm = new Vue({
  15. template: '<div><transition><div v-if="ok" class="test">foo</div></transition></div>',
  16. data: { ok: true }
  17. }).$mount(el)
  18. // should not apply transition on initial render by default
  19. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  20. vm.ok = false
  21. waitForUpdate(() => {
  22. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  23. }).thenWaitFor(nextFrame).then(() => {
  24. expect(vm.$el.children[0].className).toBe('test v-leave-active')
  25. }).thenWaitFor(duration + 10).then(() => {
  26. expect(vm.$el.children.length).toBe(0)
  27. vm.ok = true
  28. }).then(() => {
  29. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  30. }).thenWaitFor(nextFrame).then(() => {
  31. expect(vm.$el.children[0].className).toBe('test v-enter-active')
  32. }).thenWaitFor(duration + 10).then(() => {
  33. expect(vm.$el.children[0].className).toBe('test')
  34. }).then(done)
  35. })
  36. it('named transition', done => {
  37. const vm = new Vue({
  38. template: '<div><transition name="test"><div v-if="ok" class="test">foo</div></transition></div>',
  39. data: { ok: true }
  40. }).$mount(el)
  41. // should not apply transition on initial render by default
  42. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  43. vm.ok = false
  44. waitForUpdate(() => {
  45. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  46. }).thenWaitFor(nextFrame).then(() => {
  47. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  48. }).thenWaitFor(duration + 10).then(() => {
  49. expect(vm.$el.children.length).toBe(0)
  50. vm.ok = true
  51. }).then(() => {
  52. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  53. }).thenWaitFor(nextFrame).then(() => {
  54. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  55. }).thenWaitFor(duration + 10).then(() => {
  56. expect(vm.$el.children[0].className).toBe('test')
  57. }).then(done)
  58. })
  59. it('custom transition classes', done => {
  60. const vm = new Vue({
  61. template: `
  62. <div>
  63. <transition
  64. enter-class="hello"
  65. enter-active-class="hello-active"
  66. leave-class="bye"
  67. leave-active-class="byebye active">
  68. <div v-if="ok" class="test">foo</div>
  69. </transition>
  70. </div>
  71. `,
  72. data: { ok: true }
  73. }).$mount(el)
  74. // should not apply transition on initial render by default
  75. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  76. vm.ok = false
  77. waitForUpdate(() => {
  78. expect(vm.$el.children[0].className).toBe('test bye byebye active')
  79. }).thenWaitFor(nextFrame).then(() => {
  80. expect(vm.$el.children[0].className).toBe('test byebye active')
  81. }).thenWaitFor(duration + 10).then(() => {
  82. expect(vm.$el.children.length).toBe(0)
  83. vm.ok = true
  84. }).then(() => {
  85. expect(vm.$el.children[0].className).toBe('test hello hello-active')
  86. }).thenWaitFor(nextFrame).then(() => {
  87. expect(vm.$el.children[0].className).toBe('test hello-active')
  88. }).thenWaitFor(duration + 10).then(() => {
  89. expect(vm.$el.children[0].className).toBe('test')
  90. }).then(done)
  91. })
  92. it('dynamic transition', done => {
  93. const vm = new Vue({
  94. template: `
  95. <div>
  96. <transition :name="trans">
  97. <div v-if="ok" class="test">foo</div>
  98. </transition>
  99. </div>
  100. `,
  101. data: {
  102. ok: true,
  103. trans: 'test'
  104. }
  105. }).$mount(el)
  106. // should not apply transition on initial render by default
  107. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  108. vm.ok = false
  109. waitForUpdate(() => {
  110. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  111. }).thenWaitFor(nextFrame).then(() => {
  112. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  113. }).thenWaitFor(duration + 10).then(() => {
  114. expect(vm.$el.children.length).toBe(0)
  115. vm.ok = true
  116. vm.trans = 'changed'
  117. }).then(() => {
  118. expect(vm.$el.children[0].className).toBe('test changed-enter changed-enter-active')
  119. }).thenWaitFor(nextFrame).then(() => {
  120. expect(vm.$el.children[0].className).toBe('test changed-enter-active')
  121. }).thenWaitFor(duration + 10).then(() => {
  122. expect(vm.$el.children[0].className).toBe('test')
  123. }).then(done)
  124. })
  125. it('inline transition object', done => {
  126. const enter = jasmine.createSpy('enter')
  127. const leave = jasmine.createSpy('leave')
  128. const vm = new Vue({
  129. render (h) {
  130. return h('div', null, [
  131. h('transition', {
  132. props: {
  133. name: 'inline',
  134. enterClass: 'hello',
  135. enterActiveClass: 'hello-active',
  136. leaveClass: 'bye',
  137. leaveActiveClass: 'byebye active'
  138. },
  139. on: {
  140. enter,
  141. leave
  142. }
  143. }, () => [this.ok ? h('div', { class: 'test' }, 'foo') : undefined])
  144. ])
  145. },
  146. data: { ok: true }
  147. }).$mount(el)
  148. // should not apply transition on initial render by default
  149. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  150. vm.ok = false
  151. waitForUpdate(() => {
  152. expect(vm.$el.children[0].className).toBe('test bye byebye active')
  153. expect(leave).toHaveBeenCalled()
  154. }).thenWaitFor(nextFrame).then(() => {
  155. expect(vm.$el.children[0].className).toBe('test byebye active')
  156. }).thenWaitFor(duration + 10).then(() => {
  157. expect(vm.$el.children.length).toBe(0)
  158. vm.ok = true
  159. }).then(() => {
  160. expect(vm.$el.children[0].className).toBe('test hello hello-active')
  161. expect(enter).toHaveBeenCalled()
  162. }).thenWaitFor(nextFrame).then(() => {
  163. expect(vm.$el.children[0].className).toBe('test hello-active')
  164. }).thenWaitFor(duration + 10).then(() => {
  165. expect(vm.$el.children[0].className).toBe('test')
  166. }).then(done)
  167. })
  168. it('transition events', done => {
  169. const onLeaveSpy = jasmine.createSpy('leave')
  170. const onEnterSpy = jasmine.createSpy('enter')
  171. const beforeLeaveSpy = jasmine.createSpy('beforeLeave')
  172. const beforeEnterSpy = jasmine.createSpy('beforeEnter')
  173. const afterLeaveSpy = jasmine.createSpy('afterLeave')
  174. const afterEnterSpy = jasmine.createSpy('afterEnter')
  175. const vm = new Vue({
  176. template: `
  177. <div>
  178. <transition
  179. name="test"
  180. @before-enter="beforeEnter"
  181. @enter="enter"
  182. @after-enter="afterEnter"
  183. @before-leave="beforeLeave"
  184. @leave="leave"
  185. @after-leave="afterLeave">
  186. <div v-if="ok" class="test">foo</div>
  187. </transition>
  188. </div>
  189. `,
  190. data: { ok: true },
  191. methods: {
  192. beforeLeave: (el) => {
  193. expect(el).toBe(vm.$el.children[0])
  194. expect(el.className).toBe('test')
  195. beforeLeaveSpy(el)
  196. },
  197. leave: (el) => onLeaveSpy(el),
  198. afterLeave: (el) => afterLeaveSpy(el),
  199. beforeEnter: (el) => {
  200. expect(vm.$el.contains(el)).toBe(false)
  201. expect(el.className).toBe('test')
  202. beforeEnterSpy(el)
  203. },
  204. enter: (el) => {
  205. expect(vm.$el.contains(el)).toBe(true)
  206. onEnterSpy(el)
  207. },
  208. afterEnter: (el) => afterEnterSpy(el)
  209. }
  210. }).$mount(el)
  211. // should not apply transition on initial render by default
  212. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  213. let _el = vm.$el.children[0]
  214. vm.ok = false
  215. waitForUpdate(() => {
  216. expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
  217. expect(onLeaveSpy).toHaveBeenCalledWith(_el)
  218. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  219. }).thenWaitFor(nextFrame).then(() => {
  220. expect(afterLeaveSpy).not.toHaveBeenCalled()
  221. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  222. }).thenWaitFor(duration + 10).then(() => {
  223. expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
  224. expect(vm.$el.children.length).toBe(0)
  225. vm.ok = true
  226. }).then(() => {
  227. _el = vm.$el.children[0]
  228. expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
  229. expect(onEnterSpy).toHaveBeenCalledWith(_el)
  230. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  231. }).thenWaitFor(nextFrame).then(() => {
  232. expect(afterEnterSpy).not.toHaveBeenCalled()
  233. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  234. }).thenWaitFor(duration + 10).then(() => {
  235. expect(afterEnterSpy).toHaveBeenCalledWith(_el)
  236. expect(vm.$el.children[0].className).toBe('test')
  237. }).then(done)
  238. })
  239. it('explicit user callback in JavaScript hooks', done => {
  240. let next
  241. const vm = new Vue({
  242. template: `<div>
  243. <transition name="test" @enter="enter" @leave="leave">
  244. <div v-if="ok" class="test">foo</div>
  245. </transition>
  246. </div>`,
  247. data: { ok: true },
  248. methods: {
  249. enter: (el, cb) => {
  250. next = cb
  251. },
  252. leave: (el, cb) => {
  253. next = cb
  254. }
  255. }
  256. }).$mount(el)
  257. vm.ok = false
  258. waitForUpdate(() => {
  259. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  260. }).thenWaitFor(nextFrame).then(() => {
  261. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  262. }).thenWaitFor(duration + 10).then(() => {
  263. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  264. expect(next).toBeTruthy()
  265. next()
  266. expect(vm.$el.children.length).toBe(0)
  267. }).then(() => {
  268. vm.ok = true
  269. }).then(() => {
  270. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  271. }).thenWaitFor(nextFrame).then(() => {
  272. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  273. }).thenWaitFor(duration + 10).then(() => {
  274. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  275. expect(next).toBeTruthy()
  276. next()
  277. expect(vm.$el.children[0].className).toBe('test')
  278. }).then(done)
  279. })
  280. it('css: false', done => {
  281. const enterSpy = jasmine.createSpy('enter')
  282. const leaveSpy = jasmine.createSpy('leave')
  283. const vm = new Vue({
  284. template: `
  285. <div>
  286. <transition :css="false" name="test" @enter="enter" @leave="leave">
  287. <div v-if="ok" class="test">foo</div>
  288. </transition>
  289. </div>
  290. `,
  291. data: { ok: true },
  292. methods: {
  293. enter: enterSpy,
  294. leave: leaveSpy
  295. }
  296. }).$mount(el)
  297. vm.ok = false
  298. waitForUpdate(() => {
  299. expect(leaveSpy).toHaveBeenCalled()
  300. expect(vm.$el.innerHTML).toBe('')
  301. vm.ok = true
  302. }).then(() => {
  303. expect(enterSpy).toHaveBeenCalled()
  304. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  305. }).then(done)
  306. })
  307. it('no transition detected', done => {
  308. const enterSpy = jasmine.createSpy('enter')
  309. const leaveSpy = jasmine.createSpy('leave')
  310. const vm = new Vue({
  311. template: '<div><transition name="nope" @enter="enter" @leave="leave"><div v-if="ok">foo</div></transition></div>',
  312. data: { ok: true },
  313. methods: {
  314. enter: enterSpy,
  315. leave: leaveSpy
  316. }
  317. }).$mount(el)
  318. vm.ok = false
  319. waitForUpdate(() => {
  320. expect(leaveSpy).toHaveBeenCalled()
  321. expect(vm.$el.innerHTML).toBe('<div class="nope-leave nope-leave-active">foo</div>')
  322. }).thenWaitFor(nextFrame).then(() => {
  323. expect(vm.$el.innerHTML).toBe('')
  324. vm.ok = true
  325. }).then(() => {
  326. expect(enterSpy).toHaveBeenCalled()
  327. expect(vm.$el.innerHTML).toBe('<div class="nope-enter nope-enter-active">foo</div>')
  328. }).thenWaitFor(nextFrame).then(() => {
  329. expect(vm.$el.innerHTML).toMatch(/<div( class="")?>foo<\/div>/)
  330. }).then(done)
  331. })
  332. it('enterCancelled', done => {
  333. const spy = jasmine.createSpy('enterCancelled')
  334. const vm = new Vue({
  335. template: `
  336. <div>
  337. <transition name="test" @enter-cancelled="enterCancelled">
  338. <div v-if="ok" class="test">foo</div>
  339. </transition>
  340. </div>
  341. `,
  342. data: { ok: false },
  343. methods: {
  344. enterCancelled: spy
  345. }
  346. }).$mount(el)
  347. expect(vm.$el.innerHTML).toBe('')
  348. vm.ok = true
  349. waitForUpdate(() => {
  350. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  351. }).thenWaitFor(duration / 2).then(() => {
  352. vm.ok = false
  353. }).then(() => {
  354. expect(spy).toHaveBeenCalled()
  355. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  356. }).thenWaitFor(nextFrame).then(() => {
  357. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  358. }).thenWaitFor(duration + 10).then(() => {
  359. expect(vm.$el.children.length).toBe(0)
  360. }).then(done)
  361. })
  362. it('should remove stale leaving elements', done => {
  363. const spy = jasmine.createSpy('afterLeave')
  364. const vm = new Vue({
  365. template: `
  366. <div>
  367. <transition name="test" @after-leave="afterLeave">
  368. <div v-if="ok" class="test">foo</div>
  369. </transition>
  370. </div>
  371. `,
  372. data: { ok: true },
  373. methods: {
  374. afterLeave: spy
  375. }
  376. }).$mount(el)
  377. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  378. vm.ok = false
  379. waitForUpdate(() => {
  380. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  381. }).thenWaitFor(duration / 2).then(() => {
  382. vm.ok = true
  383. }).then(() => {
  384. expect(spy).toHaveBeenCalled()
  385. expect(vm.$el.children.length).toBe(1) // should have removed leaving element
  386. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  387. }).thenWaitFor(nextFrame).then(() => {
  388. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  389. }).thenWaitFor(duration + 10).then(() => {
  390. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  391. }).then(done)
  392. })
  393. it('transition with v-show', done => {
  394. const vm = new Vue({
  395. template: `
  396. <div>
  397. <transition name="test">
  398. <div v-show="ok" class="test">foo</div>
  399. </transition>
  400. </div>
  401. `,
  402. data: { ok: true }
  403. }).$mount(el)
  404. // should not apply transition on initial render by default
  405. expect(vm.$el.textContent).toBe('foo')
  406. expect(vm.$el.children[0].style.display).toBe('')
  407. vm.ok = false
  408. waitForUpdate(() => {
  409. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  410. }).thenWaitFor(nextFrame).then(() => {
  411. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  412. }).thenWaitFor(duration + 10).then(() => {
  413. expect(vm.$el.children[0].style.display).toBe('none')
  414. vm.ok = true
  415. }).then(() => {
  416. expect(vm.$el.children[0].style.display).toBe('')
  417. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  418. }).thenWaitFor(nextFrame).then(() => {
  419. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  420. }).thenWaitFor(duration + 10).then(() => {
  421. expect(vm.$el.children[0].className).toBe('test')
  422. }).then(done)
  423. })
  424. it('transition with v-show, inside child component', done => {
  425. const vm = new Vue({
  426. template: `
  427. <div>
  428. <test v-show="ok"></test>
  429. </div>
  430. `,
  431. data: { ok: true },
  432. components: {
  433. test: {
  434. template: `<transition name="test"><div class="test">foo</div></transition>`
  435. }
  436. }
  437. }).$mount(el)
  438. // should not apply transition on initial render by default
  439. expect(vm.$el.textContent).toBe('foo')
  440. expect(vm.$el.children[0].style.display).toBe('')
  441. vm.ok = false
  442. waitForUpdate(() => {
  443. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  444. }).thenWaitFor(nextFrame).then(() => {
  445. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  446. }).thenWaitFor(duration + 10).then(() => {
  447. expect(vm.$el.children[0].style.display).toBe('none')
  448. vm.ok = true
  449. }).then(() => {
  450. expect(vm.$el.children[0].style.display).toBe('')
  451. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  452. }).thenWaitFor(nextFrame).then(() => {
  453. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  454. }).thenWaitFor(duration + 10).then(() => {
  455. expect(vm.$el.children[0].className).toBe('test')
  456. }).then(done)
  457. })
  458. it('leaveCancelled (v-show only)', done => {
  459. const spy = jasmine.createSpy('leaveCancelled')
  460. const vm = new Vue({
  461. template: `
  462. <div>
  463. <transition name="test" @leave-cancelled="leaveCancelled">
  464. <div v-show="ok" class="test">foo</div>
  465. </transition>
  466. </div>
  467. `,
  468. data: { ok: true },
  469. methods: {
  470. leaveCancelled: spy
  471. }
  472. }).$mount(el)
  473. expect(vm.$el.children[0].style.display).toBe('')
  474. vm.ok = false
  475. waitForUpdate(() => {
  476. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  477. }).thenWaitFor(nextFrame).then(() => {
  478. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  479. }).thenWaitFor(10).then(() => {
  480. vm.ok = true
  481. }).then(() => {
  482. expect(spy).toHaveBeenCalled()
  483. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  484. }).thenWaitFor(nextFrame).then(() => {
  485. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  486. }).thenWaitFor(duration + 10).then(() => {
  487. expect(vm.$el.children[0].style.display).toBe('')
  488. }).then(done)
  489. })
  490. it('animations', done => {
  491. const vm = new Vue({
  492. template: `
  493. <div>
  494. <transition name="test-anim">
  495. <div v-if="ok">foo</div>
  496. </transition>
  497. </div>
  498. `,
  499. data: { ok: true }
  500. }).$mount(el)
  501. // should not apply transition on initial render by default
  502. expect(vm.$el.innerHTML).toBe('<div>foo</div>')
  503. vm.ok = false
  504. waitForUpdate(() => {
  505. expect(vm.$el.children[0].className).toBe('test-anim-leave test-anim-leave-active')
  506. }).thenWaitFor(nextFrame).then(() => {
  507. expect(vm.$el.children[0].className).toBe('test-anim-leave-active')
  508. }).thenWaitFor(duration + 10).then(() => {
  509. expect(vm.$el.children.length).toBe(0)
  510. vm.ok = true
  511. }).then(() => {
  512. expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active')
  513. }).thenWaitFor(nextFrame).then(() => {
  514. expect(vm.$el.children[0].className).toBe('test-anim-enter-active')
  515. }).thenWaitFor(duration + 10).then(() => {
  516. expect(vm.$el.children[0].className).toBe('')
  517. }).then(done)
  518. })
  519. it('explicit transition type', done => {
  520. const vm = new Vue({
  521. template: `
  522. <div>
  523. <transition name="test-anim-long" type="animation">
  524. <div v-if="ok" class="test">foo</div>
  525. </transition>
  526. </div>
  527. `,
  528. data: { ok: true }
  529. }).$mount(el)
  530. // should not apply transition on initial render by default
  531. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  532. vm.ok = false
  533. waitForUpdate(() => {
  534. expect(vm.$el.children[0].className).toBe('test test-anim-long-leave test-anim-long-leave-active')
  535. }).thenWaitFor(nextFrame).then(() => {
  536. expect(vm.$el.children[0].className).toBe('test test-anim-long-leave-active')
  537. }).thenWaitFor(duration + 5).then(() => {
  538. // should not end early due to transition presence
  539. expect(vm.$el.children[0].className).toBe('test test-anim-long-leave-active')
  540. }).thenWaitFor(duration + 5).then(() => {
  541. expect(vm.$el.children.length).toBe(0)
  542. vm.ok = true
  543. }).then(() => {
  544. expect(vm.$el.children[0].className).toBe('test test-anim-long-enter test-anim-long-enter-active')
  545. }).thenWaitFor(nextFrame).then(() => {
  546. expect(vm.$el.children[0].className).toBe('test test-anim-long-enter-active')
  547. }).thenWaitFor(duration + 5).then(() => {
  548. expect(vm.$el.children[0].className).toBe('test test-anim-long-enter-active')
  549. }).thenWaitFor(duration + 5).then(() => {
  550. expect(vm.$el.children[0].className).toBe('test')
  551. }).then(done)
  552. })
  553. it('transition on appear', done => {
  554. const vm = new Vue({
  555. template: `
  556. <div>
  557. <transition name="test"
  558. appear
  559. appear-class="test-appear"
  560. appear-active-class="test-appear-active">
  561. <div v-if="ok" class="test">foo</div>
  562. </transition>
  563. </div>
  564. `,
  565. data: { ok: true }
  566. }).$mount(el)
  567. waitForUpdate(() => {
  568. expect(vm.$el.children[0].className).toBe('test test-appear test-appear-active')
  569. }).thenWaitFor(nextFrame).then(() => {
  570. expect(vm.$el.children[0].className).toBe('test test-appear-active')
  571. }).thenWaitFor(duration + 10).then(() => {
  572. expect(vm.$el.children[0].className).toBe('test')
  573. }).then(done)
  574. })
  575. it('transition on appear with v-show', done => {
  576. const vm = new Vue({
  577. template: `
  578. <div>
  579. <transition name="test" appear>
  580. <div v-show="ok" class="test">foo</div>
  581. </transition>
  582. </div>
  583. `,
  584. data: { ok: true }
  585. }).$mount(el)
  586. waitForUpdate(() => {
  587. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  588. }).thenWaitFor(nextFrame).then(() => {
  589. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  590. }).thenWaitFor(duration + 10).then(() => {
  591. expect(vm.$el.children[0].className).toBe('test')
  592. }).then(done)
  593. })
  594. it('transition on SVG elements', done => {
  595. const vm = new Vue({
  596. template: `
  597. <svg>
  598. <transition>
  599. <circle cx="0" cy="0" r="10" v-if="ok" class="test"></circle>
  600. </transition>
  601. </svg>
  602. `,
  603. data: { ok: true }
  604. }).$mount(el)
  605. // should not apply transition on initial render by default
  606. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  607. vm.ok = false
  608. waitForUpdate(() => {
  609. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave v-leave-active')
  610. }).thenWaitFor(nextFrame).then(() => {
  611. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave-active')
  612. }).thenWaitFor(duration + 10).then(() => {
  613. expect(vm.$el.childNodes.length).toBe(1)
  614. expect(vm.$el.childNodes[0].nodeType).toBe(3) // should be an empty text node
  615. expect(vm.$el.childNodes[0].textContent).toBe('')
  616. vm.ok = true
  617. }).then(() => {
  618. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-enter v-enter-active')
  619. }).thenWaitFor(nextFrame).then(() => {
  620. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-enter-active')
  621. }).thenWaitFor(duration + 10).then(() => {
  622. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  623. }).then(done)
  624. })
  625. it('transition on child components', done => {
  626. const vm = new Vue({
  627. template: `
  628. <div>
  629. <transition>
  630. <test v-if="ok" class="test"></test>
  631. </transition>
  632. </div>
  633. `,
  634. data: { ok: true },
  635. components: {
  636. test: {
  637. template: `
  638. <transition name="test">
  639. <div>foo</div>
  640. </transition>
  641. ` // test transition override from parent
  642. }
  643. }
  644. }).$mount(el)
  645. // should not apply transition on initial render by default
  646. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  647. vm.ok = false
  648. waitForUpdate(() => {
  649. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  650. }).thenWaitFor(nextFrame).then(() => {
  651. expect(vm.$el.children[0].className).toBe('test v-leave-active')
  652. }).thenWaitFor(duration + 10).then(() => {
  653. expect(vm.$el.children.length).toBe(0)
  654. vm.ok = true
  655. }).then(() => {
  656. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  657. }).thenWaitFor(nextFrame).then(() => {
  658. expect(vm.$el.children[0].className).toBe('test v-enter-active')
  659. }).thenWaitFor(duration + 10).then(() => {
  660. expect(vm.$el.children[0].className).toBe('test')
  661. }).then(done)
  662. })
  663. it('transition inside child component', done => {
  664. const vm = new Vue({
  665. template: `
  666. <div>
  667. <test v-if="ok" class="test"></test>
  668. </div>
  669. `,
  670. data: { ok: true },
  671. components: {
  672. test: {
  673. template: `
  674. <transition>
  675. <div>foo</div>
  676. </transition>
  677. `
  678. }
  679. }
  680. }).$mount(el)
  681. // should not apply transition on initial render by default
  682. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  683. vm.ok = false
  684. waitForUpdate(() => {
  685. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  686. }).thenWaitFor(nextFrame).then(() => {
  687. expect(vm.$el.children[0].className).toBe('test v-leave-active')
  688. }).thenWaitFor(duration + 10).then(() => {
  689. expect(vm.$el.children.length).toBe(0)
  690. vm.ok = true
  691. }).then(() => {
  692. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  693. }).thenWaitFor(nextFrame).then(() => {
  694. expect(vm.$el.children[0].className).toBe('test v-enter-active')
  695. }).thenWaitFor(duration + 10).then(() => {
  696. expect(vm.$el.children[0].className).toBe('test')
  697. }).then(done)
  698. })
  699. it('custom transition higher-order component', done => {
  700. const vm = new Vue({
  701. template: '<div><my-transition><div v-if="ok" class="test">foo</div></my-transition></div>',
  702. data: { ok: true },
  703. components: {
  704. 'my-transition': {
  705. functional: true,
  706. render (h, { data, children }) {
  707. (data.props || (data.props = {})).name = 'test'
  708. return h('transition', data, children)
  709. }
  710. }
  711. }
  712. }).$mount(el)
  713. // should not apply transition on initial render by default
  714. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  715. vm.ok = false
  716. waitForUpdate(() => {
  717. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  718. }).thenWaitFor(nextFrame).then(() => {
  719. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  720. }).thenWaitFor(duration + 10).then(() => {
  721. expect(vm.$el.children.length).toBe(0)
  722. vm.ok = true
  723. }).then(() => {
  724. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  725. }).thenWaitFor(nextFrame).then(() => {
  726. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  727. }).thenWaitFor(duration + 10).then(() => {
  728. expect(vm.$el.children[0].className).toBe('test')
  729. }).then(done)
  730. })
  731. })
  732. }