transition.spec.ts 62 KB

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