transition.spec.ts 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896
  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. })
  1030. describe('transition with v-show', () => {
  1031. test(
  1032. 'named transition with v-show',
  1033. async () => {
  1034. const btnSelector = '.show-named > button'
  1035. const containerSelector = '.show-named > div'
  1036. const childSelector = `${containerSelector} > div`
  1037. expect(await html(containerSelector)).toBe(
  1038. '<div class="test">content</div>',
  1039. )
  1040. expect(await isVisible(childSelector)).toBe(true)
  1041. // leave
  1042. expect(
  1043. (await transitionStart(btnSelector, childSelector)).classNames,
  1044. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1045. await waitForInnerHTML(
  1046. containerSelector,
  1047. '<div class="test test-leave-active test-leave-to">content</div>',
  1048. )
  1049. await waitForInnerHTML(
  1050. containerSelector,
  1051. '<div class="test" style="display: none;">content</div>',
  1052. )
  1053. // enter
  1054. expect(
  1055. (await transitionStart(btnSelector, childSelector)).classNames,
  1056. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1057. await waitForInnerHTML(
  1058. containerSelector,
  1059. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1060. )
  1061. await waitForInnerHTML(
  1062. containerSelector,
  1063. '<div class="test" style="">content</div>',
  1064. )
  1065. },
  1066. E2E_TIMEOUT,
  1067. )
  1068. test(
  1069. 'transition events with v-show',
  1070. async () => {
  1071. const btnSelector = '.show-events > button'
  1072. const containerSelector = '.show-events > div'
  1073. const childSelector = `${containerSelector} > div`
  1074. expect(await html(containerSelector)).toBe(
  1075. '<div class="test">content</div>',
  1076. )
  1077. // leave
  1078. expect(
  1079. (await transitionStart(btnSelector, childSelector)).classNames,
  1080. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1081. await waitForInnerHTML(
  1082. containerSelector,
  1083. '<div class="test test-leave-active test-leave-to">content</div>',
  1084. )
  1085. let calls = await page().evaluate(() => {
  1086. return (window as any).getCalls('show')
  1087. })
  1088. expect(calls).toStrictEqual(['beforeLeave', 'onLeave'])
  1089. calls = await page().evaluate(() => {
  1090. return (window as any).getCalls('show')
  1091. })
  1092. expect(calls).not.contain('afterLeave')
  1093. await waitForInnerHTML(
  1094. containerSelector,
  1095. '<div class="test" style="display: none;">content</div>',
  1096. )
  1097. calls = await page().evaluate(() => {
  1098. return (window as any).getCalls('show')
  1099. })
  1100. expect(calls).toStrictEqual(['beforeLeave', 'onLeave', 'afterLeave'])
  1101. // clear calls
  1102. await page().evaluate(() => {
  1103. ;(window as any).resetCalls('show')
  1104. })
  1105. // enter
  1106. expect(
  1107. (await transitionStart(btnSelector, childSelector)).classNames,
  1108. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1109. await waitForInnerHTML(
  1110. containerSelector,
  1111. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1112. )
  1113. calls = await page().evaluate(() => {
  1114. return (window as any).getCalls('show')
  1115. })
  1116. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  1117. await waitForInnerHTML(
  1118. containerSelector,
  1119. '<div class="test" style="">content</div>',
  1120. )
  1121. calls = await page().evaluate(() => {
  1122. return (window as any).getCalls('show')
  1123. })
  1124. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  1125. },
  1126. E2E_TIMEOUT,
  1127. )
  1128. test(
  1129. 'onLeaveCancelled (v-show only)',
  1130. async () => {
  1131. const btnSelector = '.show-leave-cancelled > button'
  1132. const containerSelector = '.show-leave-cancelled > div'
  1133. const childSelector = `${containerSelector} > div`
  1134. expect(await html(containerSelector)).toBe(
  1135. '<div class="test">content</div>',
  1136. )
  1137. // leave
  1138. expect(
  1139. (await transitionStart(btnSelector, childSelector)).classNames,
  1140. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1141. await waitForInnerHTML(
  1142. containerSelector,
  1143. '<div class="test test-leave-active test-leave-to">content</div>',
  1144. )
  1145. // cancel (enter)
  1146. expect(
  1147. (await transitionStart(btnSelector, childSelector)).classNames,
  1148. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1149. let calls = await page().evaluate(() => {
  1150. return (window as any).getCalls('showLeaveCancel')
  1151. })
  1152. expect(calls).toStrictEqual(['leaveCancelled'])
  1153. await waitForInnerHTML(
  1154. containerSelector,
  1155. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1156. )
  1157. await waitForInnerHTML(
  1158. containerSelector,
  1159. '<div class="test" style="">content</div>',
  1160. )
  1161. },
  1162. E2E_TIMEOUT,
  1163. )
  1164. test(
  1165. 'transition on appear with v-show',
  1166. async () => {
  1167. const btnSelector = '.show-appear > button'
  1168. const containerSelector = '.show-appear > div'
  1169. const childSelector = `${containerSelector} > div`
  1170. let calls = await page().evaluate(() => {
  1171. return (window as any).getCalls('showAppear')
  1172. })
  1173. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  1174. // appear
  1175. expect(await classList(childSelector)).contains('test-appear-active')
  1176. await waitForInnerHTML(
  1177. containerSelector,
  1178. '<div class="test">content</div>',
  1179. )
  1180. calls = await page().evaluate(() => {
  1181. return (window as any).getCalls('showAppear')
  1182. })
  1183. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  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. await waitForInnerHTML(
  1193. containerSelector,
  1194. '<div class="test" style="display: none;">content</div>',
  1195. )
  1196. // enter
  1197. expect(
  1198. (await transitionStart(btnSelector, childSelector)).classNames,
  1199. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  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 events should not call onEnter with v-show false',
  1213. async () => {
  1214. const btnSelector = '.show-appear-not-enter > button'
  1215. const containerSelector = '.show-appear-not-enter > div'
  1216. const childSelector = `${containerSelector} > div`
  1217. expect(await isVisible(childSelector)).toBe(false)
  1218. let calls = await page().evaluate(() => {
  1219. return (window as any).getCalls('notEnter')
  1220. })
  1221. expect(calls).toStrictEqual([])
  1222. // enter
  1223. expect(
  1224. (await transitionStart(btnSelector, childSelector)).classNames,
  1225. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1226. calls = await page().evaluate(() => {
  1227. return (window as any).getCalls('notEnter')
  1228. })
  1229. expect(calls).toStrictEqual(['beforeEnter', 'onEnter'])
  1230. await waitForInnerHTML(
  1231. containerSelector,
  1232. '<div class="test test-enter-active test-enter-to" style="">content</div>',
  1233. )
  1234. calls = await page().evaluate(() => {
  1235. return (window as any).getCalls('notEnter')
  1236. })
  1237. expect(calls).not.contain('afterEnter')
  1238. await waitForInnerHTML(
  1239. containerSelector,
  1240. '<div class="test" style="">content</div>',
  1241. )
  1242. calls = await page().evaluate(() => {
  1243. return (window as any).getCalls('notEnter')
  1244. })
  1245. expect(calls).toStrictEqual(['beforeEnter', 'onEnter', 'afterEnter'])
  1246. },
  1247. E2E_TIMEOUT,
  1248. )
  1249. })
  1250. describe('explicit durations', () => {
  1251. test(
  1252. 'single value',
  1253. async () => {
  1254. const btnSelector = '.duration-single-value > button'
  1255. const containerSelector = '.duration-single-value > div'
  1256. const childSelector = `${containerSelector} > div`
  1257. expect(await html(containerSelector)).toBe(
  1258. '<div class="test">content</div>',
  1259. )
  1260. // leave
  1261. expect(
  1262. (await transitionStart(btnSelector, childSelector)).classNames,
  1263. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1264. await waitForInnerHTML(
  1265. containerSelector,
  1266. '<div class="test test-leave-active test-leave-to">content</div>',
  1267. )
  1268. await waitForInnerHTML(containerSelector, '')
  1269. // enter
  1270. expect(
  1271. (await transitionStart(btnSelector, childSelector)).classNames,
  1272. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1273. await waitForInnerHTML(
  1274. containerSelector,
  1275. '<div class="test test-enter-active test-enter-to">content</div>',
  1276. )
  1277. await waitForInnerHTML(
  1278. containerSelector,
  1279. '<div class="test">content</div>',
  1280. )
  1281. },
  1282. E2E_TIMEOUT,
  1283. )
  1284. test(
  1285. 'enter with explicit durations',
  1286. async () => {
  1287. const btnSelector = '.duration-enter > button'
  1288. const containerSelector = '.duration-enter > div'
  1289. const childSelector = `${containerSelector} > div`
  1290. expect(await html(containerSelector)).toBe(
  1291. '<div class="test">content</div>',
  1292. )
  1293. // leave
  1294. expect(
  1295. (await transitionStart(btnSelector, childSelector)).classNames,
  1296. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1297. await waitForInnerHTML(
  1298. containerSelector,
  1299. '<div class="test test-leave-active test-leave-to">content</div>',
  1300. )
  1301. await waitForInnerHTML(containerSelector, '')
  1302. // enter
  1303. expect(
  1304. (await transitionStart(btnSelector, childSelector)).classNames,
  1305. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1306. await waitForInnerHTML(
  1307. containerSelector,
  1308. '<div class="test test-enter-active test-enter-to">content</div>',
  1309. )
  1310. await waitForInnerHTML(
  1311. containerSelector,
  1312. '<div class="test">content</div>',
  1313. )
  1314. },
  1315. E2E_TIMEOUT,
  1316. )
  1317. test(
  1318. 'leave with explicit durations',
  1319. async () => {
  1320. const btnSelector = '.duration-leave > button'
  1321. const containerSelector = '.duration-leave > div'
  1322. const childSelector = `${containerSelector} > div`
  1323. expect(await html(containerSelector)).toBe(
  1324. '<div class="test">content</div>',
  1325. )
  1326. // leave
  1327. expect(
  1328. (await transitionStart(btnSelector, childSelector)).classNames,
  1329. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1330. await waitForInnerHTML(
  1331. containerSelector,
  1332. '<div class="test test-leave-active test-leave-to">content</div>',
  1333. )
  1334. await waitForInnerHTML(containerSelector, '')
  1335. // enter
  1336. expect(
  1337. (await transitionStart(btnSelector, childSelector)).classNames,
  1338. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1339. await waitForInnerHTML(
  1340. containerSelector,
  1341. '<div class="test test-enter-active test-enter-to">content</div>',
  1342. )
  1343. await waitForInnerHTML(
  1344. containerSelector,
  1345. '<div class="test">content</div>',
  1346. )
  1347. },
  1348. E2E_TIMEOUT,
  1349. )
  1350. test(
  1351. 'separate enter and leave',
  1352. async () => {
  1353. const btnSelector = '.duration-enter-leave > button'
  1354. const containerSelector = '.duration-enter-leave > div'
  1355. const childSelector = `${containerSelector} > div`
  1356. expect(await html(containerSelector)).toBe(
  1357. '<div class="test">content</div>',
  1358. )
  1359. // leave
  1360. expect(
  1361. (await transitionStart(btnSelector, childSelector)).classNames,
  1362. ).toStrictEqual(['test', 'test-leave-from', 'test-leave-active'])
  1363. await waitForInnerHTML(
  1364. containerSelector,
  1365. '<div class="test test-leave-active test-leave-to">content</div>',
  1366. )
  1367. await waitForInnerHTML(containerSelector, '')
  1368. // enter
  1369. expect(
  1370. (await transitionStart(btnSelector, childSelector)).classNames,
  1371. ).toStrictEqual(['test', 'test-enter-from', 'test-enter-active'])
  1372. await waitForInnerHTML(
  1373. containerSelector,
  1374. '<div class="test test-enter-active test-enter-to">content</div>',
  1375. )
  1376. await waitForInnerHTML(
  1377. containerSelector,
  1378. '<div class="test">content</div>',
  1379. )
  1380. },
  1381. E2E_TIMEOUT,
  1382. )
  1383. })
  1384. test(
  1385. 'should work with keyed element',
  1386. async () => {
  1387. const btnSelector = '.keyed > button'
  1388. const containerSelector = '.keyed > h1'
  1389. await waitForInnerHTML(containerSelector, '0')
  1390. // change key
  1391. expect(
  1392. (await transitionStart(btnSelector, containerSelector)).classNames,
  1393. ).toStrictEqual(['v-leave-from', 'v-leave-active'])
  1394. await nextFrame()
  1395. expect(await classList(containerSelector)).toStrictEqual([
  1396. 'v-leave-active',
  1397. 'v-leave-to',
  1398. ])
  1399. await waitForInnerHTML(containerSelector, '1')
  1400. // change key again
  1401. expect(
  1402. (await transitionStart(btnSelector, containerSelector)).classNames,
  1403. ).toStrictEqual(['v-leave-from', 'v-leave-active'])
  1404. await nextFrame()
  1405. expect(await classList(containerSelector)).toStrictEqual([
  1406. 'v-leave-active',
  1407. 'v-leave-to',
  1408. ])
  1409. await waitForInnerHTML(containerSelector, '2')
  1410. },
  1411. E2E_TIMEOUT,
  1412. )
  1413. test(
  1414. 'should work with out-in mode',
  1415. async () => {
  1416. const btnSelector = '.out-in > button'
  1417. const containerSelector = '.out-in > div'
  1418. expect(await html(containerSelector)).toBe(`<div>vapor compB</div>`)
  1419. // compB -> compA
  1420. // compB leave
  1421. expect(
  1422. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1423. ).toBe(`<div class="fade-leave-from fade-leave-active">vapor compB</div>`)
  1424. await waitForInnerHTML(
  1425. containerSelector,
  1426. `<div class="fade-leave-active fade-leave-to">vapor compB</div>`,
  1427. )
  1428. // compA enter
  1429. await waitForInnerHTML(
  1430. containerSelector,
  1431. `<div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1432. )
  1433. await waitForInnerHTML(
  1434. containerSelector,
  1435. `<div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1436. )
  1437. await waitForInnerHTML(
  1438. containerSelector,
  1439. `<div class="">vapor compA</div>`,
  1440. )
  1441. // compA -> compB
  1442. // compA leave
  1443. expect(
  1444. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1445. ).toBe(`<div class="fade-leave-from fade-leave-active">vapor compA</div>`)
  1446. await waitForInnerHTML(
  1447. containerSelector,
  1448. `<div class="fade-leave-active fade-leave-to">vapor compA</div>`,
  1449. )
  1450. // compB enter
  1451. await waitForInnerHTML(
  1452. containerSelector,
  1453. `<div class="fade-enter-from fade-enter-active">vapor compB</div>`,
  1454. )
  1455. await waitForInnerHTML(
  1456. containerSelector,
  1457. `<div class="fade-enter-active fade-enter-to">vapor compB</div>`,
  1458. )
  1459. await waitForInnerHTML(
  1460. containerSelector,
  1461. `<div class="">vapor compB</div>`,
  1462. )
  1463. },
  1464. E2E_TIMEOUT,
  1465. )
  1466. test(
  1467. 'should work with in-out mode',
  1468. async () => {
  1469. const btnSelector = '.in-out > button'
  1470. const containerSelector = '.in-out > div'
  1471. expect(await html(containerSelector)).toBe(`<div>vapor compB</div>`)
  1472. // compA enter
  1473. expect(
  1474. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1475. ).toBe(
  1476. `<div>vapor compB</div><div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1477. )
  1478. await waitForInnerHTML(
  1479. containerSelector,
  1480. `<div>vapor compB</div><div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1481. )
  1482. // compB leave
  1483. await waitForInnerHTML(
  1484. containerSelector,
  1485. `<div class="fade-leave-from fade-leave-active">vapor compB</div><div class="">vapor compA</div>`,
  1486. )
  1487. await waitForInnerHTML(
  1488. containerSelector,
  1489. `<div class="fade-leave-active fade-leave-to">vapor compB</div><div class="">vapor compA</div>`,
  1490. )
  1491. await waitForInnerHTML(
  1492. containerSelector,
  1493. `<div class="">vapor compA</div>`,
  1494. )
  1495. },
  1496. E2E_TIMEOUT,
  1497. )
  1498. // tests for using vdom component in createVaporApp + vaporInteropPlugin
  1499. describe('interop', () => {
  1500. test(
  1501. 'render vdom component',
  1502. async () => {
  1503. const btnSelector = '.vdom > button'
  1504. const containerSelector = '.vdom > div'
  1505. expect(await html(containerSelector)).toBe(`<div>vdom comp</div>`)
  1506. // comp leave
  1507. expect(
  1508. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1509. ).toBe(`<div class="v-leave-from v-leave-active">vdom comp</div>`)
  1510. await waitForInnerHTML(
  1511. containerSelector,
  1512. `<div class="v-leave-active v-leave-to">vdom comp</div>`,
  1513. )
  1514. await waitForInnerHTML(containerSelector, ``)
  1515. // comp enter
  1516. expect(
  1517. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1518. ).toBe(`<div class="v-enter-from v-enter-active">vdom comp</div>`)
  1519. await waitForInnerHTML(
  1520. containerSelector,
  1521. `<div class="v-enter-active v-enter-to">vdom comp</div>`,
  1522. )
  1523. await waitForInnerHTML(
  1524. containerSelector,
  1525. `<div class="">vdom comp</div>`,
  1526. )
  1527. },
  1528. E2E_TIMEOUT,
  1529. )
  1530. test(
  1531. 'switch between vdom/vapor component (out-in mode)',
  1532. async () => {
  1533. const btnSelector = '.vdom-vapor-out-in > button'
  1534. const containerSelector = '.vdom-vapor-out-in > div'
  1535. expect(await html(containerSelector)).toBe(`<div>vdom comp</div>`)
  1536. // switch to vapor comp
  1537. // vdom comp leave
  1538. expect(
  1539. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1540. ).toBe(`<div class="fade-leave-from fade-leave-active">vdom comp</div>`)
  1541. await waitForInnerHTML(
  1542. containerSelector,
  1543. `<div class="fade-leave-active fade-leave-to">vdom comp</div>`,
  1544. )
  1545. // vapor comp enter
  1546. await waitForInnerHTML(
  1547. containerSelector,
  1548. `<div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1549. )
  1550. await waitForInnerHTML(
  1551. containerSelector,
  1552. `<div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1553. )
  1554. await waitForInnerHTML(
  1555. containerSelector,
  1556. `<div class="">vapor compA</div>`,
  1557. )
  1558. // switch to vdom comp
  1559. // vapor comp leave
  1560. expect(
  1561. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1562. ).toBe(
  1563. `<div class="fade-leave-from fade-leave-active">vapor compA</div>`,
  1564. )
  1565. await waitForInnerHTML(
  1566. containerSelector,
  1567. `<div class="fade-leave-active fade-leave-to">vapor compA</div>`,
  1568. )
  1569. // vdom comp enter
  1570. await waitForInnerHTML(
  1571. containerSelector,
  1572. `<div class="fade-enter-from fade-enter-active">vdom comp</div>`,
  1573. )
  1574. await waitForInnerHTML(
  1575. containerSelector,
  1576. `<div class="fade-enter-active fade-enter-to">vdom comp</div>`,
  1577. )
  1578. await waitForInnerHTML(
  1579. containerSelector,
  1580. `<div class="">vdom comp</div>`,
  1581. )
  1582. },
  1583. E2E_TIMEOUT,
  1584. )
  1585. test(
  1586. 'switch between vdom/vapor component (in-out mode)',
  1587. async () => {
  1588. const btnSelector = '.vdom-vapor-in-out > button'
  1589. const containerSelector = '.vdom-vapor-in-out > div'
  1590. expect(await html(containerSelector)).toBe(`<div>vapor compA</div>`)
  1591. // switch to vdom comp
  1592. // vdom comp enter
  1593. expect(
  1594. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1595. ).toBe(
  1596. `<div>vapor compA</div><div class="fade-enter-from fade-enter-active">vdom comp</div>`,
  1597. )
  1598. await waitForInnerHTML(
  1599. containerSelector,
  1600. `<div>vapor compA</div><div class="fade-enter-active fade-enter-to">vdom comp</div>`,
  1601. )
  1602. // vapor comp leave
  1603. await waitForInnerHTML(
  1604. containerSelector,
  1605. `<div class="fade-leave-from fade-leave-active">vapor compA</div><div class="">vdom comp</div>`,
  1606. )
  1607. await waitForInnerHTML(
  1608. containerSelector,
  1609. `<div class="fade-leave-active fade-leave-to">vapor compA</div><div class="">vdom comp</div>`,
  1610. )
  1611. await waitForInnerHTML(
  1612. containerSelector,
  1613. `<div class="">vdom comp</div>`,
  1614. )
  1615. // switch to vapor comp
  1616. // vapor comp enter
  1617. expect(
  1618. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  1619. ).toBe(
  1620. `<div class="">vdom comp</div><div class="fade-enter-from fade-enter-active">vapor compA</div>`,
  1621. )
  1622. await waitForInnerHTML(
  1623. containerSelector,
  1624. `<div class="">vdom comp</div><div class="fade-enter-active fade-enter-to">vapor compA</div>`,
  1625. )
  1626. // vdom comp leave
  1627. await waitForInnerHTML(
  1628. containerSelector,
  1629. `<div class="fade-leave-from fade-leave-active">vdom comp</div><div class="">vapor compA</div>`,
  1630. )
  1631. await waitForInnerHTML(
  1632. containerSelector,
  1633. `<div class="fade-leave-active fade-leave-to">vdom comp</div><div class="">vapor compA</div>`,
  1634. )
  1635. await waitForInnerHTML(
  1636. containerSelector,
  1637. `<div class="">vapor compA</div>`,
  1638. )
  1639. },
  1640. E2E_TIMEOUT,
  1641. )
  1642. })
  1643. })