transition.spec.ts 58 KB

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