hydration.spec.ts 76 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410
  1. // import { type SSRContext, renderToString } from '@vue/server-renderer'
  2. import {
  3. child,
  4. createComponent,
  5. createVaporSSRApp,
  6. delegateEvents,
  7. next,
  8. renderEffect,
  9. setClass,
  10. setInsertionState,
  11. setText,
  12. template,
  13. } from '../src'
  14. import { nextTick, ref, toDisplayString } from '@vue/runtime-dom'
  15. function mountWithHydration(html: string, setup: () => any) {
  16. const container = document.createElement('div')
  17. document.body.appendChild(container)
  18. container.innerHTML = html
  19. const app = createVaporSSRApp({
  20. setup,
  21. })
  22. app.mount(container)
  23. return {
  24. container,
  25. }
  26. }
  27. const triggerEvent = (type: string, el: Element) => {
  28. const event = new Event(type, { bubbles: true })
  29. el.dispatchEvent(event)
  30. }
  31. describe('SSR hydration', () => {
  32. delegateEvents('click')
  33. beforeEach(() => {
  34. document.body.innerHTML = ''
  35. })
  36. test('root text', async () => {
  37. const msg = ref('foo')
  38. const t = template(' ')
  39. const { container } = mountWithHydration('foo', () => {
  40. const n = t()
  41. renderEffect(() => setText(n as Text, msg.value))
  42. return n
  43. })
  44. expect(container.textContent).toBe('foo')
  45. msg.value = 'bar'
  46. await nextTick()
  47. expect(container.textContent).toBe('bar')
  48. })
  49. test('root comment', () => {
  50. const t0 = template('<!---->')
  51. const { container } = mountWithHydration('<!---->', () => t0())
  52. expect(container.innerHTML).toBe('<!---->')
  53. expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
  54. })
  55. test('root with mixed element and text', async () => {
  56. const t0 = template(' A')
  57. const t1 = template('<span>foo bar</span>')
  58. const t2 = template(' ')
  59. const msg = ref('hello')
  60. const { container } = mountWithHydration(
  61. ' A<span>foo bar</span>hello',
  62. () => {
  63. const n0 = t0()
  64. const n1 = t1()
  65. const n2 = t2()
  66. renderEffect(() => setText(n2 as Text, toDisplayString(msg.value)))
  67. return [n0, n1, n2]
  68. },
  69. )
  70. expect(container.innerHTML).toBe(' A<span>foo bar</span>hello')
  71. msg.value = 'bar'
  72. await nextTick()
  73. expect(container.innerHTML).toBe(' A<span>foo bar</span>bar')
  74. })
  75. test('empty element', async () => {
  76. const t0 = template('<div></div>', true)
  77. const { container } = mountWithHydration('<div></div>', () => t0())
  78. expect(container.innerHTML).toBe('<div></div>')
  79. expect(`Hydration children mismatch in <div>`).not.toHaveBeenWarned()
  80. })
  81. test('element with text children', async () => {
  82. const t0 = template('<div> </div>', true)
  83. const msg = ref('foo')
  84. const { container } = mountWithHydration(
  85. '<div class="foo">foo</div>',
  86. () => {
  87. const n0 = t0() as Element
  88. const x0 = child(n0) as Text
  89. renderEffect(() => {
  90. const _msg = msg.value
  91. setText(x0, toDisplayString(_msg))
  92. setClass(n0, _msg)
  93. })
  94. return n0
  95. },
  96. )
  97. expect(container.innerHTML).toBe(`<div class="foo">foo</div>`)
  98. msg.value = 'bar'
  99. await nextTick()
  100. expect(container.innerHTML).toBe(`<div class="bar">bar</div>`)
  101. })
  102. test('element with elements children', async () => {
  103. const t0 = template('<div><span> </span><span></span></div>', true)
  104. const msg = ref('foo')
  105. const fn = vi.fn()
  106. const { container } = mountWithHydration(
  107. '<div><span>foo</span><span class="foo"></span></div>',
  108. () => {
  109. const n2 = t0() as Element
  110. const n0 = child(n2) as Element
  111. const n1 = next(n0) as Element
  112. const x0 = child(n0) as Text
  113. ;(n1 as any).$evtclick = fn
  114. renderEffect(() => {
  115. const _msg = msg.value
  116. setText(x0, toDisplayString(_msg))
  117. setClass(n1, _msg)
  118. })
  119. return n2
  120. },
  121. )
  122. expect(container.innerHTML).toBe(
  123. `<div><span>foo</span><span class="foo"></span></div>`,
  124. )
  125. // event handler
  126. triggerEvent('click', container.querySelector('.foo')!)
  127. expect(fn).toHaveBeenCalled()
  128. msg.value = 'bar'
  129. await nextTick()
  130. expect(container.innerHTML).toBe(
  131. `<div><span>bar</span><span class="bar"></span></div>`,
  132. )
  133. })
  134. test('basic component', async () => {
  135. const t0 = template(' ')
  136. const msg = ref('foo')
  137. const Comp = {
  138. setup() {
  139. const n0 = t0() as Text
  140. renderEffect(() => setText(n0, toDisplayString(msg.value)))
  141. return n0
  142. },
  143. }
  144. const t1 = template('<div><span></span></div>', true)
  145. const { container } = mountWithHydration(
  146. '<div><span></span>foo</div>',
  147. () => {
  148. const n1 = t1() as Element
  149. setInsertionState(n1)
  150. createComponent(Comp)
  151. return n1
  152. },
  153. )
  154. expect(container.innerHTML).toBe(`<div><span></span>foo</div>`)
  155. msg.value = 'bar'
  156. await nextTick()
  157. expect(container.innerHTML).toBe(`<div><span></span>bar</div>`)
  158. })
  159. test('fragment component', async () => {
  160. const t0 = template('<div> </div>')
  161. const t1 = template(' ')
  162. const msg = ref('foo')
  163. const Comp = {
  164. setup() {
  165. const n0 = t0() as Element
  166. const n1 = t1() as Text
  167. const x0 = child(n0) as Text
  168. renderEffect(() => {
  169. const _msg = msg.value
  170. setText(x0, toDisplayString(_msg))
  171. setText(n1, toDisplayString(_msg))
  172. })
  173. return [n0, n1]
  174. },
  175. }
  176. const t2 = template('<div><span></span></div>', true)
  177. const { container } = mountWithHydration(
  178. '<div><span></span><!--[--><div>foo</div>foo<!--]--></div>',
  179. () => {
  180. const n1 = t2() as Element
  181. setInsertionState(n1)
  182. createComponent(Comp)
  183. return n1
  184. },
  185. )
  186. expect(container.innerHTML).toBe(
  187. `<div><span></span><!--[--><div>foo</div>foo<!--]--></div>`,
  188. )
  189. msg.value = 'bar'
  190. await nextTick()
  191. expect(container.innerHTML).toBe(
  192. `<div><span></span><!--[--><div>bar</div>bar<!--]--></div>`,
  193. )
  194. })
  195. test('fragment component with prepend', async () => {
  196. const t0 = template('<div> </div>')
  197. const t1 = template(' ')
  198. const msg = ref('foo')
  199. const Comp = {
  200. setup() {
  201. const n0 = t0() as Element
  202. const n1 = t1() as Text
  203. const x0 = child(n0) as Text
  204. renderEffect(() => {
  205. const _msg = msg.value
  206. setText(x0, toDisplayString(_msg))
  207. setText(n1, toDisplayString(_msg))
  208. })
  209. return [n0, n1]
  210. },
  211. }
  212. const t2 = template('<div><span></span></div>', true)
  213. const { container } = mountWithHydration(
  214. '<div><!--[--><div>foo</div>foo<!--]--><span></span></div>',
  215. () => {
  216. const n1 = t2() as Element
  217. setInsertionState(n1, 0)
  218. createComponent(Comp)
  219. return n1
  220. },
  221. )
  222. expect(container.innerHTML).toBe(
  223. `<div><!--[--><div>foo</div>foo<!--]--><span></span></div>`,
  224. )
  225. msg.value = 'bar'
  226. await nextTick()
  227. expect(container.innerHTML).toBe(
  228. `<div><!--[--><div>bar</div>bar<!--]--><span></span></div>`,
  229. )
  230. })
  231. // test('element with ref', () => {
  232. // const el = ref()
  233. // const { vnode, container } = mountWithHydration('<div></div>', () =>
  234. // h('div', { ref: el }),
  235. // )
  236. // expect(vnode.el).toBe(container.firstChild)
  237. // expect(el.value).toBe(vnode.el)
  238. // })
  239. // test('Fragment', async () => {
  240. // const msg = ref('foo')
  241. // const fn = vi.fn()
  242. // const { vnode, container } = mountWithHydration(
  243. // '<div><!--[--><span>foo</span><!--[--><span class="foo"></span><!--]--><!--]--></div>',
  244. // () =>
  245. // h('div', [
  246. // [
  247. // h('span', msg.value),
  248. // [h('span', { class: msg.value, onClick: fn })],
  249. // ],
  250. // ]),
  251. // )
  252. // expect(vnode.el).toBe(container.firstChild)
  253. // expect(vnode.el.innerHTML).toBe(
  254. // `<!--[--><span>foo</span><!--[--><span class="foo"></span><!--]--><!--]-->`,
  255. // )
  256. // // start fragment 1
  257. // const fragment1 = (vnode.children as VNode[])[0]
  258. // expect(fragment1.el).toBe(vnode.el.childNodes[0])
  259. // const fragment1Children = fragment1.children as VNode[]
  260. // // first <span>
  261. // expect(fragment1Children[0].el!.tagName).toBe('SPAN')
  262. // expect(fragment1Children[0].el).toBe(vnode.el.childNodes[1])
  263. // // start fragment 2
  264. // const fragment2 = fragment1Children[1]
  265. // expect(fragment2.el).toBe(vnode.el.childNodes[2])
  266. // const fragment2Children = fragment2.children as VNode[]
  267. // // second <span>
  268. // expect(fragment2Children[0].el!.tagName).toBe('SPAN')
  269. // expect(fragment2Children[0].el).toBe(vnode.el.childNodes[3])
  270. // // end fragment 2
  271. // expect(fragment2.anchor).toBe(vnode.el.childNodes[4])
  272. // // end fragment 1
  273. // expect(fragment1.anchor).toBe(vnode.el.childNodes[5])
  274. // // event handler
  275. // triggerEvent('click', vnode.el.querySelector('.foo')!)
  276. // expect(fn).toHaveBeenCalled()
  277. // msg.value = 'bar'
  278. // await nextTick()
  279. // expect(vnode.el.innerHTML).toBe(
  280. // `<!--[--><span>bar</span><!--[--><span class="bar"></span><!--]--><!--]-->`,
  281. // )
  282. // })
  283. // // #7285
  284. // test('Fragment (multiple continuous text vnodes)', async () => {
  285. // // should no mismatch warning
  286. // const { container } = mountWithHydration('<!--[-->fooo<!--]-->', () => [
  287. // 'fo',
  288. // createTextVNode('o'),
  289. // 'o',
  290. // ])
  291. // expect(container.textContent).toBe('fooo')
  292. // })
  293. // test('Teleport', async () => {
  294. // const msg = ref('foo')
  295. // const fn = vi.fn()
  296. // const teleportContainer = document.createElement('div')
  297. // teleportContainer.id = 'teleport'
  298. // teleportContainer.innerHTML = `<!--teleport start anchor--><span>foo</span><span class="foo"></span><!--teleport anchor-->`
  299. // document.body.appendChild(teleportContainer)
  300. // const { vnode, container } = mountWithHydration(
  301. // '<!--teleport start--><!--teleport end-->',
  302. // () =>
  303. // h(Teleport, { to: '#teleport' }, [
  304. // h('span', msg.value),
  305. // h('span', { class: msg.value, onClick: fn }),
  306. // ]),
  307. // )
  308. // expect(vnode.el).toBe(container.firstChild)
  309. // expect(vnode.anchor).toBe(container.lastChild)
  310. // expect(vnode.target).toBe(teleportContainer)
  311. // expect(vnode.targetStart).toBe(teleportContainer.childNodes[0])
  312. // expect((vnode.children as VNode[])[0].el).toBe(
  313. // teleportContainer.childNodes[1],
  314. // )
  315. // expect((vnode.children as VNode[])[1].el).toBe(
  316. // teleportContainer.childNodes[2],
  317. // )
  318. // expect(vnode.targetAnchor).toBe(teleportContainer.childNodes[3])
  319. // // event handler
  320. // triggerEvent('click', teleportContainer.querySelector('.foo')!)
  321. // expect(fn).toHaveBeenCalled()
  322. // msg.value = 'bar'
  323. // await nextTick()
  324. // expect(teleportContainer.innerHTML).toBe(
  325. // `<!--teleport start anchor--><span>bar</span><span class="bar"></span><!--teleport anchor-->`,
  326. // )
  327. // })
  328. // test('Teleport (multiple + integration)', async () => {
  329. // const msg = ref('foo')
  330. // const fn1 = vi.fn()
  331. // const fn2 = vi.fn()
  332. // const Comp = () => [
  333. // h(Teleport, { to: '#teleport2' }, [
  334. // h('span', msg.value),
  335. // h('span', { class: msg.value, onClick: fn1 }),
  336. // ]),
  337. // h(Teleport, { to: '#teleport2' }, [
  338. // h('span', msg.value + '2'),
  339. // h('span', { class: msg.value + '2', onClick: fn2 }),
  340. // ]),
  341. // ]
  342. // const teleportContainer = document.createElement('div')
  343. // teleportContainer.id = 'teleport2'
  344. // const ctx: SSRContext = {}
  345. // const mainHtml = await renderToString(h(Comp), ctx)
  346. // expect(mainHtml).toMatchInlineSnapshot(
  347. // `"<!--[--><!--teleport start--><!--teleport end--><!--teleport start--><!--teleport end--><!--]-->"`,
  348. // )
  349. // const teleportHtml = ctx.teleports!['#teleport2']
  350. // expect(teleportHtml).toMatchInlineSnapshot(
  351. // `"<!--teleport start anchor--><span>foo</span><span class="foo"></span><!--teleport anchor--><!--teleport start anchor--><span>foo2</span><span class="foo2"></span><!--teleport anchor-->"`,
  352. // )
  353. // teleportContainer.innerHTML = teleportHtml
  354. // document.body.appendChild(teleportContainer)
  355. // const { vnode, container } = mountWithHydration(mainHtml, Comp)
  356. // expect(vnode.el).toBe(container.firstChild)
  357. // const teleportVnode1 = (vnode.children as VNode[])[0]
  358. // const teleportVnode2 = (vnode.children as VNode[])[1]
  359. // expect(teleportVnode1.el).toBe(container.childNodes[1])
  360. // expect(teleportVnode1.anchor).toBe(container.childNodes[2])
  361. // expect(teleportVnode2.el).toBe(container.childNodes[3])
  362. // expect(teleportVnode2.anchor).toBe(container.childNodes[4])
  363. // expect(teleportVnode1.target).toBe(teleportContainer)
  364. // expect(teleportVnode1.targetStart).toBe(teleportContainer.childNodes[0])
  365. // expect((teleportVnode1 as any).children[0].el).toBe(
  366. // teleportContainer.childNodes[1],
  367. // )
  368. // expect(teleportVnode1.targetAnchor).toBe(teleportContainer.childNodes[3])
  369. // expect(teleportVnode2.target).toBe(teleportContainer)
  370. // expect(teleportVnode2.targetStart).toBe(teleportContainer.childNodes[4])
  371. // expect((teleportVnode2 as any).children[0].el).toBe(
  372. // teleportContainer.childNodes[5],
  373. // )
  374. // expect(teleportVnode2.targetAnchor).toBe(teleportContainer.childNodes[7])
  375. // // // event handler
  376. // triggerEvent('click', teleportContainer.querySelector('.foo')!)
  377. // expect(fn1).toHaveBeenCalled()
  378. // triggerEvent('click', teleportContainer.querySelector('.foo2')!)
  379. // expect(fn2).toHaveBeenCalled()
  380. // msg.value = 'bar'
  381. // await nextTick()
  382. // expect(teleportContainer.innerHTML).toMatchInlineSnapshot(
  383. // `"<!--teleport start anchor--><span>bar</span><span class="bar"></span><!--teleport anchor--><!--teleport start anchor--><span>bar2</span><span class="bar2"></span><!--teleport anchor-->"`,
  384. // )
  385. // })
  386. // test('Teleport (disabled)', async () => {
  387. // const msg = ref('foo')
  388. // const fn1 = vi.fn()
  389. // const fn2 = vi.fn()
  390. // const Comp = () => [
  391. // h('div', 'foo'),
  392. // h(Teleport, { to: '#teleport3', disabled: true }, [
  393. // h('span', msg.value),
  394. // h('span', { class: msg.value, onClick: fn1 }),
  395. // ]),
  396. // h('div', { class: msg.value + '2', onClick: fn2 }, 'bar'),
  397. // ]
  398. // const teleportContainer = document.createElement('div')
  399. // teleportContainer.id = 'teleport3'
  400. // const ctx: SSRContext = {}
  401. // const mainHtml = await renderToString(h(Comp), ctx)
  402. // expect(mainHtml).toMatchInlineSnapshot(
  403. // `"<!--[--><div>foo</div><!--teleport start--><span>foo</span><span class="foo"></span><!--teleport end--><div class="foo2">bar</div><!--]-->"`,
  404. // )
  405. // const teleportHtml = ctx.teleports!['#teleport3']
  406. // expect(teleportHtml).toMatchInlineSnapshot(
  407. // `"<!--teleport start anchor--><!--teleport anchor-->"`,
  408. // )
  409. // teleportContainer.innerHTML = teleportHtml
  410. // document.body.appendChild(teleportContainer)
  411. // const { vnode, container } = mountWithHydration(mainHtml, Comp)
  412. // expect(vnode.el).toBe(container.firstChild)
  413. // const children = vnode.children as VNode[]
  414. // expect(children[0].el).toBe(container.childNodes[1])
  415. // const teleportVnode = children[1]
  416. // expect(teleportVnode.el).toBe(container.childNodes[2])
  417. // expect((teleportVnode.children as VNode[])[0].el).toBe(
  418. // container.childNodes[3],
  419. // )
  420. // expect((teleportVnode.children as VNode[])[1].el).toBe(
  421. // container.childNodes[4],
  422. // )
  423. // expect(teleportVnode.anchor).toBe(container.childNodes[5])
  424. // expect(children[2].el).toBe(container.childNodes[6])
  425. // expect(teleportVnode.target).toBe(teleportContainer)
  426. // expect(teleportVnode.targetStart).toBe(teleportContainer.childNodes[0])
  427. // expect(teleportVnode.targetAnchor).toBe(teleportContainer.childNodes[1])
  428. // // // event handler
  429. // triggerEvent('click', container.querySelector('.foo')!)
  430. // expect(fn1).toHaveBeenCalled()
  431. // triggerEvent('click', container.querySelector('.foo2')!)
  432. // expect(fn2).toHaveBeenCalled()
  433. // msg.value = 'bar'
  434. // await nextTick()
  435. // expect(container.innerHTML).toMatchInlineSnapshot(
  436. // `"<!--[--><div>foo</div><!--teleport start--><span>bar</span><span class="bar"></span><!--teleport end--><div class="bar2">bar</div><!--]-->"`,
  437. // )
  438. // })
  439. // // #6152
  440. // test('Teleport (disabled + as component root)', () => {
  441. // const { container } = mountWithHydration(
  442. // '<!--[--><div>Parent fragment</div><!--teleport start--><div>Teleport content</div><!--teleport end--><!--]-->',
  443. // () => [
  444. // h('div', 'Parent fragment'),
  445. // h(() =>
  446. // h(Teleport, { to: 'body', disabled: true }, [
  447. // h('div', 'Teleport content'),
  448. // ]),
  449. // ),
  450. // ],
  451. // )
  452. // expect(document.body.innerHTML).toBe('')
  453. // expect(container.innerHTML).toBe(
  454. // '<!--[--><div>Parent fragment</div><!--teleport start--><div>Teleport content</div><!--teleport end--><!--]-->',
  455. // )
  456. // expect(
  457. // `Hydration completed but contains mismatches.`,
  458. // ).not.toHaveBeenWarned()
  459. // })
  460. // test('Teleport (as component root)', () => {
  461. // const teleportContainer = document.createElement('div')
  462. // teleportContainer.id = 'teleport4'
  463. // teleportContainer.innerHTML = `<!--teleport start anchor-->hello<!--teleport anchor-->`
  464. // document.body.appendChild(teleportContainer)
  465. // const wrapper = {
  466. // render() {
  467. // return h(Teleport, { to: '#teleport4' }, ['hello'])
  468. // },
  469. // }
  470. // const { vnode, container } = mountWithHydration(
  471. // '<div><!--teleport start--><!--teleport end--><div></div></div>',
  472. // () => h('div', [h(wrapper), h('div')]),
  473. // )
  474. // expect(vnode.el).toBe(container.firstChild)
  475. // // component el
  476. // const wrapperVNode = (vnode as any).children[0]
  477. // const tpStart = container.firstChild?.firstChild
  478. // const tpEnd = tpStart?.nextSibling
  479. // expect(wrapperVNode.el).toBe(tpStart)
  480. // expect(wrapperVNode.component.subTree.el).toBe(tpStart)
  481. // expect(wrapperVNode.component.subTree.anchor).toBe(tpEnd)
  482. // // next node hydrate properly
  483. // const nextVNode = (vnode as any).children[1]
  484. // expect(nextVNode.el).toBe(container.firstChild?.lastChild)
  485. // })
  486. // test('Teleport (nested)', () => {
  487. // const teleportContainer = document.createElement('div')
  488. // teleportContainer.id = 'teleport5'
  489. // teleportContainer.innerHTML = `<!--teleport start anchor--><div><!--teleport start--><!--teleport end--></div><!--teleport anchor--><!--teleport start anchor--><div>child</div><!--teleport anchor-->`
  490. // document.body.appendChild(teleportContainer)
  491. // const { vnode, container } = mountWithHydration(
  492. // '<!--teleport start--><!--teleport end-->',
  493. // () =>
  494. // h(Teleport, { to: '#teleport5' }, [
  495. // h('div', [h(Teleport, { to: '#teleport5' }, [h('div', 'child')])]),
  496. // ]),
  497. // )
  498. // expect(vnode.el).toBe(container.firstChild)
  499. // expect(vnode.anchor).toBe(container.lastChild)
  500. // const childDivVNode = (vnode as any).children[0]
  501. // const div = teleportContainer.childNodes[1]
  502. // expect(childDivVNode.el).toBe(div)
  503. // expect(vnode.targetAnchor).toBe(div?.nextSibling)
  504. // const childTeleportVNode = childDivVNode.children[0]
  505. // expect(childTeleportVNode.el).toBe(div?.firstChild)
  506. // expect(childTeleportVNode.anchor).toBe(div?.lastChild)
  507. // expect(childTeleportVNode.targetAnchor).toBe(teleportContainer.lastChild)
  508. // expect(childTeleportVNode.children[0].el).toBe(
  509. // teleportContainer.lastChild?.previousSibling,
  510. // )
  511. // })
  512. // test('with data-allow-mismatch component when using onServerPrefetch', async () => {
  513. // const Comp = {
  514. // template: `
  515. // <div>Comp2</div>
  516. // `,
  517. // }
  518. // let foo: any
  519. // const App = {
  520. // setup() {
  521. // const flag = ref(true)
  522. // foo = () => {
  523. // flag.value = false
  524. // }
  525. // onServerPrefetch(() => (flag.value = false))
  526. // return { flag }
  527. // },
  528. // components: {
  529. // Comp,
  530. // },
  531. // template: `
  532. // <span data-allow-mismatch>
  533. // <Comp v-if="flag"></Comp>
  534. // </span>
  535. // `,
  536. // }
  537. // // hydrate
  538. // const container = document.createElement('div')
  539. // container.innerHTML = await renderToString(h(App))
  540. // createSSRApp(App).mount(container)
  541. // expect(container.innerHTML).toBe(
  542. // '<span data-allow-mismatch=""><div>Comp2</div></span>',
  543. // )
  544. // foo()
  545. // await nextTick()
  546. // expect(container.innerHTML).toBe(
  547. // '<span data-allow-mismatch=""><!--v-if--></span>',
  548. // )
  549. // })
  550. // test('Teleport unmount (full integration)', async () => {
  551. // const Comp1 = {
  552. // template: `
  553. // <Teleport to="#target">
  554. // <span>Teleported Comp1</span>
  555. // </Teleport>
  556. // `,
  557. // }
  558. // const Comp2 = {
  559. // template: `
  560. // <div>Comp2</div>
  561. // `,
  562. // }
  563. // const toggle = ref(true)
  564. // const App = {
  565. // template: `
  566. // <div>
  567. // <Comp1 v-if="toggle"/>
  568. // <Comp2 v-else/>
  569. // </div>
  570. // `,
  571. // components: {
  572. // Comp1,
  573. // Comp2,
  574. // },
  575. // setup() {
  576. // return { toggle }
  577. // },
  578. // }
  579. // const container = document.createElement('div')
  580. // const teleportContainer = document.createElement('div')
  581. // teleportContainer.id = 'target'
  582. // document.body.appendChild(teleportContainer)
  583. // // server render
  584. // const ctx: SSRContext = {}
  585. // container.innerHTML = await renderToString(h(App), ctx)
  586. // expect(container.innerHTML).toBe(
  587. // '<div><!--teleport start--><!--teleport end--></div>',
  588. // )
  589. // teleportContainer.innerHTML = ctx.teleports!['#target']
  590. // // hydrate
  591. // createSSRApp(App).mount(container)
  592. // expect(container.innerHTML).toBe(
  593. // '<div><!--teleport start--><!--teleport end--></div>',
  594. // )
  595. // expect(teleportContainer.innerHTML).toBe(
  596. // '<!--teleport start anchor--><span>Teleported Comp1</span><!--teleport anchor-->',
  597. // )
  598. // expect(`Hydration children mismatch`).not.toHaveBeenWarned()
  599. // toggle.value = false
  600. // await nextTick()
  601. // expect(container.innerHTML).toBe('<div><div>Comp2</div></div>')
  602. // expect(teleportContainer.innerHTML).toBe('')
  603. // })
  604. // test('Teleport unmount (mismatch + full integration)', async () => {
  605. // const Comp1 = {
  606. // template: `
  607. // <Teleport to="#target">
  608. // <span>Teleported Comp1</span>
  609. // </Teleport>
  610. // `,
  611. // }
  612. // const Comp2 = {
  613. // template: `
  614. // <div>Comp2</div>
  615. // `,
  616. // }
  617. // const toggle = ref(true)
  618. // const App = {
  619. // template: `
  620. // <div>
  621. // <Comp1 v-if="toggle"/>
  622. // <Comp2 v-else/>
  623. // </div>
  624. // `,
  625. // components: {
  626. // Comp1,
  627. // Comp2,
  628. // },
  629. // setup() {
  630. // return { toggle }
  631. // },
  632. // }
  633. // const container = document.createElement('div')
  634. // const teleportContainer = document.createElement('div')
  635. // teleportContainer.id = 'target'
  636. // document.body.appendChild(teleportContainer)
  637. // // server render
  638. // container.innerHTML = await renderToString(h(App))
  639. // expect(container.innerHTML).toBe(
  640. // '<div><!--teleport start--><!--teleport end--></div>',
  641. // )
  642. // expect(teleportContainer.innerHTML).toBe('')
  643. // // hydrate
  644. // createSSRApp(App).mount(container)
  645. // expect(container.innerHTML).toBe(
  646. // '<div><!--teleport start--><!--teleport end--></div>',
  647. // )
  648. // expect(teleportContainer.innerHTML).toBe('<span>Teleported Comp1</span>')
  649. // expect(`Hydration children mismatch`).toHaveBeenWarned()
  650. // toggle.value = false
  651. // await nextTick()
  652. // expect(container.innerHTML).toBe('<div><div>Comp2</div></div>')
  653. // expect(teleportContainer.innerHTML).toBe('')
  654. // })
  655. // test('Teleport target change (mismatch + full integration)', async () => {
  656. // const target = ref('#target1')
  657. // const Comp = {
  658. // template: `
  659. // <Teleport :to="target">
  660. // <span>Teleported</span>
  661. // </Teleport>
  662. // `,
  663. // setup() {
  664. // return { target }
  665. // },
  666. // }
  667. // const App = {
  668. // template: `
  669. // <div>
  670. // <Comp />
  671. // </div>
  672. // `,
  673. // components: {
  674. // Comp,
  675. // },
  676. // }
  677. // const container = document.createElement('div')
  678. // const teleportContainer1 = document.createElement('div')
  679. // teleportContainer1.id = 'target1'
  680. // const teleportContainer2 = document.createElement('div')
  681. // teleportContainer2.id = 'target2'
  682. // document.body.appendChild(teleportContainer1)
  683. // document.body.appendChild(teleportContainer2)
  684. // // server render
  685. // container.innerHTML = await renderToString(h(App))
  686. // expect(container.innerHTML).toBe(
  687. // '<div><!--teleport start--><!--teleport end--></div>',
  688. // )
  689. // expect(teleportContainer1.innerHTML).toBe('')
  690. // expect(teleportContainer2.innerHTML).toBe('')
  691. // // hydrate
  692. // createSSRApp(App).mount(container)
  693. // expect(container.innerHTML).toBe(
  694. // '<div><!--teleport start--><!--teleport end--></div>',
  695. // )
  696. // expect(teleportContainer1.innerHTML).toBe('<span>Teleported</span>')
  697. // expect(teleportContainer2.innerHTML).toBe('')
  698. // expect(`Hydration children mismatch`).toHaveBeenWarned()
  699. // target.value = '#target2'
  700. // await nextTick()
  701. // expect(teleportContainer1.innerHTML).toBe('')
  702. // expect(teleportContainer2.innerHTML).toBe('<span>Teleported</span>')
  703. // })
  704. // // compile SSR + client render fn from the same template & hydrate
  705. // test('full compiler integration', async () => {
  706. // const mounted: string[] = []
  707. // const log = vi.fn()
  708. // const toggle = ref(true)
  709. // const Child = {
  710. // data() {
  711. // return {
  712. // count: 0,
  713. // text: 'hello',
  714. // style: {
  715. // color: 'red',
  716. // },
  717. // }
  718. // },
  719. // mounted() {
  720. // mounted.push('child')
  721. // },
  722. // template: `
  723. // <div>
  724. // <span class="count" :style="style">{{ count }}</span>
  725. // <button class="inc" @click="count++">inc</button>
  726. // <button class="change" @click="style.color = 'green'" >change color</button>
  727. // <button class="emit" @click="$emit('foo')">emit</button>
  728. // <span class="text">{{ text }}</span>
  729. // <input v-model="text">
  730. // </div>
  731. // `,
  732. // }
  733. // const App = {
  734. // setup() {
  735. // return { toggle }
  736. // },
  737. // mounted() {
  738. // mounted.push('parent')
  739. // },
  740. // template: `
  741. // <div>
  742. // <span>hello</span>
  743. // <template v-if="toggle">
  744. // <Child @foo="log('child')"/>
  745. // <template v-if="true">
  746. // <button class="parent-click" @click="log('click')">click me</button>
  747. // </template>
  748. // </template>
  749. // <span>hello</span>
  750. // </div>`,
  751. // components: {
  752. // Child,
  753. // },
  754. // methods: {
  755. // log,
  756. // },
  757. // }
  758. // const container = document.createElement('div')
  759. // // server render
  760. // container.innerHTML = await renderToString(h(App))
  761. // // hydrate
  762. // createSSRApp(App).mount(container)
  763. // // assert interactions
  764. // // 1. parent button click
  765. // triggerEvent('click', container.querySelector('.parent-click')!)
  766. // expect(log).toHaveBeenCalledWith('click')
  767. // // 2. child inc click + text interpolation
  768. // const count = container.querySelector('.count') as HTMLElement
  769. // expect(count.textContent).toBe(`0`)
  770. // triggerEvent('click', container.querySelector('.inc')!)
  771. // await nextTick()
  772. // expect(count.textContent).toBe(`1`)
  773. // // 3. child color click + style binding
  774. // expect(count.style.color).toBe('red')
  775. // triggerEvent('click', container.querySelector('.change')!)
  776. // await nextTick()
  777. // expect(count.style.color).toBe('green')
  778. // // 4. child event emit
  779. // triggerEvent('click', container.querySelector('.emit')!)
  780. // expect(log).toHaveBeenCalledWith('child')
  781. // // 5. child v-model
  782. // const text = container.querySelector('.text')!
  783. // const input = container.querySelector('input')!
  784. // expect(text.textContent).toBe('hello')
  785. // input.value = 'bye'
  786. // triggerEvent('input', input)
  787. // await nextTick()
  788. // expect(text.textContent).toBe('bye')
  789. // })
  790. // test('handle click error in ssr mode', async () => {
  791. // const App = {
  792. // setup() {
  793. // const throwError = () => {
  794. // throw new Error('Sentry Error')
  795. // }
  796. // return { throwError }
  797. // },
  798. // template: `
  799. // <div>
  800. // <button class="parent-click" @click="throwError">click me</button>
  801. // </div>`,
  802. // }
  803. // const container = document.createElement('div')
  804. // // server render
  805. // container.innerHTML = await renderToString(h(App))
  806. // // hydrate
  807. // const app = createSSRApp(App)
  808. // const handler = (app.config.errorHandler = vi.fn())
  809. // app.mount(container)
  810. // // assert interactions
  811. // // parent button click
  812. // triggerEvent('click', container.querySelector('.parent-click')!)
  813. // expect(handler).toHaveBeenCalled()
  814. // })
  815. // test('handle blur error in ssr mode', async () => {
  816. // const App = {
  817. // setup() {
  818. // const throwError = () => {
  819. // throw new Error('Sentry Error')
  820. // }
  821. // return { throwError }
  822. // },
  823. // template: `
  824. // <div>
  825. // <input class="parent-click" @blur="throwError"/>
  826. // </div>`,
  827. // }
  828. // const container = document.createElement('div')
  829. // // server render
  830. // container.innerHTML = await renderToString(h(App))
  831. // // hydrate
  832. // const app = createSSRApp(App)
  833. // const handler = (app.config.errorHandler = vi.fn())
  834. // app.mount(container)
  835. // // assert interactions
  836. // // parent blur event
  837. // triggerEvent('blur', container.querySelector('.parent-click')!)
  838. // expect(handler).toHaveBeenCalled()
  839. // })
  840. // test('Suspense', async () => {
  841. // const AsyncChild = {
  842. // async setup() {
  843. // const count = ref(0)
  844. // return () =>
  845. // h(
  846. // 'span',
  847. // {
  848. // onClick: () => {
  849. // count.value++
  850. // },
  851. // },
  852. // count.value,
  853. // )
  854. // },
  855. // }
  856. // const { vnode, container } = mountWithHydration('<span>0</span>', () =>
  857. // h(Suspense, () => h(AsyncChild)),
  858. // )
  859. // expect(vnode.el).toBe(container.firstChild)
  860. // // wait for hydration to finish
  861. // await new Promise(r => setTimeout(r))
  862. // triggerEvent('click', container.querySelector('span')!)
  863. // await nextTick()
  864. // expect(container.innerHTML).toBe(`<span>1</span>`)
  865. // })
  866. // // #6638
  867. // test('Suspense + async component', async () => {
  868. // let isSuspenseResolved = false
  869. // let isSuspenseResolvedInChild: any
  870. // const AsyncChild = defineAsyncComponent(() =>
  871. // Promise.resolve(
  872. // defineComponent({
  873. // setup() {
  874. // isSuspenseResolvedInChild = isSuspenseResolved
  875. // const count = ref(0)
  876. // return () =>
  877. // h(
  878. // 'span',
  879. // {
  880. // onClick: () => {
  881. // count.value++
  882. // },
  883. // },
  884. // count.value,
  885. // )
  886. // },
  887. // }),
  888. // ),
  889. // )
  890. // const { vnode, container } = mountWithHydration('<span>0</span>', () =>
  891. // h(
  892. // Suspense,
  893. // {
  894. // onResolve() {
  895. // isSuspenseResolved = true
  896. // },
  897. // },
  898. // () => h(AsyncChild),
  899. // ),
  900. // )
  901. // expect(vnode.el).toBe(container.firstChild)
  902. // // wait for hydration to finish
  903. // await new Promise(r => setTimeout(r))
  904. // expect(isSuspenseResolvedInChild).toBe(false)
  905. // expect(isSuspenseResolved).toBe(true)
  906. // // assert interaction
  907. // triggerEvent('click', container.querySelector('span')!)
  908. // await nextTick()
  909. // expect(container.innerHTML).toBe(`<span>1</span>`)
  910. // })
  911. // test('Suspense (full integration)', async () => {
  912. // const mountedCalls: number[] = []
  913. // const asyncDeps: Promise<any>[] = []
  914. // const AsyncChild = defineComponent({
  915. // props: ['n'],
  916. // async setup(props) {
  917. // const count = ref(props.n)
  918. // onMounted(() => {
  919. // mountedCalls.push(props.n)
  920. // })
  921. // const p = new Promise(r => setTimeout(r, props.n * 10))
  922. // asyncDeps.push(p)
  923. // await p
  924. // return () =>
  925. // h(
  926. // 'span',
  927. // {
  928. // onClick: () => {
  929. // count.value++
  930. // },
  931. // },
  932. // count.value,
  933. // )
  934. // },
  935. // })
  936. // const done = vi.fn()
  937. // const App = {
  938. // template: `
  939. // <Suspense @resolve="done">
  940. // <div>
  941. // <AsyncChild :n="1" />
  942. // <AsyncChild :n="2" />
  943. // </div>
  944. // </Suspense>`,
  945. // components: {
  946. // AsyncChild,
  947. // },
  948. // methods: {
  949. // done,
  950. // },
  951. // }
  952. // const container = document.createElement('div')
  953. // // server render
  954. // container.innerHTML = await renderToString(h(App))
  955. // expect(container.innerHTML).toMatchInlineSnapshot(
  956. // `"<div><span>1</span><span>2</span></div>"`,
  957. // )
  958. // // reset asyncDeps from ssr
  959. // asyncDeps.length = 0
  960. // // hydrate
  961. // createSSRApp(App).mount(container)
  962. // expect(mountedCalls.length).toBe(0)
  963. // expect(asyncDeps.length).toBe(2)
  964. // // wait for hydration to complete
  965. // await Promise.all(asyncDeps)
  966. // await new Promise(r => setTimeout(r))
  967. // // should flush buffered effects
  968. // expect(mountedCalls).toMatchObject([1, 2])
  969. // expect(container.innerHTML).toMatch(
  970. // `<div><span>1</span><span>2</span></div>`,
  971. // )
  972. // const span1 = container.querySelector('span')!
  973. // triggerEvent('click', span1)
  974. // await nextTick()
  975. // expect(container.innerHTML).toMatch(
  976. // `<div><span>2</span><span>2</span></div>`,
  977. // )
  978. // const span2 = span1.nextSibling as Element
  979. // triggerEvent('click', span2)
  980. // await nextTick()
  981. // expect(container.innerHTML).toMatch(
  982. // `<div><span>2</span><span>3</span></div>`,
  983. // )
  984. // })
  985. // test('async component', async () => {
  986. // const spy = vi.fn()
  987. // const Comp = () =>
  988. // h(
  989. // 'button',
  990. // {
  991. // onClick: spy,
  992. // },
  993. // 'hello!',
  994. // )
  995. // let serverResolve: any
  996. // let AsyncComp = defineAsyncComponent(
  997. // () =>
  998. // new Promise(r => {
  999. // serverResolve = r
  1000. // }),
  1001. // )
  1002. // const App = {
  1003. // render() {
  1004. // return ['hello', h(AsyncComp), 'world']
  1005. // },
  1006. // }
  1007. // // server render
  1008. // const htmlPromise = renderToString(h(App))
  1009. // serverResolve(Comp)
  1010. // const html = await htmlPromise
  1011. // expect(html).toMatchInlineSnapshot(
  1012. // `"<!--[-->hello<button>hello!</button>world<!--]-->"`,
  1013. // )
  1014. // // hydration
  1015. // let clientResolve: any
  1016. // AsyncComp = defineAsyncComponent(
  1017. // () =>
  1018. // new Promise(r => {
  1019. // clientResolve = r
  1020. // }),
  1021. // )
  1022. // const container = document.createElement('div')
  1023. // container.innerHTML = html
  1024. // createSSRApp(App).mount(container)
  1025. // // hydration not complete yet
  1026. // triggerEvent('click', container.querySelector('button')!)
  1027. // expect(spy).not.toHaveBeenCalled()
  1028. // // resolve
  1029. // clientResolve(Comp)
  1030. // await new Promise(r => setTimeout(r))
  1031. // // should be hydrated now
  1032. // triggerEvent('click', container.querySelector('button')!)
  1033. // expect(spy).toHaveBeenCalled()
  1034. // })
  1035. // test('update async wrapper before resolve', async () => {
  1036. // const Comp = {
  1037. // render() {
  1038. // return h('h1', 'Async component')
  1039. // },
  1040. // }
  1041. // let serverResolve: any
  1042. // let AsyncComp = defineAsyncComponent(
  1043. // () =>
  1044. // new Promise(r => {
  1045. // serverResolve = r
  1046. // }),
  1047. // )
  1048. // const toggle = ref(true)
  1049. // const App = {
  1050. // setup() {
  1051. // onMounted(() => {
  1052. // // change state, this makes updateComponent(AsyncComp) execute before
  1053. // // the async component is resolved
  1054. // toggle.value = false
  1055. // })
  1056. // return () => {
  1057. // return [toggle.value ? 'hello' : 'world', h(AsyncComp)]
  1058. // }
  1059. // },
  1060. // }
  1061. // // server render
  1062. // const htmlPromise = renderToString(h(App))
  1063. // serverResolve(Comp)
  1064. // const html = await htmlPromise
  1065. // expect(html).toMatchInlineSnapshot(
  1066. // `"<!--[-->hello<h1>Async component</h1><!--]-->"`,
  1067. // )
  1068. // // hydration
  1069. // let clientResolve: any
  1070. // AsyncComp = defineAsyncComponent(
  1071. // () =>
  1072. // new Promise(r => {
  1073. // clientResolve = r
  1074. // }),
  1075. // )
  1076. // const container = document.createElement('div')
  1077. // container.innerHTML = html
  1078. // createSSRApp(App).mount(container)
  1079. // // resolve
  1080. // clientResolve(Comp)
  1081. // await new Promise(r => setTimeout(r))
  1082. // // should be hydrated now
  1083. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  1084. // expect(container.innerHTML).toMatchInlineSnapshot(
  1085. // `"<!--[-->world<h1>Async component</h1><!--]-->"`,
  1086. // )
  1087. // })
  1088. // test('hydrate safely when property used by async setup changed before render', async () => {
  1089. // const toggle = ref(true)
  1090. // const AsyncComp = {
  1091. // async setup() {
  1092. // await new Promise<void>(r => setTimeout(r, 10))
  1093. // return () => h('h1', 'Async component')
  1094. // },
  1095. // }
  1096. // const AsyncWrapper = {
  1097. // render() {
  1098. // return h(AsyncComp)
  1099. // },
  1100. // }
  1101. // const SiblingComp = {
  1102. // setup() {
  1103. // toggle.value = false
  1104. // return () => h('span')
  1105. // },
  1106. // }
  1107. // const App = {
  1108. // setup() {
  1109. // return () =>
  1110. // h(
  1111. // Suspense,
  1112. // {},
  1113. // {
  1114. // default: () => [
  1115. // h('main', {}, [
  1116. // h(AsyncWrapper, {
  1117. // prop: toggle.value ? 'hello' : 'world',
  1118. // }),
  1119. // h(SiblingComp),
  1120. // ]),
  1121. // ],
  1122. // },
  1123. // )
  1124. // },
  1125. // }
  1126. // // server render
  1127. // const html = await renderToString(h(App))
  1128. // expect(html).toMatchInlineSnapshot(
  1129. // `"<main><h1 prop="hello">Async component</h1><span></span></main>"`,
  1130. // )
  1131. // expect(toggle.value).toBe(false)
  1132. // // hydration
  1133. // // reset the value
  1134. // toggle.value = true
  1135. // expect(toggle.value).toBe(true)
  1136. // const container = document.createElement('div')
  1137. // container.innerHTML = html
  1138. // createSSRApp(App).mount(container)
  1139. // await new Promise(r => setTimeout(r, 10))
  1140. // expect(toggle.value).toBe(false)
  1141. // // should be hydrated now
  1142. // expect(container.innerHTML).toMatchInlineSnapshot(
  1143. // `"<main><h1 prop="world">Async component</h1><span></span></main>"`,
  1144. // )
  1145. // })
  1146. // test('hydrate safely when property used by deep nested async setup changed before render', async () => {
  1147. // const toggle = ref(true)
  1148. // const AsyncComp = {
  1149. // async setup() {
  1150. // await new Promise<void>(r => setTimeout(r, 10))
  1151. // return () => h('h1', 'Async component')
  1152. // },
  1153. // }
  1154. // const AsyncWrapper = { render: () => h(AsyncComp) }
  1155. // const AsyncWrapperWrapper = { render: () => h(AsyncWrapper) }
  1156. // const SiblingComp = {
  1157. // setup() {
  1158. // toggle.value = false
  1159. // return () => h('span')
  1160. // },
  1161. // }
  1162. // const App = {
  1163. // setup() {
  1164. // return () =>
  1165. // h(
  1166. // Suspense,
  1167. // {},
  1168. // {
  1169. // default: () => [
  1170. // h('main', {}, [
  1171. // h(AsyncWrapperWrapper, {
  1172. // prop: toggle.value ? 'hello' : 'world',
  1173. // }),
  1174. // h(SiblingComp),
  1175. // ]),
  1176. // ],
  1177. // },
  1178. // )
  1179. // },
  1180. // }
  1181. // // server render
  1182. // const html = await renderToString(h(App))
  1183. // expect(html).toMatchInlineSnapshot(
  1184. // `"<main><h1 prop="hello">Async component</h1><span></span></main>"`,
  1185. // )
  1186. // expect(toggle.value).toBe(false)
  1187. // // hydration
  1188. // // reset the value
  1189. // toggle.value = true
  1190. // expect(toggle.value).toBe(true)
  1191. // const container = document.createElement('div')
  1192. // container.innerHTML = html
  1193. // createSSRApp(App).mount(container)
  1194. // await new Promise(r => setTimeout(r, 10))
  1195. // expect(toggle.value).toBe(false)
  1196. // // should be hydrated now
  1197. // expect(container.innerHTML).toMatchInlineSnapshot(
  1198. // `"<main><h1 prop="world">Async component</h1><span></span></main>"`,
  1199. // )
  1200. // })
  1201. // // #3787
  1202. // test('unmount async wrapper before load', async () => {
  1203. // let resolve: any
  1204. // const AsyncComp = defineAsyncComponent(
  1205. // () =>
  1206. // new Promise(r => {
  1207. // resolve = r
  1208. // }),
  1209. // )
  1210. // const show = ref(true)
  1211. // const root = document.createElement('div')
  1212. // root.innerHTML = '<div><div>async</div></div>'
  1213. // createSSRApp({
  1214. // render() {
  1215. // return h('div', [show.value ? h(AsyncComp) : h('div', 'hi')])
  1216. // },
  1217. // }).mount(root)
  1218. // show.value = false
  1219. // await nextTick()
  1220. // expect(root.innerHTML).toBe('<div><div>hi</div></div>')
  1221. // resolve({})
  1222. // })
  1223. // //#12362
  1224. // test('nested async wrapper', async () => {
  1225. // const Toggle = defineAsyncComponent(
  1226. // () =>
  1227. // new Promise(r => {
  1228. // r(
  1229. // defineComponent({
  1230. // setup(_, { slots }) {
  1231. // const show = ref(false)
  1232. // onMounted(() => {
  1233. // nextTick(() => {
  1234. // show.value = true
  1235. // })
  1236. // })
  1237. // return () =>
  1238. // withDirectives(
  1239. // h('div', null, [renderSlot(slots, 'default')]),
  1240. // [[vShow, show.value]],
  1241. // )
  1242. // },
  1243. // }) as any,
  1244. // )
  1245. // }),
  1246. // )
  1247. // const Wrapper = defineAsyncComponent(() => {
  1248. // return new Promise(r => {
  1249. // r(
  1250. // defineComponent({
  1251. // render(this: any) {
  1252. // return renderSlot(this.$slots, 'default')
  1253. // },
  1254. // }) as any,
  1255. // )
  1256. // })
  1257. // })
  1258. // const count = ref(0)
  1259. // const fn = vi.fn()
  1260. // const Child = {
  1261. // setup() {
  1262. // onMounted(() => {
  1263. // fn()
  1264. // count.value++
  1265. // })
  1266. // return () => h('div', count.value)
  1267. // },
  1268. // }
  1269. // const App = {
  1270. // render() {
  1271. // return h(Toggle, null, {
  1272. // default: () =>
  1273. // h(Wrapper, null, {
  1274. // default: () =>
  1275. // h(Wrapper, null, {
  1276. // default: () => h(Child),
  1277. // }),
  1278. // }),
  1279. // })
  1280. // },
  1281. // }
  1282. // const root = document.createElement('div')
  1283. // root.innerHTML = await renderToString(h(App))
  1284. // expect(root.innerHTML).toMatchInlineSnapshot(
  1285. // `"<div style="display:none;"><!--[--><!--[--><!--[--><div>0</div><!--]--><!--]--><!--]--></div>"`,
  1286. // )
  1287. // createSSRApp(App).mount(root)
  1288. // await nextTick()
  1289. // await nextTick()
  1290. // expect(root.innerHTML).toMatchInlineSnapshot(
  1291. // `"<div style=""><!--[--><!--[--><!--[--><div>1</div><!--]--><!--]--><!--]--></div>"`,
  1292. // )
  1293. // expect(fn).toBeCalledTimes(1)
  1294. // })
  1295. // test('unmount async wrapper before load (fragment)', async () => {
  1296. // let resolve: any
  1297. // const AsyncComp = defineAsyncComponent(
  1298. // () =>
  1299. // new Promise(r => {
  1300. // resolve = r
  1301. // }),
  1302. // )
  1303. // const show = ref(true)
  1304. // const root = document.createElement('div')
  1305. // root.innerHTML = '<div><!--[-->async<!--]--></div>'
  1306. // createSSRApp({
  1307. // render() {
  1308. // return h('div', [show.value ? h(AsyncComp) : h('div', 'hi')])
  1309. // },
  1310. // }).mount(root)
  1311. // show.value = false
  1312. // await nextTick()
  1313. // expect(root.innerHTML).toBe('<div><div>hi</div></div>')
  1314. // resolve({})
  1315. // })
  1316. // test('elements with camel-case in svg ', () => {
  1317. // const { vnode, container } = mountWithHydration(
  1318. // '<animateTransform></animateTransform>',
  1319. // () => h('animateTransform'),
  1320. // )
  1321. // expect(vnode.el).toBe(container.firstChild)
  1322. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  1323. // })
  1324. // test('SVG as a mount container', () => {
  1325. // const svgContainer = document.createElement('svg')
  1326. // svgContainer.innerHTML = '<g></g>'
  1327. // const app = createSSRApp({
  1328. // render: () => h('g'),
  1329. // })
  1330. // expect(
  1331. // (
  1332. // app.mount(svgContainer).$.subTree as VNode<Node, Element> & {
  1333. // el: Element
  1334. // }
  1335. // ).el instanceof SVGElement,
  1336. // )
  1337. // })
  1338. // test('force hydrate prop with `.prop` modifier', () => {
  1339. // const { container } = mountWithHydration('<input type="checkbox">', () =>
  1340. // h('input', {
  1341. // type: 'checkbox',
  1342. // '.indeterminate': true,
  1343. // }),
  1344. // )
  1345. // expect((container.firstChild! as any).indeterminate).toBe(true)
  1346. // })
  1347. // test('force hydrate input v-model with non-string value bindings', () => {
  1348. // const { container } = mountWithHydration(
  1349. // '<input type="checkbox" value="true">',
  1350. // () =>
  1351. // withDirectives(
  1352. // createVNode(
  1353. // 'input',
  1354. // { type: 'checkbox', 'true-value': true },
  1355. // null,
  1356. // PatchFlags.PROPS,
  1357. // ['true-value'],
  1358. // ),
  1359. // [[vModelCheckbox, true]],
  1360. // ),
  1361. // )
  1362. // expect((container.firstChild as any)._trueValue).toBe(true)
  1363. // })
  1364. // test('force hydrate checkbox with indeterminate', () => {
  1365. // const { container } = mountWithHydration(
  1366. // '<input type="checkbox" indeterminate>',
  1367. // () =>
  1368. // createVNode(
  1369. // 'input',
  1370. // { type: 'checkbox', indeterminate: '' },
  1371. // null,
  1372. // PatchFlags.CACHED,
  1373. // ),
  1374. // )
  1375. // expect((container.firstChild as any).indeterminate).toBe(true)
  1376. // })
  1377. // test('force hydrate select option with non-string value bindings', () => {
  1378. // const { container } = mountWithHydration(
  1379. // '<select><option value="true">ok</option></select>',
  1380. // () =>
  1381. // h('select', [
  1382. // // hoisted because bound value is a constant...
  1383. // createVNode('option', { value: true }, null, -1 /* HOISTED */),
  1384. // ]),
  1385. // )
  1386. // expect((container.firstChild!.firstChild as any)._value).toBe(true)
  1387. // })
  1388. // // #7203
  1389. // test('force hydrate custom element with dynamic props', () => {
  1390. // class MyElement extends HTMLElement {
  1391. // foo = ''
  1392. // constructor() {
  1393. // super()
  1394. // }
  1395. // }
  1396. // customElements.define('my-element-7203', MyElement)
  1397. // const msg = ref('bar')
  1398. // const container = document.createElement('div')
  1399. // container.innerHTML = '<my-element-7203></my-element-7203>'
  1400. // const app = createSSRApp({
  1401. // render: () => h('my-element-7203', { foo: msg.value }),
  1402. // })
  1403. // app.mount(container)
  1404. // expect((container.firstChild as any).foo).toBe(msg.value)
  1405. // })
  1406. // // #5728
  1407. // test('empty text node in slot', () => {
  1408. // const Comp = {
  1409. // render(this: any) {
  1410. // return renderSlot(this.$slots, 'default', {}, () => [
  1411. // createTextVNode(''),
  1412. // ])
  1413. // },
  1414. // }
  1415. // const { container, vnode } = mountWithHydration('<!--[--><!--]-->', () =>
  1416. // h(Comp),
  1417. // )
  1418. // expect(container.childNodes.length).toBe(3)
  1419. // const text = container.childNodes[1]
  1420. // expect(text.nodeType).toBe(3)
  1421. // expect(vnode.el).toBe(container.childNodes[0])
  1422. // // component => slot fragment => text node
  1423. // expect((vnode as any).component?.subTree.children[0].el).toBe(text)
  1424. // })
  1425. // // #7215
  1426. // test('empty text node', () => {
  1427. // const Comp = {
  1428. // render(this: any) {
  1429. // return h('p', [''])
  1430. // },
  1431. // }
  1432. // const { container } = mountWithHydration('<p></p>', () => h(Comp))
  1433. // expect(container.childNodes.length).toBe(1)
  1434. // const p = container.childNodes[0]
  1435. // expect(p.childNodes.length).toBe(1)
  1436. // const text = p.childNodes[0]
  1437. // expect(text.nodeType).toBe(3)
  1438. // })
  1439. // // #11372
  1440. // test('object style value tracking in prod', async () => {
  1441. // __DEV__ = false
  1442. // try {
  1443. // const style = reactive({ color: 'red' })
  1444. // const Comp = {
  1445. // render(this: any) {
  1446. // return (
  1447. // openBlock(),
  1448. // createElementBlock(
  1449. // 'div',
  1450. // {
  1451. // style: normalizeStyle(style),
  1452. // },
  1453. // null,
  1454. // 4 /* STYLE */,
  1455. // )
  1456. // )
  1457. // },
  1458. // }
  1459. // const { container } = mountWithHydration(
  1460. // `<div style="color: red;"></div>`,
  1461. // () => h(Comp),
  1462. // )
  1463. // style.color = 'green'
  1464. // await nextTick()
  1465. // expect(container.innerHTML).toBe(`<div style="color: green;"></div>`)
  1466. // } finally {
  1467. // __DEV__ = true
  1468. // }
  1469. // })
  1470. // test('app.unmount()', async () => {
  1471. // const container = document.createElement('DIV')
  1472. // container.innerHTML = '<button></button>'
  1473. // const App = defineComponent({
  1474. // setup(_, { expose }) {
  1475. // const count = ref(0)
  1476. // expose({ count })
  1477. // return () =>
  1478. // h('button', {
  1479. // onClick: () => count.value++,
  1480. // })
  1481. // },
  1482. // })
  1483. // const app = createSSRApp(App)
  1484. // const vm = app.mount(container)
  1485. // await nextTick()
  1486. // expect((container as any)._vnode).toBeDefined()
  1487. // // @ts-expect-error - expose()'d properties are not available on vm type
  1488. // expect(vm.count).toBe(0)
  1489. // app.unmount()
  1490. // expect((container as any)._vnode).toBe(null)
  1491. // })
  1492. // // #6637
  1493. // test('stringified root fragment', () => {
  1494. // mountWithHydration(`<!--[--><div></div><!--]-->`, () =>
  1495. // createStaticVNode(`<div></div>`, 1),
  1496. // )
  1497. // expect(`mismatch`).not.toHaveBeenWarned()
  1498. // })
  1499. // test('transition appear', () => {
  1500. // const { vnode, container } = mountWithHydration(
  1501. // `<template><div>foo</div></template>`,
  1502. // () =>
  1503. // h(
  1504. // Transition,
  1505. // { appear: true },
  1506. // {
  1507. // default: () => h('div', 'foo'),
  1508. // },
  1509. // ),
  1510. // )
  1511. // expect(container.firstChild).toMatchInlineSnapshot(`
  1512. // <div
  1513. // class="v-enter-from v-enter-active"
  1514. // >
  1515. // foo
  1516. // </div>
  1517. // `)
  1518. // expect(vnode.el).toBe(container.firstChild)
  1519. // expect(`mismatch`).not.toHaveBeenWarned()
  1520. // })
  1521. // test('transition appear with v-if', () => {
  1522. // const show = false
  1523. // const { vnode, container } = mountWithHydration(
  1524. // `<template><!----></template>`,
  1525. // () =>
  1526. // h(
  1527. // Transition,
  1528. // { appear: true },
  1529. // {
  1530. // default: () => (show ? h('div', 'foo') : createCommentVNode('')),
  1531. // },
  1532. // ),
  1533. // )
  1534. // expect(container.firstChild).toMatchInlineSnapshot('<!---->')
  1535. // expect(vnode.el).toBe(container.firstChild)
  1536. // expect(`mismatch`).not.toHaveBeenWarned()
  1537. // })
  1538. // test('transition appear with v-show', () => {
  1539. // const show = false
  1540. // const { vnode, container } = mountWithHydration(
  1541. // `<template><div style="display: none;">foo</div></template>`,
  1542. // () =>
  1543. // h(
  1544. // Transition,
  1545. // { appear: true },
  1546. // {
  1547. // default: () =>
  1548. // withDirectives(createVNode('div', null, 'foo'), [[vShow, show]]),
  1549. // },
  1550. // ),
  1551. // )
  1552. // expect(container.firstChild).toMatchInlineSnapshot(`
  1553. // <div
  1554. // class="v-enter-from v-enter-active"
  1555. // style="display: none;"
  1556. // >
  1557. // foo
  1558. // </div>
  1559. // `)
  1560. // expect((container.firstChild as any)[vShowOriginalDisplay]).toBe('')
  1561. // expect(vnode.el).toBe(container.firstChild)
  1562. // expect(`mismatch`).not.toHaveBeenWarned()
  1563. // })
  1564. // test('transition appear w/ event listener', async () => {
  1565. // const container = document.createElement('div')
  1566. // container.innerHTML = `<template><button>0</button></template>`
  1567. // createSSRApp({
  1568. // data() {
  1569. // return {
  1570. // count: 0,
  1571. // }
  1572. // },
  1573. // template: `
  1574. // <Transition appear>
  1575. // <button @click="count++">{{count}}</button>
  1576. // </Transition>
  1577. // `,
  1578. // }).mount(container)
  1579. // expect(container.firstChild).toMatchInlineSnapshot(`
  1580. // <button
  1581. // class="v-enter-from v-enter-active"
  1582. // >
  1583. // 0
  1584. // </button>
  1585. // `)
  1586. // triggerEvent('click', container.querySelector('button')!)
  1587. // await nextTick()
  1588. // expect(container.firstChild).toMatchInlineSnapshot(`
  1589. // <button
  1590. // class="v-enter-from v-enter-active"
  1591. // >
  1592. // 1
  1593. // </button>
  1594. // `)
  1595. // })
  1596. // test('Suspense + transition appear', async () => {
  1597. // const { vnode, container } = mountWithHydration(
  1598. // `<template><div>foo</div></template>`,
  1599. // () =>
  1600. // h(Suspense, {}, () =>
  1601. // h(
  1602. // Transition,
  1603. // { appear: true },
  1604. // {
  1605. // default: () => h('div', 'foo'),
  1606. // },
  1607. // ),
  1608. // ),
  1609. // )
  1610. // expect(vnode.el).toBe(container.firstChild)
  1611. // // wait for hydration to finish
  1612. // await new Promise(r => setTimeout(r))
  1613. // expect(container.firstChild).toMatchInlineSnapshot(`
  1614. // <div
  1615. // class="v-enter-from v-enter-active"
  1616. // >
  1617. // foo
  1618. // </div>
  1619. // `)
  1620. // await nextTick()
  1621. // expect(vnode.el).toBe(container.firstChild)
  1622. // })
  1623. // // #10607
  1624. // test('update component stable slot (prod + optimized mode)', async () => {
  1625. // __DEV__ = false
  1626. // try {
  1627. // const container = document.createElement('div')
  1628. // container.innerHTML = `<template><div show="false"><!--[--><div><div><!----></div></div><div>0</div><!--]--></div></template>`
  1629. // const Comp = {
  1630. // render(this: any) {
  1631. // return (
  1632. // openBlock(),
  1633. // createElementBlock('div', null, [
  1634. // renderSlot(this.$slots, 'default'),
  1635. // ])
  1636. // )
  1637. // },
  1638. // }
  1639. // const show = ref(false)
  1640. // const clicked = ref(false)
  1641. // const Wrapper = {
  1642. // setup() {
  1643. // const items = ref<number[]>([])
  1644. // onMounted(() => {
  1645. // items.value = [1]
  1646. // })
  1647. // return () => {
  1648. // return (
  1649. // openBlock(),
  1650. // createBlock(Comp, null, {
  1651. // default: withCtx(() => [
  1652. // createElementVNode('div', null, [
  1653. // createElementVNode('div', null, [
  1654. // clicked.value
  1655. // ? (openBlock(),
  1656. // createElementBlock('div', { key: 0 }, 'foo'))
  1657. // : createCommentVNode('v-if', true),
  1658. // ]),
  1659. // ]),
  1660. // createElementVNode(
  1661. // 'div',
  1662. // null,
  1663. // items.value.length,
  1664. // 1 /* TEXT */,
  1665. // ),
  1666. // ]),
  1667. // _: 1 /* STABLE */,
  1668. // })
  1669. // )
  1670. // }
  1671. // },
  1672. // }
  1673. // createSSRApp({
  1674. // components: { Wrapper },
  1675. // data() {
  1676. // return { show }
  1677. // },
  1678. // template: `<Wrapper :show="show"/>`,
  1679. // }).mount(container)
  1680. // await nextTick()
  1681. // expect(container.innerHTML).toBe(
  1682. // `<div show="false"><!--[--><div><div><!----></div></div><div>1</div><!--]--></div>`,
  1683. // )
  1684. // show.value = true
  1685. // await nextTick()
  1686. // expect(async () => {
  1687. // clicked.value = true
  1688. // await nextTick()
  1689. // }).not.toThrow("Cannot read properties of null (reading 'insertBefore')")
  1690. // await nextTick()
  1691. // expect(container.innerHTML).toBe(
  1692. // `<div show="true"><!--[--><div><div><div>foo</div></div></div><div>1</div><!--]--></div>`,
  1693. // )
  1694. // } catch (e) {
  1695. // throw e
  1696. // } finally {
  1697. // __DEV__ = true
  1698. // }
  1699. // })
  1700. // describe('mismatch handling', () => {
  1701. // test('text node', () => {
  1702. // const { container } = mountWithHydration(`foo`, () => 'bar')
  1703. // expect(container.textContent).toBe('bar')
  1704. // expect(`Hydration text mismatch`).toHaveBeenWarned()
  1705. // })
  1706. // test('element text content', () => {
  1707. // const { container } = mountWithHydration(`<div>foo</div>`, () =>
  1708. // h('div', 'bar'),
  1709. // )
  1710. // expect(container.innerHTML).toBe('<div>bar</div>')
  1711. // expect(`Hydration text content mismatch`).toHaveBeenWarned()
  1712. // })
  1713. // test('not enough children', () => {
  1714. // const { container } = mountWithHydration(`<div></div>`, () =>
  1715. // h('div', [h('span', 'foo'), h('span', 'bar')]),
  1716. // )
  1717. // expect(container.innerHTML).toBe(
  1718. // '<div><span>foo</span><span>bar</span></div>',
  1719. // )
  1720. // expect(`Hydration children mismatch`).toHaveBeenWarned()
  1721. // })
  1722. // test('too many children', () => {
  1723. // const { container } = mountWithHydration(
  1724. // `<div><span>foo</span><span>bar</span></div>`,
  1725. // () => h('div', [h('span', 'foo')]),
  1726. // )
  1727. // expect(container.innerHTML).toBe('<div><span>foo</span></div>')
  1728. // expect(`Hydration children mismatch`).toHaveBeenWarned()
  1729. // })
  1730. // test('complete mismatch', () => {
  1731. // const { container } = mountWithHydration(
  1732. // `<div><span>foo</span><span>bar</span></div>`,
  1733. // () => h('div', [h('div', 'foo'), h('p', 'bar')]),
  1734. // )
  1735. // expect(container.innerHTML).toBe('<div><div>foo</div><p>bar</p></div>')
  1736. // expect(`Hydration node mismatch`).toHaveBeenWarnedTimes(2)
  1737. // })
  1738. // test('fragment mismatch removal', () => {
  1739. // const { container } = mountWithHydration(
  1740. // `<div><!--[--><div>foo</div><div>bar</div><!--]--></div>`,
  1741. // () => h('div', [h('span', 'replaced')]),
  1742. // )
  1743. // expect(container.innerHTML).toBe('<div><span>replaced</span></div>')
  1744. // expect(`Hydration node mismatch`).toHaveBeenWarned()
  1745. // })
  1746. // test('fragment not enough children', () => {
  1747. // const { container } = mountWithHydration(
  1748. // `<div><!--[--><div>foo</div><!--]--><div>baz</div></div>`,
  1749. // () => h('div', [[h('div', 'foo'), h('div', 'bar')], h('div', 'baz')]),
  1750. // )
  1751. // expect(container.innerHTML).toBe(
  1752. // '<div><!--[--><div>foo</div><div>bar</div><!--]--><div>baz</div></div>',
  1753. // )
  1754. // expect(`Hydration node mismatch`).toHaveBeenWarned()
  1755. // })
  1756. // test('fragment too many children', () => {
  1757. // const { container } = mountWithHydration(
  1758. // `<div><!--[--><div>foo</div><div>bar</div><!--]--><div>baz</div></div>`,
  1759. // () => h('div', [[h('div', 'foo')], h('div', 'baz')]),
  1760. // )
  1761. // expect(container.innerHTML).toBe(
  1762. // '<div><!--[--><div>foo</div><!--]--><div>baz</div></div>',
  1763. // )
  1764. // // fragment ends early and attempts to hydrate the extra <div>bar</div>
  1765. // // as 2nd fragment child.
  1766. // expect(`Hydration text content mismatch`).toHaveBeenWarned()
  1767. // // excessive children removal
  1768. // expect(`Hydration children mismatch`).toHaveBeenWarned()
  1769. // })
  1770. // test('Teleport target has empty children', () => {
  1771. // const teleportContainer = document.createElement('div')
  1772. // teleportContainer.id = 'teleport'
  1773. // document.body.appendChild(teleportContainer)
  1774. // mountWithHydration('<!--teleport start--><!--teleport end-->', () =>
  1775. // h(Teleport, { to: '#teleport' }, [h('span', 'value')]),
  1776. // )
  1777. // expect(teleportContainer.innerHTML).toBe(`<span>value</span>`)
  1778. // expect(`Hydration children mismatch`).toHaveBeenWarned()
  1779. // })
  1780. // test('comment mismatch (element)', () => {
  1781. // const { container } = mountWithHydration(`<div><span></span></div>`, () =>
  1782. // h('div', [createCommentVNode('hi')]),
  1783. // )
  1784. // expect(container.innerHTML).toBe('<div><!--hi--></div>')
  1785. // expect(`Hydration node mismatch`).toHaveBeenWarned()
  1786. // })
  1787. // test('comment mismatch (text)', () => {
  1788. // const { container } = mountWithHydration(`<div>foobar</div>`, () =>
  1789. // h('div', [createCommentVNode('hi')]),
  1790. // )
  1791. // expect(container.innerHTML).toBe('<div><!--hi--></div>')
  1792. // expect(`Hydration node mismatch`).toHaveBeenWarned()
  1793. // })
  1794. // test('class mismatch', () => {
  1795. // mountWithHydration(`<div class="foo bar"></div>`, () =>
  1796. // h('div', { class: ['foo', 'bar'] }),
  1797. // )
  1798. // mountWithHydration(`<div class="foo bar"></div>`, () =>
  1799. // h('div', { class: { foo: true, bar: true } }),
  1800. // )
  1801. // mountWithHydration(`<div class="foo bar"></div>`, () =>
  1802. // h('div', { class: 'foo bar' }),
  1803. // )
  1804. // // SVG classes
  1805. // mountWithHydration(`<svg class="foo bar"></svg>`, () =>
  1806. // h('svg', { class: 'foo bar' }),
  1807. // )
  1808. // // class with different order
  1809. // mountWithHydration(`<div class="foo bar"></div>`, () =>
  1810. // h('div', { class: 'bar foo' }),
  1811. // )
  1812. // expect(`Hydration class mismatch`).not.toHaveBeenWarned()
  1813. // mountWithHydration(`<div class="foo bar"></div>`, () =>
  1814. // h('div', { class: 'foo' }),
  1815. // )
  1816. // expect(`Hydration class mismatch`).toHaveBeenWarned()
  1817. // })
  1818. // test('style mismatch', () => {
  1819. // mountWithHydration(`<div style="color:red;"></div>`, () =>
  1820. // h('div', { style: { color: 'red' } }),
  1821. // )
  1822. // mountWithHydration(`<div style="color:red;"></div>`, () =>
  1823. // h('div', { style: `color:red;` }),
  1824. // )
  1825. // mountWithHydration(
  1826. // `<div style="color:red; font-size: 12px;"></div>`,
  1827. // () => h('div', { style: `font-size: 12px; color:red;` }),
  1828. // )
  1829. // mountWithHydration(`<div style="color:red;display:none;"></div>`, () =>
  1830. // withDirectives(createVNode('div', { style: 'color: red' }, ''), [
  1831. // [vShow, false],
  1832. // ]),
  1833. // )
  1834. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  1835. // mountWithHydration(`<div style="color:red;"></div>`, () =>
  1836. // h('div', { style: { color: 'green' } }),
  1837. // )
  1838. // expect(`Hydration style mismatch`).toHaveBeenWarnedTimes(1)
  1839. // })
  1840. // test('style mismatch when no style attribute is present', () => {
  1841. // mountWithHydration(`<div></div>`, () =>
  1842. // h('div', { style: { color: 'red' } }),
  1843. // )
  1844. // expect(`Hydration style mismatch`).toHaveBeenWarnedTimes(1)
  1845. // })
  1846. // test('style mismatch w/ v-show', () => {
  1847. // mountWithHydration(`<div style="color:red;display:none"></div>`, () =>
  1848. // withDirectives(createVNode('div', { style: 'color: red' }, ''), [
  1849. // [vShow, false],
  1850. // ]),
  1851. // )
  1852. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  1853. // mountWithHydration(`<div style="color:red;"></div>`, () =>
  1854. // withDirectives(createVNode('div', { style: 'color: red' }, ''), [
  1855. // [vShow, false],
  1856. // ]),
  1857. // )
  1858. // expect(`Hydration style mismatch`).toHaveBeenWarnedTimes(1)
  1859. // })
  1860. // test('attr mismatch', () => {
  1861. // mountWithHydration(`<div id="foo"></div>`, () => h('div', { id: 'foo' }))
  1862. // mountWithHydration(`<div spellcheck></div>`, () =>
  1863. // h('div', { spellcheck: '' }),
  1864. // )
  1865. // mountWithHydration(`<div></div>`, () => h('div', { id: undefined }))
  1866. // // boolean
  1867. // mountWithHydration(`<select multiple></div>`, () =>
  1868. // h('select', { multiple: true }),
  1869. // )
  1870. // mountWithHydration(`<select multiple></div>`, () =>
  1871. // h('select', { multiple: 'multiple' }),
  1872. // )
  1873. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1874. // mountWithHydration(`<div></div>`, () => h('div', { id: 'foo' }))
  1875. // expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(1)
  1876. // mountWithHydration(`<div id="bar"></div>`, () => h('div', { id: 'foo' }))
  1877. // expect(`Hydration attribute mismatch`).toHaveBeenWarnedTimes(2)
  1878. // })
  1879. // test('attr special case: textarea value', () => {
  1880. // mountWithHydration(`<textarea>foo</textarea>`, () =>
  1881. // h('textarea', { value: 'foo' }),
  1882. // )
  1883. // mountWithHydration(`<textarea></textarea>`, () =>
  1884. // h('textarea', { value: '' }),
  1885. // )
  1886. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1887. // mountWithHydration(`<textarea>foo</textarea>`, () =>
  1888. // h('textarea', { value: 'bar' }),
  1889. // )
  1890. // expect(`Hydration attribute mismatch`).toHaveBeenWarned()
  1891. // })
  1892. // // #11873
  1893. // test('<textarea> with newlines at the beginning', async () => {
  1894. // const render = () => h('textarea', null, '\nhello')
  1895. // const html = await renderToString(createSSRApp({ render }))
  1896. // mountWithHydration(html, render)
  1897. // expect(`Hydration text content mismatch`).not.toHaveBeenWarned()
  1898. // })
  1899. // test('<pre> with newlines at the beginning', async () => {
  1900. // const render = () => h('pre', null, '\n')
  1901. // const html = await renderToString(createSSRApp({ render }))
  1902. // mountWithHydration(html, render)
  1903. // expect(`Hydration text content mismatch`).not.toHaveBeenWarned()
  1904. // })
  1905. // test('boolean attr handling', () => {
  1906. // mountWithHydration(`<input />`, () => h('input', { readonly: false }))
  1907. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1908. // mountWithHydration(`<input readonly />`, () =>
  1909. // h('input', { readonly: true }),
  1910. // )
  1911. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1912. // mountWithHydration(`<input readonly="readonly" />`, () =>
  1913. // h('input', { readonly: true }),
  1914. // )
  1915. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1916. // })
  1917. // test('client value is null or undefined', () => {
  1918. // mountWithHydration(`<div></div>`, () =>
  1919. // h('div', { draggable: undefined }),
  1920. // )
  1921. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1922. // mountWithHydration(`<input />`, () => h('input', { type: null }))
  1923. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1924. // })
  1925. // test('should not warn against object values', () => {
  1926. // mountWithHydration(`<input />`, () => h('input', { from: {} }))
  1927. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1928. // })
  1929. // test('should not warn on falsy bindings of non-property keys', () => {
  1930. // mountWithHydration(`<button />`, () => h('button', { href: undefined }))
  1931. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1932. // })
  1933. // test('should not warn on non-renderable option values', () => {
  1934. // mountWithHydration(`<select><option>hello</option></select>`, () =>
  1935. // h('select', [h('option', { value: ['foo'] }, 'hello')]),
  1936. // )
  1937. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  1938. // })
  1939. // test('should not warn css v-bind', () => {
  1940. // const container = document.createElement('div')
  1941. // container.innerHTML = `<div style="--foo:red;color:var(--foo);" />`
  1942. // const app = createSSRApp({
  1943. // setup() {
  1944. // useCssVars(() => ({
  1945. // foo: 'red',
  1946. // }))
  1947. // return () => h('div', { style: { color: 'var(--foo)' } })
  1948. // },
  1949. // })
  1950. // app.mount(container)
  1951. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  1952. // })
  1953. // // #10317 - test case from #10325
  1954. // test('css vars should only be added to expected on component root dom', () => {
  1955. // const container = document.createElement('div')
  1956. // container.innerHTML = `<div style="--foo:red;"><div style="color:var(--foo);" /></div>`
  1957. // const app = createSSRApp({
  1958. // setup() {
  1959. // useCssVars(() => ({
  1960. // foo: 'red',
  1961. // }))
  1962. // return () =>
  1963. // h('div', null, [h('div', { style: { color: 'var(--foo)' } })])
  1964. // },
  1965. // })
  1966. // app.mount(container)
  1967. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  1968. // })
  1969. // // #11188
  1970. // test('css vars support fallthrough', () => {
  1971. // const container = document.createElement('div')
  1972. // container.innerHTML = `<div style="padding: 4px;--foo:red;"></div>`
  1973. // const app = createSSRApp({
  1974. // setup() {
  1975. // useCssVars(() => ({
  1976. // foo: 'red',
  1977. // }))
  1978. // return () => h(Child)
  1979. // },
  1980. // })
  1981. // const Child = {
  1982. // setup() {
  1983. // return () => h('div', { style: 'padding: 4px' })
  1984. // },
  1985. // }
  1986. // app.mount(container)
  1987. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  1988. // })
  1989. // // #11189
  1990. // test('should not warn for directives that mutate DOM in created', () => {
  1991. // const container = document.createElement('div')
  1992. // container.innerHTML = `<div class="test red"></div>`
  1993. // const vColor: ObjectDirective = {
  1994. // created(el, binding) {
  1995. // el.classList.add(binding.value)
  1996. // },
  1997. // }
  1998. // const app = createSSRApp({
  1999. // setup() {
  2000. // return () =>
  2001. // withDirectives(h('div', { class: 'test' }), [[vColor, 'red']])
  2002. // },
  2003. // })
  2004. // app.mount(container)
  2005. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  2006. // })
  2007. // test('escape css var name', () => {
  2008. // const container = document.createElement('div')
  2009. // container.innerHTML = `<div style="padding: 4px;--foo\\.bar:red;"></div>`
  2010. // const app = createSSRApp({
  2011. // setup() {
  2012. // useCssVars(() => ({
  2013. // 'foo.bar': 'red',
  2014. // }))
  2015. // return () => h(Child)
  2016. // },
  2017. // })
  2018. // const Child = {
  2019. // setup() {
  2020. // return () => h('div', { style: 'padding: 4px' })
  2021. // },
  2022. // }
  2023. // app.mount(container)
  2024. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  2025. // })
  2026. // })
  2027. // describe('data-allow-mismatch', () => {
  2028. // test('element text content', () => {
  2029. // const { container } = mountWithHydration(
  2030. // `<div data-allow-mismatch="text">foo</div>`,
  2031. // () => h('div', 'bar'),
  2032. // )
  2033. // expect(container.innerHTML).toBe(
  2034. // '<div data-allow-mismatch="text">bar</div>',
  2035. // )
  2036. // expect(`Hydration text content mismatch`).not.toHaveBeenWarned()
  2037. // })
  2038. // test('not enough children', () => {
  2039. // const { container } = mountWithHydration(
  2040. // `<div data-allow-mismatch="children"></div>`,
  2041. // () => h('div', [h('span', 'foo'), h('span', 'bar')]),
  2042. // )
  2043. // expect(container.innerHTML).toBe(
  2044. // '<div data-allow-mismatch="children"><span>foo</span><span>bar</span></div>',
  2045. // )
  2046. // expect(`Hydration children mismatch`).not.toHaveBeenWarned()
  2047. // })
  2048. // test('too many children', () => {
  2049. // const { container } = mountWithHydration(
  2050. // `<div data-allow-mismatch="children"><span>foo</span><span>bar</span></div>`,
  2051. // () => h('div', [h('span', 'foo')]),
  2052. // )
  2053. // expect(container.innerHTML).toBe(
  2054. // '<div data-allow-mismatch="children"><span>foo</span></div>',
  2055. // )
  2056. // expect(`Hydration children mismatch`).not.toHaveBeenWarned()
  2057. // })
  2058. // test('complete mismatch', () => {
  2059. // const { container } = mountWithHydration(
  2060. // `<div data-allow-mismatch="children"><span>foo</span><span>bar</span></div>`,
  2061. // () => h('div', [h('div', 'foo'), h('p', 'bar')]),
  2062. // )
  2063. // expect(container.innerHTML).toBe(
  2064. // '<div data-allow-mismatch="children"><div>foo</div><p>bar</p></div>',
  2065. // )
  2066. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  2067. // })
  2068. // test('fragment mismatch removal', () => {
  2069. // const { container } = mountWithHydration(
  2070. // `<div data-allow-mismatch="children"><!--[--><div>foo</div><div>bar</div><!--]--></div>`,
  2071. // () => h('div', [h('span', 'replaced')]),
  2072. // )
  2073. // expect(container.innerHTML).toBe(
  2074. // '<div data-allow-mismatch="children"><span>replaced</span></div>',
  2075. // )
  2076. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  2077. // })
  2078. // test('fragment not enough children', () => {
  2079. // const { container } = mountWithHydration(
  2080. // `<div data-allow-mismatch="children"><!--[--><div>foo</div><!--]--><div>baz</div></div>`,
  2081. // () => h('div', [[h('div', 'foo'), h('div', 'bar')], h('div', 'baz')]),
  2082. // )
  2083. // expect(container.innerHTML).toBe(
  2084. // '<div data-allow-mismatch="children"><!--[--><div>foo</div><div>bar</div><!--]--><div>baz</div></div>',
  2085. // )
  2086. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  2087. // })
  2088. // test('fragment too many children', () => {
  2089. // const { container } = mountWithHydration(
  2090. // `<div data-allow-mismatch="children"><!--[--><div>foo</div><div>bar</div><!--]--><div>baz</div></div>`,
  2091. // () => h('div', [[h('div', 'foo')], h('div', 'baz')]),
  2092. // )
  2093. // expect(container.innerHTML).toBe(
  2094. // '<div data-allow-mismatch="children"><!--[--><div>foo</div><!--]--><div>baz</div></div>',
  2095. // )
  2096. // // fragment ends early and attempts to hydrate the extra <div>bar</div>
  2097. // // as 2nd fragment child.
  2098. // expect(`Hydration text content mismatch`).not.toHaveBeenWarned()
  2099. // // excessive children removal
  2100. // expect(`Hydration children mismatch`).not.toHaveBeenWarned()
  2101. // })
  2102. // test('comment mismatch (element)', () => {
  2103. // const { container } = mountWithHydration(
  2104. // `<div data-allow-mismatch="children"><span></span></div>`,
  2105. // () => h('div', [createCommentVNode('hi')]),
  2106. // )
  2107. // expect(container.innerHTML).toBe(
  2108. // '<div data-allow-mismatch="children"><!--hi--></div>',
  2109. // )
  2110. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  2111. // })
  2112. // test('comment mismatch (text)', () => {
  2113. // const { container } = mountWithHydration(
  2114. // `<div data-allow-mismatch="children">foobar</div>`,
  2115. // () => h('div', [createCommentVNode('hi')]),
  2116. // )
  2117. // expect(container.innerHTML).toBe(
  2118. // '<div data-allow-mismatch="children"><!--hi--></div>',
  2119. // )
  2120. // expect(`Hydration node mismatch`).not.toHaveBeenWarned()
  2121. // })
  2122. // test('class mismatch', () => {
  2123. // mountWithHydration(
  2124. // `<div class="foo bar" data-allow-mismatch="class"></div>`,
  2125. // () => h('div', { class: 'foo' }),
  2126. // )
  2127. // expect(`Hydration class mismatch`).not.toHaveBeenWarned()
  2128. // })
  2129. // test('style mismatch', () => {
  2130. // mountWithHydration(
  2131. // `<div style="color:red;" data-allow-mismatch="style"></div>`,
  2132. // () => h('div', { style: { color: 'green' } }),
  2133. // )
  2134. // expect(`Hydration style mismatch`).not.toHaveBeenWarned()
  2135. // })
  2136. // test('attr mismatch', () => {
  2137. // mountWithHydration(`<div data-allow-mismatch="attribute"></div>`, () =>
  2138. // h('div', { id: 'foo' }),
  2139. // )
  2140. // mountWithHydration(
  2141. // `<div id="bar" data-allow-mismatch="attribute"></div>`,
  2142. // () => h('div', { id: 'foo' }),
  2143. // )
  2144. // expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()
  2145. // })
  2146. // })
  2147. })