transition.spec.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636
  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 system', () => {
  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('transition with v-show', done => {
  363. const vm = new Vue({
  364. template: `
  365. <div>
  366. <transition name="test">
  367. <div v-show="ok" class="test">foo</div>
  368. </transition>
  369. </div>
  370. `,
  371. data: { ok: true }
  372. }).$mount(el)
  373. // should not apply transition on initial render by default
  374. expect(vm.$el.textContent).toBe('foo')
  375. expect(vm.$el.children[0].style.display).toBe('')
  376. vm.ok = false
  377. waitForUpdate(() => {
  378. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  379. }).thenWaitFor(nextFrame).then(() => {
  380. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  381. }).thenWaitFor(duration + 10).then(() => {
  382. expect(vm.$el.children[0].style.display).toBe('none')
  383. vm.ok = true
  384. }).then(() => {
  385. expect(vm.$el.children[0].style.display).toBe('')
  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.children[0].className).toBe('test')
  391. }).then(done)
  392. })
  393. it('leaveCancelled (v-show only)', done => {
  394. const spy = jasmine.createSpy('leaveCancelled')
  395. const vm = new Vue({
  396. template: `
  397. <div>
  398. <transition name="test" @leave-cancelled="leaveCancelled">
  399. <div v-show="ok" class="test">foo</div>
  400. </transition>
  401. </div>
  402. `,
  403. data: { ok: true },
  404. methods: {
  405. leaveCancelled: spy
  406. }
  407. }).$mount(el)
  408. expect(vm.$el.children[0].style.display).toBe('')
  409. vm.ok = false
  410. waitForUpdate(() => {
  411. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  412. }).thenWaitFor(nextFrame).then(() => {
  413. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  414. }).thenWaitFor(10).then(() => {
  415. vm.ok = true
  416. }).then(() => {
  417. expect(spy).toHaveBeenCalled()
  418. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  419. }).thenWaitFor(nextFrame).then(() => {
  420. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  421. }).thenWaitFor(duration + 10).then(() => {
  422. expect(vm.$el.children[0].style.display).toBe('')
  423. }).then(done)
  424. })
  425. it('animations', done => {
  426. const vm = new Vue({
  427. template: `
  428. <div>
  429. <transition name="test-anim">
  430. <div v-if="ok" class="test">foo</div>
  431. </transition>
  432. </div>
  433. `,
  434. data: { ok: true }
  435. }).$mount(el)
  436. // should not apply transition on initial render by default
  437. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  438. vm.ok = false
  439. waitForUpdate(() => {
  440. expect(vm.$el.children[0].className).toBe('test test-anim-leave test-anim-leave-active')
  441. }).thenWaitFor(nextFrame).then(() => {
  442. expect(vm.$el.children[0].className).toBe('test test-anim-leave-active')
  443. }).thenWaitFor(duration + 10).then(() => {
  444. expect(vm.$el.children.length).toBe(0)
  445. vm.ok = true
  446. }).then(() => {
  447. expect(vm.$el.children[0].className).toBe('test test-anim-enter test-anim-enter-active')
  448. }).thenWaitFor(nextFrame).then(() => {
  449. expect(vm.$el.children[0].className).toBe('test test-anim-enter-active')
  450. }).thenWaitFor(duration + 10).then(() => {
  451. expect(vm.$el.children[0].className).toBe('test')
  452. }).then(done)
  453. })
  454. it('transition on appear', done => {
  455. const vm = new Vue({
  456. template: `
  457. <div>
  458. <transition name="test"
  459. appear
  460. appear-class="test-appear"
  461. appear-active-class="test-appear-active">
  462. <div v-if="ok" class="test">foo</div>
  463. </transition>
  464. </div>
  465. `,
  466. data: { ok: true }
  467. }).$mount(el)
  468. waitForUpdate(() => {
  469. expect(vm.$el.children[0].className).toBe('test test-appear test-appear-active')
  470. }).thenWaitFor(nextFrame).then(() => {
  471. expect(vm.$el.children[0].className).toBe('test test-appear-active')
  472. }).thenWaitFor(duration + 10).then(() => {
  473. expect(vm.$el.children[0].className).toBe('test')
  474. }).then(done)
  475. })
  476. it('transition on appear with v-show', done => {
  477. const vm = new Vue({
  478. template: `
  479. <div>
  480. <transition name="test" appear>
  481. <div v-show="ok" class="test">foo</div>
  482. </transition>
  483. </div>
  484. `,
  485. data: { ok: true }
  486. }).$mount(el)
  487. waitForUpdate(() => {
  488. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  489. }).thenWaitFor(nextFrame).then(() => {
  490. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  491. }).thenWaitFor(duration + 10).then(() => {
  492. expect(vm.$el.children[0].className).toBe('test')
  493. }).then(done)
  494. })
  495. it('transition on SVG elements', done => {
  496. const vm = new Vue({
  497. template: `
  498. <svg>
  499. <transition>
  500. <circle cx="0" cy="0" r="10" v-if="ok" class="test"></circle>
  501. </transition>
  502. </svg>
  503. `,
  504. data: { ok: true }
  505. }).$mount(el)
  506. // should not apply transition on initial render by default
  507. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  508. vm.ok = false
  509. waitForUpdate(() => {
  510. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave v-leave-active')
  511. }).thenWaitFor(nextFrame).then(() => {
  512. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave-active')
  513. }).thenWaitFor(duration + 10).then(() => {
  514. expect(vm.$el.childNodes.length).toBe(1)
  515. expect(vm.$el.childNodes[0].nodeType).toBe(3) // should be an empty text node
  516. expect(vm.$el.childNodes[0].textContent).toBe('')
  517. vm.ok = true
  518. }).then(() => {
  519. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-enter v-enter-active')
  520. }).thenWaitFor(nextFrame).then(() => {
  521. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-enter-active')
  522. }).thenWaitFor(duration + 10).then(() => {
  523. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  524. }).then(done)
  525. })
  526. it('transition on child components', done => {
  527. const vm = new Vue({
  528. template: `
  529. <div>
  530. <transition>
  531. <test v-if="ok" class="test"></test>
  532. </transition>
  533. </div>
  534. `,
  535. data: { ok: true },
  536. components: {
  537. test: {
  538. template: `
  539. <transition name="test">
  540. <div>foo</div>
  541. </transition>
  542. ` // test transition override from parent
  543. }
  544. }
  545. }).$mount(el)
  546. // should not apply transition on initial render by default
  547. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  548. vm.ok = false
  549. waitForUpdate(() => {
  550. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  551. }).thenWaitFor(nextFrame).then(() => {
  552. expect(vm.$el.children[0].className).toBe('test v-leave-active')
  553. }).thenWaitFor(duration + 10).then(() => {
  554. expect(vm.$el.children.length).toBe(0)
  555. vm.ok = true
  556. }).then(() => {
  557. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  558. }).thenWaitFor(nextFrame).then(() => {
  559. expect(vm.$el.children[0].className).toBe('test v-enter-active')
  560. }).thenWaitFor(duration + 10).then(() => {
  561. expect(vm.$el.children[0].className).toBe('test')
  562. }).then(done)
  563. })
  564. it('custom transition higher-order component', done => {
  565. const vm = new Vue({
  566. template: '<div><my-transition><div v-if="ok" class="test">foo</div></my-transition></div>',
  567. data: { ok: true },
  568. components: {
  569. 'my-transition': {
  570. functional: true,
  571. render (h, { data, children }) {
  572. (data.props || (data.props = {})).name = 'test'
  573. return h('transition', data, children)
  574. }
  575. }
  576. }
  577. }).$mount(el)
  578. // should not apply transition on initial render by default
  579. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  580. vm.ok = false
  581. waitForUpdate(() => {
  582. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  583. }).thenWaitFor(nextFrame).then(() => {
  584. expect(vm.$el.children[0].className).toBe('test test-leave-active')
  585. }).thenWaitFor(duration + 10).then(() => {
  586. expect(vm.$el.children.length).toBe(0)
  587. vm.ok = true
  588. }).then(() => {
  589. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  590. }).thenWaitFor(nextFrame).then(() => {
  591. expect(vm.$el.children[0].className).toBe('test test-enter-active')
  592. }).thenWaitFor(duration + 10).then(() => {
  593. expect(vm.$el.children[0].className).toBe('test')
  594. }).then(done)
  595. })
  596. })
  597. }