transition.spec.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305
  1. import Vue from 'vue'
  2. import injectStyles from './inject-styles'
  3. import { isIE9 } from 'core/util/env'
  4. import { nextFrame } from 'web/runtime/transition-util'
  5. if (!isIE9) {
  6. describe('Transition basic', () => {
  7. const { duration, buffer } = injectStyles()
  8. const explicitDuration = duration * 2
  9. let el
  10. beforeEach(() => {
  11. el = document.createElement('div')
  12. document.body.appendChild(el)
  13. })
  14. it('basic transition', done => {
  15. const vm = new Vue({
  16. template: '<div><transition><div v-if="ok" class="test">foo</div></transition></div>',
  17. data: { ok: true }
  18. }).$mount(el)
  19. // should not apply transition on initial render by default
  20. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  21. vm.ok = false
  22. waitForUpdate(() => {
  23. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  24. }).thenWaitFor(nextFrame).then(() => {
  25. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  26. }).thenWaitFor(duration + buffer).then(() => {
  27. expect(vm.$el.children.length).toBe(0)
  28. vm.ok = true
  29. }).then(() => {
  30. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  31. }).thenWaitFor(nextFrame).then(() => {
  32. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  33. }).thenWaitFor(duration + buffer).then(() => {
  34. expect(vm.$el.children[0].className).toBe('test')
  35. }).then(done)
  36. })
  37. it('named transition', done => {
  38. const vm = new Vue({
  39. template: '<div><transition name="test"><div v-if="ok" class="test">foo</div></transition></div>',
  40. data: { ok: true }
  41. }).$mount(el)
  42. // should not apply transition on initial render by default
  43. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  44. vm.ok = false
  45. waitForUpdate(() => {
  46. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  47. }).thenWaitFor(nextFrame).then(() => {
  48. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  49. }).thenWaitFor(duration + buffer).then(() => {
  50. expect(vm.$el.children.length).toBe(0)
  51. vm.ok = true
  52. }).then(() => {
  53. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  54. }).thenWaitFor(nextFrame).then(() => {
  55. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  56. }).thenWaitFor(duration + buffer).then(() => {
  57. expect(vm.$el.children[0].className).toBe('test')
  58. }).then(done)
  59. })
  60. it('custom transition classes', done => {
  61. const vm = new Vue({
  62. template: `
  63. <div>
  64. <transition
  65. enter-class="hello"
  66. enter-active-class="hello-active"
  67. enter-to-class="hello-to"
  68. leave-class="bye"
  69. leave-to-class="bye-to"
  70. leave-active-class="byebye active more ">
  71. <div v-if="ok" class="test">foo</div>
  72. </transition>
  73. </div>
  74. `,
  75. data: { ok: true }
  76. }).$mount(el)
  77. // should not apply transition on initial render by default
  78. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  79. vm.ok = false
  80. waitForUpdate(() => {
  81. expect(vm.$el.children[0].className).toBe('test bye byebye active more')
  82. }).thenWaitFor(nextFrame).then(() => {
  83. expect(vm.$el.children[0].className).toBe('test byebye active more bye-to')
  84. }).thenWaitFor(duration + buffer).then(() => {
  85. expect(vm.$el.children.length).toBe(0)
  86. vm.ok = true
  87. }).then(() => {
  88. expect(vm.$el.children[0].className).toBe('test hello hello-active')
  89. }).thenWaitFor(nextFrame).then(() => {
  90. expect(vm.$el.children[0].className).toBe('test hello-active hello-to')
  91. }).thenWaitFor(duration + buffer).then(() => {
  92. expect(vm.$el.children[0].className).toBe('test')
  93. }).then(done)
  94. })
  95. it('dynamic transition', done => {
  96. const vm = new Vue({
  97. template: `
  98. <div>
  99. <transition :name="trans">
  100. <div v-if="ok" class="test">foo</div>
  101. </transition>
  102. </div>
  103. `,
  104. data: {
  105. ok: true,
  106. trans: 'test'
  107. }
  108. }).$mount(el)
  109. // should not apply transition on initial render by default
  110. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  111. vm.ok = false
  112. waitForUpdate(() => {
  113. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  114. }).thenWaitFor(nextFrame).then(() => {
  115. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  116. }).thenWaitFor(duration + buffer).then(() => {
  117. expect(vm.$el.children.length).toBe(0)
  118. vm.ok = true
  119. vm.trans = 'changed'
  120. }).then(() => {
  121. expect(vm.$el.children[0].className).toBe('test changed-enter changed-enter-active')
  122. }).thenWaitFor(nextFrame).then(() => {
  123. expect(vm.$el.children[0].className).toBe('test changed-enter-active changed-enter-to')
  124. }).thenWaitFor(duration + buffer).then(() => {
  125. expect(vm.$el.children[0].className).toBe('test')
  126. }).then(done)
  127. })
  128. it('inline transition object', done => {
  129. const enter = jasmine.createSpy('enter')
  130. const leave = jasmine.createSpy('leave')
  131. const vm = new Vue({
  132. render (h) {
  133. return h('div', null, [
  134. h('transition', {
  135. props: {
  136. name: 'inline',
  137. enterClass: 'hello',
  138. enterToClass: 'hello-to',
  139. enterActiveClass: 'hello-active',
  140. leaveClass: 'bye',
  141. leaveToClass: 'bye-to',
  142. leaveActiveClass: 'byebye active'
  143. },
  144. on: {
  145. enter,
  146. leave
  147. }
  148. }, this.ok ? [h('div', { class: 'test' }, 'foo')] : undefined)
  149. ])
  150. },
  151. data: { ok: true }
  152. }).$mount(el)
  153. // should not apply transition on initial render by default
  154. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  155. vm.ok = false
  156. waitForUpdate(() => {
  157. expect(vm.$el.children[0].className).toBe('test bye byebye active')
  158. expect(leave).toHaveBeenCalled()
  159. }).thenWaitFor(nextFrame).then(() => {
  160. expect(vm.$el.children[0].className).toBe('test byebye active bye-to')
  161. }).thenWaitFor(duration + buffer).then(() => {
  162. expect(vm.$el.children.length).toBe(0)
  163. vm.ok = true
  164. }).then(() => {
  165. expect(vm.$el.children[0].className).toBe('test hello hello-active')
  166. expect(enter).toHaveBeenCalled()
  167. }).thenWaitFor(nextFrame).then(() => {
  168. expect(vm.$el.children[0].className).toBe('test hello-active hello-to')
  169. }).thenWaitFor(duration + buffer).then(() => {
  170. expect(vm.$el.children[0].className).toBe('test')
  171. }).then(done)
  172. })
  173. it('transition events', done => {
  174. const onLeaveSpy = jasmine.createSpy('leave')
  175. const onEnterSpy = jasmine.createSpy('enter')
  176. const beforeLeaveSpy = jasmine.createSpy('beforeLeave')
  177. const beforeEnterSpy = jasmine.createSpy('beforeEnter')
  178. const afterLeaveSpy = jasmine.createSpy('afterLeave')
  179. const afterEnterSpy = jasmine.createSpy('afterEnter')
  180. const vm = new Vue({
  181. template: `
  182. <div>
  183. <transition
  184. name="test"
  185. @before-enter="beforeEnter"
  186. @enter="enter"
  187. @after-enter="afterEnter"
  188. @before-leave="beforeLeave"
  189. @leave="leave"
  190. @after-leave="afterLeave">
  191. <div v-if="ok" class="test">foo</div>
  192. </transition>
  193. </div>
  194. `,
  195. data: { ok: true },
  196. methods: {
  197. beforeLeave: (el) => {
  198. expect(el).toBe(vm.$el.children[0])
  199. expect(el.className).toBe('test')
  200. beforeLeaveSpy(el)
  201. },
  202. leave: (el) => onLeaveSpy(el),
  203. afterLeave: (el) => afterLeaveSpy(el),
  204. beforeEnter: (el) => {
  205. expect(vm.$el.contains(el)).toBe(false)
  206. expect(el.className).toBe('test')
  207. beforeEnterSpy(el)
  208. },
  209. enter: (el) => {
  210. expect(vm.$el.contains(el)).toBe(true)
  211. onEnterSpy(el)
  212. },
  213. afterEnter: (el) => afterEnterSpy(el)
  214. }
  215. }).$mount(el)
  216. // should not apply transition on initial render by default
  217. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  218. let _el = vm.$el.children[0]
  219. vm.ok = false
  220. waitForUpdate(() => {
  221. expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
  222. expect(onLeaveSpy).toHaveBeenCalledWith(_el)
  223. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  224. }).thenWaitFor(nextFrame).then(() => {
  225. expect(afterLeaveSpy).not.toHaveBeenCalled()
  226. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  227. }).thenWaitFor(duration + buffer).then(() => {
  228. expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
  229. expect(vm.$el.children.length).toBe(0)
  230. vm.ok = true
  231. }).then(() => {
  232. _el = vm.$el.children[0]
  233. expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
  234. expect(onEnterSpy).toHaveBeenCalledWith(_el)
  235. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  236. }).thenWaitFor(nextFrame).then(() => {
  237. expect(afterEnterSpy).not.toHaveBeenCalled()
  238. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  239. }).thenWaitFor(duration + buffer).then(() => {
  240. expect(afterEnterSpy).toHaveBeenCalledWith(_el)
  241. expect(vm.$el.children[0].className).toBe('test')
  242. }).then(done)
  243. })
  244. it('transition events (v-show)', done => {
  245. const onLeaveSpy = jasmine.createSpy('leave')
  246. const onEnterSpy = jasmine.createSpy('enter')
  247. const beforeLeaveSpy = jasmine.createSpy('beforeLeave')
  248. const beforeEnterSpy = jasmine.createSpy('beforeEnter')
  249. const afterLeaveSpy = jasmine.createSpy('afterLeave')
  250. const afterEnterSpy = jasmine.createSpy('afterEnter')
  251. const vm = new Vue({
  252. template: `
  253. <div>
  254. <transition
  255. name="test"
  256. @before-enter="beforeEnter"
  257. @enter="enter"
  258. @after-enter="afterEnter"
  259. @before-leave="beforeLeave"
  260. @leave="leave"
  261. @after-leave="afterLeave">
  262. <div v-show="ok" class="test">foo</div>
  263. </transition>
  264. </div>
  265. `,
  266. data: { ok: true },
  267. methods: {
  268. beforeLeave: (el) => {
  269. expect(el.style.display).toBe('')
  270. expect(el).toBe(vm.$el.children[0])
  271. expect(el.className).toBe('test')
  272. beforeLeaveSpy(el)
  273. },
  274. leave: (el) => {
  275. expect(el.style.display).toBe('')
  276. onLeaveSpy(el)
  277. },
  278. afterLeave: (el) => {
  279. expect(el.style.display).toBe('none')
  280. afterLeaveSpy(el)
  281. },
  282. beforeEnter: (el) => {
  283. expect(el.className).toBe('test')
  284. expect(el.style.display).toBe('none')
  285. beforeEnterSpy(el)
  286. },
  287. enter: (el) => {
  288. expect(el.style.display).toBe('')
  289. onEnterSpy(el)
  290. },
  291. afterEnter: (el) => {
  292. expect(el.style.display).toBe('')
  293. afterEnterSpy(el)
  294. }
  295. }
  296. }).$mount(el)
  297. // should not apply transition on initial render by default
  298. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  299. let _el = vm.$el.children[0]
  300. vm.ok = false
  301. waitForUpdate(() => {
  302. expect(beforeLeaveSpy).toHaveBeenCalledWith(_el)
  303. expect(onLeaveSpy).toHaveBeenCalledWith(_el)
  304. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  305. }).thenWaitFor(nextFrame).then(() => {
  306. expect(afterLeaveSpy).not.toHaveBeenCalled()
  307. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  308. }).thenWaitFor(duration + buffer).then(() => {
  309. expect(afterLeaveSpy).toHaveBeenCalledWith(_el)
  310. expect(vm.$el.children[0].style.display).toBe('none')
  311. vm.ok = true
  312. }).then(() => {
  313. _el = vm.$el.children[0]
  314. expect(beforeEnterSpy).toHaveBeenCalledWith(_el)
  315. expect(onEnterSpy).toHaveBeenCalledWith(_el)
  316. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  317. }).thenWaitFor(nextFrame).then(() => {
  318. expect(afterEnterSpy).not.toHaveBeenCalled()
  319. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  320. }).thenWaitFor(duration + buffer).then(() => {
  321. expect(afterEnterSpy).toHaveBeenCalledWith(_el)
  322. expect(vm.$el.children[0].className).toBe('test')
  323. }).then(done)
  324. })
  325. it('explicit user callback in JavaScript hooks', done => {
  326. let next
  327. const vm = new Vue({
  328. template: `<div>
  329. <transition name="test" @enter="enter" @leave="leave">
  330. <div v-if="ok" class="test">foo</div>
  331. </transition>
  332. </div>`,
  333. data: { ok: true },
  334. methods: {
  335. enter: (el, cb) => {
  336. next = cb
  337. },
  338. leave: (el, cb) => {
  339. next = cb
  340. }
  341. }
  342. }).$mount(el)
  343. vm.ok = false
  344. waitForUpdate(() => {
  345. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  346. }).thenWaitFor(nextFrame).then(() => {
  347. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  348. }).thenWaitFor(duration + buffer).then(() => {
  349. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  350. expect(next).toBeTruthy()
  351. next()
  352. expect(vm.$el.children.length).toBe(0)
  353. }).then(() => {
  354. vm.ok = true
  355. }).then(() => {
  356. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  357. }).thenWaitFor(nextFrame).then(() => {
  358. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  359. }).thenWaitFor(duration + buffer).then(() => {
  360. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  361. expect(next).toBeTruthy()
  362. next()
  363. expect(vm.$el.children[0].className).toBe('test')
  364. }).then(done)
  365. })
  366. it('css: false', done => {
  367. const enterSpy = jasmine.createSpy('enter')
  368. const leaveSpy = jasmine.createSpy('leave')
  369. const vm = new Vue({
  370. template: `
  371. <div>
  372. <transition :css="false" name="test" @enter="enter" @leave="leave">
  373. <div v-if="ok" class="test">foo</div>
  374. </transition>
  375. </div>
  376. `,
  377. data: { ok: true },
  378. methods: {
  379. enter: enterSpy,
  380. leave: leaveSpy
  381. }
  382. }).$mount(el)
  383. vm.ok = false
  384. waitForUpdate(() => {
  385. expect(leaveSpy).toHaveBeenCalled()
  386. expect(vm.$el.innerHTML).toBe('<!---->')
  387. vm.ok = true
  388. }).then(() => {
  389. expect(enterSpy).toHaveBeenCalled()
  390. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  391. }).then(done)
  392. })
  393. it('no transition detected', done => {
  394. const enterSpy = jasmine.createSpy('enter')
  395. const leaveSpy = jasmine.createSpy('leave')
  396. const vm = new Vue({
  397. template: '<div><transition name="nope" @enter="enter" @leave="leave"><div v-if="ok">foo</div></transition></div>',
  398. data: { ok: true },
  399. methods: {
  400. enter: enterSpy,
  401. leave: leaveSpy
  402. }
  403. }).$mount(el)
  404. vm.ok = false
  405. waitForUpdate(() => {
  406. expect(leaveSpy).toHaveBeenCalled()
  407. expect(vm.$el.innerHTML).toBe('<div class="nope-leave nope-leave-active">foo</div><!---->')
  408. }).thenWaitFor(nextFrame).then(() => {
  409. expect(vm.$el.innerHTML).toBe('<!---->')
  410. vm.ok = true
  411. }).then(() => {
  412. expect(enterSpy).toHaveBeenCalled()
  413. expect(vm.$el.innerHTML).toBe('<div class="nope-enter nope-enter-active">foo</div>')
  414. }).thenWaitFor(nextFrame).then(() => {
  415. expect(vm.$el.innerHTML).toBe('<div>foo</div>')
  416. }).then(done)
  417. })
  418. it('enterCancelled', done => {
  419. const spy = jasmine.createSpy('enterCancelled')
  420. const vm = new Vue({
  421. template: `
  422. <div>
  423. <transition name="test" @enter-cancelled="enterCancelled">
  424. <div v-if="ok" class="test">foo</div>
  425. </transition>
  426. </div>
  427. `,
  428. data: { ok: false },
  429. methods: {
  430. enterCancelled: spy
  431. }
  432. }).$mount(el)
  433. expect(vm.$el.innerHTML).toBe('<!---->')
  434. vm.ok = true
  435. waitForUpdate(() => {
  436. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  437. }).thenWaitFor(nextFrame).then(() => {
  438. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  439. }).thenWaitFor(duration / 2).then(() => {
  440. vm.ok = false
  441. }).then(() => {
  442. expect(spy).toHaveBeenCalled()
  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 test-leave-to')
  446. }).thenWaitFor(duration + buffer).then(() => {
  447. expect(vm.$el.children.length).toBe(0)
  448. }).then(done)
  449. })
  450. it('should remove stale leaving elements', done => {
  451. const spy = jasmine.createSpy('afterLeave')
  452. const vm = new Vue({
  453. template: `
  454. <div>
  455. <transition name="test" @after-leave="afterLeave">
  456. <div v-if="ok" class="test">foo</div>
  457. </transition>
  458. </div>
  459. `,
  460. data: { ok: true },
  461. methods: {
  462. afterLeave: spy
  463. }
  464. }).$mount(el)
  465. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  466. vm.ok = false
  467. waitForUpdate(() => {
  468. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  469. }).thenWaitFor(duration / 2).then(() => {
  470. vm.ok = true
  471. }).then(() => {
  472. expect(spy).toHaveBeenCalled()
  473. expect(vm.$el.children.length).toBe(1) // should have removed leaving element
  474. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  475. }).thenWaitFor(nextFrame).then(() => {
  476. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  477. }).thenWaitFor(duration + buffer).then(() => {
  478. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  479. }).then(done)
  480. })
  481. it('transition with v-show', done => {
  482. const vm = new Vue({
  483. template: `
  484. <div>
  485. <transition name="test">
  486. <div v-show="ok" class="test">foo</div>
  487. </transition>
  488. </div>
  489. `,
  490. data: { ok: true }
  491. }).$mount(el)
  492. // should not apply transition on initial render by default
  493. expect(vm.$el.textContent).toBe('foo')
  494. expect(vm.$el.children[0].style.display).toBe('')
  495. expect(vm.$el.children[0].className).toBe('test')
  496. vm.ok = false
  497. waitForUpdate(() => {
  498. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  499. }).thenWaitFor(nextFrame).then(() => {
  500. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  501. }).thenWaitFor(duration + buffer).then(() => {
  502. expect(vm.$el.children[0].style.display).toBe('none')
  503. vm.ok = true
  504. }).then(() => {
  505. expect(vm.$el.children[0].style.display).toBe('')
  506. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  507. }).thenWaitFor(nextFrame).then(() => {
  508. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  509. }).thenWaitFor(duration + buffer).then(() => {
  510. expect(vm.$el.children[0].className).toBe('test')
  511. }).then(done)
  512. })
  513. it('transition with v-show, inside child component', done => {
  514. const vm = new Vue({
  515. template: `
  516. <div>
  517. <test v-show="ok"></test>
  518. </div>
  519. `,
  520. data: { ok: true },
  521. components: {
  522. test: {
  523. template: `<transition name="test"><div class="test">foo</div></transition>`
  524. }
  525. }
  526. }).$mount(el)
  527. // should not apply transition on initial render by default
  528. expect(vm.$el.textContent).toBe('foo')
  529. expect(vm.$el.children[0].style.display).toBe('')
  530. vm.ok = false
  531. waitForUpdate(() => {
  532. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  533. }).thenWaitFor(nextFrame).then(() => {
  534. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  535. }).thenWaitFor(duration + buffer).then(() => {
  536. expect(vm.$el.children[0].style.display).toBe('none')
  537. vm.ok = true
  538. }).then(() => {
  539. expect(vm.$el.children[0].style.display).toBe('')
  540. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  541. }).thenWaitFor(nextFrame).then(() => {
  542. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  543. }).thenWaitFor(duration + buffer).then(() => {
  544. expect(vm.$el.children[0].className).toBe('test')
  545. }).then(done)
  546. })
  547. it('leaveCancelled (v-show only)', done => {
  548. const spy = jasmine.createSpy('leaveCancelled')
  549. const vm = new Vue({
  550. template: `
  551. <div>
  552. <transition name="test" @leave-cancelled="leaveCancelled">
  553. <div v-show="ok" class="test">foo</div>
  554. </transition>
  555. </div>
  556. `,
  557. data: { ok: true },
  558. methods: {
  559. leaveCancelled: spy
  560. }
  561. }).$mount(el)
  562. expect(vm.$el.children[0].style.display).toBe('')
  563. vm.ok = false
  564. waitForUpdate(() => {
  565. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  566. }).thenWaitFor(nextFrame).then(() => {
  567. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  568. }).thenWaitFor(10).then(() => {
  569. vm.ok = true
  570. }).then(() => {
  571. expect(spy).toHaveBeenCalled()
  572. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  573. }).thenWaitFor(nextFrame).then(() => {
  574. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  575. }).thenWaitFor(duration + buffer).then(() => {
  576. expect(vm.$el.children[0].style.display).toBe('')
  577. }).then(done)
  578. })
  579. it('leave transition with v-show: cancelled on next frame', done => {
  580. const vm = new Vue({
  581. template: `
  582. <div>
  583. <transition name="test">
  584. <div v-show="ok" class="test">foo</div>
  585. </transition>
  586. </div>
  587. `,
  588. data: { ok: true }
  589. }).$mount(el)
  590. vm.ok = false
  591. waitForUpdate(() => {
  592. vm.ok = true
  593. }).thenWaitFor(nextFrame).then(() => {
  594. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  595. }).thenWaitFor(duration + buffer).then(() => {
  596. expect(vm.$el.children[0].className).toBe('test')
  597. }).then(done)
  598. })
  599. it('enter transition with v-show: cancelled on next frame', done => {
  600. const vm = new Vue({
  601. template: `
  602. <div>
  603. <transition name="test">
  604. <div v-show="ok" class="test">foo</div>
  605. </transition>
  606. </div>
  607. `,
  608. data: { ok: false }
  609. }).$mount(el)
  610. vm.ok = true
  611. waitForUpdate(() => {
  612. vm.ok = false
  613. }).thenWaitFor(nextFrame).then(() => {
  614. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  615. }).thenWaitFor(duration + buffer).then(() => {
  616. expect(vm.$el.children[0].className).toBe('test')
  617. }).then(done)
  618. })
  619. it('animations', done => {
  620. const vm = new Vue({
  621. template: `
  622. <div>
  623. <transition name="test-anim">
  624. <div v-if="ok">foo</div>
  625. </transition>
  626. </div>
  627. `,
  628. data: { ok: true }
  629. }).$mount(el)
  630. // should not apply transition on initial render by default
  631. expect(vm.$el.innerHTML).toBe('<div>foo</div>')
  632. vm.ok = false
  633. waitForUpdate(() => {
  634. expect(vm.$el.children[0].className).toBe('test-anim-leave test-anim-leave-active')
  635. }).thenWaitFor(nextFrame).then(() => {
  636. expect(vm.$el.children[0].className).toBe('test-anim-leave-active test-anim-leave-to')
  637. }).thenWaitFor(duration + buffer).then(() => {
  638. expect(vm.$el.children.length).toBe(0)
  639. vm.ok = true
  640. }).then(() => {
  641. expect(vm.$el.children[0].className).toBe('test-anim-enter test-anim-enter-active')
  642. }).thenWaitFor(nextFrame).then(() => {
  643. expect(vm.$el.children[0].className).toBe('test-anim-enter-active test-anim-enter-to')
  644. }).thenWaitFor(duration + buffer).then(() => {
  645. expect(vm.$el.children[0].className).toBe('')
  646. }).then(done)
  647. })
  648. it('explicit transition type', done => {
  649. const vm = new Vue({
  650. template: `
  651. <div>
  652. <transition name="test-anim-long" type="animation">
  653. <div v-if="ok" class="test">foo</div>
  654. </transition>
  655. </div>
  656. `,
  657. data: { ok: true }
  658. }).$mount(el)
  659. // should not apply transition on initial render by default
  660. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  661. vm.ok = false
  662. waitForUpdate(() => {
  663. expect(vm.$el.children[0].className).toBe('test test-anim-long-leave test-anim-long-leave-active')
  664. }).thenWaitFor(nextFrame).then(() => {
  665. expect(vm.$el.children[0].className).toBe('test test-anim-long-leave-active test-anim-long-leave-to')
  666. }).thenWaitFor(duration + 5).then(() => {
  667. // should not end early due to transition presence
  668. expect(vm.$el.children[0].className).toBe('test test-anim-long-leave-active test-anim-long-leave-to')
  669. }).thenWaitFor(duration + 5).then(() => {
  670. expect(vm.$el.children.length).toBe(0)
  671. vm.ok = true
  672. }).then(() => {
  673. expect(vm.$el.children[0].className).toBe('test test-anim-long-enter test-anim-long-enter-active')
  674. }).thenWaitFor(nextFrame).then(() => {
  675. expect(vm.$el.children[0].className).toBe('test test-anim-long-enter-active test-anim-long-enter-to')
  676. }).thenWaitFor(duration + 5).then(() => {
  677. expect(vm.$el.children[0].className).toBe('test test-anim-long-enter-active test-anim-long-enter-to')
  678. }).thenWaitFor(duration + 5).then(() => {
  679. expect(vm.$el.children[0].className).toBe('test')
  680. }).then(done)
  681. })
  682. it('transition on appear', done => {
  683. const vm = new Vue({
  684. template: `
  685. <div>
  686. <transition name="test"
  687. appear
  688. appear-class="test-appear"
  689. appear-to-class="test-appear-to"
  690. appear-active-class="test-appear-active">
  691. <div v-if="ok" class="test">foo</div>
  692. </transition>
  693. </div>
  694. `,
  695. data: { ok: true }
  696. }).$mount(el)
  697. waitForUpdate(() => {
  698. expect(vm.$el.children[0].className).toBe('test test-appear test-appear-active')
  699. }).thenWaitFor(nextFrame).then(() => {
  700. expect(vm.$el.children[0].className).toBe('test test-appear-active test-appear-to')
  701. }).thenWaitFor(duration + buffer).then(() => {
  702. expect(vm.$el.children[0].className).toBe('test')
  703. }).then(done)
  704. })
  705. it('transition on appear with v-show', done => {
  706. const vm = new Vue({
  707. template: `
  708. <div>
  709. <transition name="test" appear>
  710. <div v-show="ok" class="test">foo</div>
  711. </transition>
  712. </div>
  713. `,
  714. data: { ok: true }
  715. }).$mount(el)
  716. waitForUpdate(() => {
  717. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  718. }).thenWaitFor(nextFrame).then(() => {
  719. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  720. }).thenWaitFor(duration + buffer).then(() => {
  721. expect(vm.$el.children[0].className).toBe('test')
  722. }).then(done)
  723. })
  724. it('transition on SVG elements', done => {
  725. const vm = new Vue({
  726. template: `
  727. <svg>
  728. <transition>
  729. <circle cx="0" cy="0" r="10" v-if="ok" class="test"></circle>
  730. </transition>
  731. </svg>
  732. `,
  733. data: { ok: true }
  734. }).$mount(el)
  735. // should not apply transition on initial render by default
  736. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  737. vm.ok = false
  738. waitForUpdate(() => {
  739. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave v-leave-active')
  740. }).thenWaitFor(nextFrame).then(() => {
  741. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-leave-active v-leave-to')
  742. }).thenWaitFor(duration + buffer).then(() => {
  743. expect(vm.$el.childNodes.length).toBe(1)
  744. expect(vm.$el.childNodes[0].nodeType).toBe(8) // should be an empty comment node
  745. expect(vm.$el.childNodes[0].textContent).toBe('')
  746. vm.ok = true
  747. }).then(() => {
  748. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-enter v-enter-active')
  749. }).thenWaitFor(nextFrame).then(() => {
  750. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test v-enter-active v-enter-to')
  751. }).thenWaitFor(duration + buffer).then(() => {
  752. expect(vm.$el.childNodes[0].getAttribute('class')).toBe('test')
  753. }).then(done)
  754. })
  755. it('transition on child components', done => {
  756. const vm = new Vue({
  757. template: `
  758. <div>
  759. <transition>
  760. <test v-if="ok" class="test"></test>
  761. </transition>
  762. </div>
  763. `,
  764. data: { ok: true },
  765. components: {
  766. test: {
  767. template: `
  768. <transition name="test">
  769. <div>foo</div>
  770. </transition>
  771. ` // test transition override from parent
  772. }
  773. }
  774. }).$mount(el)
  775. // should not apply transition on initial render by default
  776. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  777. vm.ok = false
  778. waitForUpdate(() => {
  779. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  780. }).thenWaitFor(nextFrame).then(() => {
  781. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  782. }).thenWaitFor(duration + buffer).then(() => {
  783. expect(vm.$el.children.length).toBe(0)
  784. vm.ok = true
  785. }).then(() => {
  786. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  787. }).thenWaitFor(nextFrame).then(() => {
  788. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  789. }).thenWaitFor(duration + buffer).then(() => {
  790. expect(vm.$el.children[0].className).toBe('test')
  791. }).then(done)
  792. })
  793. it('transition inside child component with v-if', done => {
  794. const vm = new Vue({
  795. template: `
  796. <div>
  797. <test v-if="ok" class="test"></test>
  798. </div>
  799. `,
  800. data: { ok: true },
  801. components: {
  802. test: {
  803. template: `
  804. <transition>
  805. <div>foo</div>
  806. </transition>
  807. `
  808. }
  809. }
  810. }).$mount(el)
  811. // should not apply transition on initial render by default
  812. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  813. vm.ok = false
  814. waitForUpdate(() => {
  815. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  816. }).thenWaitFor(nextFrame).then(() => {
  817. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  818. }).thenWaitFor(duration + buffer).then(() => {
  819. expect(vm.$el.children.length).toBe(0)
  820. vm.ok = true
  821. }).then(() => {
  822. expect(vm.$el.children[0].className).toBe('test')
  823. }).then(done)
  824. })
  825. it('transition with appear inside child component with v-if', done => {
  826. const vm = new Vue({
  827. template: `
  828. <div>
  829. <test v-if="ok" class="test"></test>
  830. </div>
  831. `,
  832. data: { ok: true },
  833. components: {
  834. test: {
  835. template: `
  836. <transition appear
  837. appear-class="test-appear"
  838. appear-to-class="test-appear-to"
  839. appear-active-class="test-appear-active">
  840. <div>foo</div>
  841. </transition>
  842. `
  843. }
  844. }
  845. }).$mount(el)
  846. waitForUpdate(() => {
  847. expect(vm.$el.children[0].className).toBe('test test-appear test-appear-active')
  848. }).thenWaitFor(nextFrame).then(() => {
  849. expect(vm.$el.children[0].className).toBe('test test-appear-active test-appear-to')
  850. }).thenWaitFor(duration + buffer).then(() => {
  851. expect(vm.$el.children[0].className).toBe('test')
  852. vm.ok = false
  853. }).then(() => {
  854. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  855. }).thenWaitFor(nextFrame).then(() => {
  856. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  857. }).thenWaitFor(duration + buffer).then(() => {
  858. expect(vm.$el.children.length).toBe(0)
  859. }).then(done)
  860. })
  861. it('transition inside nested child component with v-if', done => {
  862. const vm = new Vue({
  863. template: `
  864. <div>
  865. <foo v-if="ok" class="test"></foo>
  866. </div>
  867. `,
  868. data: { ok: true },
  869. components: {
  870. foo: {
  871. template: '<bar></bar>',
  872. components: {
  873. bar: {
  874. template: '<transition><div>foo</div></transition>'
  875. }
  876. }
  877. }
  878. }
  879. }).$mount(el)
  880. // should not apply transition on initial render by default
  881. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  882. vm.ok = false
  883. waitForUpdate(() => {
  884. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  885. }).thenWaitFor(nextFrame).then(() => {
  886. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  887. }).thenWaitFor(duration + buffer).then(() => {
  888. expect(vm.$el.children.length).toBe(0)
  889. vm.ok = true
  890. }).then(() => {
  891. expect(vm.$el.children[0].className).toBe('test')
  892. }).then(done)
  893. })
  894. it('transition with appear inside nested child component with v-if', done => {
  895. const vm = new Vue({
  896. template: `
  897. <div>
  898. <foo v-if="ok" class="test"></foo>
  899. </div>
  900. `,
  901. data: { ok: true },
  902. components: {
  903. foo: {
  904. template: '<bar></bar>',
  905. components: {
  906. bar: {
  907. template: `
  908. <transition appear
  909. appear-class="test-appear"
  910. appear-to-class="test-appear-to"
  911. appear-active-class="test-appear-active">
  912. <div>foo</div>
  913. </transition>
  914. `
  915. }
  916. }
  917. }
  918. }
  919. }).$mount(el)
  920. waitForUpdate(() => {
  921. expect(vm.$el.children[0].className).toBe('test test-appear test-appear-active')
  922. }).thenWaitFor(nextFrame).then(() => {
  923. expect(vm.$el.children[0].className).toBe('test test-appear-active test-appear-to')
  924. }).thenWaitFor(duration + buffer).then(() => {
  925. expect(vm.$el.children[0].className).toBe('test')
  926. vm.ok = false
  927. }).then(() => {
  928. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  929. }).thenWaitFor(nextFrame).then(() => {
  930. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  931. }).thenWaitFor(duration + buffer).then(() => {
  932. expect(vm.$el.children.length).toBe(0)
  933. }).then(done)
  934. })
  935. it('custom transition higher-order component', done => {
  936. const vm = new Vue({
  937. template: '<div><my-transition><div v-if="ok" class="test">foo</div></my-transition></div>',
  938. data: { ok: true },
  939. components: {
  940. 'my-transition': {
  941. functional: true,
  942. render (h, { data, children }) {
  943. (data.props || (data.props = {})).name = 'test'
  944. return h('transition', data, children)
  945. }
  946. }
  947. }
  948. }).$mount(el)
  949. // should not apply transition on initial render by default
  950. expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
  951. vm.ok = false
  952. waitForUpdate(() => {
  953. expect(vm.$el.children[0].className).toBe('test test-leave test-leave-active')
  954. }).thenWaitFor(nextFrame).then(() => {
  955. expect(vm.$el.children[0].className).toBe('test test-leave-active test-leave-to')
  956. }).thenWaitFor(duration + buffer).then(() => {
  957. expect(vm.$el.children.length).toBe(0)
  958. vm.ok = true
  959. }).then(() => {
  960. expect(vm.$el.children[0].className).toBe('test test-enter test-enter-active')
  961. }).thenWaitFor(nextFrame).then(() => {
  962. expect(vm.$el.children[0].className).toBe('test test-enter-active test-enter-to')
  963. }).thenWaitFor(duration + buffer).then(() => {
  964. expect(vm.$el.children[0].className).toBe('test')
  965. }).then(done)
  966. })
  967. it('warn when used on multiple elements', () => {
  968. new Vue({
  969. template: `<transition><p>1</p><p>2</p></transition>`
  970. }).$mount()
  971. expect(`<transition> can only be used on a single element`).toHaveBeenWarned()
  972. })
  973. describe('explicit durations -', () => {
  974. it('single value', done => {
  975. const vm = new Vue({
  976. template: `
  977. <div>
  978. <transition duration="${explicitDuration}">
  979. <div v-if="ok" class="test">foo</div>
  980. </transition>
  981. </div>
  982. `,
  983. data: { ok: true }
  984. }).$mount(el)
  985. vm.ok = false
  986. waitForUpdate(() => {
  987. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  988. }).thenWaitFor(nextFrame).then(() => {
  989. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  990. }).thenWaitFor(explicitDuration + buffer).then(() => {
  991. expect(vm.$el.children.length).toBe(0)
  992. vm.ok = true
  993. }).then(() => {
  994. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  995. }).thenWaitFor(nextFrame).then(() => {
  996. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  997. }).thenWaitFor(explicitDuration + buffer).then(() => {
  998. expect(vm.$el.children[0].className).toBe('test')
  999. }).then(done)
  1000. })
  1001. it('enter and auto leave', done => {
  1002. const vm = new Vue({
  1003. template: `
  1004. <div>
  1005. <transition :duration="{ enter: ${explicitDuration} }">
  1006. <div v-if="ok" class="test">foo</div>
  1007. </transition>
  1008. </div>
  1009. `,
  1010. data: { ok: true }
  1011. }).$mount(el)
  1012. vm.ok = false
  1013. waitForUpdate(() => {
  1014. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1015. }).thenWaitFor(nextFrame).then(() => {
  1016. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  1017. }).thenWaitFor(duration + buffer).then(() => {
  1018. expect(vm.$el.children.length).toBe(0)
  1019. vm.ok = true
  1020. }).then(() => {
  1021. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  1022. }).thenWaitFor(nextFrame).then(() => {
  1023. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  1024. }).thenWaitFor(explicitDuration + buffer).then(() => {
  1025. expect(vm.$el.children[0].className).toBe('test')
  1026. }).then(done)
  1027. })
  1028. it('leave and auto enter', done => {
  1029. const vm = new Vue({
  1030. template: `
  1031. <div>
  1032. <transition :duration="{ leave: ${explicitDuration} }">
  1033. <div v-if="ok" class="test">foo</div>
  1034. </transition>
  1035. </div>
  1036. `,
  1037. data: { ok: true }
  1038. }).$mount(el)
  1039. vm.ok = false
  1040. waitForUpdate(() => {
  1041. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1042. }).thenWaitFor(nextFrame).then(() => {
  1043. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  1044. }).thenWaitFor(explicitDuration + buffer).then(() => {
  1045. expect(vm.$el.children.length).toBe(0)
  1046. vm.ok = true
  1047. }).then(() => {
  1048. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  1049. }).thenWaitFor(nextFrame).then(() => {
  1050. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  1051. }).thenWaitFor(duration + buffer).then(() => {
  1052. expect(vm.$el.children[0].className).toBe('test')
  1053. }).then(done)
  1054. })
  1055. it('separate enter and leave', done => {
  1056. const enter = explicitDuration
  1057. const leave = explicitDuration * 2
  1058. const vm = new Vue({
  1059. template: `
  1060. <div>
  1061. <transition :duration="{ enter: ${enter}, leave: ${leave} }">
  1062. <div v-if="ok" class="test">foo</div>
  1063. </transition>
  1064. </div>
  1065. `,
  1066. data: { ok: true }
  1067. }).$mount(el)
  1068. vm.ok = false
  1069. waitForUpdate(() => {
  1070. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1071. }).thenWaitFor(nextFrame).then(() => {
  1072. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  1073. }).thenWaitFor(leave + buffer).then(() => {
  1074. expect(vm.$el.children.length).toBe(0)
  1075. vm.ok = true
  1076. }).then(() => {
  1077. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  1078. }).thenWaitFor(nextFrame).then(() => {
  1079. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  1080. }).thenWaitFor(enter + buffer).then(() => {
  1081. expect(vm.$el.children[0].className).toBe('test')
  1082. }).then(done)
  1083. })
  1084. it('enter and leave + duration change', done => {
  1085. const enter1 = explicitDuration * 2
  1086. const enter2 = explicitDuration
  1087. const leave1 = explicitDuration * 0.5
  1088. const leave2 = explicitDuration * 3
  1089. const vm = new Vue({
  1090. template: `
  1091. <div>
  1092. <transition :duration="{ enter: enter, leave: leave }">
  1093. <div v-if="ok" class="test">foo</div>
  1094. </transition>
  1095. </div>
  1096. `,
  1097. data: {
  1098. ok: true,
  1099. enter: enter1,
  1100. leave: leave1
  1101. }
  1102. }).$mount(el)
  1103. vm.ok = false
  1104. waitForUpdate(() => {
  1105. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1106. }).thenWaitFor(nextFrame).then(() => {
  1107. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  1108. }).thenWaitFor(leave1 + buffer).then(() => {
  1109. expect(vm.$el.children.length).toBe(0)
  1110. vm.ok = true
  1111. }).then(() => {
  1112. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  1113. }).thenWaitFor(nextFrame).then(() => {
  1114. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  1115. }).thenWaitFor(enter1 + buffer).then(() => {
  1116. expect(vm.$el.children[0].className).toBe('test')
  1117. vm.enter = enter2
  1118. vm.leave = leave2
  1119. }).then(() => {
  1120. vm.ok = false
  1121. }).then(() => {
  1122. expect(vm.$el.children[0].className).toBe('test v-leave v-leave-active')
  1123. }).thenWaitFor(nextFrame).then(() => {
  1124. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  1125. }).thenWaitFor(leave2 + buffer).then(() => {
  1126. expect(vm.$el.children.length).toBe(0)
  1127. vm.ok = true
  1128. }).then(() => {
  1129. expect(vm.$el.children[0].className).toBe('test v-enter v-enter-active')
  1130. }).thenWaitFor(nextFrame).then(() => {
  1131. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  1132. }).thenWaitFor(enter2 + buffer).then(() => {
  1133. expect(vm.$el.children[0].className).toBe('test')
  1134. }).then(done)
  1135. }, 10000)
  1136. it('warn invalid durations', done => {
  1137. const vm = new Vue({
  1138. template: `
  1139. <div>
  1140. <transition :duration="{ enter: NaN, leave: 'foo' }">
  1141. <div v-if="ok" class="test">foo</div>
  1142. </transition>
  1143. </div>
  1144. `,
  1145. data: {
  1146. ok: true
  1147. }
  1148. }).$mount(el)
  1149. vm.ok = false
  1150. waitForUpdate(() => {
  1151. expect(`<transition> explicit leave duration is not a valid number - got "foo"`).toHaveBeenWarned()
  1152. }).thenWaitFor(duration + buffer).then(() => {
  1153. vm.ok = true
  1154. }).then(() => {
  1155. expect(`<transition> explicit enter duration is NaN`).toHaveBeenWarned()
  1156. }).then(done)
  1157. })
  1158. })
  1159. // #6687
  1160. it('transition on child components with empty root node', done => {
  1161. const vm = new Vue({
  1162. template: `
  1163. <div>
  1164. <transition mode="out-in">
  1165. <component class="test" :is="view"></component>
  1166. </transition>
  1167. </div>
  1168. `,
  1169. data: { view: 'one' },
  1170. components: {
  1171. 'one': {
  1172. template: '<div v-if="false">one</div>'
  1173. },
  1174. 'two': {
  1175. template: '<div>two</div>'
  1176. }
  1177. }
  1178. }).$mount(el)
  1179. // should not apply transition on initial render by default
  1180. expect(vm.$el.innerHTML).toBe('<!---->')
  1181. vm.view = 'two'
  1182. waitForUpdate(() => {
  1183. expect(vm.$el.innerHTML).toBe('<div class="test v-enter v-enter-active">two</div>')
  1184. }).thenWaitFor(nextFrame).then(() => {
  1185. expect(vm.$el.children[0].className).toBe('test v-enter-active v-enter-to')
  1186. }).thenWaitFor(duration + buffer).then(() => {
  1187. expect(vm.$el.children[0].className).toBe('test')
  1188. vm.view = 'one'
  1189. }).then(() => {
  1190. // incoming comment node is appended instantly because it doesn't have
  1191. // data and therefore doesn't go through the transition module.
  1192. expect(vm.$el.innerHTML).toBe('<div class="test v-leave v-leave-active">two</div><!---->')
  1193. }).thenWaitFor(nextFrame).then(() => {
  1194. expect(vm.$el.children[0].className).toBe('test v-leave-active v-leave-to')
  1195. }).thenWaitFor(duration + buffer).then(() => {
  1196. expect(vm.$el.innerHTML).toBe('<!---->')
  1197. }).then(done)
  1198. })
  1199. // #8199
  1200. it('should not throw error when replaced by v-html contents', (done) => {
  1201. const vm = new Vue({
  1202. template: `
  1203. <div>
  1204. <div v-if="ok" :class="ok">
  1205. <transition>
  1206. <span>a</span>
  1207. </transition>
  1208. </div>
  1209. <div v-else v-html="ok"></div>
  1210. </div>
  1211. `,
  1212. data: { ok: true }
  1213. }).$mount(el)
  1214. vm.ok = false
  1215. waitForUpdate(() => {
  1216. expect(vm.$el.children[0].innerHTML).toBe('false')
  1217. }).then(done)
  1218. })
  1219. })
  1220. }