Transition.spec.ts 51 KB

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