transition-group.spec.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. import {
  2. E2E_TIMEOUT,
  3. setupPuppeteer,
  4. } from '../../../packages/vue/__tests__/e2e/e2eUtils'
  5. import { expect } from 'vitest'
  6. import { startE2ETestServer } from './server'
  7. const { page, html, transitionStart, waitForInnerHTML } = setupPuppeteer()
  8. const appearTransitionStart = (containerSelector: string) =>
  9. page().evaluate(selector => {
  10. ;(window as any).setAppear()
  11. return Promise.resolve().then(
  12. () => (document.querySelector(selector) as HTMLElement)!.innerHTML,
  13. )
  14. }, containerSelector)
  15. function toSlug(value: string) {
  16. return value
  17. .trim()
  18. .toLowerCase()
  19. .replace(/[^a-z0-9]+/g, '-')
  20. .replace(/^-+|-+$/g, '')
  21. }
  22. function resolveCaseId(testName: string) {
  23. const parts = testName
  24. .split(' > ')
  25. .map(item => item.trim())
  26. .filter(Boolean)
  27. const testTitle = parts[parts.length - 1]
  28. if (!testTitle) {
  29. throw new Error(`[transition-group] Invalid test name: "${testName}"`)
  30. }
  31. const suiteParts = parts.slice(1, -1)
  32. const folderParts = suiteParts.length ? suiteParts : [parts[0]]
  33. const folderPath = folderParts.map(toSlug).join('/')
  34. return `${folderPath}/${toSlug(testTitle)}`
  35. }
  36. describe('vapor transition-group', () => {
  37. let server: Awaited<ReturnType<typeof startE2ETestServer>>
  38. let port = 0
  39. beforeAll(async () => {
  40. server = await startE2ETestServer('transition-group', import.meta.dirname)
  41. port = server.port
  42. })
  43. afterAll(() => {
  44. server.close()
  45. })
  46. beforeEach(async () => {
  47. const testName = expect.getState().currentTestName || ''
  48. const caseId = resolveCaseId(testName)
  49. const baseUrl = `http://localhost:${port}/transition-group/?case=${caseId}`
  50. await page().goto(baseUrl)
  51. await page().waitForSelector('#app')
  52. })
  53. test(
  54. 'enter',
  55. async () => {
  56. const btnSelector = '.enter > button'
  57. const containerSelector = '.enter > div'
  58. expect(await html(containerSelector)).toBe(
  59. `<div class="test">a</div>` +
  60. `<div class="test">b</div>` +
  61. `<div class="test">c</div>`,
  62. )
  63. expect(
  64. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  65. ).toBe(
  66. `<div class="test">a</div>` +
  67. `<div class="test">b</div>` +
  68. `<div class="test">c</div>` +
  69. `<div class="test test-enter-from test-enter-active">d</div>` +
  70. `<div class="test test-enter-from test-enter-active">e</div>`,
  71. )
  72. await waitForInnerHTML(
  73. containerSelector,
  74. `<div class="test">a</div>` +
  75. `<div class="test">b</div>` +
  76. `<div class="test">c</div>` +
  77. `<div class="test test-enter-active test-enter-to">d</div>` +
  78. `<div class="test test-enter-active test-enter-to">e</div>`,
  79. )
  80. await waitForInnerHTML(
  81. containerSelector,
  82. `<div class="test">a</div>` +
  83. `<div class="test">b</div>` +
  84. `<div class="test">c</div>` +
  85. `<div class="test">d</div>` +
  86. `<div class="test">e</div>`,
  87. )
  88. },
  89. E2E_TIMEOUT,
  90. )
  91. test(
  92. 'if + for enter',
  93. async () => {
  94. const btnSelector = '.if-for-enter > button.toggle'
  95. const addBtnSelector = '.if-for-enter > button.add'
  96. const containerSelector = '.if-for-enter > div'
  97. expect(await html(containerSelector)).toBe(`<ul></ul>`)
  98. expect(
  99. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  100. ).toBe(
  101. `<ul>` +
  102. `<li class="test v-enter-from v-enter-active">0</li>` +
  103. `<li class="test v-enter-from v-enter-active">1</li>` +
  104. `</ul>`,
  105. )
  106. await waitForInnerHTML(
  107. containerSelector,
  108. `<ul>` +
  109. `<li class="test v-enter-active v-enter-to">0</li>` +
  110. `<li class="test v-enter-active v-enter-to">1</li>` +
  111. `</ul>`,
  112. )
  113. await waitForInnerHTML(
  114. containerSelector,
  115. `<ul><li class="test">0</li><li class="test">1</li></ul>`,
  116. )
  117. // add a new item
  118. expect(
  119. (await transitionStart(addBtnSelector, containerSelector)).innerHTML,
  120. ).toBe(
  121. `<ul>` +
  122. `<li class="test">0</li>` +
  123. `<li class="test">1</li>` +
  124. `<li class="test v-enter-from v-enter-active">2</li>` +
  125. `</ul>`,
  126. )
  127. await waitForInnerHTML(
  128. containerSelector,
  129. `<ul>` +
  130. `<li class="test">0</li>` +
  131. `<li class="test">1</li>` +
  132. `<li class="test">2</li>` +
  133. `</ul>`,
  134. )
  135. },
  136. E2E_TIMEOUT,
  137. )
  138. test(
  139. 'for + if enter',
  140. async () => {
  141. const btnSelector = '.for-if-enter > button.toggle'
  142. const addBtnSelector = '.for-if-enter > button.add'
  143. const containerSelector = '.for-if-enter > div'
  144. expect(await html(containerSelector)).toBe(`<ul></ul>`)
  145. expect(
  146. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  147. ).toBe(
  148. `<ul>` +
  149. `<li class="test v-enter-from v-enter-active">0</li>` +
  150. `<li class="test v-enter-from v-enter-active">1</li>` +
  151. `</ul>`,
  152. )
  153. await waitForInnerHTML(
  154. containerSelector,
  155. `<ul>` +
  156. `<li class="test v-enter-active v-enter-to">0</li>` +
  157. `<li class="test v-enter-active v-enter-to">1</li>` +
  158. `</ul>`,
  159. )
  160. await waitForInnerHTML(
  161. containerSelector,
  162. `<ul><li class="test">0</li><li class="test">1</li></ul>`,
  163. )
  164. // add a new item
  165. expect(
  166. (await transitionStart(addBtnSelector, containerSelector)).innerHTML,
  167. ).toBe(
  168. `<ul>` +
  169. `<li class="test">0</li>` +
  170. `<li class="test">1</li>` +
  171. `<li class="test v-enter-from v-enter-active">2</li>` +
  172. `</ul>`,
  173. )
  174. await waitForInnerHTML(
  175. containerSelector,
  176. `<ul>` +
  177. `<li class="test">0</li>` +
  178. `<li class="test">1</li>` +
  179. `<li class="test">2</li>` +
  180. `</ul>`,
  181. )
  182. },
  183. E2E_TIMEOUT,
  184. )
  185. test(
  186. 'leave',
  187. async () => {
  188. const btnSelector = '.leave > button'
  189. const containerSelector = '.leave > div'
  190. expect(await html(containerSelector)).toBe(
  191. `<div class="test">a</div>` +
  192. `<div class="test">b</div>` +
  193. `<div class="test">c</div>`,
  194. )
  195. expect(
  196. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  197. ).toBe(
  198. `<div class="test test-leave-from test-leave-active">a</div>` +
  199. `<div class="test">b</div>` +
  200. `<div class="test test-leave-from test-leave-active">c</div>`,
  201. )
  202. await waitForInnerHTML(
  203. containerSelector,
  204. `<div class="test test-leave-active test-leave-to">a</div>` +
  205. `<div class="test">b</div>` +
  206. `<div class="test test-leave-active test-leave-to">c</div>`,
  207. )
  208. await waitForInnerHTML(containerSelector, `<div class="test">b</div>`)
  209. },
  210. E2E_TIMEOUT,
  211. )
  212. test(
  213. 'enter + leave',
  214. async () => {
  215. const btnSelector = '.enter-leave > button'
  216. const containerSelector = '.enter-leave > div'
  217. expect(await html(containerSelector)).toBe(
  218. `<div class="test">a</div>` +
  219. `<div class="test">b</div>` +
  220. `<div class="test">c</div>`,
  221. )
  222. expect(
  223. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  224. ).toBe(
  225. `<div class="test test-leave-from test-leave-active">a</div>` +
  226. `<div class="test">b</div>` +
  227. `<div class="test">c</div>` +
  228. `<div class="test test-enter-from test-enter-active">d</div>`,
  229. )
  230. await waitForInnerHTML(
  231. containerSelector,
  232. `<div class="test test-leave-active test-leave-to">a</div>` +
  233. `<div class="test">b</div>` +
  234. `<div class="test">c</div>` +
  235. `<div class="test test-enter-active test-enter-to">d</div>`,
  236. )
  237. await waitForInnerHTML(
  238. containerSelector,
  239. `<div class="test">b</div>` +
  240. `<div class="test">c</div>` +
  241. `<div class="test">d</div>`,
  242. )
  243. },
  244. E2E_TIMEOUT,
  245. )
  246. test(
  247. 'appear',
  248. async () => {
  249. const btnSelector = '.appear > button'
  250. const containerSelector = '.appear > div'
  251. expect(await html('.appear')).toBe(`<button>appear button</button>`)
  252. // appear
  253. expect(await appearTransitionStart(containerSelector)).toBe(
  254. `<div class="test test-appear-from test-appear-active">a</div>` +
  255. `<div class="test test-appear-from test-appear-active">b</div>` +
  256. `<div class="test test-appear-from test-appear-active">c</div>`,
  257. )
  258. await waitForInnerHTML(
  259. containerSelector,
  260. `<div class="test test-appear-active test-appear-to">a</div>` +
  261. `<div class="test test-appear-active test-appear-to">b</div>` +
  262. `<div class="test test-appear-active test-appear-to">c</div>`,
  263. )
  264. await waitForInnerHTML(
  265. containerSelector,
  266. `<div class="test">a</div>` +
  267. `<div class="test">b</div>` +
  268. `<div class="test">c</div>`,
  269. )
  270. // enter
  271. expect(
  272. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  273. ).toBe(
  274. `<div class="test">a</div>` +
  275. `<div class="test">b</div>` +
  276. `<div class="test">c</div>` +
  277. `<div class="test test-enter-from test-enter-active">d</div>` +
  278. `<div class="test test-enter-from test-enter-active">e</div>`,
  279. )
  280. await waitForInnerHTML(
  281. containerSelector,
  282. `<div class="test">a</div>` +
  283. `<div class="test">b</div>` +
  284. `<div class="test">c</div>` +
  285. `<div class="test test-enter-active test-enter-to">d</div>` +
  286. `<div class="test test-enter-active test-enter-to">e</div>`,
  287. )
  288. await waitForInnerHTML(
  289. containerSelector,
  290. `<div class="test">a</div>` +
  291. `<div class="test">b</div>` +
  292. `<div class="test">c</div>` +
  293. `<div class="test">d</div>` +
  294. `<div class="test">e</div>`,
  295. )
  296. },
  297. E2E_TIMEOUT,
  298. )
  299. test(
  300. 'move',
  301. async () => {
  302. const btnSelector = '.move > button'
  303. const containerSelector = '.move > div'
  304. expect(await html(containerSelector)).toBe(
  305. `<div class="test">a</div>` +
  306. `<div class="test">b</div>` +
  307. `<div class="test">c</div>`,
  308. )
  309. expect(
  310. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  311. ).toBe(
  312. `<div class="test group-enter-from group-enter-active">d</div>` +
  313. `<div class="test">b</div>` +
  314. `<div class="test group-move" style="">a</div>` +
  315. `<div class="test group-leave-from group-leave-active group-move" style="">c</div>`,
  316. )
  317. await waitForInnerHTML(
  318. containerSelector,
  319. `<div class="test group-enter-active group-enter-to">d</div>` +
  320. `<div class="test">b</div>` +
  321. `<div class="test group-move" style="">a</div>` +
  322. `<div class="test group-leave-active group-move group-leave-to" style="">c</div>`,
  323. )
  324. await waitForInnerHTML(
  325. containerSelector,
  326. `<div class="test">d</div>` +
  327. `<div class="test">b</div>` +
  328. `<div class="test" style="">a</div>`,
  329. )
  330. },
  331. E2E_TIMEOUT,
  332. )
  333. test('dynamic name', async () => {
  334. const btnSelector = '.dynamic-name button.toggleBtn'
  335. const btnChangeName = '.dynamic-name button.changeNameBtn'
  336. const containerSelector = '.dynamic-name > div'
  337. expect(await html(containerSelector)).toBe(
  338. `<div>a</div>` + `<div>b</div>` + `<div>c</div>`,
  339. )
  340. // invalid name
  341. expect(
  342. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  343. ).toBe(`<div>b</div>` + `<div>c</div>` + `<div>a</div>`)
  344. // change name
  345. expect(
  346. (await transitionStart(btnChangeName, containerSelector)).innerHTML,
  347. ).toBe(
  348. `<div class="group-move" style="">a</div>` +
  349. `<div class="group-move" style="">b</div>` +
  350. `<div class="group-move" style="">c</div>`,
  351. )
  352. await waitForInnerHTML(
  353. containerSelector,
  354. `<div class="" style="">a</div>` +
  355. `<div class="" style="">b</div>` +
  356. `<div class="" style="">c</div>`,
  357. )
  358. })
  359. test('events', async () => {
  360. const btnSelector = '.events > button'
  361. const containerSelector = '.events > div'
  362. expect(await html('.events')).toBe(`<button>events button</button>`)
  363. // appear
  364. expect(await appearTransitionStart(containerSelector)).toBe(
  365. `<div class="test test-appear-from test-appear-active">a</div>` +
  366. `<div class="test test-appear-from test-appear-active">b</div>` +
  367. `<div class="test test-appear-from test-appear-active">c</div>`,
  368. )
  369. await waitForInnerHTML(
  370. containerSelector,
  371. `<div class="test test-appear-active test-appear-to">a</div>` +
  372. `<div class="test test-appear-active test-appear-to">b</div>` +
  373. `<div class="test test-appear-active test-appear-to">c</div>`,
  374. )
  375. let calls = await page().evaluate(() => {
  376. return (window as any).getCalls()
  377. })
  378. expect(calls).toContain('beforeAppear')
  379. expect(calls).toContain('onAppear')
  380. expect(calls).not.toContain('afterAppear')
  381. await waitForInnerHTML(
  382. containerSelector,
  383. `<div class="test">a</div>` +
  384. `<div class="test">b</div>` +
  385. `<div class="test">c</div>`,
  386. )
  387. expect(
  388. await page().evaluate(() => {
  389. return (window as any).getCalls()
  390. }),
  391. ).toContain('afterAppear')
  392. // enter + leave
  393. expect(
  394. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  395. ).toBe(
  396. `<div class="test test-leave-from test-leave-active">a</div>` +
  397. `<div class="test">b</div>` +
  398. `<div class="test">c</div>` +
  399. `<div class="test test-enter-from test-enter-active">d</div>`,
  400. )
  401. calls = await page().evaluate(() => {
  402. return (window as any).getCalls()
  403. })
  404. expect(calls).toContain('beforeLeave')
  405. expect(calls).toContain('onLeave')
  406. expect(calls).not.toContain('afterLeave')
  407. expect(calls).toContain('beforeEnter')
  408. expect(calls).toContain('onEnter')
  409. expect(calls).not.toContain('afterEnter')
  410. await waitForInnerHTML(
  411. containerSelector,
  412. `<div class="test test-leave-active test-leave-to">a</div>` +
  413. `<div class="test">b</div>` +
  414. `<div class="test">c</div>` +
  415. `<div class="test test-enter-active test-enter-to">d</div>`,
  416. )
  417. calls = await page().evaluate(() => {
  418. return (window as any).getCalls()
  419. })
  420. expect(calls).not.toContain('afterLeave')
  421. expect(calls).not.toContain('afterEnter')
  422. await waitForInnerHTML(
  423. containerSelector,
  424. `<div class="test">b</div>` +
  425. `<div class="test">c</div>` +
  426. `<div class="test">d</div>`,
  427. )
  428. calls = await page().evaluate(() => {
  429. return (window as any).getCalls()
  430. })
  431. expect(calls).toContain('afterLeave')
  432. expect(calls).toContain('afterEnter')
  433. })
  434. test(
  435. 'reusable transition group',
  436. async () => {
  437. const btnSelector = '.reusable-transition-group > button'
  438. const containerSelector = '.reusable-transition-group > div'
  439. expect(await html(containerSelector)).toBe(
  440. `<div class="test">a</div>` +
  441. `<div class="test">b</div>` +
  442. `<div class="test">c</div>`,
  443. )
  444. expect(
  445. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  446. ).toBe(
  447. `<div class="test group-enter-from group-enter-active">d</div>` +
  448. `<div class="test">b</div>` +
  449. `<div class="test group-move" style="">a</div>` +
  450. `<div class="test group-leave-from group-leave-active group-move" style="">c</div>`,
  451. )
  452. await waitForInnerHTML(
  453. containerSelector,
  454. `<div class="test group-enter-active group-enter-to">d</div>` +
  455. `<div class="test">b</div>` +
  456. `<div class="test group-move" style="">a</div>` +
  457. `<div class="test group-leave-active group-move group-leave-to" style="">c</div>`,
  458. )
  459. await waitForInnerHTML(
  460. containerSelector,
  461. `<div class="test">d</div>` +
  462. `<div class="test">b</div>` +
  463. `<div class="test" style="">a</div>`,
  464. )
  465. },
  466. E2E_TIMEOUT,
  467. )
  468. describe('interop', () => {
  469. test('render vdom component', async () => {
  470. const btnSelector = '.interop > button'
  471. const containerSelector = '.interop > div'
  472. expect(await html(containerSelector)).toBe(
  473. `<div><div>a</div></div>` +
  474. `<div><div>b</div></div>` +
  475. `<div><div>c</div></div>`,
  476. )
  477. expect(
  478. (await transitionStart(btnSelector, containerSelector)).innerHTML,
  479. ).toBe(
  480. `<div class="test-leave-from test-leave-active"><div>a</div></div>` +
  481. `<div class="test-move" style=""><div>b</div></div>` +
  482. `<div class="test-move" style=""><div>c</div></div>` +
  483. `<div class="test-enter-from test-enter-active"><div>d</div></div>`,
  484. )
  485. await waitForInnerHTML(
  486. containerSelector,
  487. `<div class="test-leave-active test-leave-to"><div>a</div></div>` +
  488. `<div class="test-move" style=""><div>b</div></div>` +
  489. `<div class="test-move" style=""><div>c</div></div>` +
  490. `<div class="test-enter-active test-enter-to"><div>d</div></div>`,
  491. )
  492. await waitForInnerHTML(
  493. containerSelector,
  494. `<div class="" style=""><div>b</div></div>` +
  495. `<div class="" style=""><div>c</div></div>` +
  496. `<div class=""><div>d</div></div>`,
  497. )
  498. })
  499. })
  500. })