UNPKG

@wdio/shared-store-service

Version:
269 lines (261 loc) 8.73 kB
var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/server.ts var server_exports = {}; __export(server_exports, { __resourcePoolStore: () => __resourcePoolStore, __store: () => __store, startServer: () => startServer }); import polka from "polka"; import { json } from "@polka/parse"; var store, resourcePoolStore, __store, __resourcePoolStore, validateBody, MAX_TIMEOUT, DEFAULT_TIMEOUT, startServer; var init_server = __esm({ "src/server.ts"() { "use strict"; store = {}; resourcePoolStore = /* @__PURE__ */ new Map(); __store = store; __resourcePoolStore = resourcePoolStore; validateBody = (req, res, next) => { if (!req.path.endsWith("/get") && !req.path.endsWith("/set")) { return next(); } if (req.method === "POST" && typeof req.body.key !== "string") { res.end(JSON.stringify({ error: "Invalid payload, key is required." })); } next(); }; MAX_TIMEOUT = 15e3; DEFAULT_TIMEOUT = 1e3; startServer = () => new Promise((resolve, reject) => { const app = polka().use(json(), validateBody).post("/", (req, res) => { const key = req.body.key; if (key === "*") { throw new Error(`You can't set a value with key "*" as this is a reserved key`); } store[key] = req.body.value; return res.end(); }).get("/:key", (req, res) => { const key = req.params.key; const value = key === "*" ? store : store[key]; res.end(JSON.stringify({ value })); }).post("/pool", (req, res, next) => { const key = req.body.key; const value = req.body.value; if (!Array.isArray(value)) { return next("Resource pool must be an array of values"); } resourcePoolStore.set(key, value); return res.end(); }).get("/pool/:key", async (req, res, next) => { const key = req.params.key; if (!resourcePoolStore.has(key)) { return next(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`); } let pool = resourcePoolStore.get(key) || []; if (pool.length > 0) { return res.end(JSON.stringify({ value: pool.shift() })); } const timeout = Math.min(parseInt(req.query.timeout) || DEFAULT_TIMEOUT, MAX_TIMEOUT); try { const result = await new Promise((resolve2, reject2) => { setTimeout(function secondAttempt() { pool = resourcePoolStore.get(key) || []; if (pool.length > 0) { resolve2({ value: pool.shift() }); } reject2(`'${key}' resource pool is empty. Set values to it first using 'setResourcePool' or 'addValueToPool'`); }, timeout); }); res.end(JSON.stringify(result)); } catch (err) { return next(err); } }).post("/pool/:key", (req, res, next) => { const key = req.params.key; const value = req.body.value; const pool = resourcePoolStore.get(key); if (!pool) { return next(`'${key}' resource pool does not exist. Set it first using 'setResourcePool'`); } pool.push(value); return res.end(); }); app.listen(0, (err) => { if (err) { return reject(err); } resolve({ app, port: app.server.address().port }); }); }); } }); // src/launcher.ts import logger from "@wdio/logger"; // src/client.ts var baseUrlResolve; var baseUrlPromise = new Promise((resolve) => { baseUrlResolve = resolve; }); var headers = { "Content-Type": "application/json" }; var isBaseUrlReady = false; var setPort = (port) => { baseUrlResolve(`http://localhost:${port}`); isBaseUrlReady = true; }; var getValue = async (key) => { if (!isBaseUrlReady) { throw new Error("Attempting to use `getValue` before the server has been initialized."); } const baseUrl = await baseUrlPromise; const res = await fetch(`${baseUrl}/${key}`, { method: "get", headers }).catch(errHandler); const responseBody = await res.json(); return responseBody.value ?? void 0; }; var setValue = async (key, value) => { const setPromise = baseUrlPromise.then((baseUrl) => { return fetch(`${baseUrl}/`, { method: "post", body: JSON.stringify({ key, value }), headers }).catch(errHandler); }); return isBaseUrlReady ? (await setPromise).status : Promise.resolve(); }; var setResourcePool = async (key, value) => { const setPromise = baseUrlPromise.then((baseUrl) => { return fetch(`${baseUrl}/pool`, { method: "post", body: JSON.stringify({ key, value }), headers }).catch(errHandler); }); return isBaseUrlReady ? (await setPromise).status : Promise.resolve(); }; var getValueFromPool = async (key, options) => { if (!isBaseUrlReady) { throw new Error("Attempting to use `getValueFromPool` before the server has been initialized."); } const baseUrl = await baseUrlPromise; const res = await fetch(`${baseUrl}/pool/${key}${typeof options?.timeout === "number" ? `?timeout=${options.timeout}` : ""}`, { method: "get", headers }).catch(errHandler); const responseBody = await res.json(); return responseBody.value ?? void 0; }; var addValueToPool = async (key, value) => { if (!isBaseUrlReady) { throw new Error("Attempting to use `addValueToPool` before the server has been initialized."); } const baseUrl = await baseUrlPromise; const res = await fetch(`${baseUrl}/pool/${key}`, { method: "post", body: JSON.stringify({ value }), headers }).catch(errHandler); return res.status; }; var errHandler = async (err) => { throw new Error(`${err.message || "Shared store server threw an error"}`); }; // src/constants.ts var CUSTOM_CAP = "wdio:sharedStoreServicePort"; // src/launcher.ts var log = logger("@wdio/shared-store-service"); var server; var SharedStoreLauncher = class { _app; async onPrepare(_, capabilities) { server = await Promise.resolve().then(() => (init_server(), server_exports)); const { port, app } = await server.startServer(); this._app = app; setPort(port); const capsList = Array.isArray(capabilities) ? capabilities : Object.values(capabilities).map((multiremoteOption) => multiremoteOption.capabilities); const caps = capsList.flatMap((c) => { const multiremote = c; if (!multiremote.browserName && multiremote[Object.keys(multiremote)[0]].capabilities) { return Object.values(multiremote).map( (options) => options.capabilities?.alwaysMatch || options.capabilities ); } const w3cCaps = c; return w3cCaps.alwaysMatch || c; }); caps.forEach((c) => { c[CUSTOM_CAP] = port; }); log.info(`Started shared server on port ${port}`); } async onComplete() { return new Promise((resolve) => { if (this._app && this._app.server.close) { this._app.server.close(() => resolve()); } return resolve(); }); } }; // src/service.ts var SharedStoreService = class { _browser; constructor(_, caps) { const port = caps[CUSTOM_CAP] || caps.alwaysMatch?.[CUSTOM_CAP] || (Object.values(caps)[0]?.capabilities)[CUSTOM_CAP]; if (!port) { throw new Error("SharedStoreService: port not found in capabilities"); } setPort(port); } before(caps, specs, _browser) { this._browser = _browser; const sharedStore = Object.create({}, { get: { value: (key) => getValue(key) }, set: { value: (key, value) => setValue(key, value) }, setResourcePool: { value: (key, value) => setResourcePool(key, value) }, getValueFromPool: { value: (key, options) => getValueFromPool(key, options) }, addValueToPool: { value: (key, value) => addValueToPool(key, value) } }); this._browser.sharedStore = sharedStore; const browser = this._browser; if (!this._browser.capabilities && browser.instances) { browser.instances.forEach((browserName) => { browser.getInstance(browserName).sharedStore = sharedStore; }); } } }; // src/index.ts var index_default = SharedStoreService; var launcher = SharedStoreLauncher; export { addValueToPool, index_default as default, getValue, getValueFromPool, launcher, setResourcePool, setValue };