mirror of https://github.com/vuejs/core.git
test(custom-element): test custom element hydration w/ declarative shadow dom
This commit is contained in:
parent
4085def1ba
commit
90caac473d
|
@ -69,9 +69,12 @@ const isSVGContainer = (container: Element) =>
|
|||
const isMathMLContainer = (container: Element) =>
|
||||
container.namespaceURI!.includes('MathML')
|
||||
|
||||
const getContainerType = (container: Element): 'svg' | 'mathml' | undefined => {
|
||||
if (isSVGContainer(container)) return 'svg'
|
||||
if (isMathMLContainer(container)) return 'mathml'
|
||||
const getContainerType = (
|
||||
container: Element | ShadowRoot,
|
||||
): 'svg' | 'mathml' | undefined => {
|
||||
if (container.nodeType !== DOMNodeTypes.ELEMENT) return undefined
|
||||
if (isSVGContainer(container as Element)) return 'svg'
|
||||
if (isMathMLContainer(container as Element)) return 'mathml'
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
|
|
@ -250,8 +250,6 @@ export class VueElement
|
|||
super()
|
||||
if (this.shadowRoot && _createApp !== createApp) {
|
||||
this._root = this.shadowRoot
|
||||
// TODO hydration needs to be reworked
|
||||
this._mount(_def)
|
||||
} else {
|
||||
if (__DEV__ && this.shadowRoot) {
|
||||
warn(
|
||||
|
@ -265,10 +263,11 @@ export class VueElement
|
|||
} else {
|
||||
this._root = this
|
||||
}
|
||||
if (!(this._def as ComponentOptions).__asyncLoader) {
|
||||
// for sync component defs we can immediately resolve props
|
||||
this._resolveProps(this._def)
|
||||
}
|
||||
}
|
||||
|
||||
if (!(this._def as ComponentOptions).__asyncLoader) {
|
||||
// for sync component defs we can immediately resolve props
|
||||
this._resolveProps(this._def)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import puppeteer, {
|
|||
type PuppeteerLaunchOptions,
|
||||
} from 'puppeteer'
|
||||
|
||||
export const E2E_TIMEOUT = 30 * 1000
|
||||
export const E2E_TIMEOUT: number = 30 * 1000
|
||||
|
||||
const puppeteerOptions: PuppeteerLaunchOptions = {
|
||||
args: process.env.CI ? ['--no-sandbox', '--disable-setuid-sandbox'] : [],
|
||||
|
@ -13,12 +13,13 @@ const puppeteerOptions: PuppeteerLaunchOptions = {
|
|||
}
|
||||
|
||||
const maxTries = 30
|
||||
export const timeout = (n: number) => new Promise(r => setTimeout(r, n))
|
||||
export const timeout = (n: number): Promise<any> =>
|
||||
new Promise(r => setTimeout(r, n))
|
||||
|
||||
export async function expectByPolling(
|
||||
poll: () => Promise<any>,
|
||||
expected: string,
|
||||
) {
|
||||
): Promise<void> {
|
||||
for (let tries = 0; tries < maxTries; tries++) {
|
||||
const actual = (await poll()) || ''
|
||||
if (actual.indexOf(expected) > -1 || tries === maxTries - 1) {
|
||||
|
@ -55,10 +56,7 @@ export function setupPuppeteer(args?: string[]) {
|
|||
page.on('console', e => {
|
||||
if (e.type() === 'error') {
|
||||
const err = e.args()[0]
|
||||
console.error(
|
||||
`Error from Puppeteer-loaded page:\n`,
|
||||
err.remoteObject().description,
|
||||
)
|
||||
console.error(`Error from Puppeteer-loaded page:\n`, err.remoteObject())
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<script src="../../dist/vue.global.js"></script>
|
||||
|
||||
<my-element
|
||||
><template shadowrootmode="open"><button>1</button></template></my-element
|
||||
>
|
||||
<my-element-async
|
||||
><template shadowrootmode="open"
|
||||
><button>1</button></template
|
||||
></my-element-async
|
||||
>
|
||||
|
||||
<script>
|
||||
const {
|
||||
h,
|
||||
ref,
|
||||
defineSSRCustomElement,
|
||||
defineAsyncComponent,
|
||||
onMounted,
|
||||
useHost,
|
||||
} = Vue
|
||||
|
||||
const def = {
|
||||
setup() {
|
||||
const count = ref(1)
|
||||
const el = useHost()
|
||||
onMounted(() => (el.style.border = '1px solid red'))
|
||||
|
||||
return () => h('button', { onClick: () => count.value++ }, count.value)
|
||||
},
|
||||
}
|
||||
|
||||
customElements.define('my-element', defineSSRCustomElement(def))
|
||||
customElements.define(
|
||||
'my-element-async',
|
||||
defineSSRCustomElement(
|
||||
defineAsyncComponent(
|
||||
() =>
|
||||
new Promise(r => {
|
||||
window.resolve = () => r(def)
|
||||
}),
|
||||
),
|
||||
),
|
||||
)
|
||||
</script>
|
|
@ -0,0 +1,35 @@
|
|||
import path from 'node:path'
|
||||
import { setupPuppeteer } from './e2eUtils'
|
||||
|
||||
const { page, click, text } = setupPuppeteer()
|
||||
|
||||
// this must be tested in actual Chrome because jsdom does not support
|
||||
// declarative shadow DOM
|
||||
test('ssr custom element hydration', async () => {
|
||||
await page().goto(
|
||||
`file://${path.resolve(__dirname, './ssr-custom-element.html')}`,
|
||||
)
|
||||
|
||||
function getColor() {
|
||||
return page().evaluate(() => {
|
||||
return [
|
||||
(document.querySelector('my-element') as any).style.border,
|
||||
(document.querySelector('my-element-async') as any).style.border,
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
expect(await getColor()).toMatchObject(['1px solid red', ''])
|
||||
await page().evaluate(() => (window as any).resolve()) // exposed by test
|
||||
expect(await getColor()).toMatchObject(['1px solid red', '1px solid red'])
|
||||
|
||||
async function assertInteraction(el: string) {
|
||||
const selector = `${el} >>> button`
|
||||
expect(await text(selector)).toBe('1')
|
||||
await click(selector)
|
||||
expect(await text(selector)).toBe('2')
|
||||
}
|
||||
|
||||
await assertInteraction('my-element')
|
||||
await assertInteraction('my-element-async')
|
||||
})
|
Loading…
Reference in New Issue