UNPKG

@wdio/browser-runner

Version:
395 lines (394 loc) 11.8 kB
// src/browser/integrations/stencil.ts import { h } from "@stencil/core"; import { $ } from "@wdio/globals"; import { bootstrapLazy, flushAll, insertVdomAnnotations, registerComponents, registerModule, renderVdom, setSupportsShadowDom, startAutoApplyChanges, styles, writeTask } from "@stencil/core/internal/testing/index.js"; process.nextTick = (cb) => setTimeout(cb, 0); window.React = { createElement: h }; function render(opts) { if (!opts) { throw new Error("NewSpecPageOptions required"); } const components = opts.components || []; const stencilStage = document.querySelector("stencil-stage"); if (stencilStage) { stencilStage.remove(); } const container = document.createElement("stencil-stage"); document.body.appendChild(container); if (Array.isArray(components)) { registerComponents(components); } if (opts.hydrateClientSide) { opts.includeAnnotations = true; } if (opts.hydrateServerSide) { opts.includeAnnotations = true; setSupportsShadowDom(false); } else { opts.includeAnnotations = !!opts.includeAnnotations; if (opts.supportsShadowDom === false) { setSupportsShadowDom(false); } else { setSupportsShadowDom(true); } } const cmpTags = /* @__PURE__ */ new Set(); const lazyBundles = components.map((Cstr) => { if (Cstr.COMPILER_META == null) { throw new Error('Invalid component class: Missing static "COMPILER_META" property.'); } cmpTags.add(Cstr.COMPILER_META.tagName); Cstr.isProxied = false; proxyComponentLifeCycles(Cstr); const bundleId = `${Cstr.COMPILER_META.tagName}.${Math.round(Math.random() * 899999) + 1e5}`; const stylesMeta = Cstr.COMPILER_META.styles; if (Array.isArray(stylesMeta)) { if (stylesMeta.length > 1) { const styles2 = {}; stylesMeta.forEach((style) => { styles2[style.modeName] = style.styleStr; }); Cstr.style = styles2; } else if (stylesMeta.length === 1) { Cstr.style = stylesMeta[0].styleStr; } } registerModule(bundleId, Cstr); if (!customElements.get(Cstr.COMPILER_META.tagName)) { customElements.define(Cstr.COMPILER_META.tagName, Cstr); } const lazyBundleRuntimeMeta = formatLazyBundleRuntimeMeta(bundleId, [Cstr.COMPILER_META]); return lazyBundleRuntimeMeta; }); const page = { container, styles, flushAll, unmount: () => container.remove() }; if (typeof opts.direction === "string") { document.documentElement.setAttribute("dir", opts.direction); } if (typeof opts.language === "string") { document.documentElement.setAttribute("lang", opts.language); } bootstrapLazy(lazyBundles); if (typeof opts.template === "function") { const cmpMeta = { $flags$: 0, $tagName$: "body" }; const ref = { $ancestorComponent$: void 0, $flags$: 0, $modeName$: void 0, $cmpMeta$: cmpMeta, $hostElement$: container }; renderVdom(ref, opts.template()); } else if (typeof opts.html === "string") { container.innerHTML = opts.html; } let rootComponent = null; Object.defineProperty(page, "root", { get() { if (!rootComponent) { rootComponent = findRootComponent(cmpTags, container); } if (rootComponent) { return rootComponent; } return container.firstElementChild; } }); Object.defineProperty(page, "$root", { get() { return $(page.root); } }); Object.defineProperty(page, "$container", { get() { return $(container); } }); if (opts.hydrateServerSide) { insertVdomAnnotations(document, []); } if (opts.autoApplyChanges) { startAutoApplyChanges(); } return page; } function proxyComponentLifeCycles(Cstr) { if (typeof Cstr.prototype?.__componentWillLoad === "function") { Cstr.prototype.componentWillLoad = Cstr.prototype.__componentWillLoad; Cstr.prototype.__componentWillLoad = null; } if (typeof Cstr.prototype?.__componentWillUpdate === "function") { Cstr.prototype.componentWillUpdate = Cstr.prototype.__componentWillUpdate; Cstr.prototype.__componentWillUpdate = null; } if (typeof Cstr.prototype?.__componentWillRender === "function") { Cstr.prototype.componentWillRender = Cstr.prototype.__componentWillRender; Cstr.prototype.__componentWillRender = null; } if (typeof Cstr.prototype?.componentWillLoad === "function") { Cstr.prototype.__componentWillLoad = Cstr.prototype.componentWillLoad; Cstr.prototype.componentWillLoad = function() { const result = this.__componentWillLoad(); if (result && typeof result.then === "function") { writeTask(() => result); } else { writeTask(() => Promise.resolve()); } return result; }; } if (typeof Cstr.prototype?.componentWillUpdate === "function") { Cstr.prototype.__componentWillUpdate = Cstr.prototype.componentWillUpdate; Cstr.prototype.componentWillUpdate = function() { const result = this.__componentWillUpdate(); if (result && typeof result.then === "function") { writeTask(() => result); } else { writeTask(() => Promise.resolve()); } return result; }; } if (typeof Cstr.prototype?.componentWillRender === "function") { Cstr.prototype.__componentWillRender = Cstr.prototype.componentWillRender; Cstr.prototype.componentWillRender = function() { const result = this.__componentWillRender(); if (result && typeof result.then === "function") { writeTask(() => result); } else { writeTask(() => Promise.resolve()); } return result; }; } } function findRootComponent(cmpTags, node) { if (node) { const children = node.children; const childrenLength = children.length; for (let i = 0; i < childrenLength; i++) { const elm = children[i]; if (cmpTags.has(elm.nodeName.toLowerCase())) { return elm; } } for (let i = 0; i < childrenLength; i++) { const r = findRootComponent(cmpTags, children[i]); if (r) { return r; } } } return null; } function waitForChanges(documentElement = document.documentElement) { return new Promise((resolve) => { requestAnimationFrame(() => { const promiseChain = []; const waitComponentOnReady = (elm, promises) => { if ("shadowRoot" in elm && elm.shadowRoot instanceof ShadowRoot) { waitComponentOnReady(elm.shadowRoot, promises); } const children = elm.children; const len = children.length; for (let i = 0; i < len; i++) { const childElm = children[i]; const childStencilElm = childElm; if (childElm.tagName.includes("-") && typeof childStencilElm.componentOnReady === "function") { promises.push(childStencilElm.componentOnReady().then(() => { })); } waitComponentOnReady(childElm, promises); } }; waitComponentOnReady(documentElement, promiseChain); Promise.all(promiseChain).then(() => resolve()).catch(() => resolve()); }); }); } var CMP_FLAGS = /* @__PURE__ */ ((CMP_FLAGS2) => { CMP_FLAGS2[CMP_FLAGS2["shadowDomEncapsulation"] = 1] = "shadowDomEncapsulation"; CMP_FLAGS2[CMP_FLAGS2["scopedCssEncapsulation"] = 2] = "scopedCssEncapsulation"; CMP_FLAGS2[CMP_FLAGS2["hasSlotRelocation"] = 4] = "hasSlotRelocation"; CMP_FLAGS2[CMP_FLAGS2["needsShadowDomShim"] = 8] = "needsShadowDomShim"; CMP_FLAGS2[CMP_FLAGS2["shadowDelegatesFocus"] = 16] = "shadowDelegatesFocus"; CMP_FLAGS2[CMP_FLAGS2["hasMode"] = 32] = "hasMode"; CMP_FLAGS2[CMP_FLAGS2["needsScopedEncapsulation"] = 10] = "needsScopedEncapsulation"; return CMP_FLAGS2; })(CMP_FLAGS || {}); var formatLazyBundleRuntimeMeta = (bundleId, cmps) => { return [bundleId, cmps.map((cmp) => formatComponentRuntimeMeta(cmp, true))]; }; var formatComponentRuntimeMeta = (compilerMeta, includeMethods) => { let flags = 0; if (compilerMeta.encapsulation === "shadow") { flags |= 1 /* shadowDomEncapsulation */; if (compilerMeta.shadowDelegatesFocus) { flags |= 16 /* shadowDelegatesFocus */; } } else if (compilerMeta.encapsulation === "scoped") { flags |= 2 /* scopedCssEncapsulation */; } if (compilerMeta.encapsulation !== "shadow" && compilerMeta.htmlTagNames.includes("slot")) { flags |= 4 /* hasSlotRelocation */; } if (compilerMeta.hasMode) { flags |= 32 /* hasMode */; } const members = formatComponentRuntimeMembers(compilerMeta, includeMethods); const hostListeners = formatHostListeners(compilerMeta); return trimFalsy([ flags, compilerMeta.tagName, Object.keys(members).length > 0 ? members : void 0, hostListeners.length > 0 ? hostListeners : void 0 ]); }; var formatComponentRuntimeMembers = (compilerMeta, includeMethods = true) => { return { ...formatPropertiesRuntimeMember(compilerMeta.properties), ...formatStatesRuntimeMember(compilerMeta.states), ...includeMethods ? formatMethodsRuntimeMember(compilerMeta.methods) : {} }; }; var formatPropertiesRuntimeMember = (properties) => { const runtimeMembers = {}; properties.forEach((member) => { runtimeMembers[member.name] = trimFalsy([ /** * [0] member type */ formatFlags(member), formatAttrName(member) ]); }); return runtimeMembers; }; var formatFlags = (compilerProperty) => { let type = formatPropType(compilerProperty.type); if (compilerProperty.mutable) { type |= 1024 /* Mutable */; } if (compilerProperty.reflect) { type |= 512 /* ReflectAttr */; } return type; }; var formatAttrName = (compilerProperty) => { if (typeof compilerProperty.attribute === "string") { if (compilerProperty.name === compilerProperty.attribute) { return void 0; } return compilerProperty.attribute; } return void 0; }; var formatPropType = (type) => { if (type === "string") { return 1 /* String */; } if (type === "number") { return 2 /* Number */; } if (type === "boolean") { return 4 /* Boolean */; } if (type === "any") { return 8 /* Any */; } return 16 /* Unknown */; }; var formatStatesRuntimeMember = (states) => { const runtimeMembers = {}; states.forEach((member) => { runtimeMembers[member.name] = [ /** * [0] member flags */ 32 /* State */ ]; }); return runtimeMembers; }; var formatMethodsRuntimeMember = (methods) => { const runtimeMembers = {}; methods.forEach((member) => { runtimeMembers[member.name] = [ /** * [0] member flags */ 64 /* Method */ ]; }); return runtimeMembers; }; var formatHostListeners = (compilerMeta) => { return compilerMeta.listeners.map((compilerListener) => { const hostListener = [ computeListenerFlags(compilerListener), compilerListener.name, compilerListener.method ]; return hostListener; }); }; var computeListenerFlags = (listener) => { let flags = 0; if (listener.capture) { flags |= 2 /* Capture */; } if (listener.passive) { flags |= 1 /* Passive */; } switch (listener.target) { case "document": flags |= 4 /* TargetDocument */; break; case "window": flags |= 8 /* TargetWindow */; break; case "body": flags |= 16 /* TargetBody */; break; case "parent": flags |= 32 /* TargetParent */; break; } return flags; }; var trimFalsy = (data) => { const arr = data; for (let i = arr.length - 1; i >= 0; i--) { if (arr[i]) { break; } arr.pop(); } return arr; }; export { CMP_FLAGS, formatComponentRuntimeMeta, render, waitForChanges };