|
|
@@ -12,6 +12,7 @@ import {
|
|
|
} from '@vue/runtime-dom'
|
|
|
import { renderToString } from '@vue/server-renderer'
|
|
|
import { mockWarn } from '@vue/shared'
|
|
|
+import { SSRContext } from 'packages/server-renderer/src/renderToString'
|
|
|
|
|
|
function mountWithHydration(html: string, render: () => any) {
|
|
|
const container = document.createElement('div')
|
|
|
@@ -157,7 +158,7 @@ describe('SSR hydration', () => {
|
|
|
const fn = jest.fn()
|
|
|
const portalContainer = document.createElement('div')
|
|
|
portalContainer.id = 'portal'
|
|
|
- portalContainer.innerHTML = `<span>foo</span><span class="foo"></span>`
|
|
|
+ portalContainer.innerHTML = `<span>foo</span><span class="foo"></span><!---->`
|
|
|
document.body.appendChild(portalContainer)
|
|
|
|
|
|
const { vnode, container } = mountWithHydration('<!--portal-->', () =>
|
|
|
@@ -182,7 +183,69 @@ describe('SSR hydration', () => {
|
|
|
msg.value = 'bar'
|
|
|
await nextTick()
|
|
|
expect(portalContainer.innerHTML).toBe(
|
|
|
- `<span>bar</span><span class="bar"></span>`
|
|
|
+ `<span>bar</span><span class="bar"></span><!---->`
|
|
|
+ )
|
|
|
+ })
|
|
|
+
|
|
|
+ test('Portal (multiple + integration)', async () => {
|
|
|
+ const msg = ref('foo')
|
|
|
+ const fn1 = jest.fn()
|
|
|
+ const fn2 = jest.fn()
|
|
|
+
|
|
|
+ const Comp = () => [
|
|
|
+ h(Portal, { target: '#portal2' }, [
|
|
|
+ h('span', msg.value),
|
|
|
+ h('span', { class: msg.value, onClick: fn1 })
|
|
|
+ ]),
|
|
|
+ h(Portal, { target: '#portal2' }, [
|
|
|
+ h('span', msg.value + '2'),
|
|
|
+ h('span', { class: msg.value + '2', onClick: fn2 })
|
|
|
+ ])
|
|
|
+ ]
|
|
|
+
|
|
|
+ const portalContainer = document.createElement('div')
|
|
|
+ portalContainer.id = 'portal2'
|
|
|
+ const ctx: SSRContext = {}
|
|
|
+ const mainHtml = await renderToString(h(Comp), ctx)
|
|
|
+ expect(mainHtml).toMatchInlineSnapshot(
|
|
|
+ `"<!--[--><!--portal--><!--portal--><!--]-->"`
|
|
|
+ )
|
|
|
+
|
|
|
+ const portalHtml = ctx.portals!['#portal2']
|
|
|
+ expect(portalHtml).toMatchInlineSnapshot(
|
|
|
+ `"<span>foo</span><span class=\\"foo\\"></span><!----><span>foo2</span><span class=\\"foo2\\"></span><!---->"`
|
|
|
+ )
|
|
|
+
|
|
|
+ portalContainer.innerHTML = portalHtml
|
|
|
+ document.body.appendChild(portalContainer)
|
|
|
+
|
|
|
+ const { vnode, container } = mountWithHydration(mainHtml, Comp)
|
|
|
+ expect(vnode.el).toBe(container.firstChild)
|
|
|
+ const portalVnode1 = (vnode.children as VNode[])[0]
|
|
|
+ const portalVnode2 = (vnode.children as VNode[])[1]
|
|
|
+ expect(portalVnode1.el).toBe(container.childNodes[1])
|
|
|
+ expect(portalVnode2.el).toBe(container.childNodes[2])
|
|
|
+
|
|
|
+ expect((portalVnode1 as any).children[0].el).toBe(
|
|
|
+ portalContainer.childNodes[0]
|
|
|
+ )
|
|
|
+ expect(portalVnode1.anchor).toBe(portalContainer.childNodes[2])
|
|
|
+ expect((portalVnode2 as any).children[0].el).toBe(
|
|
|
+ portalContainer.childNodes[3]
|
|
|
+ )
|
|
|
+ expect(portalVnode2.anchor).toBe(portalContainer.childNodes[5])
|
|
|
+
|
|
|
+ // // event handler
|
|
|
+ triggerEvent('click', portalContainer.querySelector('.foo')!)
|
|
|
+ expect(fn1).toHaveBeenCalled()
|
|
|
+
|
|
|
+ triggerEvent('click', portalContainer.querySelector('.foo2')!)
|
|
|
+ expect(fn2).toHaveBeenCalled()
|
|
|
+
|
|
|
+ msg.value = 'bar'
|
|
|
+ await nextTick()
|
|
|
+ expect(portalContainer.innerHTML).toMatchInlineSnapshot(
|
|
|
+ `"<span>bar</span><span class=\\"bar\\"></span><!----><span>bar2</span><span class=\\"bar2\\"></span><!---->"`
|
|
|
)
|
|
|
})
|
|
|
|