@wdio/browser-runner
Version:
A WebdriverIO runner to run unit tests tests in the browser.
395 lines (394 loc) • 11.8 kB
JavaScript
// 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
};