@wdio/browser-runner
Version:
A WebdriverIO runner to run unit tests tests in the browser.
324 lines (317 loc) • 18.2 kB
JavaScript
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
// src/browser/setup.ts
import { automationProtocolPath } from "virtual:wdio";
import { expect } from "expect-webdriverio";
import { remote } from "webdriverio";
import { _setGlobal } from "@wdio/globals";
// src/browser/frameworks/mocha.ts
import safeStringify from "safe-stringify";
import { setupEnv, formatMessage } from "@wdio/mocha-framework/common";
import { MESSAGE_TYPES } from "@wdio/types";
// src/browser/utils.ts
function getCID() {
var _a;
const urlParamString = new URLSearchParams(window.location.search);
const cid = (
// initial request contains cid as query parameter
urlParamString.get("cid") || // if not provided check for document cookie, set by `@wdio/runner` package
((_a = (document.cookie.split(";") || []).find((c) => c.includes("WDIO_CID"))) == null ? void 0 : _a.trim().split("=").pop())
);
if (!cid) {
throw new Error('"cid" query parameter is missing');
}
return cid;
}
var showPopupWarning = (name, value, defaultValue) => (...params) => {
const formatedParams = params.map((p) => JSON.stringify(p)).join(", ");
console.warn("WebdriverIO encountered a `".concat(name, "(").concat(formatedParams, ")` call that it cannot handle by default, so it returned `").concat(value, "`. Read more in https://webdriver.io/docs/runner#limitations.\n If needed, mock the `").concat(name, '` call manually like:\n ```\n import { spyOn } from "@wdio/browser-runner"\n spyOn(window, "').concat(name, '")').concat(defaultValue ? ".mockReturnValue(".concat(JSON.stringify(defaultValue), ")") : "", "\n ").concat(name, "(").concat(formatedParams, ")\n expect(").concat(name, ").toHaveBeenCalledWith(").concat(formatedParams, ")\n ```"));
return value;
};
var pick = (keys, obj) => {
return Object.fromEntries(
Object.entries(obj).filter(([k]) => keys.includes(k))
);
};
var RELEVANT_TEST_PROPS = ["type", "title", "body", "async", "sync", "timedOut", "pending", "parent", "test"];
function filterTestArgument(arg, file) {
if (!arg) {
return arg;
}
const newArgs = pick(RELEVANT_TEST_PROPS, arg);
return {
...newArgs,
file: arg.file || file,
test: filterTestArgument(newArgs.test, file),
parent: filterTestArgument(newArgs.parent, file)
};
}
// src/constants.ts
var WDIO_EVENT_NAME = "wdio:workerMessage";
var EVENTS = {
"suite": "suite:start",
"suite end": "suite:end",
"test": "test:start",
"test end": "test:end",
"hook": "hook:start",
"hook end": "hook:end",
"pass": "test:pass",
"fail": "test:fail",
"retry": "test:retry",
"pending": "test:pending"
};
// src/browser/frameworks/mocha.ts
var startTime = Date.now();
if (!window.Mocha) {
throw new Error("Can't find Mocha attached to the `window` scope, was it installed? Run `npm install mocha` and run again!");
}
var BaseReporter = window.Mocha.reporters.html;
var HTMLReporter = class extends BaseReporter {
constructor(runner, options) {
const getElementById = document.getElementById.bind(document);
document.getElementById = () => {
var _a, _b;
return (_b = (_a = document.querySelector("mocha-framework")) == null ? void 0 : _a.shadowRoot) == null ? void 0 : _b.querySelector("#mocha");
};
super(runner, options);
document.getElementById = getElementById;
}
addCodeToggle() {
}
};
var _root, _spec, _require, _hookResolver, _runnerEvents, _isMinified, _MochaFramework_instances, onFinish_fn, handleSocketMessage_fn, handleHookResult_fn, getHook_fn, hookTrigger_fn, sendTestReport_fn;
var MochaFramework = class extends HTMLElement {
constructor() {
super();
__privateAdd(this, _MochaFramework_instances);
__privateAdd(this, _root);
__privateAdd(this, _spec);
__privateAdd(this, _require);
__privateAdd(this, _hookResolver, /* @__PURE__ */ new Map());
__privateAdd(this, _runnerEvents, []);
__privateAdd(this, _isMinified, false);
__privateSet(this, _root, this.attachShadow({ mode: "open" }));
__privateSet(this, _spec, this.getAttribute("spec"));
__privateSet(this, _require, window.__wdioEnv__.args.require || []);
delete window.__wdioEnv__.args.require;
if (!__privateGet(this, _spec)) {
throw new Error('"spec" attribute required but not set');
}
mocha.setup({
...window.__wdioEnv__.args,
reporter: HTMLReporter
});
}
static get observedAttributes() {
return ["minified"];
}
get spec() {
return __privateGet(this, _spec);
}
connectedCallback() {
var _a;
__privateGet(this, _root).appendChild(template.content.cloneNode(true));
(_a = __privateGet(this, _root).querySelector(".btnCollapseExpand")) == null ? void 0 : _a.addEventListener("click", () => {
if (__privateGet(this, _isMinified)) {
this.shadowRoot.host.removeAttribute("minified");
} else {
this.shadowRoot.host.setAttribute("minified", "minified");
}
});
}
attributeChangedCallback(name, oldValue, newValue) {
if (name === "minified") {
__privateSet(this, _isMinified, typeof newValue === "string");
const reporterElem = __privateGet(this, _root).querySelector(".reporter");
if (reporterElem) {
reporterElem.className = __privateGet(this, _isMinified) ? "reporter minified" : "reporter";
}
document.body.style.width = "calc(100% - ".concat(__privateGet(this, _isMinified) ? "65px" : "500px", ")");
}
}
async run() {
var _a;
const globalTeardownScripts = [];
const globalSetupScripts = [];
for (const r of __privateGet(this, _require)) {
const { mochaGlobalSetup, mochaGlobalTeardown } = await import(
/* @vite-ignore */
r
) || {};
if (typeof mochaGlobalSetup === "function") {
globalSetupScripts.push(mochaGlobalSetup);
}
if (typeof mochaGlobalTeardown === "function") {
globalTeardownScripts.push(mochaGlobalTeardown);
}
}
const cid = getCID();
if (!cid) {
throw new Error('"cid" query parameter is missing');
}
const beforeHook = __privateMethod(this, _MochaFramework_instances, getHook_fn).call(this, "beforeHook");
const beforeTest = __privateMethod(this, _MochaFramework_instances, getHook_fn).call(this, "beforeTest");
const afterHook = __privateMethod(this, _MochaFramework_instances, getHook_fn).call(this, "afterHook");
const afterTest = __privateMethod(this, _MochaFramework_instances, getHook_fn).call(this, "afterTest");
setupEnv(cid, window.__wdioEnv__.args, beforeTest, beforeHook, afterTest, afterHook);
const file = __privateGet(this, _spec);
await import(
/* @vite-ignore */
file
);
for (const setupScript of globalSetupScripts) {
await setupScript();
}
(_a = import.meta.hot) == null ? void 0 : _a.on(WDIO_EVENT_NAME, __privateMethod(this, _MochaFramework_instances, handleSocketMessage_fn).bind(this));
const self = this;
const mochaBeforeHook = globalThis.before || globalThis.suiteSetup;
mochaBeforeHook(async function() {
var _a2, _b, _c;
const { title, tests, pending, delayed } = ((_b = (_a2 = this.test) == null ? void 0 : _a2.parent) == null ? void 0 : _b.suites[0]) || {};
await __privateMethod(_c = self, _MochaFramework_instances, getHook_fn).call(_c, "beforeSuite")({
...{ title, tests, pending, delayed },
file
});
});
const mochaAfterHook = globalThis.after || globalThis.suiteTeardown;
mochaAfterHook(async function() {
var _a2, _b, _c;
const { title, tests, pending, delayed } = ((_b = (_a2 = this.test) == null ? void 0 : _a2.parent) == null ? void 0 : _b.suites[0]) || {};
await __privateMethod(_c = self, _MochaFramework_instances, getHook_fn).call(_c, "afterSuite")({
...{ title, tests, pending, delayed },
file,
duration: Date.now() - startTime
});
});
const spinner = __privateGet(this, _root).querySelector(".lds-ring");
if (spinner) {
spinner.remove();
}
const runner = mocha.run(async (failures) => {
await __privateMethod(this, _MochaFramework_instances, onFinish_fn).call(this, failures);
for (const teardownScript of globalTeardownScripts) {
await teardownScript();
}
});
Object.entries(EVENTS).map(([mochaEvent, wdioEvent]) => runner.on(mochaEvent, (payload) => {
__privateGet(this, _runnerEvents).push(formatMessage({ type: wdioEvent, payload, err: payload.err }));
}));
}
};
_root = new WeakMap();
_spec = new WeakMap();
_require = new WeakMap();
_hookResolver = new WeakMap();
_runnerEvents = new WeakMap();
_isMinified = new WeakMap();
_MochaFramework_instances = new WeakSet();
onFinish_fn = async function(failures) {
await __privateMethod(this, _MochaFramework_instances, getHook_fn).call(this, "after")(failures, window.__wdioEnv__.capabilities, [__privateGet(this, _spec)]);
__privateMethod(this, _MochaFramework_instances, sendTestReport_fn).call(this, { failures, events: __privateGet(this, _runnerEvents) });
console.log("[WDIO] Finished test suite in ".concat(Date.now() - startTime, "ms"));
};
handleSocketMessage_fn = function(message) {
if (message.type === MESSAGE_TYPES.hookResultMessage) {
return __privateMethod(this, _MochaFramework_instances, handleHookResult_fn).call(this, message.value);
}
};
handleHookResult_fn = function(result) {
const resolver = __privateGet(this, _hookResolver).get(result.id);
if (!resolver) {
return console.warn("[WDIO] couldn't find resolve for id \"".concat(result.id, '"'));
}
__privateGet(this, _hookResolver).delete(result.id);
if (result.error) {
return resolver.reject(result.error);
}
return resolver.resolve();
};
getHook_fn = function(name) {
return (...args) => new Promise((resolve, reject) => {
var _a;
const id = __privateGet(this, _hookResolver).size + 1;
const cid = getCID();
if (!cid) {
return reject(new Error('"cid" query parameter is missing'));
}
__privateGet(this, _hookResolver).set(id, { resolve, reject });
args = args.map((arg) => {
if (typeof arg !== "object") {
return arg;
}
if ("error" in arg && arg.error instanceof Error) {
const errorObject = {
// Pull all enumerable properties, supporting properties on custom Errors
...arg.error,
// Explicitly pull Error's non-enumerable properties
message: arg.error.message,
name: arg.error.name,
stack: arg.error.stack,
type: arg.error.type || arg.error.name,
matcherResult: arg.error.matcherResult,
expected: arg.error.expected,
actual: arg.error.actual
};
return {
...arg,
error: errorObject
};
}
return {
...filterTestArgument(arg, __privateGet(this, _spec)),
..."type" in arg && "title" in arg ? { parent: arg.parent } : {},
..."test" in arg && arg.test ? { test: filterTestArgument(arg.test, __privateGet(this, _spec)) } : {},
..."currentTest" in arg && arg.currentTest ? { currentTest: filterTestArgument(arg.currentTest, __privateGet(this, _spec)) } : {}
};
});
(_a = import.meta.hot) == null ? void 0 : _a.send(WDIO_EVENT_NAME, __privateMethod(this, _MochaFramework_instances, hookTrigger_fn).call(this, { name, id, cid, args }));
});
};
hookTrigger_fn = function(value) {
return {
type: MESSAGE_TYPES.hookTriggerMessage,
value: JSON.parse(safeStringify(value))
};
};
sendTestReport_fn = function(value) {
var _a;
(_a = import.meta.hot) == null ? void 0 : _a.send(WDIO_EVENT_NAME, {
type: MESSAGE_TYPES.browserTestResult,
value: JSON.parse(safeStringify(value))
});
};
var template = document.createElement("template");
template.innerHTML = /*html*/
'\n<style>\n @import "@wdio/browser-runner/third_party/mocha.css";\n\n .reporter {\n transition: width .3s;\n box-shadow: -5px 0px 10px rgb(0 0 0 / 30%);\n position: absolute;\n top: 0;\n right: 0;\n width: 500px;\n height: 100%;\n margin: 0;\n color: var(--mocha-color);\n background-color: var(--mocha-bg-color);\n background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNjRweCIgaGVpZ2h0PSI2NHB4IiB2aWV3Qm94PSIwIDAgNjQgNjQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8dGl0bGU+TG9nbyBSZWd1bGFyPC90aXRsZT4KICAgIDxnIGlkPSJMb2dvLVJlZ3VsYXIiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxyZWN0IGlkPSJSZWN0YW5nbGUiIGZpbGw9IiNFQTU5MDYiIHg9IjAiIHk9IjAiIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgcng9IjUiPjwvcmVjdD4KICAgICAgICA8cGF0aCBkPSJNOCwxNiBMOCw0OCBMNiw0OCBMNiwxNiBMOCwxNiBaIE00MywxNiBDNTEuODM2NTU2LDE2IDU5LDIzLjE2MzQ0NCA1OSwzMiBDNTksNDAuODM2NTU2IDUxLjgzNjU1Niw0OCA0Myw0OCBDMzQuMTYzNDQ0LDQ4IDI3LDQwLjgzNjU1NiAyNywzMiBDMjcsMjMuMTYzNDQ0IDM0LjE2MzQ0NCwxNiA0MywxNiBaIE0yNywxNiBMMTQuMTA2LDQ3Ljk5OTIwNzggTDExLjk5OSw0Ny45OTkyMDc4IEwyNC44OTQsMTYgTDI3LDE2IFogTTQzLDE4IEMzNS4yNjgwMTM1LDE4IDI5LDI0LjI2ODAxMzUgMjksMzIgQzI5LDM5LjczMTk4NjUgMzUuMjY4MDEzNSw0NiA0Myw0NiBDNTAuNzMxOTg2NSw0NiA1NywzOS43MzE5ODY1IDU3LDMyIEM1NywyNC4yNjgwMTM1IDUwLjczMTk4NjUsMTggNDMsMTggWiIgaWQ9IkNvbWJpbmVkLVNoYXBlIiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+CiAgICA8L2c+Cjwvc3ZnPg==);\n background-repeat: no-repeat;\n background-size: 30px;\n background-position: 15px 20px;\n }\n .lds-ring {\n display: inline-block;\n position: absolute;\n top: calc(50% - 40px);\n left: calc(50% - 40px);\n width: 80px;\n height: 80px;\n }\n .lds-ring div {\n box-sizing: border-box;\n display: block;\n position: absolute;\n width: 64px;\n height: 64px;\n margin: 8px;\n border: 4px solid #EA5907;\n border-radius: 50%;\n animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;\n border-color: #EA5907 transparent transparent transparent;\n }\n .lds-ring div:nth-child(1) {\n animation-delay: -0.45s;\n }\n .lds-ring div:nth-child(2) {\n animation-delay: -0.3s;\n }\n .lds-ring div:nth-child(3) {\n animation-delay: -0.15s;\n }\n @keyframes lds-ring {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }\n .reporter.minified {\n width: 65px;\n }\n\n #mocha {\n margin: 0;\n }\n ul#mocha-report {\n transition: opacity .3s;\n opacity: 1;\n padding: 50px 15px 0 0;\n }\n .minified ul#mocha-report {\n opacity: 0;\n }\n .minified .progress {\n display: block;\n float: none;\n }\n .minified #mocha-stats {\n width: 50px;\n padding-top: 60px;\n }\n .minified #mocha-stats li {\n font-size: .9em;\n }\n\n @keyframes fadeOutIn {\n 0% { opacity: 1; }\n 20% { opacity: 0; }\n 100% { opacity: 1; }\n }\n\n .btnCollapseExpand {\n display: block;\n width: 50px;\n position: absolute;\n height: 50px;\n bottom: 0;\n right: 0;\n transition: transform .3s;\n transform: scale(1) translateX(-434px);\n margin: 10px 8px;\n background: transparent;\n border: 0;\n cursor: pointer;\n }\n .minified .btnCollapseExpand {\n transform: scale(-1) translateX(0px);\n animation-name: fadeOutIn;\n animation-duration: .3s;\n }\n</style>\n<div class="reporter">\n <div id="mocha"></div>\n <div class="lds-ring"><div></div><div></div><div></div><div></div></div>\n <button class="btnCollapseExpand">\n <svg width="50" height="40" viewBox="2 0 32 32" xmlns="http://www.w3.org/2000/svg">\n <path fill="#fff" d="M13.11 29.113c7.243 0 13.113-5.871 13.113-13.113S20.353 2.887 13.11 2.887C5.868 2.887-.003 8.758-.003 16S5.868 29.113 13.11 29.113zm0-25.177c6.652 0 12.064 5.412 12.064 12.064S19.762 28.064 13.11 28.064C6.457 28.064 1.046 22.652 1.046 16S6.457 3.936 13.11 3.936z"/>\n <path fill="#fff" d="m13.906 21.637.742.742L21.026 16l-6.378-6.379-.742.742 5.112 5.112H6.291v1.049h12.727z"/>\n </svg>\n </button>\n</div>\n';
customElements.define("mocha-framework", MochaFramework);
// src/browser/setup.ts
globalThis.alert = showPopupWarning("alert", void 0);
globalThis.confirm = showPopupWarning("confirm", false, true);
globalThis.prompt = showPopupWarning("prompt", null, "your value");
var browser = await remote({
automationProtocol: automationProtocolPath,
capabilities: window.__wdioEnv__.capabilities
});
_setGlobal("browser", browser, window.__wdioEnv__.injectGlobals);
_setGlobal("driver", browser, window.__wdioEnv__.injectGlobals);
_setGlobal("expect", expect, window.__wdioEnv__.injectGlobals);
_setGlobal("$", browser.$.bind(browser), window.__wdioEnv__.injectGlobals);
_setGlobal("$$", browser.$$.bind(browser), window.__wdioEnv__.injectGlobals);
var mochaFramework = document.querySelector("mocha-framework");
if (mochaFramework) {
mochaFramework.run().catch((err) => {
if (!err.stack) {
return;
}
return window.__wdioErrors__.push({
message: "".concat(err.message, ": ").concat(err.stack),
filename: mochaFramework.spec
});
});
}