Transition.spec.ts 51 KB

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