2
0

transition.spec.ts 63 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023
  1. import path from 'node:path'
  2. import {
  3. E2E_TIMEOUT,
  4. setupPuppeteer,
  5. } from '../../../packages/vue/__tests__/e2e/e2eUtils'
  6. import connect from 'connect'
  7. import sirv from 'sirv'
  8. import { nextTick } from 'vue'
  9. const {
  10. page,
  11. classList,
  12. timeout,
  13. isVisible,
  14. html,
  15. nextFrame,
  16. transitionStart,
  17. waitForInnerHTML,
  18. click,
  19. domClick,
  20. } = setupPuppeteer()
  21. const duration = process.env.CI ? 200 : 50
  22. const buffer = process.env.CI ? 50 : 20
  23. describe('vapor transition', () => {
  24. let server: any
  25. const port = '8195'
  26. beforeAll(() => {
  27. server = connect()
  28. .use(sirv(path.resolve(import.meta.dirname, '../dist')))
  29. .listen(port)
  30. process.on('SIGTERM', () => server && server.close())
  31. })
  32. afterAll(() => {
  33. server.close()
  34. })
  35. beforeEach(async () => {
  36. const baseUrl = `http://localhost:${port}/transition/`
  37. await page().evaluateOnNewDocument(dur => {
  38. ;(window as any).__TRANSITION_DURATION__ = dur
  39. }, duration)
  40. await page().goto(baseUrl)
  41. await page().waitForSelector('#app')
  42. })
  43. describe('transition with v-if', () => {
  44. test(
  45. 'basic transition',
  46. async () => {
  47. const btnSelector = '.if-basic > button'
  48. const containerSelector = '.if-basic > div'
  49. const childSelector = `${containerSelector} > div`
  50. expect(await html(containerSelector)).toBe(
  51. `<div class="test">content</div>`,
  52. )
  53. // leave
  54. expect(
  55. (await transitionStart(btnSelector, childSelector)).outerHTML,
  56. ).toBe(`<div class="test v-leave-from v-leave-active">content</div>`)
  57. await waitForInnerHTML(
  58. containerSelector,
  59. `<div class="test v-leave-active v-leave-to">content</div>`,
  60. )
  61. await waitForInnerHTML(containerSelector, ``)
  62. // enter
  63. expect(
  64. (await transitionStart(btnSelector, childSelector)).outerHTML,
  65. ).toBe(`<div class="test v-enter-from v-enter-active">content</div>`)
  66. await waitForInnerHTML(
  67. containerSelector,
  68. `<div class="test v-enter-active v-enter-to">content</div>`,
  69. )
  70. await waitForInnerHTML(
  71. containerSelector,
  72. '<div class="test">content</div>',
  73. )
  74. },
  75. E2E_TIMEOUT,
  76. )
  77. test(
  78. 'if/else-if/else chain transition',
  79. async () => {
  80. const btnSelector = '.if-else-chain > button'
  81. const containerSelector = '.if-else-chain > div'
  82. const childSelector = `${containerSelector} > div`
  83. expect(await html(containerSelector)).toBe('<div class="test">0</div>')
  84. expect(
  85. (await transitionStart(btnSelector, childSelector)).outerHTML,
  86. ).toBe(`<div class="test v-leave-from v-leave-active">0</div>`)
  87. await waitForInnerHTML(containerSelector, '<div class="test">1</div>')
  88. expect(
  89. (await transitionStart(btnSelector, childSelector)).outerHTML,
  90. ).toBe(`<div class="test v-leave-from v-leave-active">1</div>`)
  91. await waitForInnerHTML(containerSelector, '<div class="test">2</div>')
  92. },
  93. E2E_TIMEOUT,
  94. )
  95. test(
  96. 'if/else-if/else chain transition (out-in mode)',
  97. async () => {
  98. const btnSelector = '.if-else-chain-out-in > button'
  99. const containerSelector = '.if-else-chain-out-in > div'
  100. const childSelector = `${containerSelector} > div`
  101. expect(await html(containerSelector)).toBe('<div class="test">0</div>')
  102. expect(
  103. (await transitionStart(btnSelector, childSelector)).outerHTML,
  104. ).toBe(`<div class="test v-leave-from v-leave-active">0</div>`)
  105. await waitForInnerHTML(
  106. containerSelector,
  107. '<div class="test v-leave-active v-leave-to">0</div>',
  108. )
  109. await waitForInnerHTML(
  110. containerSelector,
  111. '<div class="test v-enter-from v-enter-active">1</div>',
  112. )
  113. await waitForInnerHTML(
  114. containerSelector,
  115. '<div class="test v-enter-active v-enter-to">1</div>',
  116. )
  117. await waitForInnerHTML(containerSelector, '<div class="test">1</div>')
  118. expect(
  119. (await transitionStart(btnSelector, childSelector)).outerHTML,
  120. ).toBe(`<div class="test v-leave-from v-leave-active">1</div>`)
  121. await waitForInnerHTML(
  122. containerSelector,
  123. '<div class="test v-leave-active v-leave-to">1</div>',
  124. )
  125. await waitForInnerHTML(
  126. containerSelector,
  127. '<div class="test v-enter-from v-enter-active">2</div>',
  128. )
  129. await waitForInnerHTML(
  130. containerSelector,
  131. '<div class="test v-enter-active v-enter-to">2</div>',
  132. )
  133. await waitForInnerHTML(containerSelector, '<div class="test">2</div>')
  134. },
  135. E2E_TIMEOUT,
  136. )
  137. test(
  138. 'named transition',
  139. async () => {
  140. const btnSelector = '.if-named > button'
  141. const containerSelector = '.if-named > div'
  142. const childSelector = `${containerSelector} > div`
  143. expect(await html(containerSelector)).toBe(
  144. '<div class="test">content</div>',
  145. )
  146. // leave
  147. expect(
  148. (await transitionStart(btnSelector, childSelector)).outerHTML,
  149. ).toBe(
  150. `<div class="test test-leave-from test-leave-active">content</div>`,
  151. )
  152. await waitForInnerHTML(
  153. containerSelector,
  154. '<div class="test test-leave-active test-leave-to">content</div>',
  155. )
  156. await waitForInnerHTML(containerSelector, '')
  157. // enter
  158. expect(
  159. (await transitionStart(btnSelector, childSelector)).outerHTML,
  160. ).toBe(
  161. `<div class="test test-enter-from test-enter-active">content</div>`,
  162. )
  163. await waitForInnerHTML(
  164. containerSelector,
  165. '<div class="test test-enter-active test-enter-to">content</div>',
  166. )
  167. await waitForInnerHTML(
  168. containerSelector,
  169. '<div class="test">content</div>',
  170. )
  171. },
  172. E2E_TIMEOUT,
  173. )
  174. test(
  175. 'custom transition classes',
  176. async () => {
  177. const btnSelector = '.if-custom-classes > button'
  178. const containerSelector = '.if-custom-classes > div'
  179. const childSelector = `${containerSelector} > div`
  180. expect(await html(containerSelector)).toBe(
  181. '<div class="test">content</div>',
  182. )
  183. // leave
  184. expect(
  185. (await transitionStart(btnSelector, childSelector)).outerHTML,
  186. ).toBe(`<div class="test bye-from bye-active">content</div>`)
  187. await waitForInnerHTML(
  188. containerSelector,
  189. '<div class="test bye-active bye-to">content</div>',
  190. )
  191. await waitForInnerHTML(containerSelector, '')
  192. // enter
  193. expect(
  194. (await transitionStart(btnSelector, childSelector)).outerHTML,
  195. ).toBe(`<div class="test hello-from hello-active">content</div>`)
  196. await waitForInnerHTML(
  197. containerSelector,
  198. '<div class="test hello-active hello-to">content</div>',
  199. )
  200. await waitForInnerHTML(
  201. containerSelector,
  202. '<div class="test">content</div>',
  203. )
  204. },
  205. E2E_TIMEOUT,
  206. )
  207. test(
  208. 'transition with dynamic name',
  209. async () => {
  210. const btnSelector = '.if-dynamic-name > button.toggle'
  211. const btnChangeNameSelector = '.if-dynamic-name > button.change'
  212. const containerSelector = '.if-dynamic-name > div'
  213. const childSelector = `${containerSelector} > div`
  214. expect(await html(containerSelector)).toBe(
  215. '<div class="test">content</div>',
  216. )
  217. // leave
  218. expect(
  219. (await transitionStart(btnSelector, childSelector)).outerHTML,
  220. ).toBe(
  221. `<div class="test test-leave-from test-leave-active">content</div>`,
  222. )
  223. await waitForInnerHTML(
  224. containerSelector,
  225. '<div class="test test-leave-active test-leave-to">content</div>',
  226. )
  227. await waitForInnerHTML(containerSelector, '')
  228. // enter
  229. await domClick(btnChangeNameSelector)
  230. await waitForInnerHTML(btnChangeNameSelector, 'changed')
  231. expect(
  232. (await transitionStart(btnSelector, childSelector)).classNames,
  233. ).toStrictEqual(['test', 'changed-enter-from', 'changed-enter-active'])
  234. await waitForInnerHTML(
  235. containerSelector,
  236. '<div class="test changed-enter-active changed-enter-to">content</div>',
  237. )
  238. await waitForInnerHTML(
  239. containerSelector,
  240. '<div class="test">content</div>',
  241. )
  242. },
  243. E2E_TIMEOUT,
  244. )
  245. test(
  246. 'transition events without appear',
  247. async () => {
  248. const btnSelector = '.if-events-without-appear > button'
  249. const containerSelector = '.if-events-without-appear > div'
  250. const childSelector = `${containerSelector} > div`
  251. expect(await html(containerSelector)).toBe(
  252. '<div class="test">content</div>',
  253. )
  254. // leave
  255. expect(
  256. (await transitionStart(btnSelector, childSelector)).classNames,
  257. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  258. let calls = await page().evaluate(() => {
  259. return (window as any).getCalls('withoutAppear')
  260. })
  261. expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
  262. await waitForInnerHTML(
  263. containerSelector,
  264. '<div class="test test-leave-active test-leave-to">content</div>',
  265. )
  266. expect(
  267. await page().evaluate(() => {
  268. return (window as any).getCalls('withoutAppear')
  269. }),
  270. ).not.contain('afterLeave')
  271. await waitForInnerHTML(containerSelector, '')
  272. expect(
  273. await page().evaluate(() => {
  274. return (window as any).getCalls('withoutAppear')
  275. }),
  276. ).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
  277. await page().evaluate(() => {
  278. ;(window as any).resetCalls('withoutAppear')
  279. })
  280. // enter
  281. expect(
  282. (await transitionStart(btnSelector, childSelector)).classNames,
  283. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  284. calls = await page().evaluate(() => {
  285. return (window as any).getCalls('withoutAppear')
  286. })
  287. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  288. await waitForInnerHTML(
  289. containerSelector,
  290. '<div class="test test-enter-active test-enter-to">content</div>',
  291. )
  292. expect(
  293. await page().evaluate(() => {
  294. return (window as any).getCalls('withoutAppear')
  295. }),
  296. ).not.contain('afterEnter')
  297. await waitForInnerHTML(
  298. containerSelector,
  299. '<div class="test">content</div>',
  300. )
  301. expect(
  302. await page().evaluate(() => {
  303. return (window as any).getCalls('withoutAppear')
  304. }),
  305. ).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  306. },
  307. E2E_TIMEOUT,
  308. )
  309. test(
  310. 'events with arguments',
  311. async () => {
  312. const btnSelector = '.if-events-with-args > button'
  313. const containerSelector = '.if-events-with-args > div'
  314. const childSelector = `${containerSelector} > div`
  315. expect(await html(containerSelector)).toBe(
  316. '<div class="test">content</div>',
  317. )
  318. // leave
  319. await click(btnSelector)
  320. let calls = await page().evaluate(() => {
  321. return (window as any).getCalls('withArgs')
  322. })
  323. expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
  324. expect(await classList(childSelector)).toStrictEqual([
  325. 'test',
  326. 'before-leave',
  327. 'leave',
  328. ])
  329. await timeout(200 + buffer)
  330. calls = await page().evaluate(() => {
  331. return (window as any).getCalls('withArgs')
  332. })
  333. expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
  334. expect(await html(containerSelector)).toBe('')
  335. await page().evaluate(() => {
  336. ;(window as any).resetCalls('withArgs')
  337. })
  338. // enter
  339. await click(btnSelector)
  340. calls = await page().evaluate(() => {
  341. return (window as any).getCalls('withArgs')
  342. })
  343. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  344. expect(await classList(childSelector)).toStrictEqual([
  345. 'test',
  346. 'before-enter',
  347. 'enter',
  348. ])
  349. await timeout(200 + buffer)
  350. calls = await page().evaluate(() => {
  351. return (window as any).getCalls('withArgs')
  352. })
  353. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  354. expect(await html(containerSelector)).toBe(
  355. '<div class="test before-enter enter after-enter">content</div>',
  356. )
  357. },
  358. E2E_TIMEOUT,
  359. )
  360. test(
  361. 'onEnterCancelled',
  362. async () => {
  363. const btnSelector = '.if-enter-cancelled > button'
  364. const containerSelector = '.if-enter-cancelled > div'
  365. const childSelector = `${containerSelector} > div`
  366. expect(await html(containerSelector)).toBe('')
  367. // enter
  368. expect(
  369. (await transitionStart(btnSelector, childSelector)).classNames,
  370. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  371. await waitForInnerHTML(
  372. containerSelector,
  373. '<div class="test test-enter-active test-enter-to">content</div>',
  374. )
  375. // cancel (leave)
  376. expect(
  377. (await transitionStart(btnSelector, childSelector)).classNames,
  378. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  379. let calls = await page().evaluate(() => {
  380. return (window as any).getCalls('enterCancel')
  381. })
  382. expect(calls).toStrictEqual(['enterCancelled'])
  383. await waitForInnerHTML(
  384. containerSelector,
  385. '<div class="test test-leave-active test-leave-to">content</div>',
  386. )
  387. await waitForInnerHTML(containerSelector, '')
  388. },
  389. E2E_TIMEOUT,
  390. )
  391. test(
  392. 'transition on appear',
  393. async () => {
  394. const btnSelector = '.if-appear > button'
  395. const containerSelector = '.if-appear > div'
  396. const childSelector = `${containerSelector} > div`
  397. // appear
  398. expect(await classList(childSelector)).contains('test-appear-active')
  399. await waitForInnerHTML(
  400. containerSelector,
  401. '<div class="test">content</div>',
  402. )
  403. // leave
  404. expect(
  405. (await transitionStart(btnSelector, childSelector)).classNames,
  406. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  407. await waitForInnerHTML(
  408. containerSelector,
  409. '<div class="test test-leave-active test-leave-to">content</div>',
  410. )
  411. await waitForInnerHTML(containerSelector, '')
  412. // enter
  413. expect(
  414. (await transitionStart(btnSelector, childSelector)).classNames,
  415. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  416. await waitForInnerHTML(
  417. containerSelector,
  418. '<div class="test test-enter-active test-enter-to">content</div>',
  419. )
  420. await waitForInnerHTML(
  421. containerSelector,
  422. '<div class="test">content</div>',
  423. )
  424. },
  425. E2E_TIMEOUT,
  426. )
  427. test(
  428. 'transition events with appear',
  429. async () => {
  430. const btnSelector = '.if-events-with-appear > button'
  431. const containerSelector = '.if-events-with-appear > div'
  432. const childSelector = `${containerSelector} > div`
  433. // appear
  434. expect(await classList(childSelector)).contains('test-appear-active')
  435. let calls = await page().evaluate(() => {
  436. return (window as any).getCalls('withAppear')
  437. })
  438. expect(calls).toStrictEqual(['beforeAppear', 'onAppear'])
  439. await waitForInnerHTML(
  440. containerSelector,
  441. '<div class="test">content</div>',
  442. )
  443. calls = await page().evaluate(() => {
  444. return (window as any).getCalls('withAppear')
  445. })
  446. expect(calls).toStrictEqual(['beforeAppear', 'onAppear', 'afterAppear'])
  447. await page().evaluate(() => {
  448. ;(window as any).resetCalls('withAppear')
  449. })
  450. // leave
  451. expect(
  452. (await transitionStart(btnSelector, childSelector)).classNames,
  453. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  454. calls = await page().evaluate(() => {
  455. return (window as any).getCalls('withAppear')
  456. })
  457. expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
  458. await waitForInnerHTML(
  459. containerSelector,
  460. '<div class="test test-leave-active test-leave-to">content</div>',
  461. )
  462. calls = await page().evaluate(() => {
  463. return (window as any).getCalls('withAppear')
  464. })
  465. expect(calls).not.contain('afterLeave')
  466. await waitForInnerHTML(containerSelector, '')
  467. calls = await page().evaluate(() => {
  468. return (window as any).getCalls('withAppear')
  469. })
  470. expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
  471. await page().evaluate(() => {
  472. ;(window as any).resetCalls('withAppear')
  473. })
  474. // enter
  475. expect(
  476. (await transitionStart(btnSelector, childSelector)).classNames,
  477. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  478. calls = await page().evaluate(() => {
  479. return (window as any).getCalls('withAppear')
  480. })
  481. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  482. await waitForInnerHTML(
  483. containerSelector,
  484. '<div class="test test-enter-active test-enter-to">content</div>',
  485. )
  486. calls = await page().evaluate(() => {
  487. return (window as any).getCalls('withAppear')
  488. })
  489. expect(calls).not.contain('afterEnter')
  490. await waitForInnerHTML(
  491. containerSelector,
  492. '<div class="test">content</div>',
  493. )
  494. calls = await page().evaluate(() => {
  495. return (window as any).getCalls('withAppear')
  496. })
  497. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  498. },
  499. E2E_TIMEOUT,
  500. )
  501. test(
  502. 'css: false',
  503. async () => {
  504. const btnSelector = '.if-css-false > button'
  505. const containerSelector = '.if-css-false > div'
  506. const childSelector = `${containerSelector} > div`
  507. expect(await html(containerSelector)).toBe(
  508. '<div class="test">content</div>',
  509. )
  510. // leave
  511. await click(btnSelector)
  512. let calls = await page().evaluate(() => {
  513. return (window as any).getCalls('cssFalse')
  514. })
  515. expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
  516. expect(await html(containerSelector)).toBe('')
  517. await page().evaluate(() => {
  518. ;(window as any).resetCalls('cssFalse')
  519. })
  520. // enter
  521. await transitionStart(btnSelector, childSelector)
  522. calls = await page().evaluate(() => {
  523. return (window as any).getCalls('cssFalse')
  524. })
  525. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  526. expect(await html(containerSelector)).toBe(
  527. '<div class="test">content</div>',
  528. )
  529. },
  530. E2E_TIMEOUT,
  531. )
  532. test(
  533. 'no transition detected',
  534. async () => {
  535. const btnSelector = '.if-no-trans > button'
  536. const containerSelector = '.if-no-trans > div'
  537. const childSelector = `${containerSelector} > div`
  538. expect(await html(containerSelector)).toBe('<div>content</div>')
  539. // leave
  540. expect(
  541. (await transitionStart(btnSelector, childSelector)).classNames,
  542. ).toStrictEqual(['noop-leave-from', 'noop-leave-active'])
  543. await waitForInnerHTML(containerSelector, '')
  544. // enter
  545. expect(
  546. (await transitionStart(btnSelector, childSelector)).classNames,
  547. ).toStrictEqual(['noop-enter-from', 'noop-enter-active'])
  548. await waitForInnerHTML(containerSelector, '<div class="">content</div>')
  549. },
  550. E2E_TIMEOUT,
  551. )
  552. test(
  553. 'animations',
  554. async () => {
  555. const btnSelector = '.if-ani > button'
  556. const containerSelector = '.if-ani > div'
  557. const childSelector = `${containerSelector} > div`
  558. expect(await html(containerSelector)).toBe('<div>content</div>')
  559. // leave
  560. expect(
  561. (await transitionStart(btnSelector, childSelector)).classNames,
  562. ).toStrictEqual(['test-anim-leave-from', 'test-anim-leave-active'])
  563. await waitForInnerHTML(
  564. containerSelector,
  565. '<div class="test-anim-leave-active test-anim-leave-to">content</div>',
  566. )
  567. await waitForInnerHTML(containerSelector, '')
  568. // enter
  569. expect(
  570. (await transitionStart(btnSelector, childSelector)).classNames,
  571. ).toStrictEqual(['test-anim-enter-from', 'test-anim-enter-active'])
  572. await waitForInnerHTML(
  573. containerSelector,
  574. '<div class="test-anim-enter-active test-anim-enter-to">content</div>',
  575. )
  576. await waitForInnerHTML(containerSelector, '<div class="">content</div>')
  577. },
  578. E2E_TIMEOUT,
  579. )
  580. test(
  581. 'explicit transition type',
  582. async () => {
  583. const btnSelector = '.if-ani-explicit-type > button'
  584. const containerSelector = '.if-ani-explicit-type > div'
  585. const childSelector = `${containerSelector} > div`
  586. expect(await html(containerSelector)).toBe('<div>content</div>')
  587. // leave
  588. expect(
  589. (await transitionStart(btnSelector, childSelector)).classNames,
  590. ).toStrictEqual([
  591. 'test-anim-long-leave-from',
  592. 'test-anim-long-leave-active',
  593. ])
  594. await waitForInnerHTML(
  595. containerSelector,
  596. '<div class="test-anim-long-leave-active test-anim-long-leave-to">content</div>',
  597. )
  598. await waitForInnerHTML(containerSelector, '')
  599. // enter
  600. expect(
  601. (await transitionStart(btnSelector, childSelector)).classNames,
  602. ).toStrictEqual([
  603. 'test-anim-long-enter-from',
  604. 'test-anim-long-enter-active',
  605. ])
  606. await waitForInnerHTML(
  607. containerSelector,
  608. '<div class="test-anim-long-enter-active test-anim-long-enter-to">content</div>',
  609. )
  610. await waitForInnerHTML(containerSelector, '<div class="">content</div>')
  611. },
  612. E2E_TIMEOUT,
  613. )
  614. test(
  615. 'transition on SVG elements',
  616. async () => {
  617. const btnSelector = '.svg > button'
  618. const containerSelector = '.svg > #container'
  619. expect(await html(containerSelector)).toBe(
  620. '<circle cx="0" cy="0" r="10" class="test"></circle>',
  621. )
  622. // leave
  623. await click(btnSelector)
  624. await waitForInnerHTML(
  625. containerSelector,
  626. '<circle cx="0" cy="0" r="10" class="test test-leave-from test-leave-active"></circle>',
  627. )
  628. await waitForInnerHTML(
  629. containerSelector,
  630. '<circle cx="0" cy="0" r="10" class="test test-leave-active test-leave-to"></circle>',
  631. )
  632. await waitForInnerHTML(containerSelector, '')
  633. // enter
  634. await click(btnSelector)
  635. await waitForInnerHTML(
  636. containerSelector,
  637. '<circle cx="0" cy="0" r="10" class="test test-enter-from test-enter-active"></circle>',
  638. )
  639. await waitForInnerHTML(
  640. containerSelector,
  641. '<circle cx="0" cy="0" r="10" class="test test-enter-active test-enter-to"></circle>',
  642. )
  643. await waitForInnerHTML(
  644. containerSelector,
  645. '<circle cx="0" cy="0" r="10" class="test"></circle>',
  646. )
  647. },
  648. E2E_TIMEOUT,
  649. )
  650. test(
  651. 'custom transition higher-order component',
  652. async () => {
  653. const btnSelector = '.if-high-order > button'
  654. const containerSelector = '.if-high-order > div'
  655. const childSelector = `${containerSelector} > div`
  656. expect(await html(containerSelector)).toBe(
  657. '<div class="test">content</div>',
  658. )
  659. // leave
  660. expect(
  661. (await transitionStart(btnSelector, childSelector)).classNames,
  662. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  663. await waitForInnerHTML(
  664. containerSelector,
  665. '<div class="test test-leave-active test-leave-to">content</div>',
  666. )
  667. await waitForInnerHTML(containerSelector, '')
  668. // enter
  669. expect(
  670. (await transitionStart(btnSelector, childSelector)).classNames,
  671. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  672. await waitForInnerHTML(
  673. containerSelector,
  674. '<div class="test test-enter-active test-enter-to">content</div>',
  675. )
  676. await waitForInnerHTML(
  677. containerSelector,
  678. '<div class="test">content</div>',
  679. )
  680. },
  681. E2E_TIMEOUT,
  682. )
  683. test(
  684. 'transition on child components with empty root node',
  685. async () => {
  686. const btnSelector = '.if-empty-root > button.toggle'
  687. const btnChangeSelector = '.if-empty-root > button.change'
  688. const containerSelector = '.if-empty-root > div'
  689. const childSelector = `${containerSelector} > div`
  690. expect(await html(containerSelector)).toBe('')
  691. // change view -> 'two'
  692. await click(btnChangeSelector)
  693. // enter
  694. expect(
  695. (await transitionStart(btnSelector, childSelector)).classNames,
  696. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  697. await waitForInnerHTML(
  698. containerSelector,
  699. '<div class="test test-enter-active test-enter-to">two</div>',
  700. )
  701. await waitForInnerHTML(containerSelector, '<div class="test">two</div>')
  702. // change view -> 'one'
  703. await click(btnChangeSelector)
  704. // leave
  705. expect(
  706. (await transitionStart(btnSelector, childSelector)).classNames,
  707. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  708. await waitForInnerHTML(
  709. containerSelector,
  710. '<div class="test test-leave-active test-leave-to">two</div>',
  711. )
  712. await waitForInnerHTML(containerSelector, '')
  713. },
  714. E2E_TIMEOUT,
  715. )
  716. test(
  717. 'transition with v-if at component root-level',
  718. async () => {
  719. const btnSelector = '.if-at-component-root-level > button.toggle'
  720. const btnChangeSelector = '.if-at-component-root-level > button.change'
  721. const containerSelector = '.if-at-component-root-level > div'
  722. const childSelector = `${containerSelector} > div`
  723. expect(await html(containerSelector)).toBe('')
  724. // change view -> 'two'
  725. await click(btnChangeSelector)
  726. // enter
  727. expect(
  728. (await transitionStart(btnSelector, childSelector)).classNames,
  729. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  730. await waitForInnerHTML(
  731. containerSelector,
  732. '<div class="test test-enter-active test-enter-to">two</div>',
  733. )
  734. await waitForInnerHTML(containerSelector, '<div class="test">two</div>')
  735. // change view -> 'one'
  736. await click(btnChangeSelector)
  737. // leave
  738. expect(
  739. (await transitionStart(btnSelector, childSelector)).classNames,
  740. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  741. await waitForInnerHTML(
  742. containerSelector,
  743. '<div class="test test-leave-active test-leave-to">two</div>',
  744. )
  745. await waitForInnerHTML(containerSelector, '')
  746. },
  747. E2E_TIMEOUT,
  748. )
  749. test(
  750. 'wrapping transition + fallthrough attrs',
  751. async () => {
  752. const btnSelector = '.if-fallthrough-attr > button'
  753. const containerSelector = '.if-fallthrough-attr > div'
  754. expect(await html(containerSelector)).toBe('<div foo="1">content</div>')
  755. await click(btnSelector)
  756. // toggle again before leave finishes
  757. await nextTick()
  758. await click(btnSelector)
  759. await waitForInnerHTML(
  760. containerSelector,
  761. '<div foo="1" class="">content</div>',
  762. )
  763. },
  764. E2E_TIMEOUT,
  765. )
  766. test(
  767. 'transition + fallthrough attrs (in-out mode)',
  768. async () => {
  769. const btnSelector = '.if-fallthrough-attr-in-out > button'
  770. const containerSelector = '.if-fallthrough-attr-in-out > div'
  771. expect(await html(containerSelector)).toBe('<div foo="1">one</div>')
  772. // toggle
  773. await click(btnSelector)
  774. await nextTick()
  775. await waitForInnerHTML(
  776. containerSelector,
  777. '<div foo="1" class="">two</div>',
  778. )
  779. let calls = await page().evaluate(() => {
  780. return (window as any).getCalls('ifInOut')
  781. })
  782. expect(calls).toStrictEqual([
  783. 'beforeEnter',
  784. 'onEnter',
  785. 'afterEnter',
  786. 'beforeLeave',
  787. 'onLeave',
  788. 'afterLeave',
  789. ])
  790. expect(await html(containerSelector)).toBe(
  791. '<div foo="1" class="">two</div>',
  792. )
  793. // clear calls
  794. await page().evaluate(() => {
  795. ;(window as any).resetCalls('ifInOut')
  796. })
  797. // toggle back
  798. await click(btnSelector)
  799. await nextTick()
  800. await waitForInnerHTML(
  801. containerSelector,
  802. '<div foo="1" class="">one</div>',
  803. )
  804. calls = await page().evaluate(() => {
  805. return (window as any).getCalls('ifInOut')
  806. })
  807. expect(calls).toStrictEqual([
  808. 'beforeEnter',
  809. 'onEnter',
  810. 'afterEnter',
  811. 'beforeLeave',
  812. 'onLeave',
  813. 'afterLeave',
  814. ])
  815. expect(await html(containerSelector)).toBe(
  816. '<div foo="1" class="">one</div>',
  817. )
  818. },
  819. E2E_TIMEOUT,
  820. )
  821. })
  822. describe('transition with KeepAlive', () => {
  823. test('unmount innerChild (out-in mode)', async () => {
  824. const btnSelector = '.keep-alive > button'
  825. const containerSelector = '.keep-alive > div'
  826. await waitForInnerHTML(containerSelector, '<div>0</div>')
  827. await click(btnSelector)
  828. await waitForInnerHTML(containerSelector, '')
  829. const calls = await page().evaluate(() => {
  830. return (window as any).getCalls('unmount')
  831. })
  832. expect(calls).toStrictEqual(['TrueBranch'])
  833. })
  834. // #11775
  835. test(
  836. 'switch child then update include (out-in mode)',
  837. async () => {
  838. const containerSelector = '.keep-alive-update-include > div'
  839. const btnSwitchToB = '.keep-alive-update-include > #switchToB'
  840. const btnSwitchToA = '.keep-alive-update-include > #switchToA'
  841. const btnSwitchToC = '.keep-alive-update-include > #switchToC'
  842. expect(await html(containerSelector)).toBe('<div>CompA</div>')
  843. await click(btnSwitchToB)
  844. await nextTick()
  845. await click(btnSwitchToC)
  846. await waitForInnerHTML(containerSelector, '<div class="">CompC</div>')
  847. await click(btnSwitchToA)
  848. await waitForInnerHTML(containerSelector, '<div class="">CompA</div>')
  849. let calls = await page().evaluate(() => {
  850. return (window as any).getCalls('unmount')
  851. })
  852. expect(calls).toStrictEqual(['CompC unmounted'])
  853. // Unlike vdom, CompA does not update because there are no state changes
  854. // expect CompA only update once
  855. // calls = await page().evaluate(() => {
  856. // return (window as any).getCalls('updated')
  857. // })
  858. // expect(calls).toStrictEqual(['CompA updated'])
  859. },
  860. E2E_TIMEOUT,
  861. )
  862. // #10827
  863. test(
  864. 'switch and update child then update include (out-in mode)',
  865. async () => {
  866. const containerSelector = '.keep-alive-switch-then-update-include > div'
  867. const btnSwitchToA =
  868. '.keep-alive-switch-then-update-include > #switchToA'
  869. const btnSwitchToB =
  870. '.keep-alive-switch-then-update-include > #switchToB'
  871. expect(await html(containerSelector)).toBe('<div>CompA2</div>')
  872. await click(btnSwitchToB)
  873. await waitForInnerHTML(containerSelector, '<div class="">CompB2</div>')
  874. await click(btnSwitchToA)
  875. await waitForInnerHTML(containerSelector, '<div class="">CompA2</div>')
  876. let calls = await page().evaluate(() => {
  877. return (window as any).getCalls('unmount')
  878. })
  879. expect(calls).toStrictEqual(['CompB2 unmounted'])
  880. },
  881. E2E_TIMEOUT,
  882. )
  883. // #12860
  884. test(
  885. 'unmount children',
  886. async () => {
  887. const containerSelector = '.keep-alive-unmount-children > #container'
  888. const btnSelector = '.keep-alive-unmount-children > #toggleBtn'
  889. await waitForInnerHTML(containerSelector, '<div>0</div>')
  890. expect(await html(containerSelector)).toBe('<div>0</div>')
  891. await click(btnSelector)
  892. await waitForInnerHTML(containerSelector, '')
  893. const calls = await page().evaluate(() => {
  894. return (window as any).getCalls('unmount')
  895. })
  896. expect(calls).toStrictEqual(['UnmountBranch'])
  897. const storageInner = await page().evaluate(() => {
  898. const container = (
  899. window as any
  900. ).getKeepAliveUnmountStorageContainer?.()
  901. return container ? container.innerHTML : null
  902. })
  903. expect(storageInner).toBe('')
  904. },
  905. E2E_TIMEOUT,
  906. )
  907. // #13153
  908. test(
  909. 'move kept-alive node before v-show transition leave finishes',
  910. async () => {
  911. const containerSelector = '.keep-alive-move-before-leave-finishes > div'
  912. const btnToggle = '.keep-alive-move-before-leave-finishes > button'
  913. const changeShowBtn = `${containerSelector} #changeShowBtn`
  914. expect(await html(containerSelector)).toBe(
  915. `<div><h2>I should show</h2></div>` +
  916. `<h2>This is page1</h2>` +
  917. `<button id="changeShowBtn">true</button>`,
  918. )
  919. // trigger v-show transition leave
  920. await click(changeShowBtn)
  921. await waitForInnerHTML(
  922. containerSelector,
  923. `<div class="test-leave-from test-leave-active"><h2>I shouldn't show </h2></div>` +
  924. `<h2>This is page1</h2>` +
  925. `<button id="changeShowBtn">false</button>`,
  926. )
  927. // switch to page2, before leave finishes
  928. // expect v-show element's display to be none
  929. await click(btnToggle)
  930. await nextTick()
  931. await waitForInnerHTML(
  932. containerSelector,
  933. `<div class="test-leave-from test-leave-active" style="display: none;"><h2>I shouldn't show </h2></div>` +
  934. `<h2>This is page2</h2>`,
  935. )
  936. // switch back to page1
  937. // expect v-show element's display to be none
  938. await click(btnToggle)
  939. await nextTick()
  940. await waitForInnerHTML(
  941. containerSelector,
  942. `<div class="test-enter-from test-enter-active" style="display: none;"><h2>I shouldn't show </h2></div>` +
  943. `<h2>This is page1</h2>` +
  944. `<button id="changeShowBtn">false</button>`,
  945. )
  946. await waitForInnerHTML(
  947. containerSelector,
  948. `<div class="" style="display: none;"><h2>I shouldn't show </h2></div>` +
  949. `<h2>This is page1</h2>` +
  950. `<button id="changeShowBtn">false</button>`,
  951. )
  952. },
  953. E2E_TIMEOUT,
  954. )
  955. })
  956. describe.todo('transition with Suspense', () => {})
  957. describe('transition with Teleport', () => {
  958. test(
  959. 'apply transition to teleport child',
  960. async () => {
  961. const btnSelector = '.with-teleport > button'
  962. const containerSelector = '.with-teleport > .container'
  963. const targetSelector = `.with-teleport > .target`
  964. await waitForInnerHTML(containerSelector, '')
  965. await waitForInnerHTML(targetSelector, '')
  966. // enter
  967. expect(
  968. (await transitionStart(btnSelector, `${targetSelector} div`))
  969. .classNames,
  970. ).toStrictEqual(['test', 'v-enter-from', 'v-enter-active'])
  971. await waitForInnerHTML(
  972. targetSelector,
  973. '<div class="test v-enter-active v-enter-to">vapor compB</div>',
  974. )
  975. await waitForInnerHTML(
  976. targetSelector,
  977. '<div class="test">vapor compB</div>',
  978. )
  979. expect(await html(containerSelector)).toBe('')
  980. // leave
  981. expect(
  982. (await transitionStart(btnSelector, `${targetSelector} div`))
  983. .classNames,
  984. ).toStrictEqual(['test', 'v-leave-from', 'v-leave-active'])
  985. await waitForInnerHTML(
  986. targetSelector,
  987. '<div class="test v-leave-active v-leave-to">vapor compB</div>',
  988. )
  989. await waitForInnerHTML(targetSelector, '')
  990. expect(await html(containerSelector)).toBe('')
  991. },
  992. E2E_TIMEOUT,
  993. )
  994. })
  995. describe('transition with AsyncComponent', () => {
  996. test('apply transition to inner component', async () => {
  997. const btnSelector = '.async > button'
  998. const containerSelector = '.async > div'
  999. expect(await html(containerSelector)).toBe('')
  1000. // toggle
  1001. await click(btnSelector)
  1002. await nextTick()
  1003. // not yet resolved
  1004. expect(await html(containerSelector)).toBe('')
  1005. // wait resolving
  1006. await timeout(50)
  1007. // enter (resolved)
  1008. expect(await html(containerSelector)).toBe(
  1009. '<div class="v-enter-from v-enter-active">vapor compA</div>',
  1010. )
  1011. await waitForInnerHTML(
  1012. containerSelector,
  1013. '<div class="v-enter-active v-enter-to">vapor compA</div>',
  1014. )
  1015. await waitForInnerHTML(
  1016. containerSelector,
  1017. '<div class="">vapor compA</div>',
  1018. )
  1019. // leave
  1020. await click(btnSelector)
  1021. await nextTick()
  1022. expect(await html(containerSelector)).toBe(
  1023. '<div class="v-leave-from v-leave-active">vapor compA</div>',
  1024. )
  1025. await waitForInnerHTML(
  1026. containerSelector,
  1027. '<div class="v-leave-active v-leave-to">vapor compA</div>',
  1028. )
  1029. await waitForInnerHTML(containerSelector, '')
  1030. // enter again
  1031. await click(btnSelector)
  1032. // use the already resolved component
  1033. expect(await html(containerSelector)).toBe(
  1034. '<div class="v-enter-from v-enter-active">vapor compA</div>',
  1035. )
  1036. await waitForInnerHTML(
  1037. containerSelector,
  1038. '<div class="v-enter-active v-enter-to">vapor compA</div>',
  1039. )
  1040. await waitForInnerHTML(
  1041. containerSelector,
  1042. '<div class="">vapor compA</div>',
  1043. )
  1044. })
  1045. test('apply transition to pre-resolved async component', async () => {
  1046. const btnSelector = '.async-resolved > button'
  1047. const containerSelector = '.async-resolved #container'
  1048. const hiddenCompSelector = '.async-resolved #hidden-async'
  1049. // Wait for the hidden AsyncCompResolved to resolve and render
  1050. await waitForInnerHTML(
  1051. hiddenCompSelector,
  1052. '<div style="display: none;">vapor compA</div>',
  1053. )
  1054. expect(await html(containerSelector)).toBe('')
  1055. await click(btnSelector)
  1056. expect(await html(containerSelector)).toBe(
  1057. '<div class="v-enter-from v-enter-active">vapor compA</div>',
  1058. )
  1059. await waitForInnerHTML(
  1060. containerSelector,
  1061. '<div class="v-enter-active v-enter-to">vapor compA</div>',
  1062. )
  1063. await waitForInnerHTML(
  1064. containerSelector,
  1065. '<div class="">vapor compA</div>',
  1066. )
  1067. // leave
  1068. await click(btnSelector)
  1069. await nextTick()
  1070. expect(await html(containerSelector)).toBe(
  1071. '<div class="v-leave-from v-leave-active">vapor compA</div>',
  1072. )
  1073. await waitForInnerHTML(
  1074. containerSelector,
  1075. '<div class="v-leave-active v-leave-to">vapor compA</div>',
  1076. )
  1077. await waitForInnerHTML(containerSelector, '')
  1078. // enter again
  1079. await click(btnSelector)
  1080. expect(await html(containerSelector)).toBe(
  1081. '<div class="v-enter-from v-enter-active">vapor compA</div>',
  1082. )
  1083. await waitForInnerHTML(
  1084. containerSelector,
  1085. '<div class="v-enter-active v-enter-to">vapor compA</div>',
  1086. )
  1087. await waitForInnerHTML(
  1088. containerSelector,
  1089. '<div class="">vapor compA</div>',
  1090. )
  1091. })
  1092. })
  1093. describe('transition with v-show', () => {
  1094. test(
  1095. 'named transition with v-show',
  1096. async () => {
  1097. const btnSelector = '.show-named > button'
  1098. const containerSelector = '.show-named > div'
  1099. const childSelector = `${containerSelector} > div`
  1100. expect(await html(containerSelector)).toBe(
  1101. '<div class="test">content</div>',
  1102. )
  1103. expect(await isVisible(childSelector)).toBe(true)
  1104. // leave
  1105. expect(
  1106. (await transitionStart(btnSelector, childSelector)).classNames,
  1107. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1108. await waitForInnerHTML(
  1109. containerSelector,
  1110. '<div class="test test-leave-active test-leave-to">content</div>',
  1111. )
  1112. await waitForInnerHTML(
  1113. containerSelector,
  1114. '<div class="test" style="display: none;">content</div>',
  1115. )
  1116. // enter
  1117. expect(
  1118. (await transitionStart(btnSelector, childSelector)).classNames,
  1119. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1120. await waitForInnerHTML(
  1121. containerSelector,
  1122. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1123. )
  1124. await waitForInnerHTML(
  1125. containerSelector,
  1126. '<div class="test" style="">content</div>',
  1127. )
  1128. },
  1129. E2E_TIMEOUT,
  1130. )
  1131. test(
  1132. 'transition events with v-show',
  1133. async () => {
  1134. const btnSelector = '.show-events > button'
  1135. const containerSelector = '.show-events > div'
  1136. const childSelector = `${containerSelector} > div`
  1137. expect(await html(containerSelector)).toBe(
  1138. '<div class="test">content</div>',
  1139. )
  1140. // leave
  1141. expect(
  1142. (await transitionStart(btnSelector, childSelector)).classNames,
  1143. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1144. await waitForInnerHTML(
  1145. containerSelector,
  1146. '<div class="test test-leave-active test-leave-to">content</div>',
  1147. )
  1148. let calls = await page().evaluate(() => {
  1149. return (window as any).getCalls('show')
  1150. })
  1151. expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
  1152. calls = await page().evaluate(() => {
  1153. return (window as any).getCalls('show')
  1154. })
  1155. expect(calls).not.contain('afterLeave')
  1156. await waitForInnerHTML(
  1157. containerSelector,
  1158. '<div class="test" style="display: none;">content</div>',
  1159. )
  1160. calls = await page().evaluate(() => {
  1161. return (window as any).getCalls('show')
  1162. })
  1163. expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
  1164. // clear calls
  1165. await page().evaluate(() => {
  1166. ;(window as any).resetCalls('show')
  1167. })
  1168. // enter
  1169. expect(
  1170. (await transitionStart(btnSelector, childSelector)).classNames,
  1171. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1172. await waitForInnerHTML(
  1173. containerSelector,
  1174. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1175. )
  1176. calls = await page().evaluate(() => {
  1177. return (window as any).getCalls('show')
  1178. })
  1179. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  1180. await waitForInnerHTML(
  1181. containerSelector,
  1182. '<div class="test" style="">content</div>',
  1183. )
  1184. calls = await page().evaluate(() => {
  1185. return (window as any).getCalls('show')
  1186. })
  1187. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  1188. },
  1189. E2E_TIMEOUT,
  1190. )
  1191. test(
  1192. 'onLeaveCancelled (v-show only)',
  1193. async () => {
  1194. const btnSelector = '.show-leave-cancelled > button'
  1195. const containerSelector = '.show-leave-cancelled > div'
  1196. const childSelector = `${containerSelector} > div`
  1197. expect(await html(containerSelector)).toBe(
  1198. '<div class="test">content</div>',
  1199. )
  1200. // leave
  1201. expect(
  1202. (await transitionStart(btnSelector, childSelector)).classNames,
  1203. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1204. await waitForInnerHTML(
  1205. containerSelector,
  1206. '<div class="test test-leave-active test-leave-to">content</div>',
  1207. )
  1208. // cancel (enter)
  1209. expect(
  1210. (await transitionStart(btnSelector, childSelector)).classNames,
  1211. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1212. let calls = await page().evaluate(() => {
  1213. return (window as any).getCalls('showLeaveCancel')
  1214. })
  1215. expect(calls).toStrictEqual(['leaveCancelled'])
  1216. await waitForInnerHTML(
  1217. containerSelector,
  1218. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1219. )
  1220. await waitForInnerHTML(
  1221. containerSelector,
  1222. '<div class="test" style="">content</div>',
  1223. )
  1224. },
  1225. E2E_TIMEOUT,
  1226. )
  1227. test(
  1228. 'transition on appear with v-show',
  1229. async () => {
  1230. const btnSelector = '.show-appear > button'
  1231. const containerSelector = '.show-appear > div'
  1232. const childSelector = `${containerSelector} > div`
  1233. let calls = await page().evaluate(() => {
  1234. return (window as any).getCalls('showAppear')
  1235. })
  1236. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  1237. // appear
  1238. expect(await classList(childSelector)).contains('test-appear-active')
  1239. await waitForInnerHTML(
  1240. containerSelector,
  1241. '<div class="test">content</div>',
  1242. )
  1243. calls = await page().evaluate(() => {
  1244. return (window as any).getCalls('showAppear')
  1245. })
  1246. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  1247. // leave
  1248. expect(
  1249. (await transitionStart(btnSelector, childSelector)).classNames,
  1250. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1251. await waitForInnerHTML(
  1252. containerSelector,
  1253. '<div class="test test-leave-active test-leave-to">content</div>',
  1254. )
  1255. await waitForInnerHTML(
  1256. containerSelector,
  1257. '<div class="test" style="display: none;">content</div>',
  1258. )
  1259. // enter
  1260. expect(
  1261. (await transitionStart(btnSelector, childSelector)).classNames,
  1262. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1263. await waitForInnerHTML(
  1264. containerSelector,
  1265. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1266. )
  1267. await waitForInnerHTML(
  1268. containerSelector,
  1269. '<div class="test" style="">content</div>',
  1270. )
  1271. },
  1272. E2E_TIMEOUT,
  1273. )
  1274. test(
  1275. 'transition events should not call onEnter with v-show false',
  1276. async () => {
  1277. const btnSelector = '.show-appear-not-enter > button'
  1278. const containerSelector = '.show-appear-not-enter > div'
  1279. const childSelector = `${containerSelector} > div`
  1280. expect(await isVisible(childSelector)).toBe(false)
  1281. let calls = await page().evaluate(() => {
  1282. return (window as any).getCalls('notEnter')
  1283. })
  1284. expect(calls).toStrictEqual([])
  1285. // enter
  1286. expect(
  1287. (await transitionStart(btnSelector, childSelector)).classNames,
  1288. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1289. calls = await page().evaluate(() => {
  1290. return (window as any).getCalls('notEnter')
  1291. })
  1292. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  1293. await waitForInnerHTML(
  1294. containerSelector,
  1295. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1296. )
  1297. calls = await page().evaluate(() => {
  1298. return (window as any).getCalls('notEnter')
  1299. })
  1300. expect(calls).not.contain('afterEnter')
  1301. await waitForInnerHTML(
  1302. containerSelector,
  1303. '<div class="test" style="">content</div>',
  1304. )
  1305. calls = await page().evaluate(() => {
  1306. return (window as any).getCalls('notEnter')
  1307. })
  1308. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  1309. },
  1310. E2E_TIMEOUT,
  1311. )
  1312. })
  1313. describe('explicit durations', () => {
  1314. test(
  1315. 'single value',
  1316. async () => {
  1317. const btnSelector = '.duration-single-value > button'
  1318. const containerSelector = '.duration-single-value > div'
  1319. const childSelector = `${containerSelector} > div`
  1320. expect(await html(containerSelector)).toBe(
  1321. '<div class="test">content</div>',
  1322. )
  1323. // leave
  1324. expect(
  1325. (await transitionStart(btnSelector, childSelector)).classNames,
  1326. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1327. await waitForInnerHTML(
  1328. containerSelector,
  1329. '<div class="test test-leave-active test-leave-to">content</div>',
  1330. )
  1331. await waitForInnerHTML(containerSelector, '')
  1332. // enter
  1333. expect(
  1334. (await transitionStart(btnSelector, childSelector)).classNames,
  1335. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1336. await waitForInnerHTML(
  1337. containerSelector,
  1338. '<div class="test test-enter-active test-enter-to">content</div>',
  1339. )
  1340. await waitForInnerHTML(
  1341. containerSelector,
  1342. '<div class="test">content</div>',
  1343. )
  1344. },
  1345. E2E_TIMEOUT,
  1346. )
  1347. test(
  1348. 'enter with explicit durations',
  1349. async () => {
  1350. const btnSelector = '.duration-enter > button'
  1351. const containerSelector = '.duration-enter > div'
  1352. const childSelector = `${containerSelector} > div`
  1353. expect(await html(containerSelector)).toBe(
  1354. '<div class="test">content</div>',
  1355. )
  1356. // leave
  1357. expect(
  1358. (await transitionStart(btnSelector, childSelector)).classNames,
  1359. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1360. await waitForInnerHTML(
  1361. containerSelector,
  1362. '<div class="test test-leave-active test-leave-to">content</div>',
  1363. )
  1364. await waitForInnerHTML(containerSelector, '')
  1365. // enter
  1366. expect(
  1367. (await transitionStart(btnSelector, childSelector)).classNames,
  1368. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1369. await waitForInnerHTML(
  1370. containerSelector,
  1371. '<div class="test test-enter-active test-enter-to">content</div>',
  1372. )
  1373. await waitForInnerHTML(
  1374. containerSelector,
  1375. '<div class="test">content</div>',
  1376. )
  1377. },
  1378. E2E_TIMEOUT,
  1379. )
  1380. test(
  1381. 'leave with explicit durations',
  1382. async () => {
  1383. const btnSelector = '.duration-leave > button'
  1384. const containerSelector = '.duration-leave > div'
  1385. const childSelector = `${containerSelector} > div`
  1386. expect(await html(containerSelector)).toBe(
  1387. '<div class="test">content</div>',
  1388. )
  1389. // leave
  1390. expect(
  1391. (await transitionStart(btnSelector, childSelector)).classNames,
  1392. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1393. await waitForInnerHTML(
  1394. containerSelector,
  1395. '<div class="test test-leave-active test-leave-to">content</div>',
  1396. )
  1397. await waitForInnerHTML(containerSelector, '')
  1398. // enter
  1399. expect(
  1400. (await transitionStart(btnSelector, childSelector)).classNames,
  1401. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1402. await waitForInnerHTML(
  1403. containerSelector,
  1404. '<div class="test test-enter-active test-enter-to">content</div>',
  1405. )
  1406. await waitForInnerHTML(
  1407. containerSelector,
  1408. '<div class="test">content</div>',
  1409. )
  1410. },
  1411. E2E_TIMEOUT,
  1412. )
  1413. test(
  1414. 'separate enter and leave',
  1415. async () => {
  1416. const btnSelector = '.duration-enter-leave > button'
  1417. const containerSelector = '.duration-enter-leave > div'
  1418. const childSelector = `${containerSelector} > div`
  1419. expect(await html(containerSelector)).toBe(
  1420. '<div class="test">content</div>',
  1421. )
  1422. // leave
  1423. expect(
  1424. (await transitionStart(btnSelector, childSelector)).classNames,
  1425. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1426. await waitForInnerHTML(
  1427. containerSelector,
  1428. '<div class="test test-leave-active test-leave-to">content</div>',
  1429. )
  1430. await waitForInnerHTML(containerSelector, '')
  1431. // enter
  1432. expect(
  1433. (await transitionStart(btnSelector, childSelector)).classNames,
  1434. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1435. await waitForInnerHTML(
  1436. containerSelector,
  1437. '<div class="test test-enter-active test-enter-to">content</div>',
  1438. )
  1439. await waitForInnerHTML(
  1440. containerSelector,
  1441. '<div class="test">content</div>',
  1442. )
  1443. },
  1444. E2E_TIMEOUT,
  1445. )
  1446. })
  1447. test(
  1448. 'should work with keyed element',
  1449. async () => {
  1450. const btnSelector = '.keyed > button'
  1451. const containerSelector = '.keyed > h1'
  1452. await waitForInnerHTML(containerSelector, '0')
  1453. // change key
  1454. expect(
  1455. (await transitionStart(btnSelector, containerSelector)).classNames,
  1456. ).toStrictEqual(['v-leave-from', 'v-leave-active'])
  1457. await nextFrame()
  1458. expect(await classList(containerSelector)).toStrictEqual([
  1459. 'v-leave-active',
  1460. 'v-leave-to',
  1461. ])
  1462. await waitForInnerHTML(containerSelector, '1')
  1463. // change key again
  1464. expect(
  1465. (await transitionStart(btnSelector, containerSelector)).classNames,
  1466. ).toStrictEqual(['v-leave-from', 'v-leave-active'])
  1467. await nextFrame()
  1468. expect(await classList(containerSelector)).toStrictEqual([
  1469. 'v-leave-active',
  1470. 'v-leave-to',
  1471. ])
  1472. await waitForInnerHTML(containerSelector, '2')
  1473. },
  1474. E2E_TIMEOUT,
  1475. )
  1476. test(
  1477. 'should work with reusable Transition + keyed element',
  1478. async () => {
  1479. const btnSelector = '.reusable-keyed > button'
  1480. const containerSelector = '.reusable-keyed > h1'
  1481. await waitForInnerHTML(containerSelector, '0')
  1482. // change key
  1483. expect(
  1484. (await transitionStart(btnSelector, containerSelector)).classNames,
  1485. ).toStrictEqual(['test-leave-from', 'test-leave-active'])
  1486. await nextFrame()
  1487. expect(await classList(containerSelector)).toStrictEqual([
  1488. 'test-leave-active',
  1489. 'test-leave-to',
  1490. ])
  1491. await waitForInnerHTML(containerSelector, '1')
  1492. // change key again
  1493. expect(
  1494. (await transitionStart(btnSelector, containerSelector)).classNames,
  1495. ).toStrictEqual(['test-leave-from', 'test-leave-active'])
  1496. await nextFrame()
  1497. expect(await classList(containerSelector)).toStrictEqual([
  1498. 'test-leave-active',
  1499. 'test-leave-to',
  1500. ])
  1501. await waitForInnerHTML(containerSelector, '2')
  1502. },
  1503. E2E_TIMEOUT,
  1504. )
  1505. test(
  1506. 'should work with out-in mode',
  1507. async () => {
  1508. const btnSelector = '.out-in > button'
  1509. const containerSelector = '.out-in > div'
  1510. expect(await html(containerSelector)).toBe(`<div>vapor compB</div>`)
  1511. // compB -> compA
  1512. // compB leave
  1513. expect(
  1514. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1515. ).toBe(`<div class="fade-leave-from fade-leave-active">vapor compB</div>`)
  1516. await waitForInnerHTML(
  1517. containerSelector,
  1518. `<div class="fade-leave-active fade-leave-to">vapor compB</div>`,
  1519. )
  1520. // compA enter
  1521. await waitForInnerHTML(
  1522. containerSelector,
  1523. `<div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1524. )
  1525. await waitForInnerHTML(
  1526. containerSelector,
  1527. `<div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1528. )
  1529. await waitForInnerHTML(
  1530. containerSelector,
  1531. `<div class="">vapor compA</div>`,
  1532. )
  1533. // compA -> compB
  1534. // compA leave
  1535. expect(
  1536. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1537. ).toBe(`<div class="fade-leave-from fade-leave-active">vapor compA</div>`)
  1538. await waitForInnerHTML(
  1539. containerSelector,
  1540. `<div class="fade-leave-active fade-leave-to">vapor compA</div>`,
  1541. )
  1542. // compB enter
  1543. await waitForInnerHTML(
  1544. containerSelector,
  1545. `<div class="fade-enter-from fade-enter-active">vapor compB</div>`,
  1546. )
  1547. await waitForInnerHTML(
  1548. containerSelector,
  1549. `<div class="fade-enter-active fade-enter-to">vapor compB</div>`,
  1550. )
  1551. await waitForInnerHTML(
  1552. containerSelector,
  1553. `<div class="">vapor compB</div>`,
  1554. )
  1555. },
  1556. E2E_TIMEOUT,
  1557. )
  1558. test(
  1559. 'should work with in-out mode',
  1560. async () => {
  1561. const btnSelector = '.in-out > button'
  1562. const containerSelector = '.in-out > div'
  1563. expect(await html(containerSelector)).toBe(`<div>vapor compB</div>`)
  1564. // compA enter
  1565. expect(
  1566. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1567. ).toBe(
  1568. `<div>vapor compB</div><div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1569. )
  1570. await waitForInnerHTML(
  1571. containerSelector,
  1572. `<div>vapor compB</div><div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1573. )
  1574. // compB leave
  1575. await waitForInnerHTML(
  1576. containerSelector,
  1577. `<div class="fade-leave-from fade-leave-active">vapor compB</div><div class="">vapor compA</div>`,
  1578. )
  1579. await waitForInnerHTML(
  1580. containerSelector,
  1581. `<div class="fade-leave-active fade-leave-to">vapor compB</div><div class="">vapor compA</div>`,
  1582. )
  1583. await waitForInnerHTML(
  1584. containerSelector,
  1585. `<div class="">vapor compA</div>`,
  1586. )
  1587. },
  1588. E2E_TIMEOUT,
  1589. )
  1590. // tests for using vdom component in createVaporApp + vaporInteropPlugin
  1591. describe('interop', () => {
  1592. test(
  1593. 'render vdom component',
  1594. async () => {
  1595. const btnSelector = '.vdom > button'
  1596. const containerSelector = '.vdom > div'
  1597. expect(await html(containerSelector)).toBe(`<div>vdom comp</div>`)
  1598. // comp leave
  1599. expect(
  1600. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1601. ).toBe(`<div class="v-leave-from v-leave-active">vdom comp</div>`)
  1602. await waitForInnerHTML(
  1603. containerSelector,
  1604. `<div class="v-leave-active v-leave-to">vdom comp</div>`,
  1605. )
  1606. await waitForInnerHTML(containerSelector, ``)
  1607. // comp enter
  1608. expect(
  1609. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1610. ).toBe(`<div class="v-enter-from v-enter-active">vdom comp</div>`)
  1611. await waitForInnerHTML(
  1612. containerSelector,
  1613. `<div class="v-enter-active v-enter-to">vdom comp</div>`,
  1614. )
  1615. await waitForInnerHTML(
  1616. containerSelector,
  1617. `<div class="">vdom comp</div>`,
  1618. )
  1619. },
  1620. E2E_TIMEOUT,
  1621. )
  1622. test(
  1623. 'switch between vdom/vapor component (out-in mode)',
  1624. async () => {
  1625. const btnSelector = '.vdom-vapor-out-in > button'
  1626. const containerSelector = '.vdom-vapor-out-in > div'
  1627. expect(await html(containerSelector)).toBe(`<div>vdom comp</div>`)
  1628. // switch to vapor comp
  1629. // vdom comp leave
  1630. expect(
  1631. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1632. ).toBe(`<div class="fade-leave-from fade-leave-active">vdom comp</div>`)
  1633. await waitForInnerHTML(
  1634. containerSelector,
  1635. `<div class="fade-leave-active fade-leave-to">vdom comp</div>`,
  1636. )
  1637. // vapor comp enter
  1638. await waitForInnerHTML(
  1639. containerSelector,
  1640. `<div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1641. )
  1642. await waitForInnerHTML(
  1643. containerSelector,
  1644. `<div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1645. )
  1646. await waitForInnerHTML(
  1647. containerSelector,
  1648. `<div class="">vapor compA</div>`,
  1649. )
  1650. // switch to vdom comp
  1651. // vapor comp leave
  1652. expect(
  1653. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1654. ).toBe(
  1655. `<div class="fade-leave-from fade-leave-active">vapor compA</div>`,
  1656. )
  1657. await waitForInnerHTML(
  1658. containerSelector,
  1659. `<div class="fade-leave-active fade-leave-to">vapor compA</div>`,
  1660. )
  1661. // vdom comp enter
  1662. await waitForInnerHTML(
  1663. containerSelector,
  1664. `<div class="fade-enter-from fade-enter-active">vdom comp</div>`,
  1665. )
  1666. await waitForInnerHTML(
  1667. containerSelector,
  1668. `<div class="fade-enter-active fade-enter-to">vdom comp</div>`,
  1669. )
  1670. await waitForInnerHTML(
  1671. containerSelector,
  1672. `<div class="">vdom comp</div>`,
  1673. )
  1674. },
  1675. E2E_TIMEOUT,
  1676. )
  1677. test(
  1678. 'switch between vdom/vapor component (in-out mode)',
  1679. async () => {
  1680. const btnSelector = '.vdom-vapor-in-out > button'
  1681. const containerSelector = '.vdom-vapor-in-out > div'
  1682. expect(await html(containerSelector)).toBe(`<div>vapor compA</div>`)
  1683. // switch to vdom comp
  1684. // vdom comp enter
  1685. expect(
  1686. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1687. ).toBe(
  1688. `<div>vapor compA</div><div class="fade-enter-from fade-enter-active">vdom comp</div>`,
  1689. )
  1690. await waitForInnerHTML(
  1691. containerSelector,
  1692. `<div>vapor compA</div><div class="fade-enter-active fade-enter-to">vdom comp</div>`,
  1693. )
  1694. // vapor comp leave
  1695. await waitForInnerHTML(
  1696. containerSelector,
  1697. `<div class="fade-leave-from fade-leave-active">vapor compA</div><div class="">vdom comp</div>`,
  1698. )
  1699. await waitForInnerHTML(
  1700. containerSelector,
  1701. `<div class="fade-leave-active fade-leave-to">vapor compA</div><div class="">vdom comp</div>`,
  1702. )
  1703. await waitForInnerHTML(
  1704. containerSelector,
  1705. `<div class="">vdom comp</div>`,
  1706. )
  1707. // switch to vapor comp
  1708. // vapor comp enter
  1709. expect(
  1710. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1711. ).toBe(
  1712. `<div class="">vdom comp</div><div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1713. )
  1714. await waitForInnerHTML(
  1715. containerSelector,
  1716. `<div class="">vdom comp</div><div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1717. )
  1718. // vdom comp leave
  1719. await waitForInnerHTML(
  1720. containerSelector,
  1721. `<div class="fade-leave-from fade-leave-active">vdom comp</div><div class="">vapor compA</div>`,
  1722. )
  1723. await waitForInnerHTML(
  1724. containerSelector,
  1725. `<div class="fade-leave-active fade-leave-to">vdom comp</div><div class="">vapor compA</div>`,
  1726. )
  1727. await waitForInnerHTML(
  1728. containerSelector,
  1729. `<div class="">vapor compA</div>`,
  1730. )
  1731. },
  1732. E2E_TIMEOUT,
  1733. )
  1734. })
  1735. })