tftsr-devops_investigation/node_modules/webdriverio/build/index.js

9493 lines
416 KiB
JavaScript
Raw Normal View History

feat: initial implementation of TFTSR IT Triage & RCA application Implements Phases 1-8 of the TFTSR implementation plan. Rust backend (Tauri 2.x, src-tauri/): - Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama - PII detection engine: 11 regex patterns with overlap resolution - SQLCipher AES-256 encrypted database with 10 versioned migrations - 28 Tauri IPC commands for triage, analysis, document, and system ops - Ollama: hardware probe, model recommendations, pull/delete with events - RCA and blameless post-mortem Markdown document generators - PDF export via printpdf - Audit log: SHA-256 hash of every external data send - Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2) Frontend (React 18 + TypeScript + Vite, src/): - 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings - 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives - 3 Zustand stores: session, settings (persisted), history - Type-safe tauriCommands.ts matching Rust backend types exactly - 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs) DevOps: - .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push - .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload Verified: - cargo check: zero errors - tsc --noEmit: zero errors - vitest run: 13/13 unit tests passing Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
var __defProp = Object.defineProperty;
var __typeError = (msg) => {
throw TypeError(msg);
};
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
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);
var __privateWrapper = (obj, member, setter, getter) => ({
set _(value) {
__privateSet(obj, member, value, setter);
},
get _() {
return __privateGet(obj, member, getter);
}
});
// src/index.ts
import logger29 from "@wdio/logger";
import WebDriver, { DEFAULTS } from "webdriver";
import { validateConfig } from "@wdio/config";
import { enableFileLogging, wrapCommand as wrapCommand3, isBidi } from "@wdio/utils";
// src/multiremote.ts
import zip from "lodash.zip";
import clone2 from "lodash.clonedeep";
import { webdriverMonad as webdriverMonad2, wrapCommand as wrapCommand2 } from "@wdio/utils";
// src/middlewares.ts
import { ELEMENT_KEY as ELEMENT_KEY21 } from "webdriver";
import { getBrowserObject as getBrowserObject38 } from "@wdio/utils";
// src/utils/implicitWait.ts
import logger from "@wdio/logger";
import { getBrowserObject } from "@wdio/utils";
var log = logger("webdriverio");
async function implicitWait(currentElement, commandName) {
const browser = getBrowserObject(currentElement);
const skipForMobileScroll = browser.isMobile && await browser.isNativeContext && (commandName === "scrollIntoView" || commandName === "tap");
if (!currentElement.elementId && !/(waitUntil|waitFor|isExisting|is?\w+Displayed|is?\w+Clickable)/.test(commandName) && !skipForMobileScroll) {
log.debug(
"command ".concat(commandName, ' was called on an element ("').concat(currentElement.selector, '") ') + "that wasn't found, waiting for it..."
);
try {
await currentElement.waitForExist();
return currentElement.parent.$(currentElement.selector).getElement();
} catch {
if (currentElement.selector.toString().includes("this.previousElementSibling")) {
throw new Error(
"Can't call ".concat(commandName, ' on previous element of element with selector "').concat(currentElement.parent.selector, "\" because sibling wasn't found")
);
}
if (currentElement.selector.toString().includes("this.nextElementSibling")) {
throw new Error(
"Can't call ".concat(commandName, ' on next element of element with selector "').concat(currentElement.parent.selector, "\" because sibling wasn't found")
);
}
if (currentElement.selector.toString().includes("this.parentElement")) {
throw new Error(
"Can't call ".concat(commandName, ' on parent element of element with selector "').concat(currentElement.parent.selector, "\" because it wasn't found")
);
}
throw new Error(
"Can't call ".concat(commandName, ' on element with selector "').concat(currentElement.selector, "\" because element wasn't found")
);
}
}
return currentElement;
}
// src/utils/refetchElement.ts
async function refetchElement(currentElement, commandName) {
const selectors = [];
while (currentElement.elementId && currentElement.parent) {
selectors.push({ selector: currentElement.selector, index: currentElement.index || 0 });
currentElement = currentElement.parent;
}
selectors.reverse();
const length = selectors.length;
return selectors.reduce(async (elementPromise, { selector, index }, currentIndex) => {
var _a;
const resolvedElement = await elementPromise;
let nextElement2 = index > 0 ? await ((_a = resolvedElement.$$(selector)[index]) == null ? void 0 : _a.getElement()) : null;
nextElement2 = nextElement2 || await resolvedElement.$(selector).getElement();
return await implicitWait(nextElement2, currentIndex + 1 < length ? "$" : commandName);
}, Promise.resolve(currentElement));
}
// src/utils/index.ts
import cssValue from "css-value";
import rgb2hex from "rgb2hex";
import GraphemeSplitter from "grapheme-splitter";
import logger28 from "@wdio/logger";
import isPlainObject from "is-plain-obj";
import { ELEMENT_KEY as ELEMENT_KEY20 } from "webdriver";
import { UNICODE_CHARACTERS as UNICODE_CHARACTERS2, asyncIterators, getBrowserObject as getBrowserObject37 } from "@wdio/utils";
// src/commands/browser.ts
var browser_exports = {};
__export(browser_exports, {
$: () => $,
$$: () => $$,
SESSION_MOCKS: () => SESSION_MOCKS,
action: () => action,
actions: () => actions,
addInitScript: () => addInitScript,
call: () => call,
custom$: () => custom$,
custom$$: () => custom$$,
debug: () => debug,
deepLink: () => deepLink,
deleteCookies: () => deleteCookies,
downloadFile: () => downloadFile,
emulate: () => emulate,
execute: () => execute,
executeAsync: () => executeAsync,
getContext: () => getContext,
getContexts: () => getContexts,
getCookies: () => getCookies,
getPuppeteer: () => getPuppeteer,
getWindowSize: () => getWindowSize,
keys: () => keys,
mock: () => mock,
mockClearAll: () => mockClearAll,
mockRestoreAll: () => mockRestoreAll,
newWindow: () => newWindow2,
pause: () => pause,
react$: () => react$3,
react$$: () => react$$3,
relaunchActiveApp: () => relaunchActiveApp,
reloadSession: () => reloadSession,
restore: () => restore,
savePDF: () => savePDF,
saveRecordingScreen: () => saveRecordingScreen,
saveScreenshot: () => saveScreenshot,
scroll: () => scroll,
setCookies: () => setCookies,
setTimeout: () => setTimeout2,
setViewport: () => setViewport,
setWindowSize: () => setWindowSize,
swipe: () => swipe,
switchContext: () => switchContext,
switchFrame: () => switchFrame,
switchWindow: () => switchWindow,
tap: () => tap,
throttle: () => throttle,
throttleCPU: () => throttleCPU,
throttleNetwork: () => throttleNetwork,
touchAction: () => touchAction2,
uploadFile: () => uploadFile,
url: () => url,
waitUntil: () => waitUntil
});
// src/utils/getElementObject.ts
import { webdriverMonad, wrapCommand } from "@wdio/utils";
import clone from "lodash.clonedeep";
import { ELEMENT_KEY } from "webdriver";
import { getBrowserObject as getBrowserObject2 } from "@wdio/utils";
var WebDriverError = class extends Error {
constructor(obj) {
const { name, stack } = obj;
const { error, stacktrace } = obj;
super(error || name || "");
Object.assign(this, {
message: obj.message,
stack: stacktrace || stack
});
}
};
function getElement(selector, res, props = { isReactElement: false, isShadowElement: false }) {
const browser = getBrowserObject2(this);
const browserCommandKeys = Object.keys(browser_exports);
const propertiesObject = {
/**
* filter out browser commands from object
*/
...Object.entries(clone(browser.__propertiesObject__)).reduce((commands, [name, descriptor]) => {
if (!browserCommandKeys.includes(name)) {
commands[name] = descriptor;
}
return commands;
}, {}),
...getPrototype("element"),
scope: { value: "element" }
};
propertiesObject.emit = { value: this.emit.bind(this) };
const element = webdriverMonad(this.options, (client) => {
const elementId = getElementFromResponse(res);
if (elementId) {
client.elementId = elementId;
client[ELEMENT_KEY] = elementId;
if (res && this.isBidi && "locator" in res) {
client.locator = res.locator;
}
} else {
client.error = res;
}
if (selector) {
client.selector = selector;
}
client.parent = this;
client.isReactElement = props.isReactElement;
client.isShadowElement = props.isShadowElement;
return client;
}, propertiesObject);
const elementInstance = element(this.sessionId, elementErrorHandler(wrapCommand));
const origAddCommand = elementInstance.addCommand.bind(elementInstance);
elementInstance.addCommand = (name, fn) => {
browser.__propertiesObject__[name] = { value: fn };
origAddCommand(name, fn);
};
return elementInstance;
}
var getElements = function getElements2(selector, elemResponse, props = { isReactElement: false, isShadowElement: false }) {
const browser = getBrowserObject2(this);
const browserCommandKeys = Object.keys(browser_exports);
const propertiesObject = {
/**
* filter out browser commands from object
*/
...Object.entries(clone(browser.__propertiesObject__)).reduce((commands, [name, descriptor]) => {
if (!browserCommandKeys.includes(name)) {
commands[name] = descriptor;
}
return commands;
}, {}),
...getPrototype("element")
};
if (elemResponse.length === 0) {
return [];
}
const elements = [elemResponse].flat(1).map((res, i) => {
if (res.selector && "$$" in res) {
return res;
}
propertiesObject.scope = { value: "element" };
propertiesObject.emit = { value: this.emit.bind(this) };
const element = webdriverMonad(this.options, (client) => {
const elementId = getElementFromResponse(res);
if (elementId) {
client.elementId = elementId;
client[ELEMENT_KEY] = elementId;
if (res && this.isBidi && "locator" in res) {
client.locator = res.locator;
}
} else {
res = res;
client.error = res instanceof Error ? res : new WebDriverError(res);
}
client.selector = Array.isArray(selector) ? selector[i].selector : selector;
client.parent = this;
client.index = i;
client.isReactElement = props.isReactElement;
client.isShadowElement = props.isShadowElement;
return client;
}, propertiesObject);
const elementInstance = element(this.sessionId, elementErrorHandler(wrapCommand));
const origAddCommand = elementInstance.addCommand.bind(elementInstance);
elementInstance.addCommand = (name, fn) => {
browser.__propertiesObject__[name] = { value: fn };
origAddCommand(name, fn);
};
return elementInstance;
});
return elements;
};
// src/constants.ts
import { UNICODE_CHARACTERS, HOOK_DEFINITION } from "@wdio/utils";
var WDIO_DEFAULTS = {
/**
* allows to specify automation protocol
*/
automationProtocol: {
type: "string",
default: "webdriver",
validate: (param) => {
if (typeof param !== "string") {
throw new Error("automationProtocol should be a string");
}
if (typeof import.meta.resolve !== "function") {
return;
}
try {
import.meta.resolve(param);
} catch (err) {
const error = err instanceof Error ? err : new Error("unknown error");
throw new Error("Couldn't find automation protocol \"".concat(param, '": ').concat(error.message));
}
}
},
/**
* capabilities of WebDriver sessions
*/
capabilities: {
type: "object",
validate: (param) => {
if (typeof param === "object") {
return true;
}
throw new Error('the "capabilities" options needs to be an object or a list of objects');
},
required: true
},
/**
* Shorten navigateTo command calls by setting a base url
*/
baseUrl: {
type: "string"
},
/**
* Default interval for all waitFor* commands
*/
waitforInterval: {
type: "number",
default: 100
},
/**
* Default timeout for all waitFor* commands
*/
waitforTimeout: {
type: "number",
default: 5e3
},
/**
* Hooks
*/
onReload: HOOK_DEFINITION,
beforeCommand: HOOK_DEFINITION,
afterCommand: HOOK_DEFINITION
};
var FF_REMOTE_DEBUG_ARG = "-remote-debugging-port";
var DEEP_SELECTOR = ">>>";
var ARIA_SELECTOR = "aria/";
var restoreFunctions = /* @__PURE__ */ new Map();
var Key = {
/**
* Special control key that works cross browser for Mac, where it's the command key, and for
* Windows or Linux, where it is the control key.
*/
Ctrl: "WDIO_CONTROL",
NULL: UNICODE_CHARACTERS.NULL,
Cancel: UNICODE_CHARACTERS.Cancel,
Help: UNICODE_CHARACTERS.Help,
Backspace: UNICODE_CHARACTERS.Backspace,
Tab: UNICODE_CHARACTERS.Tab,
Clear: UNICODE_CHARACTERS.Clear,
Return: UNICODE_CHARACTERS.Return,
Enter: UNICODE_CHARACTERS.Enter,
Shift: UNICODE_CHARACTERS.Shift,
Control: UNICODE_CHARACTERS.Control,
Alt: UNICODE_CHARACTERS.Alt,
Pause: UNICODE_CHARACTERS.Pause,
Escape: UNICODE_CHARACTERS.Escape,
Space: UNICODE_CHARACTERS.Space,
PageUp: UNICODE_CHARACTERS.PageUp,
PageDown: UNICODE_CHARACTERS.PageDown,
End: UNICODE_CHARACTERS.End,
Home: UNICODE_CHARACTERS.Home,
ArrowLeft: UNICODE_CHARACTERS.ArrowLeft,
ArrowUp: UNICODE_CHARACTERS.ArrowUp,
ArrowRight: UNICODE_CHARACTERS.ArrowRight,
ArrowDown: UNICODE_CHARACTERS.ArrowDown,
Insert: UNICODE_CHARACTERS.Insert,
Delete: UNICODE_CHARACTERS.Delete,
Semicolon: UNICODE_CHARACTERS.Semicolon,
Equals: UNICODE_CHARACTERS.Equals,
Numpad0: UNICODE_CHARACTERS["Numpad 0"],
Numpad1: UNICODE_CHARACTERS["Numpad 1"],
Numpad2: UNICODE_CHARACTERS["Numpad 2"],
Numpad3: UNICODE_CHARACTERS["Numpad 3"],
Numpad4: UNICODE_CHARACTERS["Numpad 4"],
Numpad5: UNICODE_CHARACTERS["Numpad 5"],
Numpad6: UNICODE_CHARACTERS["Numpad 6"],
Numpad7: UNICODE_CHARACTERS["Numpad 7"],
Numpad8: UNICODE_CHARACTERS["Numpad 8"],
Numpad9: UNICODE_CHARACTERS["Numpad 9"],
Multiply: UNICODE_CHARACTERS.Multiply,
Add: UNICODE_CHARACTERS.Add,
Separator: UNICODE_CHARACTERS.Separator,
Subtract: UNICODE_CHARACTERS.Subtract,
Decimal: UNICODE_CHARACTERS.Decimal,
Divide: UNICODE_CHARACTERS.Divide,
F1: UNICODE_CHARACTERS.F1,
F2: UNICODE_CHARACTERS.F2,
F3: UNICODE_CHARACTERS.F3,
F4: UNICODE_CHARACTERS.F4,
F5: UNICODE_CHARACTERS.F5,
F6: UNICODE_CHARACTERS.F6,
F7: UNICODE_CHARACTERS.F7,
F8: UNICODE_CHARACTERS.F8,
F9: UNICODE_CHARACTERS.F9,
F10: UNICODE_CHARACTERS.F10,
F11: UNICODE_CHARACTERS.F11,
F12: UNICODE_CHARACTERS.F12,
Command: UNICODE_CHARACTERS.Command,
ZenkakuHankaku: UNICODE_CHARACTERS.ZenkakuHankaku
};
// src/commands/browser/$$.ts
async function $$(selector) {
var _a;
if (this.isBidi && typeof selector === "string" && !selector.startsWith(DEEP_SELECTOR)) {
if ((_a = globalThis.wdio) == null ? void 0 : _a.execute) {
const command = "$$";
const res3 = "elementId" in this ? await globalThis.wdio.executeWithScope(command, this.elementId, selector) : await globalThis.wdio.execute(command, selector);
const elements3 = await getElements.call(this, selector, res3);
return enhanceElementsArray(elements3, this, selector);
}
const res2 = await findDeepElements.call(this, selector);
const elements2 = await getElements.call(this, selector, res2);
return enhanceElementsArray(elements2, getParent.call(this, res2), selector);
}
let res = Array.isArray(selector) ? selector : await findElements.call(this, selector);
if (Array.isArray(selector) && isElement(selector[0])) {
res = [];
for (const el of selector) {
const $el = await findElement.call(this, el);
if ($el) {
res.push($el);
}
}
}
const elements = await getElements.call(this, selector, res);
return enhanceElementsArray(elements, getParent.call(this, res), selector);
}
function getParent(res) {
let parent = res.length > 0 ? res[0].parent || this : this;
if (typeof parent.$ === "undefined") {
parent = "selector" in parent ? getElement.call(this, parent.selector, parent) : this;
}
return parent;
}
// src/commands/browser/$.ts
import { ELEMENT_KEY as ELEMENT_KEY2 } from "webdriver";
async function $(selector) {
if (globalThis.wdio && typeof selector === "string" && !selector.startsWith(DEEP_SELECTOR)) {
const res2 = "elementId" in this ? await globalThis.wdio.executeWithScope("$", this.elementId, selector) : await globalThis.wdio.execute("$", selector);
return getElement.call(this, selector, res2);
}
if (typeof selector === "object") {
const elementRef = selector;
if (typeof elementRef[ELEMENT_KEY2] === "string") {
return getElement.call(this, void 0, elementRef);
}
}
const res = await findElement.call(this, selector);
return getElement.call(this, selector, res);
}
// src/utils/actions/base.ts
import { ELEMENT_KEY as ELEMENT_KEY3 } from "webdriver";
var keyActionIds = 0;
var pointerActionIds = 0;
var wheelActionIds = 0;
var _id, _type, _parameters, _instance;
var BaseAction = class {
constructor(instance, type, params) {
this.instance = instance;
__privateAdd(this, _id);
__privateAdd(this, _type);
__privateAdd(this, _parameters);
__privateAdd(this, _instance);
__publicField(this, "sequence", []);
__privateSet(this, _instance, instance);
if (params == null ? void 0 : params.id) {
__privateSet(this, _id, params.id);
} else {
switch (type) {
case "key":
__privateSet(this, _id, "key".concat(++keyActionIds));
break;
case "pointer":
__privateSet(this, _id, "pointer".concat(++pointerActionIds));
break;
case "wheel":
__privateSet(this, _id, "wheel".concat(++wheelActionIds));
break;
default:
__privateSet(this, _id, "action".concat(type));
}
}
__privateSet(this, _type, type);
__privateSet(this, _parameters, (params == null ? void 0 : params.parameters) || {});
}
toJSON() {
return {
id: __privateGet(this, _id),
type: __privateGet(this, _type),
parameters: __privateGet(this, _parameters),
actions: this.sequence
};
}
/**
* Inserts a pause action for the specified device, ensuring it idles for a tick.
* @param duration idle time of tick
*/
pause(duration) {
this.sequence.push({ type: "pause", duration });
return this;
}
/**
* Perform action sequence
* @param skipRelease set to true if `releaseActions` command should not be invoked
*/
async perform(skipRelease = false) {
for (const seq of this.sequence) {
if (!seq.origin || typeof seq.origin === "string") {
continue;
}
if (typeof seq.origin.then === "function") {
await seq.origin.waitForExist();
seq.origin = await seq.origin;
}
if (!seq.origin[ELEMENT_KEY3]) {
throw new Error("Couldn't find element for \"".concat(seq.type, '" action sequence'));
}
seq.origin = { [ELEMENT_KEY3]: seq.origin[ELEMENT_KEY3] };
}
await __privateGet(this, _instance).performActions([this.toJSON()]);
if (!skipRelease) {
await __privateGet(this, _instance).releaseActions();
}
}
};
_id = new WeakMap();
_type = new WeakMap();
_parameters = new WeakMap();
_instance = new WeakMap();
// src/environment.ts
var isNode = !!(typeof process !== "undefined" && process.version);
var environment = {
value: {
get readFileSync() {
throw new Error("Can't read files form file system in this environment");
},
get downloadFile() {
throw new Error("The `downloadFile` command is not available in this environment");
},
get savePDF() {
throw new Error("The `savePDF` command is not available in this environment");
},
get saveRecordingScreen() {
throw new Error("The `saveRecordingScreen` command is not available in this environment");
},
get uploadFile() {
throw new Error("The `uploadFile` command is not available in this environment");
},
get saveScreenshot() {
throw new Error("The `saveScreenshot` command for WebdriverIO.Browser is not available in this environment");
},
get saveElementScreenshot() {
throw new Error("The `saveScreenshot` command for WebdriverIO.Element is not available in this environment");
},
get osType() {
return () => "browser";
},
get variables() {
return isNode ? process.env : {};
}
}
};
// src/utils/actions/key.ts
var _KeyAction_instances, sanitizeKey_fn;
var KeyAction = class extends BaseAction {
constructor(instance, params) {
super(instance, "key", params);
__privateAdd(this, _KeyAction_instances);
}
/**
* Generates a key up action.
* @param value key value
*/
up(value) {
this.sequence.push({ type: "keyUp", value: __privateMethod(this, _KeyAction_instances, sanitizeKey_fn).call(this, value) });
return this;
}
/**
* Generates a key down action.
* @param value key value
*/
down(value) {
this.sequence.push({ type: "keyDown", value: __privateMethod(this, _KeyAction_instances, sanitizeKey_fn).call(this, value) });
return this;
}
};
_KeyAction_instances = new WeakSet();
sanitizeKey_fn = function(value) {
var _a;
if (typeof value !== "string") {
throw new Error('Invalid type for key input: "'.concat(typeof value, '", expected a string!'));
}
const platformName = this.instance.capabilities.platformName;
const isMac = (
// check capabilities first
platformName && platformName.match(/mac(\s)*os/i) || // if not set, expect we run locally
((_a = this.instance.options.hostname) == null ? void 0 : _a.match(/0\.0\.0\.0|127\.0\.0\.1|local/i)) && environment.value.osType().match(/darwin/i)
);
if (value === Key.Ctrl) {
return isMac ? Key.Command : Key.Control;
}
if (value.length > 1) {
throw new Error('Your key input contains more than one character: "'.concat(value, '", only one is allowed though!'));
}
return value;
};
// src/utils/actions/pointer.ts
var buttonNumbers = [0, 1, 2];
var buttonNames = ["left", "middle", "right"];
var buttonValue = [...buttonNumbers, ...buttonNames];
var ORIGIN_DEFAULT = "viewport";
var BUTTON_DEFAULT = 0;
var POINTER_TYPE_DEFAULT = "mouse";
var UP_PARAM_DEFAULTS = {
button: BUTTON_DEFAULT
};
var PARAM_DEFAULTS = {
...UP_PARAM_DEFAULTS,
width: 0,
height: 0,
pressure: 0,
tangentialPressure: 0,
tiltX: 0,
tiltY: 0,
twist: 0,
altitudeAngle: 0,
azimuthAngle: 0
};
var MOVE_PARAM_DEFAULTS = {
x: 0,
y: 0,
duration: 100,
origin: ORIGIN_DEFAULT
};
function removeDefaultParams(seq) {
for (const [key, value] of Object.entries(seq)) {
if (value === 0 && !["x", "y", "button", "duration"].includes(key)) {
delete seq[key];
}
}
}
function mapButton(params) {
const buttons = {
left: 0,
middle: 1,
right: 2
};
if (typeof params === "number") {
return { button: params };
}
if (typeof params === "string") {
return { button: buttons[params] };
}
if (typeof params === "object" && typeof params.button === "string") {
return { ...params, button: buttons[params.button] };
}
return params;
}
var PointerAction = class extends BaseAction {
constructor(instance, params = {}) {
if (!params.parameters) {
params.parameters = { pointerType: POINTER_TYPE_DEFAULT };
}
super(instance, "pointer", params);
}
move(params = {}, y) {
const seq = {
type: "pointerMove",
// default params
...PARAM_DEFAULTS,
...UP_PARAM_DEFAULTS,
...MOVE_PARAM_DEFAULTS
};
if (typeof params === "number") {
Object.assign(seq, { x: params, y });
} else if (params) {
Object.assign(seq, params);
}
removeDefaultParams(seq);
this.sequence.push(seq);
return this;
}
up(params = UP_PARAM_DEFAULTS) {
this.sequence.push({
type: "pointerUp",
...mapButton(params)
});
return this;
}
down(params = {}) {
const seq = {
type: "pointerDown",
...PARAM_DEFAULTS,
...mapButton(params)
};
removeDefaultParams(seq);
this.sequence.push(seq);
return this;
}
/**
* An action that cancels this pointer's current input.
*/
cancel() {
this.sequence.push({ type: "pointerCancel" });
return this;
}
};
// src/utils/actions/wheel.ts
var DEFAULT_SCROLL_PARAMS = {
x: 0,
y: 0,
deltaX: 0,
deltaY: 0,
duration: 0
};
var WheelAction = class extends BaseAction {
constructor(instance, params) {
super(instance, "wheel", params);
}
/**
* Scrolls a page to given coordinates or origin.
*/
scroll(params) {
this.sequence.push({ type: "scroll", ...DEFAULT_SCROLL_PARAMS, ...params });
return this;
}
};
// src/commands/browser/action.ts
function action(type, opts) {
if (type === "key") {
return new KeyAction(this, opts);
}
if (type === "pointer") {
return new PointerAction(this, opts);
}
if (type === "wheel") {
return new WheelAction(this, opts);
}
throw new Error('Unsupported action type "'.concat(type, '", supported are "key", "pointer", "wheel"'));
}
// src/commands/browser/actions.ts
async function actions(actions2) {
await this.performActions(actions2.map((action2) => action2.toJSON()));
await this.releaseActions();
}
// src/utils/bidi/index.ts
import { ELEMENT_KEY as ELEMENT_KEY4 } from "webdriver";
// src/commands/constant.ts
var TOUCH_ACTIONS = ["press", "longPress", "tap", "moveTo", "wait", "release"];
var POS_ACTIONS = TOUCH_ACTIONS.slice(0, 4);
var ACCEPTED_OPTIONS = ["x", "y", "element"];
var SCRIPT_PREFIX = "/* __wdio script__ */";
var SCRIPT_SUFFIX = "/* __wdio script end__ */";
var resqScript = '!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.resq=e():(t.window=t.window||{},t.window.resq=e())}(window,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=16)}([function(t,e,r){"use strict";r.d(e,"a",(function(){return m})),r.d(e,"d",(function(){return j})),r.d(e,"b",(function(){return M})),r.d(e,"c",(function(){return P}));var n=r(1),o=r.n(n),u=r(14),i=r.n(u),c=r(2),f=r.n(c),s=r(15),a=r.n(s);function l(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n)}return r}var p=Array.isArray,d=Object.keys;function x(t){return"function"==typeof t}function y(t){return t instanceof HTMLElement||t instanceof Text}function h(t){return"object"===f()(t)&&!p(t)}function b(t){if(!t||"string"==typeof t)return t;var e=function(t){for(var e=1;e<arguments.length;e++){var r=null!=arguments[e]?arguments[e]:{};e%2?l(Object(r),!0).forEach((function(e){i()(t,e,r[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(r)):l(Object(r)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(r,e))}))}return t}({},t);return delete e.children,e}function v(t,e){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return!(!p(t)||!p(e))&&(r?t.length===e.length&&!t.find((function(t){return!e.includes(t)})):t.some((function(t){return e.includes(t)})))}function _(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=[];if(!d(t).length)return!0;if(null===e||!d(e).length)return!1;if(r)return a()(t,e);var o=d(t).filter((function(t){return d(e).includes(t)}));return o.forEach((function(r){h(t[r])&&h(e[r])&&(n=n.concat(_(t[r],e[r]))),(t[r]===e[r]||v(t[r],e[r]))&&n.push(e)})),n.length>0&&n.filter((function(t){return t})).length===o.length}function m(t){var e,r={children:[]};if(!t)return r;r.name=x(e=t.type)?e.displayName||e.name:e,r.props=b(t.memoizedProps),r.state=function(t){if(t){var e=t.baseState;return e||t}}(t.memoizedState);var n=t.child;if(n)for(r.children.push(n);n.sibling;)r.children.push(n.sibling),n=n.sibling;return r.children=r.children.map((function(t){return m(t)})),x(t.type)&&function(t){return t.children.length>1}(r)?(r.node=function(t){return t.children.map((function(t){return t.node})).filter((function(t){return!!t}))}(r),r.isFragment=!0):r.node=function(t){return y(t.stateNode)?t.stateNode:t.child&&y(t.child.stateNode)?t.child.stateNode:null}(t),r}function g(t){for(;t.length;){var e=t.shift();if(e.node)return e.node;e.children&&Array.isArray(e.children)&&t.push.apply(t,o()(e.children))}}function O(t,e){for(var r=[];t.length;){var n=t.shift().children;n&&Array.isArray(n)&&n.forEach((function(n){e(n)&&(!n.node&&Array.isArray(n.children)&&(n.node=g(n.children.concat([]))),r.push(n)),t.push(n)}))}return r}function w(t,e){var r=function(t){if(t){var e=t.split("(");return 1===e.length?t:e.find((fu
var formatArgs = function(scope, actions2) {
return actions2.map((action2) => {
if (Array.isArray(action2)) {
return formatArgs(scope, action2);
}
if (typeof action2 === "string") {
action2 = { action: action2 };
}
const formattedAction = {
action: action2.action,
options: {}
};
const actionElement = action2.element && typeof action2.element.elementId === "string" ? action2.element.elementId : scope.elementId;
if (POS_ACTIONS.includes(action2.action) && formattedAction.options && actionElement) {
formattedAction.options.element = actionElement;
}
if (formattedAction.options && typeof action2.x === "number" && isFinite(action2.x)) {
formattedAction.options.x = action2.x;
}
if (formattedAction.options && typeof action2.y === "number" && isFinite(action2.y)) {
formattedAction.options.y = action2.y;
}
if (formattedAction.options && action2.ms) {
formattedAction.options.ms = action2.ms;
}
if (formattedAction.options && Object.keys(formattedAction.options).length === 0) {
delete formattedAction.options;
}
return formattedAction;
});
};
var validateParameters = (params) => {
const options = Object.keys(params.options || {});
if (params.action === "release" && options.length !== 0) {
throw new Error(
'action "release" doesn\'t accept any options ' + '("'.concat(options.join('", "'), '" found)')
);
}
if (params.action === "wait" && (options.includes("x") || options.includes("y"))) {
throw new Error('action "wait" doesn\'t accept x or y options');
}
if (POS_ACTIONS.includes(params.action)) {
for (const option in params.options) {
if (!ACCEPTED_OPTIONS.includes(option)) {
throw new Error('action "'.concat(params.action, '" doesn\'t accept "').concat(option, '" as option'));
}
}
if (options.length === 0) {
throw new Error(
'Touch actions like "'.concat(params.action, '" need at least some kind of ') + 'position information like "element", "x" or "y" options, you\'ve none given.'
);
}
}
};
var touchAction = function(actions2) {
if (!this.multiTouchPerform || !this.touchPerform) {
throw new Error("touchAction can be used with Appium only.");
}
if (!Array.isArray(actions2)) {
actions2 = [actions2];
}
const formattedAction = formatArgs(this, actions2);
const protocolCommand = Array.isArray(actions2[0]) ? this.multiTouchPerform.bind(this) : this.touchPerform.bind(this);
formattedAction.forEach((params) => validateParameters(params));
return protocolCommand(formattedAction);
};
// src/utils/bidi/error.ts
var _params, _result, _WebdriverBidiExeception_instances, getCustomStack_fn, getFailureLine_fn;
var WebdriverBidiExeception = class extends Error {
constructor(params, result) {
super(result.exceptionDetails.text);
__privateAdd(this, _WebdriverBidiExeception_instances);
__privateAdd(this, _params);
__privateAdd(this, _result);
this.name = "WebdriverBidiExeception";
__privateSet(this, _params, params);
__privateSet(this, _result, result);
this.stack = __privateMethod(this, _WebdriverBidiExeception_instances, getCustomStack_fn).call(this);
}
};
_params = new WeakMap();
_result = new WeakMap();
_WebdriverBidiExeception_instances = new WeakSet();
getCustomStack_fn = function() {
const origStack = this.stack;
const failureLine = __privateMethod(this, _WebdriverBidiExeception_instances, getFailureLine_fn).call(this);
const stack = (origStack == null ? void 0 : origStack.split("\n")) || [];
const wrapCommandIndex = stack.findLastIndex((line) => line.includes("Context.executeAsync"));
const executeLine = stack[wrapCommandIndex - 1];
if (failureLine && executeLine) {
const line = executeLine.replace("file://", "").split(":");
const row = line.length > 3 ? line[2] : line[1];
const [errorMessage, ...restOfStack] = stack;
const linePrefix = " ".concat(row, " \u2502 ");
const codeLine = [
linePrefix + failureLine,
" ".repeat(linePrefix.length - 2) + "\u2575 " + "~".repeat(failureLine.length),
""
];
return [errorMessage, executeLine, ...codeLine, ...restOfStack].join("\n");
}
return origStack;
};
/**
* This is an attempt to identify the snippet of code that caused an execute(Async) function to
* throw an exception
* @param {string} script script that executed in the browser
* @param {number} columnNumber column in which the scrpt threw an exception
* @returns the line of failure in which the code threw an exception or `undefined` if we could not find it
*/
getFailureLine_fn = function() {
var _a;
const script = __privateGet(this, _params).functionDeclaration;
const exceptionDetails = __privateGet(this, _result).exceptionDetails;
const userScript = script.split("\n").find((l) => l.includes(SCRIPT_PREFIX));
if (!userScript) {
return;
}
let length = 0;
const isMinified = script.split("\n").some((line) => line.includes(SCRIPT_PREFIX) && line.includes(SCRIPT_SUFFIX));
if (isMinified) {
for (const line of userScript.split(";")) {
if (length + line.length >= exceptionDetails.columnNumber) {
return line.includes(SCRIPT_SUFFIX) ? line.slice(0, line.indexOf(SCRIPT_SUFFIX)) : line;
}
length += line.length;
}
} else {
const slicedScript = script.slice(
script.indexOf(SCRIPT_PREFIX) + SCRIPT_PREFIX.length,
script.indexOf(SCRIPT_SUFFIX)
);
const lineDiff = 9;
const line = (_a = slicedScript.split("\n")[exceptionDetails.lineNumber - lineDiff]) == null ? void 0 : _a.slice(exceptionDetails.columnNumber);
return line;
}
return void 0;
};
// src/utils/bidi/index.ts
function parseScriptResult(params, result) {
const type = result.type;
if (type === "success" /* Success */) {
return deserialize(result.result);
}
if (type === "exception" /* Exception */) {
throw new WebdriverBidiExeception(params, result);
}
throw new Error("Unknown evaluate result type: ".concat(type));
}
var references = /* @__PURE__ */ new Map();
function deserialize(result) {
const deserializedValue = deserializeValue(result);
references.clear();
return deserializedValue;
}
function deserializeValue(result) {
if (result && "internalId" in result && typeof result.internalId === "string") {
if ("value" in result) {
references.set(result.internalId, result.value);
} else {
result.value = references.get(result.internalId);
}
}
const { type, value } = result;
if (type === "regexp" /* RegularExpression */) {
return new RegExp(value.pattern, value.flags);
}
if (type === "array" /* Array */) {
return value.map((element) => deserializeValue(element));
}
if (type === "date" /* Date */) {
return new Date(value);
}
if (type === "map" /* Map */) {
return new Map(value.map(([key, value2]) => [typeof key === "string" ? key : deserializeValue(key), deserializeValue(value2)]));
}
if (type === "set" /* Set */) {
return new Set(value.map((element) => deserializeValue(element)));
}
if (type === "number" /* Number */ && value === "NaN") {
return NaN;
}
if (type === "number" /* Number */ && value === "Infinity") {
return Infinity;
}
if (type === "number" /* Number */ && value === "-Infinity") {
return -Infinity;
}
if (type === "number" /* Number */ && value === "-0") {
return -0;
}
if (type === "bigint" /* BigInt */) {
return BigInt(value);
}
if (type === "null" /* Null */) {
return null;
}
if (type === "object" /* Object */) {
return Object.fromEntries((value || []).map(([key, value2]) => {
return [typeof key === "string" ? key : deserializeValue(key), deserializeValue(value2)];
}));
}
if (type === "node" /* Node */) {
return { [ELEMENT_KEY4]: result.sharedId };
}
if (type === "error" /* Error */) {
return new Error("<unserializable error>");
}
return value;
}
// src/commands/browser/addInitScript.ts
async function addInitScript(script, ...args) {
if (typeof script !== "function") {
throw new Error("The `addInitScript` command requires a function as first parameter, but got: " + typeof script);
}
if (!this.isBidi) {
throw new Error("This command is only supported when automating browser using WebDriver Bidi protocol");
}
const serializedParameters = (args || []).map((arg) => JSON.stringify(arg));
const context = await this.getWindowHandle();
const src = "return " + script.toString();
const fn = "(emit) => {\n const closure = new Function(".concat(JSON.stringify(src), ")\n return closure()(").concat(serializedParameters.length ? "".concat(serializedParameters.join(", "), ", emit") : "emit", ")\n }");
const channel = btoa(fn.toString());
const result = await this.scriptAddPreloadScript({
functionDeclaration: fn,
arguments: [{
type: "channel",
value: { channel }
}],
contexts: [context]
});
await this.sessionSubscribe({
events: ["script.message"]
});
const eventHandler = /* @__PURE__ */ new Map();
const messageHandler = (msg) => {
if (msg.channel === channel) {
const handler = eventHandler.get("data") || [];
return handler.forEach((fn2) => fn2(deserialize(msg.data)));
}
};
this.on("script.message", messageHandler);
const resetFn = (() => {
eventHandler.clear();
this.off("script.message", messageHandler);
return this.scriptRemovePreloadScript({ script: result.script });
});
const returnVal = {
remove: resetFn,
on: (event, listener) => {
var _a;
if (!eventHandler.has(event)) {
eventHandler.set(event, []);
}
(_a = eventHandler.get(event)) == null ? void 0 : _a.push(listener);
}
};
return returnVal;
}
// src/commands/browser/call.ts
function call(fn) {
if (typeof fn === "function") {
return fn();
}
throw new Error('Command argument for "call" needs to be a function');
}
// src/commands/browser/custom$$.ts
import { ELEMENT_KEY as ELEMENT_KEY5 } from "webdriver";
async function custom$$(strategyName, ...strategyArguments) {
const strategy = this.strategies.get(strategyName);
if (!strategy) {
throw Error("No strategy found for " + strategyName);
}
const strategyRef = { strategy, strategyName, strategyArguments };
let res = await this.execute(strategy, ...strategyArguments);
if (!Array.isArray(res)) {
res = [res];
}
res = res.filter((el) => !!el && typeof el[ELEMENT_KEY5] === "string");
const elements = res.length ? await getElements.call(this, strategyRef, res) : [];
return enhanceElementsArray(elements, this, strategyName, "custom$$", strategyArguments);
}
// src/commands/browser/custom$.ts
import { ELEMENT_KEY as ELEMENT_KEY6 } from "webdriver";
async function custom$(strategyName, ...strategyArguments) {
const strategy = this.strategies.get(strategyName);
if (!strategy) {
throw Error("No strategy found for " + strategyName);
}
const strategyRef = { strategy, strategyName, strategyArguments };
let res = await this.execute(strategy, ...strategyArguments);
if (Array.isArray(res)) {
res = res[0];
}
if (res && typeof res[ELEMENT_KEY6] === "string") {
return await getElement.call(this, strategyRef, res);
}
return await getElement.call(this, strategyRef, new Error("no such element"));
}
// src/commands/browser/debug.ts
import { serializeError } from "serialize-error";
import WDIORepl from "@wdio/repl";
function debug(commandTimeout = 5e3) {
const repl = new WDIORepl();
const { introMessage } = WDIORepl;
const process2 = globalThis.process;
if (!environment.value.variables.WDIO_WORKER_ID || typeof process2.send !== "function") {
console.log(WDIORepl.introMessage);
const context = {
browser: this,
driver: this,
$: this.$.bind(this),
$$: this.$$.bind(this)
};
return repl.start(context);
}
process2._debugProcess(process2.pid);
process2.send({
origin: "debugger",
name: "start",
params: { commandTimeout, introMessage }
});
let commandResolve = (
/* istanbul ignore next */
() => {
}
);
process2.on("message", (m) => {
if (m.origin !== "debugger") {
return;
}
if (m.name === "stop") {
process2._debugEnd(process2.pid);
return commandResolve();
}
if (m.name === "eval") {
repl.eval(m.content.cmd, global, void 0, (err, result) => {
if (typeof process2.send !== "function") {
return;
}
if (err) {
process2.send({
origin: "debugger",
name: "result",
params: {
error: true,
...serializeError(err)
}
});
}
if (typeof result === "function") {
result = "[Function: ".concat(result.name, "]");
}
process2.send({
origin: "debugger",
name: "result",
params: { result }
});
});
}
});
return new Promise((resolve) => commandResolve = resolve);
}
// src/commands/browser/deleteCookies.ts
import logger2 from "@wdio/logger";
var log2 = logger2("webdriverio");
async function deleteCookies(filter) {
const filterArray = typeof filter === "undefined" ? void 0 : Array.isArray(filter) ? filter : [filter];
if (!this.isBidi) {
await deleteCookiesClassic.call(this, getNamesForClassic(filterArray));
return;
}
let url2;
try {
url2 = new URL(await this.getUrl());
if (url2.origin === "null") {
await deleteCookiesClassic.call(this, getNamesForClassic(filterArray));
return;
}
} catch {
await deleteCookiesClassic.call(this, getNamesForClassic(filterArray));
return;
}
const partition = {
type: "storageKey",
sourceOrigin: url2.origin
};
try {
const { cookies } = await this.storageGetCookies({ partition });
if (cookies.length === 0 && !this.isMobile) {
await deleteCookiesClassic.call(this, getNamesForClassic(filterArray));
return;
}
} catch (err) {
log2.warn("BiDi deleteCookies check failed, falling back to classic: ".concat(err.message));
await deleteCookiesClassic.call(this, getNamesForClassic(filterArray));
return;
}
try {
if (!filterArray) {
await this.storageDeleteCookies({ partition });
return;
}
const bidiFilter = filterArray.map((f) => {
if (typeof f === "string") {
return { name: f };
}
if (typeof f === "object") {
return f;
}
throw new Error("Invalid value for cookie filter, expected 'string' or 'remote.StorageCookieFilter' but found \"".concat(typeof f, '"'));
});
await Promise.all(bidiFilter.map((filter2) => this.storageDeleteCookies({ filter: filter2, partition })));
} catch (err) {
log2.warn("BiDi deleteCookies failed, falling back to classic: ".concat(err.message));
await deleteCookiesClassic.call(this, getNamesForClassic(filterArray));
}
return;
}
function getNamesForClassic(filterArray) {
return filterArray == null ? void 0 : filterArray.map((f) => {
if (typeof f === "object") {
const name = f.name;
if (!name) {
throw new Error("In WebDriver Classic you can only filter for cookie names");
}
return name;
}
if (typeof f === "string") {
return f;
}
throw new Error("Invalid value for cookie filter, expected 'string' or 'remote.StorageCookieFilter' but found \"".concat(typeof f, '"'));
});
}
function deleteCookiesClassic(names) {
if (names === void 0) {
return this.deleteAllCookies();
}
const namesList = Array.isArray(names) ? names : [names];
if (namesList.every((obj) => typeof obj !== "string")) {
return Promise.reject(new Error("Invalid input (see https://webdriver.io/docs/api/browser/deleteCookies for documentation)"));
}
return Promise.all(namesList.map((name) => this.deleteCookie(name)));
}
// src/commands/browser/downloadFile.ts
async function downloadFile(fileName, targetDirectory) {
return environment.value.downloadFile.call(this, fileName, targetDirectory);
}
// src/clock.ts
import logger3 from "@wdio/logger";
var log3 = logger3("webdriverio:ClockManager");
function installFakeTimers(options) {
window.__clock = window.__wdio_sinon.install(options);
}
function uninstallFakeTimers() {
window.__clock.uninstall();
}
var fakerScript = 'function FakeTimers () {\n/*\n * Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no. All rights reserved.\n * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\n * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.\n * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * @typedef {object} IdleDeadline\n * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout\n * @property {function():number} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period\n */\n\n/**\n * Queues a function to be called during a browser\'s idle periods\n *\n * @callback RequestIdleCallback\n * @param {function(IdleDeadline)} callback\n * @param {{timeout: number}} options - an options object\n * @returns {number} the id\n */\n\n/**\n * @callback NextTick\n * @param {VoidVarArgsFunc} callback - the callback to run\n * @param {...*} args - optional arguments to call the callback with\n * @returns {void}\n */\n\n/**\n * @callback SetImmediate\n * @param {VoidVarArgsFunc} callback - the callback to run\n * @param {...*} args - optional arguments to call the callback with\n * @returns {NodeImmediate}\n */\n\n/**\n * @callback VoidVarArgsFunc\n * @param {...*} callback - the callback to run\n * @returns {void}\n */\n\n/**\n * @typedef RequestAnimationFrame\n * @property {function(number):void} requestAnimationFrame\n * @returns {number} - the id\n */\n\n/**\n * @typedef Performance\n * @property {function(): number} now\n */\n\n/* eslint-disable jsdoc/require-property-description */\n/**\n * @typedef {object} Clock\n * @property {number} now - the current time\n * @property {Date} Date - the Date constructor\n * @property {number} loopLimit - the maximum number of timers before assuming an infinite loop\n * @property {RequestIdleCallback} requestIdleCallback\n * @property {function(number):void} cancelIdleCallback\n * @property {setTimeout} setTimeout\n * @property {clearTimeout} clearTimeout\n * @property {NextTick} nextTick\n * @property {queueMicrotask} queueMicrotask\n * @property {setInterval} setInterval\n * @property {clearInterval} clearInterval\n * @property {SetImmediate} setImmediate\n * @property {function(NodeImmediate):void} clearImmediate\n * @property {function():number} countTimers\n * @property {RequestAnimationFrame} requestAnimationFrame\n * @property {function(number):void} cancelAnimationFrame\n * @property {function():void} runMicrotasks\n * @property {function(string | number): number} tick\n * @property {function(string | number): Promise<number>} tickAsync\n * @property {function(): number} next\n * @property {function(): Promise<number>} nextAsync\n * @property {function(): number} runAll\n * @prop
var _browser, _resetFn, _isInstalled;
var ClockManager = class {
constructor(browser) {
__privateAdd(this, _browser);
__privateAdd(this, _resetFn, () => Promise.resolve());
__privateAdd(this, _isInstalled, false);
__privateSet(this, _browser, browser);
}
/**
* Install fake timers on the browser. If you call the `emulate` command, WebdriverIO will automatically install
* the fake timers for you. You can use this method to re-install the fake timers if you have called `restore`.
*
* @param options {FakeTimerInstallOpts} Options to pass to the fake clock
* @returns {Promise<void>}
*/
async install(options) {
if (__privateGet(this, _isInstalled)) {
return log3.warn("Fake timers are already installed");
}
if (globalThis.window) {
return;
}
const emulateOptions = options || {};
const functionDeclaration = fakerScript;
const installOptions = {
...emulateOptions,
now: emulateOptions.now && emulateOptions.now instanceof Date ? emulateOptions.now.getTime() : emulateOptions.now
};
const [, libScript, restoreInstallScript] = await Promise.all([
/**
* install fake timers for current ex
*/
__privateGet(this, _browser).executeScript("return (".concat(functionDeclaration, ").apply(null, arguments)"), []).then(() => __privateGet(this, _browser).execute(installFakeTimers, installOptions)),
/**
* add preload script to to emulate clock for upcoming page loads
*/
__privateGet(this, _browser).scriptAddPreloadScript({ functionDeclaration }),
__privateGet(this, _browser).addInitScript(installFakeTimers, installOptions)
]);
__privateSet(this, _resetFn, async () => Promise.all([
__privateGet(this, _browser).scriptRemovePreloadScript({ script: libScript.script }),
__privateGet(this, _browser).execute(uninstallFakeTimers),
restoreInstallScript
]));
__privateSet(this, _isInstalled, true);
}
/**
* Restore all overridden native functions. This is automatically called between tests, so should not
* generally be needed.
*
* ```ts
* it('should restore the clock', async () => {
* console.log(new Date()) // returns e.g. 1722560447102
*
* const clock = await browser.emulate('clock', { now: new Date(2021, 3, 14) })
* console.log(await browser.execute(() => new Date().getTime())) // returns 1618383600000
*
* await clock.restore()
* console.log(await browser.execute(() => new Date().getTime())) // returns 1722560447102
* })
* ```
*
* @returns {Promise<void>}
*/
async restore() {
await __privateGet(this, _resetFn).call(this);
__privateSet(this, _isInstalled, false);
}
/**
* Move the clock the specified number of `milliseconds`. Any timers within the affected range of time will be called.
* @param ms {number} The number of milliseconds to move the clock.
*
* ```ts
* it('should move the clock', async () => {
* console.log(new Date()) // returns e.g. 1722560447102
*
* const clock = await browser.emulate('clock', { now: new Date(2021, 3, 14) })
* console.log(await browser.execute(() => new Date().getTime())) // returns 1618383600000
*
* await clock.tick(1000)
* console.log(await browser.execute(() => new Date().getTime())) // returns 1618383601000
* })
* ```
*
* @param {number} ms The number of milliseconds to move the clock.
* @returns {Promise<void>}
*/
async tick(ms) {
await __privateGet(this, _browser).execute((ms2) => window.__clock.tick(ms2), ms);
}
/**
* Change the system time to the new now. Now can be a timestamp, date object, or not passed in which defaults
* to 0. No timers will be called, nor will the time left before they trigger change.
*
* ```ts
* it('should set the system time', async () => {
* const clock = await browser.emulate('clock', { now: new Date(2021, 3, 14) })
* console.log(await browser.execute(() => new Date().getTime())) // returns 1618383600000
*
* await clock.setSystemTime(new Date(2011, 3, 15))
* console.log(await browser.execute(() => new Date().getTime())) // returns 1302850800000
* })
* ```
*
* @param date {Date|number} The new date to set the system time to.
* @returns {Promise<void>}
*/
async setSystemTime(date) {
const serializableSystemTime = date instanceof Date ? date.getTime() : date;
await __privateGet(this, _browser).execute((date2) => window.__clock.setSystemTime(date2), serializableSystemTime);
}
};
_browser = new WeakMap();
_resetFn = new WeakMap();
_isInstalled = new WeakMap();
// src/deviceDescriptorsSource.ts
var deviceDescriptorsSource = {
"Blackberry PlayBook": {
userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/18.0 Safari/536.2+",
viewport: {
width: 600,
height: 1024
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true
},
"Blackberry PlayBook landscape": {
userAgent: "Mozilla/5.0 (PlayBook; U; RIM Tablet OS 2.1.0; en-US) AppleWebKit/536.2+ (KHTML like Gecko) Version/18.0 Safari/536.2+",
viewport: {
width: 1024,
height: 600
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true
},
"BlackBerry Z30": {
userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/18.0 Mobile Safari/537.10+",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"BlackBerry Z30 landscape": {
userAgent: "Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/18.0 Mobile Safari/537.10+",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Galaxy Note 3": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/18.0 Mobile Safari/534.30",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Galaxy Note 3 landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.3; en-us; SM-N900T Build/JSS15J) AppleWebKit/534.30 (KHTML, like Gecko) Version/18.0 Mobile Safari/534.30",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Galaxy Note II": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/18.0 Mobile Safari/534.30",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Galaxy Note II landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.1; en-us; GT-N7100 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/18.0 Mobile Safari/534.30",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Galaxy S III": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/18.0 Mobile Safari/534.30",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Galaxy S III landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/18.0 Mobile Safari/534.30",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Galaxy S5": {
userAgent: "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Galaxy S5 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Galaxy S8": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 360,
height: 740
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Galaxy S8 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 740,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Galaxy S9 +": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 320,
height: 658
},
deviceScaleFactor: 4.5,
isMobile: true,
hasTouch: true
},
"Galaxy S9 + landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 658,
height: 320
},
deviceScaleFactor: 4.5,
isMobile: true,
hasTouch: true
},
"Galaxy Tab S4": {
userAgent: "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 712,
height: 1138
},
deviceScaleFactor: 2.25,
isMobile: true,
hasTouch: true
},
"Galaxy Tab S4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 1138,
height: 712
},
deviceScaleFactor: 2.25,
isMobile: true,
hasTouch: true
},
"iPad(gen 5)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad(gen 5) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 768
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad(gen 6)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad(gen 6) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 768
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad(gen 7)": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 810,
height: 1080
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad(gen 7) landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 1080,
height: 810
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad Mini": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 768,
height: 1024
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad Mini landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 1024,
height: 768
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad Pro 11": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 834,
height: 1194
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPad Pro 11 landscape": {
userAgent: "Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 1194,
height: 834
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 6": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 6 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 6 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 414,
height: 736
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 6 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 736,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 7": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 7 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 7 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 414,
height: 736
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 7 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 736,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 8": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 667
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 8 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 667,
height: 375
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 8 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 414,
height: 736
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 8 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 736,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone SE": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/18.0 Mobile/14E304 Safari/602.1",
viewport: {
width: 320,
height: 568
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone SE landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/18.0 Mobile/14E304 Safari/602.1",
viewport: {
width: 568,
height: 320
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone X": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 375,
height: 812
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone X landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/18.0 Mobile/15A372 Safari/604.1",
viewport: {
width: 812,
height: 375
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone XR": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 414,
height: 896
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone XR landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 896,
height: 414
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 11": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 414,
height: 715
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 11 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 800,
height: 364
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"iPhone 11 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 375,
height: 635
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 11 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 724,
height: 325
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 11 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 414,
height: 715
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 11 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 808,
height: 364
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 750,
height: 340
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 750,
height: 340
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 428,
height: 746
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 832,
height: 378
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 Mini": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 375,
height: 629
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 12 Mini landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 712,
height: 325
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 750,
height: 342
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 750,
height: 342
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 428,
height: 746
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 832,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 Mini": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 375,
height: 629
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 13 Mini landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 712,
height: 327
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 390,
height: 664
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 750,
height: 340
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 428,
height: 746
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 832,
height: 378
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 393,
height: 660
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 734,
height: 343
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 430,
height: 740
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 14 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 814,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 393,
height: 659
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 734,
height: 343
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 Plus": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 430,
height: 739
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 Plus landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 814,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 Pro": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 393,
height: 659
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 Pro landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 734,
height: 343
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 Pro Max": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 430,
height: 739
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"iPhone 15 Pro Max landscape": {
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 17_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Mobile/15E148 Safari/604.1",
viewport: {
width: 814,
height: 380
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Kindle Fire HDX": {
userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
viewport: {
width: 800,
height: 1280
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Kindle Fire HDX landscape": {
userAgent: "Mozilla/5.0 (Linux; U; en-us; KFAPWI Build/JDQ39) AppleWebKit/535.19 (KHTML, like Gecko) Silk/3.13 Safari/535.19 Silk-Accelerated=true",
viewport: {
width: 1280,
height: 800
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"LG Optimus L70": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 384,
height: 640
},
deviceScaleFactor: 1.25,
isMobile: true,
hasTouch: true
},
"LG Optimus L70 landscape": {
userAgent: "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 640,
height: 384
},
deviceScaleFactor: 1.25,
isMobile: true,
hasTouch: true
},
"Microsoft Lumia 550": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Microsoft Lumia 550 landscape": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Microsoft Lumia 950": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 4,
isMobile: true,
hasTouch: true
},
"Microsoft Lumia 950 landscape": {
userAgent: "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36 Edge/14.14263",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 4,
isMobile: true,
hasTouch: true
},
"Nexus 10": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 800,
height: 1280
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Nexus 10 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 1280,
height: 800
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Nexus 4": {
userAgent: "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 384,
height: 640
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Nexus 4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 640,
height: 384
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Nexus 5": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Nexus 5 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Nexus 5X": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 412,
height: 732
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
},
"Nexus 5X landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 732,
height: 412
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
},
"Nexus 6": {
userAgent: "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 412,
height: 732
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true
},
"Nexus 6 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 732,
height: 412
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true
},
"Nexus 6P": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 412,
height: 732
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true
},
"Nexus 6P landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 732,
height: 412
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true
},
"Nexus 7": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 600,
height: 960
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Nexus 7 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 960,
height: 600
},
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true
},
"Nokia Lumia 520": {
userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)",
viewport: {
width: 320,
height: 533
},
deviceScaleFactor: 1.5,
isMobile: true,
hasTouch: true
},
"Nokia Lumia 520 landscape": {
userAgent: "Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; IEMobile/10.0; ARM; Touch; NOKIA; Lumia 520)",
viewport: {
width: 533,
height: 320
},
deviceScaleFactor: 1.5,
isMobile: true,
hasTouch: true
},
"Nokia N9": {
userAgent: "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
viewport: {
width: 480,
height: 854
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true
},
"Nokia N9 landscape": {
userAgent: "Mozilla/5.0 (MeeGo; NokiaN9) AppleWebKit/534.13 (KHTML, like Gecko) NokiaBrowser/8.5.0 Mobile Safari/534.13",
viewport: {
width: 854,
height: 480
},
deviceScaleFactor: 1,
isMobile: true,
hasTouch: true
},
"Pixel 2": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 411,
height: 731
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
},
"Pixel 2 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 731,
height: 411
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
},
"Pixel 2 XL": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 411,
height: 823
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true
},
"Pixel 2 XL landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 823,
height: 411
},
deviceScaleFactor: 3.5,
isMobile: true,
hasTouch: true
},
"Pixel 3": {
userAgent: "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 393,
height: 786
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true
},
"Pixel 3 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 786,
height: 393
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true
},
"Pixel 4": {
userAgent: "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 353,
height: 745
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Pixel 4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 745,
height: 353
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Pixel 4a(5G)": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 412,
height: 765
},
deviceScaleFactor: 2.63,
isMobile: true,
hasTouch: true
},
"Pixel 4a(5G) landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 840,
height: 312
},
deviceScaleFactor: 2.63,
isMobile: true,
hasTouch: true
},
"Pixel 5": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 393,
height: 727
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true
},
"Pixel 5 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 802,
height: 293
},
deviceScaleFactor: 2.75,
isMobile: true,
hasTouch: true
},
"Pixel 7": {
userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 412,
height: 839
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
},
"Pixel 7 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 863,
height: 360
},
deviceScaleFactor: 2.625,
isMobile: true,
hasTouch: true
},
"Moto G4": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 360,
height: 640
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Moto G4 landscape": {
userAgent: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Mobile Safari/537.36",
viewport: {
width: 640,
height: 360
},
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
},
"Desktop Chrome HiDPI": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false
},
"Desktop Edge HiDPI": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36 Edg/128.0.6613.18",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false
},
"Desktop Firefox HiDPI": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false
},
"Desktop Safari": {
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.0 Safari/605.1.15",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false
},
"Desktop Chrome": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false
},
"Desktop Edge": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.18 Safari/537.36 Edg/128.0.6613.18",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false
},
"Desktop Firefox": {
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
viewport: {
width: 1280,
height: 720
},
deviceScaleFactor: 1,
isMobile: false,
hasTouch: false
}
};
// src/commands/browser/emulate.ts
function storeRestoreFunction(browser, scope, fn) {
var _a, _b;
if (!restoreFunctions.has(browser)) {
restoreFunctions.set(browser, /* @__PURE__ */ new Map());
}
const restoreFunctionsList = (_a = restoreFunctions.get(browser)) == null ? void 0 : _a.get(scope);
const updatedList = restoreFunctionsList ? [...restoreFunctionsList, fn] : [fn];
(_b = restoreFunctions.get(browser)) == null ? void 0 : _b.set(scope, updatedList);
}
async function emulate(scope, options) {
if (!this.isBidi) {
throw new Error("emulate command is only supported for Bidi");
}
if (scope === "geolocation") {
if (!options) {
throw new Error("Missing geolocation emulation options");
}
const patchedFn = options instanceof Error ? "cbError(new Error(".concat(JSON.stringify(options.message), "))") : "cbSuccess({\n coords: ".concat(JSON.stringify(options), ",\n timestamp: Date.now()\n })");
const res = await this.scriptAddPreloadScript({
functionDeclaration: (
/*js*/
"() => {\n Object.defineProperty(navigator.geolocation, 'getCurrentPosition', {\n value: (cbSuccess, cbError) => ".concat(patchedFn, "\n })\n }")
)
});
const resetFn = async () => this.scriptRemovePreloadScript({ script: res.script });
storeRestoreFunction(this, "geolocation", resetFn);
return resetFn;
}
if (scope === "userAgent") {
if (typeof options !== "string") {
throw new Error("Expected userAgent emulation options to be a string, received ".concat(typeof options));
}
const res = await this.scriptAddPreloadScript({
functionDeclaration: (
/*js*/
"() => {\n Object.defineProperty(navigator, 'userAgent', {\n value: ".concat(JSON.stringify(options), "\n })\n }")
)
});
const resetFn = async () => {
return this.scriptRemovePreloadScript({ script: res.script });
};
storeRestoreFunction(this, "userAgent", resetFn);
return resetFn;
}
if (scope === "clock") {
const clock = new ClockManager(this);
await clock.install(options);
storeRestoreFunction(this, "clock", clock.restore.bind(clock));
return clock;
}
if (scope === "colorScheme") {
if (options !== "light" && options !== "dark") {
throw new Error('Expected "colorScheme" emulation options to be either "light" or "dark", received "'.concat(options, '"'));
}
const res = await this.scriptAddPreloadScript({
functionDeclaration: (
/*js*/
"() => {\n const originalMatchMedia = window.matchMedia\n Object.defineProperty(window, 'matchMedia', {\n value: (query) => {\n const colorSchemeQuery = query.match(/\\(prefers-color-scheme:(\\s)*(dark|light)\\)/i)\n if (colorSchemeQuery) {\n const result = originalMatchMedia(query)\n Object.defineProperty(result, 'matches', {\n value: colorSchemeQuery[2] === \"".concat(options, '",\n configurable: true\n })\n return result\n }\n\n return originalMatchMedia(query)\n },\n configurable: true\n })\n }')
)
});
const resetFn = async () => this.scriptRemovePreloadScript({ script: res.script });
storeRestoreFunction(this, "colorScheme", resetFn);
return resetFn;
}
if (scope === "onLine") {
if (typeof options !== "boolean") {
throw new Error('Expected "onLine" emulation options to be a boolean, received "'.concat(typeof options, '"'));
}
const res = await this.scriptAddPreloadScript({
functionDeclaration: (
/*js*/
"() => {\n Object.defineProperty(navigator, 'onLine', {\n value: ".concat(options, "\n })\n }")
)
});
const resetFn = async () => this.scriptRemovePreloadScript({ script: res.script });
storeRestoreFunction(this, "onLine", resetFn);
return resetFn;
}
if (scope === "device") {
if (typeof options !== "string") {
throw new Error('Expected "device" emulation options to be a string, received "'.concat(typeof options, '"'));
}
const device = deviceDescriptorsSource[options];
if (!device) {
throw new Error('Unknown device name "'.concat(options, '", please use one of the following: ').concat(Object.keys(deviceDescriptorsSource).join(", ")));
}
const [restoreUserAgent] = await Promise.all([
this.emulate("userAgent", device.userAgent),
this.setViewport({
...device.viewport,
devicePixelRatio: device.deviceScaleFactor
})
]);
const desktopViewport = deviceDescriptorsSource["Desktop Chrome"];
const restoreFn = async () => Promise.all([
restoreUserAgent(),
this.setViewport({ ...desktopViewport.viewport, devicePixelRatio: desktopViewport.deviceScaleFactor })
]);
return restoreFn;
}
throw new Error('Invalid scope "'.concat(scope, '", expected one of "geolocation", "userAgent", "colorScheme", "onLine", "device" or "clock"'));
}
// src/commands/browser/execute.ts
import { getBrowserObject as getBrowserObject3 } from "@wdio/utils";
// src/utils/bidi/value.ts
import { ELEMENT_KEY as ELEMENT_KEY7 } from "webdriver";
var TYPE_CONSTANT = "type";
var VALUE_CONSTANT = "value";
var LocalValue = class _LocalValue {
constructor(type, value) {
__publicField(this, "type");
__publicField(this, "value");
if (type === "undefined" /* Undefined */ || type === "null" /* Null */) {
this.type = type;
} else {
this.type = type;
this.value = value;
}
}
/**
* Creates a new LocalValue object with a string value.
*
* @param {string} value - The string value to be stored in the LocalValue object.
* @returns {LocalValue} - The created LocalValue object.
*/
static createStringValue(value) {
return new _LocalValue("string" /* String */, value);
}
/**
* Creates a new LocalValue object with a number value.
*
* @param {number} value - The number value.
* @returns {LocalValue} - The created LocalValue object.
*/
static createNumberValue(value) {
return new _LocalValue("number" /* Number */, value);
}
/**
* Creates a new LocalValue object with a special number value.
*
* @param {number} value - The value of the special number.
* @returns {LocalValue} - The created LocalValue object.
*/
static createSpecialNumberValue(value) {
if (Number.isNaN(value)) {
return new _LocalValue("number" /* SpecialNumber */, "NaN");
}
if (Object.is(value, -0)) {
return new _LocalValue("number" /* SpecialNumber */, "-0");
}
if (value === Infinity) {
return new _LocalValue("number" /* SpecialNumber */, "Infinity");
}
if (value === -Infinity) {
return new _LocalValue("number" /* SpecialNumber */, "-Infinity");
}
return new _LocalValue("number" /* SpecialNumber */, value);
}
/**
* Creates a new LocalValue object with an undefined value.
* @returns {LocalValue} - The created LocalValue object.
*/
static createUndefinedValue() {
return new _LocalValue("undefined" /* Undefined */);
}
/**
* Creates a new LocalValue object with a null value.
* @returns {LocalValue} - The created LocalValue object.
*/
static createNullValue() {
return new _LocalValue("null" /* Null */);
}
/**
* Creates a new LocalValue object with a boolean value.
*
* @param {boolean} value - The boolean value.
* @returns {LocalValue} - The created LocalValue object.
*/
static createBooleanValue(value) {
return new _LocalValue("boolean" /* Boolean */, value);
}
/**
* Creates a new LocalValue object with a BigInt value.
*
* @param {BigInt} value - The BigInt value.
* @returns {LocalValue} - The created LocalValue object.
*/
static createBigIntValue(value) {
return new _LocalValue("bigint" /* BigInt */, value);
}
/**
* Creates a new LocalValue object with an array.
*
* @param {Array} value - The array.
* @returns {LocalValue} - The created LocalValue object.
*/
static createArrayValue(value) {
return new _LocalValue("array" /* Array */, value);
}
/**
* Creates a new LocalValue object with date value.
*
* @param {string} value - The date.
* @returns {LocalValue} - The created LocalValue object.
*/
static createDateValue(value) {
return new _LocalValue("date" /* Date */, value);
}
/**
* Creates a new LocalValue object of map value.
* @param {Map} map - The map.
* @returns {LocalValue} - The created LocalValue object.
*/
static createMapValue(map) {
const value = [];
Object.entries(map).forEach((entry) => {
value.push(entry);
});
return new _LocalValue("map" /* Map */, value);
}
/**
* Creates a new LocalValue object from the passed object.
*
* @param {Object} map - The object.
* @returns {LocalValue} - The created LocalValue object.
*/
static createObjectValue(object) {
const value = [];
Object.entries(object).forEach(([key, val]) => {
value.push([key, _LocalValue.getArgument(val)]);
});
return new _LocalValue("object" /* Object */, value);
}
/**
* Creates a new LocalValue object of regular expression value.
*
* @param {string} value - The value of the regular expression.
* @returns {LocalValue} - The created LocalValue object.
*/
static createRegularExpressionValue(value) {
return new _LocalValue("regexp" /* RegularExpression */, value);
}
/**
* Creates a new LocalValue object with the specified value.
* @param {Set} value - The value to be set.
* @returns {LocalValue} - The created LocalValue object.
*/
static createSetValue(value) {
return new _LocalValue("set" /* Set */, value);
}
/**
* Creates a new LocalValue object with the given channel value
*
* @param {ChannelValue} value - The channel value.
* @returns {LocalValue} - The created LocalValue object.
*/
static createChannelValue(value) {
return new _LocalValue("channel" /* Channel */, value);
}
static createReferenceValue(handle, sharedId) {
return new ReferenceValue(handle, sharedId);
}
static getArgument(argument) {
const type = typeof argument;
switch (type) {
case "string" /* String */:
return _LocalValue.createStringValue(argument);
case "number" /* Number */:
if (Number.isNaN(argument) || Object.is(argument, -0) || !Number.isFinite(argument)) {
return _LocalValue.createSpecialNumberValue(argument);
}
return _LocalValue.createNumberValue(argument);
case "boolean" /* Boolean */:
return _LocalValue.createBooleanValue(argument);
case "bigint" /* BigInt */:
return _LocalValue.createBigIntValue(argument);
case "undefined" /* Undefined */:
return _LocalValue.createUndefinedValue();
case "object" /* Object */:
if (argument === null) {
return _LocalValue.createNullValue();
}
if (argument instanceof Date) {
return _LocalValue.createDateValue(argument);
}
if (argument instanceof Map) {
const map = [];
argument.forEach((value, key) => {
const objectKey = typeof key === "string" ? key : _LocalValue.getArgument(key);
const objectValue = _LocalValue.getArgument(value);
map.push([objectKey, objectValue]);
});
return new _LocalValue("map" /* Map */, map);
}
if (argument instanceof Set) {
const set = [];
argument.forEach((value) => {
set.push(_LocalValue.getArgument(value));
});
return _LocalValue.createSetValue(set);
}
if (argument instanceof Array) {
const arr = [];
argument.forEach((value) => {
arr.push(_LocalValue.getArgument(value));
});
return _LocalValue.createArrayValue(arr);
}
if (argument instanceof RegExp) {
return _LocalValue.createRegularExpressionValue({
pattern: argument.source,
flags: argument.flags
});
}
if (argument && ELEMENT_KEY7 in argument) {
return _LocalValue.createReferenceValue(
"sharedId" /* SharedId */,
argument[ELEMENT_KEY7]
);
}
return _LocalValue.createObjectValue(argument);
}
throw new Error("Unsupported type: ".concat(type));
}
asMap() {
return {
[TYPE_CONSTANT]: this.type,
...!(this.type === "null" /* Null */ || this.type === "undefined" /* Undefined */) ? { [VALUE_CONSTANT]: this.value } : {}
};
}
};
var ReferenceValue = class {
/**
* Constructs a new ReferenceValue object.
* @param {string} handle - The handle value.
* @param {string} sharedId - The shared ID value.
*/
constructor(handle, sharedId) {
__publicField(this, "handle");
__publicField(this, "sharedId");
if (handle === "handle" /* Handle */) {
this.handle = sharedId;
} else if (handle === "sharedId" /* SharedId */) {
this.sharedId = sharedId;
} else {
this.handle = handle;
this.sharedId = sharedId;
}
}
asMap() {
const toReturn = {};
if (typeof this.handle !== "undefined") {
toReturn["handle" /* Handle */] = this.handle;
}
if (typeof this.sharedId !== "undefined") {
toReturn["sharedId" /* SharedId */] = this.sharedId;
}
return toReturn;
}
};
// src/session/context.ts
import logger4 from "@wdio/logger";
// src/session/session.ts
var sessionManager = /* @__PURE__ */ new Map();
var listenerRegisteredSession = /* @__PURE__ */ new Set();
var _browser2, _scope, _onCommandListener, _SessionManager_instances, onCommand_fn;
var SessionManager = class {
/**
* SessionManager constructor
* Logic in here should be executed for all session singletons, e.g. remove instance
* of itself when a session was deleted.
* @param browser WebdriverIO.Browser
* @param scope scope of the session manager, e.g. context, network etc.
*/
constructor(browser, scope) {
__privateAdd(this, _SessionManager_instances);
__privateAdd(this, _browser2);
__privateAdd(this, _scope);
__privateAdd(this, _onCommandListener, __privateMethod(this, _SessionManager_instances, onCommand_fn).bind(this));
__privateSet(this, _browser2, browser);
__privateSet(this, _scope, scope);
const registrationId = "".concat(__privateGet(this, _browser2).sessionId, "-").concat(__privateGet(this, _scope));
if (!listenerRegisteredSession.has(registrationId)) {
__privateGet(this, _browser2).on("command", __privateGet(this, _onCommandListener));
listenerRegisteredSession.add(registrationId);
}
}
removeListeners() {
__privateGet(this, _browser2).off("command", __privateGet(this, _onCommandListener));
}
initialize() {
return void 0;
}
/**
* check if session manager should be enabled, if
*/
isEnabled() {
return (
// we are in a Bidi session
__privateGet(this, _browser2).isBidi && // we are not running unit tests
!environment.value.variables.WDIO_UNIT_TESTS
);
}
static getSessionManager(browser, Manager) {
const scope = Manager.name;
let sessionManagerInstances = sessionManager.get(scope);
if (!sessionManagerInstances) {
sessionManagerInstances = /* @__PURE__ */ new Map();
sessionManager.set(scope, sessionManagerInstances);
}
let sessionManagerInstance = sessionManagerInstances.get(browser);
if (!sessionManagerInstance) {
sessionManagerInstance = new Manager(browser);
sessionManagerInstances.set(browser, sessionManagerInstance);
}
return sessionManagerInstance;
}
};
_browser2 = new WeakMap();
_scope = new WeakMap();
_onCommandListener = new WeakMap();
_SessionManager_instances = new WeakSet();
onCommand_fn = function(ev) {
if (ev.command === "deleteSession") {
const sessionManagerInstances = sessionManager.get(__privateGet(this, _scope));
const sessionManagerInstance = sessionManagerInstances == null ? void 0 : sessionManagerInstances.get(__privateGet(this, _browser2));
if (sessionManagerInstance && sessionManagerInstances) {
sessionManagerInstance.removeListeners();
sessionManagerInstances.delete(__privateGet(this, _browser2));
}
}
};
// src/utils/mobile.ts
function getNativeContext({ capabilities, isMobile }) {
var _a, _b;
if (!capabilities || typeof capabilities !== "object" || !isMobile) {
return false;
}
const isBrowserNameFalse = !!(capabilities == null ? void 0 : capabilities.browserName) === false;
const isAutoWebviewFalse = !// @ts-expect-error
((capabilities == null ? void 0 : capabilities.autoWebview) === true || capabilities["appium:autoWebview"] === true || ((_a = capabilities["appium:options"]) == null ? void 0 : _a.autoWebview) === true || ((_b = capabilities["lt:options"]) == null ? void 0 : _b.autoWebview) === true);
return isBrowserNameFalse && isMobile && isAutoWebviewFalse;
}
function getMobileContext({ capabilities, isAndroid, isNativeContext }) {
var _a;
return isNativeContext ? "NATIVE_APP" : (
// Android webviews are always WEBVIEW_<package_name>, Chrome will always be CHROMIUM
// We can only determine it for Android and Chrome, for all other, including iOS, we return undefined
isAndroid && ((_a = capabilities == null ? void 0 : capabilities.browserName) == null ? void 0 : _a.toLowerCase()) === "chrome" ? "CHROMIUM" : void 0
);
}
function calculateAndroidPinchAndZoomSpeed({ browser, duration, scale }) {
var _a;
const deviceScreenSize = (((_a = browser.capabilities) == null ? void 0 : _a.deviceScreenSize) || "1080x2400").split("x").reduce((a, b) => a * b);
const baseDistance = Math.sqrt(deviceScreenSize);
const gestureDistance = Math.max(baseDistance * Math.abs(scale), baseDistance * 0.1);
const durationSeconds = duration / 1e3;
return Math.floor(gestureDistance / durationSeconds);
}
function validatePinchAndZoomOptions({ browser, gesture, options }) {
if (typeof options !== "undefined" && (typeof options !== "object" || Array.isArray(options))) {
throw new TypeError("Options must be an object");
}
const DEFAULT_SCALE = 0.5;
const DEFAULT_DURATION = browser.isIOS ? 1.5 : 1500;
const MIN_SCALE = 0;
const MAX_SCALE = 1;
const MIN_DURATION_MS = 500;
const MAX_DURATION_MS = 1e4;
const { scale: scaleOption, duration: durationOption } = options;
const scale = typeof scaleOption === "number" ? scaleOption >= MIN_SCALE && scaleOption <= MAX_SCALE ? scaleOption : (() => {
throw new Error("The 'scale' option must be a number between ".concat(MIN_SCALE, " and ").concat(MAX_SCALE));
})() : DEFAULT_SCALE;
const duration = typeof durationOption === "number" ? durationOption >= MIN_DURATION_MS && durationOption <= MAX_DURATION_MS ? browser.isIOS ? durationOption / 1e3 : durationOption : (() => {
throw new Error("The 'duration' option must be between ".concat(MIN_DURATION_MS, " and ").concat(MAX_DURATION_MS, " ms (").concat(MIN_DURATION_MS / 1e3, " and ").concat(MAX_DURATION_MS / 1e3, " seconds)"));
})() : DEFAULT_DURATION;
return {
duration,
scale: browser.isIOS && gesture === "zoom" ? scale * 10 : scale
};
}
// src/session/context.ts
var log4 = logger4("webdriverio:context");
var COMMANDS_REQUIRING_RESET = ["deleteSession", "refresh", "switchToParentFrame"];
function getContextManager(browser) {
return SessionManager.getSessionManager(browser, ContextManager);
}
var _browser3, _currentContext, _mobileContext, _isNativeContext, _getContextSupport, _currentWindowHandle, _onCommandResultBidiAndClassicListener, _onCommandListener2, _onCommandResultMobileListener, _navigationStartedListener, _ContextManager_instances, navigationStarted_fn, onCommandResultBidiAndClassic_fn, onCommand_fn2, onCommandResultMobile_fn;
var _ContextManager = class _ContextManager extends SessionManager {
constructor(browser) {
super(browser, _ContextManager.name);
__privateAdd(this, _ContextManager_instances);
__privateAdd(this, _browser3);
__privateAdd(this, _currentContext);
__privateAdd(this, _mobileContext);
__privateAdd(this, _isNativeContext);
__privateAdd(this, _getContextSupport, true);
__privateAdd(this, _currentWindowHandle);
__privateAdd(this, _onCommandResultBidiAndClassicListener);
__privateAdd(this, _onCommandListener2);
__privateAdd(this, _onCommandResultMobileListener);
__privateAdd(this, _navigationStartedListener);
__privateSet(this, _browser3, browser);
const capabilities = __privateGet(this, _browser3).capabilities;
__privateSet(this, _isNativeContext, getNativeContext({ capabilities, isMobile: __privateGet(this, _browser3).isMobile }));
__privateSet(this, _mobileContext, getMobileContext({
capabilities,
isAndroid: __privateGet(this, _browser3).isAndroid,
isNativeContext: __privateGet(this, _isNativeContext)
}));
__privateSet(this, _onCommandResultBidiAndClassicListener, __privateMethod(this, _ContextManager_instances, onCommandResultBidiAndClassic_fn).bind(this));
__privateSet(this, _onCommandListener2, __privateMethod(this, _ContextManager_instances, onCommand_fn2).bind(this));
__privateSet(this, _onCommandResultMobileListener, __privateMethod(this, _ContextManager_instances, onCommandResultMobile_fn).bind(this));
__privateSet(this, _navigationStartedListener, __privateMethod(this, _ContextManager_instances, navigationStarted_fn).bind(this));
__privateGet(this, _browser3).on("result", __privateGet(this, _onCommandResultBidiAndClassicListener));
if (!this.isEnabled() && !__privateGet(this, _browser3).isMobile) {
return;
}
__privateGet(this, _browser3).on("command", __privateGet(this, _onCommandListener2));
if (__privateGet(this, _browser3).isMobile) {
__privateGet(this, _browser3).on("result", __privateGet(this, _onCommandResultMobileListener));
} else {
__privateGet(this, _browser3).sessionSubscribe({
events: ["browsingContext.navigationStarted"]
});
__privateGet(this, _browser3).on("browsingContext.navigationStarted", __privateGet(this, _navigationStartedListener));
}
}
removeListeners() {
super.removeListeners();
__privateGet(this, _browser3).off("result", __privateGet(this, _onCommandResultBidiAndClassicListener));
__privateGet(this, _browser3).off("command", __privateGet(this, _onCommandListener2));
if (__privateGet(this, _browser3).isMobile) {
__privateGet(this, _browser3).off("result", __privateGet(this, _onCommandResultMobileListener));
} else {
__privateGet(this, _browser3).off("browsingContext.navigationStarted", __privateGet(this, _navigationStartedListener));
}
}
/**
* set context at the start of the session
*/
async initialize() {
if (environment.value.variables.WDIO_UNIT_TESTS) {
return "";
}
if (__privateGet(this, _browser3).isMobile && !__privateGet(this, _isNativeContext) && !__privateGet(this, _mobileContext) && __privateGet(this, _getContextSupport)) {
const context = await __privateGet(this, _browser3).getContext().catch((err) => {
log4.warn(
"Error getting context: ".concat(err, "\n\n") + "WebDriver capabilities: ".concat(JSON.stringify(__privateGet(this, _browser3).capabilities), "\n") + "Requested WebDriver capabilities: ".concat(JSON.stringify(__privateGet(this, _browser3).requestedCapabilities))
);
if (err.message.includes("Request failed with status code 405")) {
__privateSet(this, _getContextSupport, false);
}
return void 0;
});
__privateSet(this, _mobileContext, typeof context === "string" ? context : typeof context === "object" ? context.id : void 0);
}
const windowHandle = __privateGet(this, _mobileContext) || await __privateGet(this, _browser3).getWindowHandle();
this.setCurrentContext(windowHandle);
return windowHandle;
}
setCurrentContext(context) {
__privateSet(this, _currentContext, context);
if (__privateGet(this, _browser3).isMobile) {
__privateSet(this, _isNativeContext, context ? context === "NATIVE_APP" : __privateGet(this, _isNativeContext));
__privateSet(this, _mobileContext, context || void 0);
}
}
async getCurrentContext() {
if (!__privateGet(this, _currentContext)) {
return this.initialize();
}
return __privateGet(this, _currentContext);
}
/**
* Sets the cached current window handle value.
* @param handle current window handle to set
*/
setCurrentWindowHandle(handle) {
__privateSet(this, _currentWindowHandle, handle);
}
/**
* Returns the cached window handle.
*
* @returns the current window handle, or undefined if the current window is closed.
*/
getCurrentWindowHandle() {
return __privateGet(this, _currentWindowHandle);
}
get isNativeContext() {
return __privateGet(this, _isNativeContext);
}
get mobileContext() {
return __privateGet(this, _mobileContext);
}
/**
* Get the flat context tree for the current session
* @returns a flat list of all contexts in the current session
*/
async getFlatContextTree() {
const tree = await __privateGet(this, _browser3).browsingContextGetTree({});
const mapContext = (context) => [
context.context,
...(context.children || []).map(mapContext).flat(Infinity)
];
const allContexts = tree.contexts.map(mapContext).flat(Infinity).reduce((acc, ctx) => {
const context = this.findContext(ctx, tree.contexts, "byContextId");
acc[ctx] = context;
return acc;
}, {});
return allContexts;
}
/**
* Find the parent context of a given context id
* @param contextId the context id you want to find the parent of
* @param contexts the list of contexts to search through returned from `browsingContextGetTree`
* @returns the parent context of the context with the given id
*/
findParentContext(contextId, contexts) {
var _a;
for (const context of contexts) {
if ((_a = context.children) == null ? void 0 : _a.some((child) => child.context === contextId)) {
return context;
}
if (Array.isArray(context.children) && context.children.length > 0) {
const result = this.findParentContext(contextId, context.children);
if (result) {
return result;
}
}
}
return void 0;
}
/**
* Find a context by URL or ID
* @param urlOrId The URL or ID of the context to find
* @param contexts The list of contexts to search through returned from `browsingContextGetTree`
* @param matcherType The type of matcher to use to find the context
* @returns The context with the given URL or ID
*/
findContext(urlOrId, contexts, matcherType) {
const matcher = {
byUrl,
byUrlContaining,
byContextId
}[matcherType];
for (const context of contexts || []) {
if (matcher(context, urlOrId)) {
return context;
}
if (Array.isArray(context.children) && context.children.length > 0) {
const result = this.findContext(urlOrId, context.children, matcherType);
if (result) {
return result;
}
}
}
return void 0;
}
};
_browser3 = new WeakMap();
_currentContext = new WeakMap();
_mobileContext = new WeakMap();
_isNativeContext = new WeakMap();
_getContextSupport = new WeakMap();
_currentWindowHandle = new WeakMap();
_onCommandResultBidiAndClassicListener = new WeakMap();
_onCommandListener2 = new WeakMap();
_onCommandResultMobileListener = new WeakMap();
_navigationStartedListener = new WeakMap();
_ContextManager_instances = new WeakSet();
navigationStarted_fn = async function(nav) {
if (!__privateGet(this, _currentContext) || nav.context === __privateGet(this, _currentContext)) {
return;
}
const { contexts } = await __privateGet(this, _browser3).browsingContextGetTree({});
const hasContext = this.findContext(__privateGet(this, _currentContext), contexts, "byContextId");
const newContext = contexts.find((context) => context.context === nav.context);
if (!hasContext && newContext) {
this.setCurrentContext(newContext.context);
await __privateGet(this, _browser3).switchToWindow(__privateGet(this, _currentContext));
return;
}
};
onCommandResultBidiAndClassic_fn = function(event) {
if (event.command === "closeWindow") {
__privateSet(this, _currentWindowHandle, void 0);
const windowHandles = event.result.value || [];
if (windowHandles.length === 0) {
throw new Error("All window handles were removed, causing WebdriverIO to close the session.");
}
__privateSet(this, _currentContext, windowHandles[0]);
return __privateGet(this, _browser3).switchToWindow(__privateGet(this, _currentContext));
}
if (event.command === "getWindowHandle") {
const windowHandle = event.result.value || void 0;
__privateSet(this, _currentWindowHandle, windowHandle);
}
if (event.command === "switchToWindow") {
const err = event.result.error || void 0;
if (!err) {
const windowHandle = event.body.handle || void 0;
__privateSet(this, _currentWindowHandle, windowHandle);
}
}
};
onCommand_fn2 = function(event) {
if (event.command === "switchToParentFrame") {
if (!__privateGet(this, _currentContext)) {
return;
}
return __privateGet(this, _browser3).browsingContextGetTree({}).then(({ contexts }) => {
const parentContext = this.findParentContext(__privateGet(this, _currentContext), contexts);
if (!parentContext) {
return;
}
this.setCurrentContext(parentContext.context);
});
}
if (event.command === "switchToWindow") {
this.setCurrentContext(event.body.handle);
}
if (COMMANDS_REQUIRING_RESET.includes(event.command)) {
__privateSet(this, _currentContext, void 0);
}
if (__privateGet(this, _browser3).isMobile && event.command === "switchAppiumContext") {
__privateSet(this, _mobileContext, event.body.name);
}
};
onCommandResultMobile_fn = function(event) {
if (event.command === "getAppiumContext") {
this.setCurrentContext(event.result.value);
}
if (event.command === "switchAppiumContext" && event.result.value === null && __privateGet(this, _mobileContext)) {
this.setCurrentContext(__privateGet(this, _mobileContext));
}
};
var ContextManager = _ContextManager;
function byUrl(context, url2) {
return context.url === url2;
}
function byUrlContaining(context, url2) {
return context.url.includes(url2);
}
function byContextId(context, contextId) {
return context.context === contextId;
}
// src/scripts/polyfill.ts
var polyfillFn = function webdriverioPolyfill() {
var __defProp2 = Object.defineProperty;
var __name = function(target, _value) {
return __defProp2(target, "name", { value: _value, configurable: true });
};
var __globalThis = typeof globalThis === "object" && globalThis || typeof window === "object" && window;
__globalThis.__name = __name;
};
// src/commands/browser/execute.ts
async function execute(script, ...args) {
if (typeof script !== "string" && typeof script !== "function") {
throw new Error("number or type of arguments don't agree with execute protocol command");
}
if (this.isBidi && !this.isMultiremote) {
const browser = getBrowserObject3(this);
const contextManager = getContextManager(browser);
const context = await contextManager.getCurrentContext();
const userScript = typeof script === "string" ? new Function(script) : script;
const functionDeclaration = createFunctionDeclarationFromString(userScript);
const params = {
functionDeclaration,
awaitPromise: true,
arguments: args.map((arg) => LocalValue.getArgument(arg)),
target: {
context
}
};
const result = await browser.scriptCallFunction(params);
return parseScriptResult(params, result);
}
if (typeof script === "function") {
script = "\n ".concat(polyfillFn, "\n webdriverioPolyfill();\n return (").concat(script, ").apply(null, arguments)\n ");
}
return this.executeScript(script, verifyArgsAndStripIfElement(args));
}
// src/commands/browser/executeAsync.ts
import { getBrowserObject as getBrowserObject4 } from "@wdio/utils";
async function executeAsync(script, ...args) {
if (typeof script !== "string" && typeof script !== "function") {
throw new Error("number or type of arguments don't agree with execute protocol command");
}
if (this.isBidi && !this.isMultiremote) {
const browser = getBrowserObject4(this);
const contextManager = getContextManager(browser);
const context = await contextManager.getCurrentContext();
const userScript = typeof script === "string" ? new Function(script) : script;
const functionDeclaration = new Function("\n const args = Array.from(arguments)\n return new Promise(async (resolve, reject) => {\n const cb = (result) => resolve(result)\n try {\n await (".concat(userScript.toString(), ").apply(this, [...args, cb])\n } catch (err) {\n return reject(err)\n }\n })\n ")).toString();
const params = {
functionDeclaration,
awaitPromise: true,
arguments: args.map((arg) => LocalValue.getArgument(arg)),
target: {
context
}
};
const result = await browser.scriptCallFunction(params);
return parseScriptResult(params, result);
}
if (typeof script === "function") {
script = "\n ".concat(polyfillFn, "\n webdriverioPolyfill()\n return (").concat(script, ").apply(null, arguments)\n ");
}
return this.executeAsyncScript(script, verifyArgsAndStripIfElement(args));
}
// src/commands/browser/getCookies.ts
import logger5 from "@wdio/logger";
var log5 = logger5("webdriverio");
async function getCookies(filter) {
const usesMultipleFilter = Array.isArray(filter) && filter.length > 1;
if (!this.isBidi || usesMultipleFilter) {
return getCookiesClassic.call(this, filter);
}
const cookieFilter = getCookieFilter(filter);
let url2;
try {
url2 = new URL(await this.getUrl());
if (url2.origin === "null") {
return getCookiesClassic.call(this, filter);
}
} catch {
return getCookiesClassic.call(this, filter);
}
const params = {
partition: {
type: "storageKey",
sourceOrigin: url2.origin
}
};
if (typeof cookieFilter !== "undefined") {
params.filter = cookieFilter;
}
try {
const { cookies } = await this.storageGetCookies(params);
if (cookies.length === 0) {
log5.debug("BiDi getCookies returned empty, falling back to classic");
return getCookiesClassic.call(this, filter);
}
return cookies.map((cookie) => ({
...cookie,
value: cookie.value.type === "base64" ? Buffer.from(cookie.value.value, "base64").toString("utf-8") : cookie.value.value
}));
} catch (err) {
log5.warn("BiDi getCookies failed, falling back to classic: ".concat(err.message));
return getCookiesClassic.call(this, filter);
}
}
async function getCookiesClassic(names) {
if (!names) {
return this.getAllCookies();
}
const usesMultipleFilter = Array.isArray(names) && names.length > 1;
if (usesMultipleFilter) {
log5.warn(
"Passing a string array as filter for `getCookies` is deprecated and its support will be removed in an upcoming version of WebdriverIO!"
);
const allCookies2 = await this.getAllCookies();
return allCookies2.filter((cookie) => names.includes(cookie.name));
}
const filter = getCookieFilter(names);
const allCookies = await this.getAllCookies();
return allCookies.filter((cookie) => {
var _a;
return !filter || cookie.name && filter.name === cookie.name || cookie.value && ((_a = filter.value) == null ? void 0 : _a.value) === cookie.value || cookie.path && filter.path === cookie.path || cookie.domain && filter.domain === cookie.domain || cookie.sameSite && filter.sameSite === cookie.sameSite || cookie.expiry && filter.expiry === cookie.expiry || typeof cookie.httpOnly === "boolean" && filter.httpOnly === cookie.httpOnly || typeof cookie.secure === "boolean" && filter.secure === cookie.secure;
});
}
function getCookieFilter(names) {
if (!names) {
return;
}
if (Array.isArray(names) && names.length > 1) {
throw new Error("Multiple cookie name filters are not supported");
}
return (Array.isArray(names) ? names : [names]).map((filter) => {
if (typeof filter === "string") {
log5.warn("Passing string values into `getCookie` is deprecated and its support will be removed in an upcoming version of WebdriverIO!");
return { name: filter };
}
return filter;
})[0];
}
// src/commands/browser/getPuppeteer.ts
import logger6 from "@wdio/logger";
import { userImport } from "@wdio/utils";
var log6 = logger6("webdriverio");
var DEBUG_PIPE_FLAG = "remote-debugging-pipe";
async function getPuppeteer() {
var _a, _b, _c, _d, _e, _f;
if (globalThis.wdio) {
throw new Error("Puppeteer is not supported in browser runner");
}
const puppeteer = await userImport("puppeteer-core");
if (!puppeteer) {
throw new Error(
'You need to install "puppeteer-core" package as a dependency in order to use the "getPuppeteer" method'
);
}
if ((_a = this.puppeteer) == null ? void 0 : _a.connected) {
log6.debug("Reusing existing puppeteer session");
return this.puppeteer;
}
const { headers } = this.options;
const cdpEndpoint = this.capabilities["se:cdp"];
if (cdpEndpoint) {
this.puppeteer = await puppeteer.connect({
browserWSEndpoint: cdpEndpoint,
defaultViewport: null,
headers
});
return this.puppeteer;
}
const requestedCapabilities = ((_b = this.requestedCapabilities) == null ? void 0 : _b.alwaysMatch) || this.requestedCapabilities;
const isAerokubeSession = requestedCapabilities["selenoid:options"] || requestedCapabilities["moon:options"];
if (isAerokubeSession) {
const { hostname, port } = this.options;
this.puppeteer = await puppeteer.connect({
browserWSEndpoint: "ws://".concat(hostname, ":").concat(port, "/devtools/").concat(this.sessionId),
defaultViewport: null,
headers
});
return this.puppeteer;
}
const chromiumOptions = this.capabilities["goog:chromeOptions"] || this.capabilities["ms:edgeOptions"];
if (chromiumOptions && chromiumOptions.debuggerAddress) {
this.puppeteer = await puppeteer.connect({
browserURL: "http://".concat(chromiumOptions.debuggerAddress.replace("localhost", "0.0.0.0")),
defaultViewport: null
});
return this.puppeteer;
} else if (
/**
* if --remote-debugging-pipe is set as Chrome flag, we can't attach to the session
* as there won't be a `debuggerAddress` available in the capabilities. Provide this
* better error message to the user.
*/
chromiumOptions && (((_c = chromiumOptions.args) == null ? void 0 : _c.includes(DEBUG_PIPE_FLAG)) || ((_d = chromiumOptions.args) == null ? void 0 : _d.includes("--".concat(DEBUG_PIPE_FLAG))))
) {
throw new Error("Cannot attach to Chrome Devtools session if --".concat(DEBUG_PIPE_FLAG, " is set as Chrome flag."));
}
if (((_e = this.capabilities.browserName) == null ? void 0 : _e.toLowerCase()) === "firefox") {
if (!this.capabilities.browserVersion) {
throw new Error('Can\'t find "browserVersion" in capabilities');
}
const majorVersion = parseInt(this.capabilities.browserVersion.split(".").shift() || "", 10);
if (majorVersion >= 79) {
const reqCaps = this.requestedCapabilities.alwaysMatch || this.requestedCapabilities;
let browserURL;
if (this.capabilities["moz:debuggerAddress"]) {
browserURL = this.capabilities["moz:debuggerAddress"];
} else {
const ffOptions = this.capabilities["moz:firefoxOptions"];
const ffArgs = ((_f = reqCaps["moz:firefoxOptions"]) == null ? void 0 : _f.args) || [];
const rdPort = ffOptions && ffOptions.debuggerAddress ? ffOptions.debuggerAddress : ffArgs[ffArgs.findIndex((arg) => arg === FF_REMOTE_DEBUG_ARG) + 1];
if (rdPort) {
browserURL = "http://localhost:".concat(rdPort);
}
}
if (!browserURL) {
throw new Error(
'Could\'t find a websocket url within returned capabilities to connect to! Make sure you have "moz:debuggerAddress" set to `true` in your Firefox capabilities'
);
}
this.puppeteer = await puppeteer.connect({
browserURL,
defaultViewport: null
});
return this.puppeteer;
}
}
throw new Error(
"Using DevTools capabilities is not supported for this session. This feature is only supported for local testing on Chrome, Firefox and Chromium Edge."
);
}
// src/commands/browser/getWindowSize.ts
import { getBrowserObject as getBrowserObject5 } from "@wdio/utils";
async function getWindowSize() {
const browser = getBrowserObject5(this);
const { width, height } = await browser.getWindowRect();
return { width, height };
}
// src/commands/browser/keys.ts
async function keys(value) {
let keySequence = [];
if (typeof value === "string") {
keySequence = checkUnicode(value);
} else if (Array.isArray(value)) {
const charArray = value;
for (const charSet of charArray) {
keySequence = keySequence.concat(checkUnicode(charSet));
}
} else {
throw new Error('"keys" command requires a string or array of strings as parameter');
}
const keyAction = this.action("key");
keySequence.forEach((value2) => keyAction.down(value2));
if (!this.isIOS) {
keyAction.pause(10);
}
keySequence.forEach((value2) => keyAction.up(value2));
return keyAction.perform(true);
}
// src/commands/browser/mock.ts
import { getBrowserObject as getBrowserObject6 } from "@wdio/utils";
// src/utils/interception/index.ts
import logger7 from "@wdio/logger";
import { URLPattern } from "urlpattern-polyfill";
// src/utils/Timer.ts
var TIMEOUT_ERROR = "timeout";
var NOOP = () => {
};
var _retPromise;
var Timer = class {
constructor(_delay, _timeout, _fn, _leading = false, _signal) {
this._delay = _delay;
this._timeout = _timeout;
this._fn = _fn;
this._leading = _leading;
this._signal = _signal;
__privateAdd(this, _retPromise);
__publicField(this, "_conditionExecutedCnt", 0);
__publicField(this, "_resolve", NOOP);
__publicField(this, "_reject", NOOP);
__publicField(this, "_startTime");
__publicField(this, "_ticks", 0);
__publicField(this, "_timeoutId");
__publicField(this, "_mainTimeoutId");
__publicField(this, "_lastError");
__privateSet(this, _retPromise, new Promise((resolve, reject) => {
this._resolve = resolve;
this._reject = reject;
}));
this._start();
}
then(thennable, catchable) {
return __privateGet(this, _retPromise).then(thennable, catchable);
}
catch(catchable) {
return __privateGet(this, _retPromise).catch(catchable);
}
_start() {
this._startTime = Date.now();
if (this._leading) {
this._tick();
} else {
this._timeoutId = setTimeout(this._tick.bind(this), this._delay);
}
if (this._wasConditionExecuted()) {
return;
}
this._mainTimeoutId = setTimeout(() => {
if (!this._wasConditionExecuted()) {
return;
}
const reason = this._lastError || new Error(TIMEOUT_ERROR);
this._reject(reason);
this._stop();
}, this._timeout);
}
_stop() {
if (this._timeoutId) {
clearTimeout(this._timeoutId);
}
delete this._timeoutId;
}
_stopMain() {
if (this._mainTimeoutId) {
clearTimeout(this._mainTimeoutId);
}
}
_tick() {
try {
const result = this._fn();
if (!result) {
return this._checkCondition(new Error(TIMEOUT_ERROR));
}
if (typeof result.then !== "function") {
return this._checkCondition(void 0, result);
}
result.then(
(res) => this._checkCondition(void 0, res),
(err) => this._checkCondition(err)
);
} catch (err) {
return this._checkCondition(err);
}
}
_checkCondition(err, res) {
var _a;
this._lastError = err;
if ((_a = this._signal) == null ? void 0 : _a.aborted) {
this._reject(this._lastError || new Error("Aborted"));
this._stop();
this._stopMain();
return;
}
++this._conditionExecutedCnt;
if (res) {
this._resolve(res);
this._stop();
this._stopMain();
return;
}
const diff = Date.now() - (this._startTime || 0) - this._ticks++ * this._delay;
const delay = Math.max(0, this._delay - diff);
this._stop();
if (this._hasTime(delay)) {
this._timeoutId = setTimeout(this._tick.bind(this), delay);
} else {
this._stopMain();
const reason = this._lastError || new Error(TIMEOUT_ERROR);
this._reject(reason);
}
}
_hasTime(delay) {
return Date.now() - (this._startTime || 0) + delay <= this._timeout;
}
_wasConditionExecuted() {
return this._conditionExecutedCnt > 0;
}
};
_retPromise = new WeakMap();
var Timer_default = Timer;
// src/utils/interception/utils.ts
function parseOverwrite(overwrite, request) {
const result = {};
if ("body" in overwrite && overwrite.body) {
const bodyOverwrite = typeof overwrite.body === "function" ? overwrite.body(request) : overwrite.body;
result.body = (bodyOverwrite == null ? void 0 : bodyOverwrite.type) === "string" || (bodyOverwrite == null ? void 0 : bodyOverwrite.type) === "base64" ? bodyOverwrite : typeof bodyOverwrite === "string" ? { type: "string", value: bodyOverwrite } : { type: "base64", value: btoa(JSON.stringify(bodyOverwrite || "")) };
}
if ("headers" in overwrite) {
const headersOverwrite = typeof overwrite.headers === "function" ? overwrite.headers(request) : overwrite.headers;
result.headers = Object.entries(headersOverwrite || {}).map(([name, value]) => ({
name,
value: { type: "string", value }
}));
}
if ("cookies" in overwrite && overwrite.cookies) {
const cookieOverwrite = typeof overwrite.cookies === "function" ? overwrite.cookies(request) || [] : overwrite.cookies;
result.cookies = cookieOverwrite.map((cookie) => {
var _a;
return {
name: cookie.name,
value: {
type: "string",
value: cookie.value
},
domain: cookie.domain,
path: cookie.path,
expires: cookie.expiry,
httpOnly: cookie.httpOnly,
secure: cookie.secure,
sameSite: (_a = cookie.sameSite) == null ? void 0 : _a.toLowerCase()
};
});
}
if ("statusCode" in overwrite && overwrite.statusCode) {
const statusCodeOverwrite = typeof overwrite.statusCode === "function" ? overwrite.statusCode(request) : overwrite.statusCode;
result.statusCode = statusCodeOverwrite;
}
if ("method" in overwrite) {
result.method = typeof overwrite.method === "function" ? overwrite.method(request) : overwrite.method;
}
if ("url" in overwrite) {
result.url = typeof overwrite.url === "function" ? overwrite.url(request) : overwrite.url;
}
return result;
}
function getPatternParam(pattern, key) {
const value = pattern[key];
if (value === "*" || value.includes("*")) {
return;
}
if (key === "port" && pattern.port === "") {
return pattern.protocol === "https" ? "443" : "80";
}
return value;
}
// src/utils/interception/index.ts
var log7 = logger7("WebDriverInterception");
var hasSubscribedToEvents = false;
var _pattern, _mockId, _filterOptions, _browser4, _eventHandler, _restored, _requestOverwrites, _respondOverwrites, _calls, _responseBodies, _WebDriverInterception_instances, emit_fn, addEventHandler_fn, handleBeforeRequestSent_fn, handleResponseStarted_fn, handleNetworkProvideResponseError_fn, isRequestMatching_fn, matchesFilterOptions_fn, _setOverwrite, ensureNotRestored_fn;
var _WebDriverInterception = class _WebDriverInterception {
constructor(pattern, mockId, filterOptions, browser) {
__privateAdd(this, _WebDriverInterception_instances);
__privateAdd(this, _pattern);
__privateAdd(this, _mockId);
__privateAdd(this, _filterOptions);
__privateAdd(this, _browser4);
__privateAdd(this, _eventHandler, /* @__PURE__ */ new Map());
__privateAdd(this, _restored, false);
__privateAdd(this, _requestOverwrites, []);
__privateAdd(this, _respondOverwrites, []);
__privateAdd(this, _calls, []);
__privateAdd(this, _responseBodies, /* @__PURE__ */ new Map());
__privateAdd(this, _setOverwrite, (overwriteProp, { overwrite, abort, once }) => {
return once ? [
...overwriteProp.filter(({ once: once2 }) => once2),
{ overwrite, abort, once }
] : [{ overwrite, abort }];
});
__privateSet(this, _pattern, pattern);
__privateSet(this, _mockId, mockId);
__privateSet(this, _filterOptions, filterOptions);
__privateSet(this, _browser4, browser);
browser.on("network.beforeRequestSent", __privateMethod(this, _WebDriverInterception_instances, handleBeforeRequestSent_fn).bind(this));
browser.on("network.responseStarted", __privateMethod(this, _WebDriverInterception_instances, handleResponseStarted_fn).bind(this));
}
static async initiate(url2, filterOptions, browser) {
const pattern = parseUrlPattern(url2);
if (!hasSubscribedToEvents) {
await browser.sessionSubscribe({
events: [
"network.beforeRequestSent",
"network.responseStarted"
]
});
log7.info("subscribed to network events");
hasSubscribedToEvents = true;
}
const interception = await browser.networkAddIntercept({
phases: ["beforeRequestSent", "responseStarted"],
urlPatterns: [{
type: "pattern",
protocol: getPatternParam(pattern, "protocol"),
hostname: getPatternParam(pattern, "hostname"),
pathname: getPatternParam(pattern, "pathname"),
port: getPatternParam(pattern, "port"),
search: getPatternParam(pattern, "search")
}]
});
return new _WebDriverInterception(pattern, interception.intercept, filterOptions, browser);
}
/**
* Get the raw binary data for a mock response by request ID
* @param {string} requestId The ID of the request to retrieve the binary response for
* @returns {Buffer | null} The binary data as a Buffer, or null if no matching binary response is found
*/
getBinaryResponse(requestId) {
const body = __privateGet(this, _responseBodies).get(requestId);
if ((body == null ? void 0 : body.type) !== "base64") {
return null;
}
if (/[^A-Za-z0-9+/=\s]/.test(body.value)) {
log7.warn("Invalid base64 data for request ".concat(requestId));
return null;
}
return Buffer.from(body.value, "base64");
}
/**
* Simulate a responseStarted event for testing purposes
* @param request NetworkResponseCompletedParameters to simulate
*/
simulateResponseStarted(request) {
try {
__privateMethod(this, _WebDriverInterception_instances, handleResponseStarted_fn).call(this, request);
} catch (e) {
console.log("DEBUG: Error in simulateResponseStarted:", e);
throw e;
}
}
debugResponseBodies() {
return __privateGet(this, _responseBodies);
}
/**
* allows access to all requests made with given pattern
*/
get calls() {
return __privateGet(this, _calls);
}
/**
* Resets all information stored in the `mock.calls` set.
*/
clear() {
__privateSet(this, _calls, []);
__privateGet(this, _responseBodies).clear();
return this;
}
/**
* Does what `mock.clear()` does and makes removes custom request overrides
* and response overwrites
*/
reset() {
this.clear();
__privateSet(this, _respondOverwrites, []);
__privateSet(this, _requestOverwrites, []);
return this;
}
/**
* Does everything that `mock.reset()` does, and also
* removes any mocked return values or implementations.
* Restored mock does not emit events and could not mock responses
*/
async restore() {
this.reset();
__privateSet(this, _respondOverwrites, []);
__privateSet(this, _restored, true);
const handle = await __privateGet(this, _browser4).getWindowHandle();
log7.trace("Restoring mock for ".concat(handle));
SESSION_MOCKS[handle].delete(this);
if (__privateGet(this, _mockId)) {
await __privateGet(this, _browser4).networkRemoveIntercept({ intercept: __privateGet(this, _mockId) });
}
return this;
}
/**
* Always use request modification for the next request done by the browser.
* @param payload payload to overwrite the request
* @param once apply overwrite only once for the next request
* @returns this instance to chain commands
*/
request(overwrite, once) {
__privateMethod(this, _WebDriverInterception_instances, ensureNotRestored_fn).call(this);
__privateSet(this, _requestOverwrites, __privateGet(this, _setOverwrite).call(this, __privateGet(this, _requestOverwrites), { overwrite, once }));
return this;
}
/**
* alias for `mock.request(…, true)`
*/
requestOnce(payload) {
return this.request(payload, true);
}
/**
* Always respond with same overwrite
* @param {*} payload payload to overwrite the response
* @param {*} params additional respond parameters to overwrite
* @param {boolean} once apply overwrite only once for the next request
* @returns this instance to chain commands
*/
respond(payload, params = {}, once) {
__privateMethod(this, _WebDriverInterception_instances, ensureNotRestored_fn).call(this);
const body = Buffer.isBuffer(payload) ? { type: "base64", value: payload.toString("base64") } : { type: "string", value: typeof payload === "string" ? payload : JSON.stringify(payload) };
const overwrite = { body, ...params };
__privateSet(this, _respondOverwrites, __privateGet(this, _setOverwrite).call(this, __privateGet(this, _respondOverwrites), { overwrite, once }));
return this;
}
/**
* alias for `mock.respond(…, true)`
*/
respondOnce(payload, params = {}) {
return this.respond(payload, params, true);
}
/**
* Abort the request with an error code
* @param {string} errorReason error code of the response
* @param {boolean} once if request should be aborted only once for the next request
*/
abort(once) {
__privateMethod(this, _WebDriverInterception_instances, ensureNotRestored_fn).call(this);
__privateSet(this, _requestOverwrites, __privateGet(this, _setOverwrite).call(this, __privateGet(this, _requestOverwrites), { abort: true, once }));
return this;
}
/**
* alias for `mock.abort(true)`
*/
abortOnce() {
return this.abort(true);
}
/**
* Redirect request to another URL
* @param {string} redirectUrl URL to redirect to
* @param {boolean} sticky if request should be redirected for all following requests
*/
redirect(redirectUrl, once) {
__privateMethod(this, _WebDriverInterception_instances, ensureNotRestored_fn).call(this);
const requestWith = { url: redirectUrl };
this.request(requestWith, once);
return this;
}
/**
* alias for `mock.redirect(…, true)`
*/
redirectOnce(redirectUrl) {
return this.redirect(redirectUrl, true);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
on(event, callback) {
__privateMethod(this, _WebDriverInterception_instances, addEventHandler_fn).call(this, event, callback);
return this;
}
waitForResponse({
timeout = __privateGet(this, _browser4).options.waitforTimeout,
interval = __privateGet(this, _browser4).options.waitforInterval,
timeoutMsg
} = {}) {
if (typeof timeout !== "number") {
timeout = __privateGet(this, _browser4).options.waitforTimeout;
}
if (typeof interval !== "number") {
interval = __privateGet(this, _browser4).options.waitforInterval;
}
const fn = async () => this.calls && (await this.calls).length > 0;
const timer = new Timer_default(interval, timeout, fn, true);
return __privateGet(this, _browser4).call(() => timer.catch((e) => {
if (e.message === "timeout") {
if (typeof timeoutMsg === "string") {
throw new Error(timeoutMsg);
}
throw new Error("waitForResponse timed out after ".concat(timeout, "ms"));
}
throw new Error("waitForResponse failed with the following reason: ".concat(e && e.message || e));
}));
}
};
_pattern = new WeakMap();
_mockId = new WeakMap();
_filterOptions = new WeakMap();
_browser4 = new WeakMap();
_eventHandler = new WeakMap();
_restored = new WeakMap();
_requestOverwrites = new WeakMap();
_respondOverwrites = new WeakMap();
_calls = new WeakMap();
_responseBodies = new WeakMap();
_WebDriverInterception_instances = new WeakSet();
emit_fn = function(event, args) {
if (!__privateGet(this, _eventHandler).has(event)) {
return;
}
const handlers = __privateGet(this, _eventHandler).get(event) || [];
for (const handler of handlers) {
handler(args);
}
};
addEventHandler_fn = function(event, handler) {
if (!__privateGet(this, _eventHandler).has(event)) {
__privateGet(this, _eventHandler).set(event, []);
}
const handlers = __privateGet(this, _eventHandler).get(event);
handlers == null ? void 0 : handlers.push(handler);
};
handleBeforeRequestSent_fn = function(request) {
var _a;
if (!__privateMethod(this, _WebDriverInterception_instances, isRequestMatching_fn).call(this, request)) {
if ((_a = request.intercepts) == null ? void 0 : _a.includes(__privateGet(this, _mockId))) {
return __privateGet(this, _browser4).networkContinueRequest({
request: request.request.request
});
}
return;
}
if (!__privateMethod(this, _WebDriverInterception_instances, matchesFilterOptions_fn).call(this, request)) {
return __privateGet(this, _browser4).networkContinueRequest({
request: request.request.request
});
}
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "request", request);
const hasRequestOverwrites = __privateGet(this, _requestOverwrites).length > 0;
if (hasRequestOverwrites) {
const { overwrite, abort } = __privateGet(this, _requestOverwrites)[0].once ? __privateGet(this, _requestOverwrites).shift() || {} : __privateGet(this, _requestOverwrites)[0];
if (abort) {
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "fail", request.request.request);
return __privateGet(this, _browser4).networkFailRequest({ request: request.request.request });
}
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "overwrite", request);
return __privateGet(this, _browser4).networkContinueRequest({
request: request.request.request,
...overwrite ? parseOverwrite(overwrite, request) : {}
});
}
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "continue", request.request.request);
return __privateGet(this, _browser4).networkContinueRequest({
request: request.request.request
});
};
handleResponseStarted_fn = function(request) {
var _a;
if (!__privateMethod(this, _WebDriverInterception_instances, isRequestMatching_fn).call(this, request)) {
if ((_a = request.intercepts) == null ? void 0 : _a.includes(__privateGet(this, _mockId))) {
return __privateGet(this, _browser4).networkProvideResponse({
request: request.request.request
}).catch(__privateMethod(this, _WebDriverInterception_instances, handleNetworkProvideResponseError_fn));
}
return;
}
if (!__privateMethod(this, _WebDriverInterception_instances, matchesFilterOptions_fn).call(this, request)) {
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "continue", request.request.request);
return __privateGet(this, _browser4).networkProvideResponse({
request: request.request.request
}).catch(__privateMethod(this, _WebDriverInterception_instances, handleNetworkProvideResponseError_fn));
}
__privateGet(this, _calls).push(request);
if (__privateGet(this, _respondOverwrites).length === 0 || !__privateGet(this, _respondOverwrites)[0].overwrite) {
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "continue", request.request.request);
return __privateGet(this, _browser4).networkProvideResponse({
request: request.request.request
}).catch(__privateMethod(this, _WebDriverInterception_instances, handleNetworkProvideResponseError_fn));
}
const { overwrite } = __privateGet(this, _respondOverwrites)[0].once ? __privateGet(this, _respondOverwrites).shift() || {} : __privateGet(this, _respondOverwrites)[0];
if (overwrite) {
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "overwrite", request);
const responseData = parseOverwrite(overwrite, request);
if (responseData.body) {
__privateGet(this, _responseBodies).set(request.request.request, responseData.body);
}
return __privateGet(this, _browser4).networkProvideResponse({
request: request.request.request,
...responseData
}).catch(__privateMethod(this, _WebDriverInterception_instances, handleNetworkProvideResponseError_fn));
}
__privateMethod(this, _WebDriverInterception_instances, emit_fn).call(this, "continue", request.request.request);
return __privateGet(this, _browser4).networkProvideResponse({
request: request.request.request
}).catch(__privateMethod(this, _WebDriverInterception_instances, handleNetworkProvideResponseError_fn));
};
/**
* It appears that the networkProvideResponse method may throw an "no such request" error even though the request
* is marked as "blocked", in these cases we can safely ignore the error.
* @param err Bidi message error
*/
handleNetworkProvideResponseError_fn = function(err) {
if (err.message.endsWith("no such request")) {
return;
}
throw err;
};
isRequestMatching_fn = function(request) {
const matches = __privateGet(this, _pattern) && __privateGet(this, _pattern).test(request.request.url);
return request.isBlocked && matches;
};
matchesFilterOptions_fn = function(request) {
let isRequestMatching = true;
if (isRequestMatching && __privateGet(this, _filterOptions).method) {
isRequestMatching = typeof __privateGet(this, _filterOptions).method === "function" ? __privateGet(this, _filterOptions).method(request.request.method) : __privateGet(this, _filterOptions).method.toLowerCase() === request.request.method.toLowerCase();
}
if (isRequestMatching && __privateGet(this, _filterOptions).requestHeaders) {
isRequestMatching = typeof __privateGet(this, _filterOptions).requestHeaders === "function" ? __privateGet(this, _filterOptions).requestHeaders(request.request.headers.reduce((acc, { name, value }) => {
acc[name] = value.type === "string" ? value.value : Buffer.from(value.value, "base64").toString();
return acc;
}, {})) : Object.entries(__privateGet(this, _filterOptions).requestHeaders).every(([key, value]) => {
const header = request.request.headers.find(({ name }) => name === key);
if (!header) {
return false;
}
return header.value.type === "string" ? header.value.value === value : Buffer.from(header.value.value, "base64").toString() === value;
});
}
if (isRequestMatching && __privateGet(this, _filterOptions).responseHeaders && "response" in request) {
isRequestMatching = typeof __privateGet(this, _filterOptions).responseHeaders === "function" ? __privateGet(this, _filterOptions).responseHeaders(request.response.headers.reduce((acc, { name, value }) => {
acc[name] = value.type === "string" ? value.value : Buffer.from(value.value, "base64").toString();
return acc;
}, {})) : Object.entries(__privateGet(this, _filterOptions).responseHeaders).every(([key, value]) => {
const header = request.response.headers.find(({ name }) => name === key);
if (!header) {
return false;
}
return header.value.type === "string" ? header.value.value === value : Buffer.from(header.value.value, "base64").toString() === value;
});
}
if (isRequestMatching && __privateGet(this, _filterOptions).statusCode && "response" in request) {
isRequestMatching = typeof __privateGet(this, _filterOptions).statusCode === "function" ? __privateGet(this, _filterOptions).statusCode(request.response.status) : __privateGet(this, _filterOptions).statusCode === request.response.status;
}
return isRequestMatching;
};
_setOverwrite = new WeakMap();
ensureNotRestored_fn = function() {
if (__privateGet(this, _restored)) {
throw new Error("This can't be done on restored mock");
}
};
var WebDriverInterception = _WebDriverInterception;
function parseUrlPattern(url2) {
if (typeof url2 === "object") {
return url2;
}
if (url2.startsWith("http")) {
return new URLPattern(url2);
}
return new URLPattern({
pathname: url2
});
}
// src/commands/browser/mock.ts
var SESSION_MOCKS = {};
async function mock(url2, filterOptions) {
if (!this.isBidi) {
throw new Error("Mocking is only supported when running tests using WebDriver Bidi");
}
const browser = getBrowserObject6(this);
const contextManager = getContextManager(browser);
const context = await contextManager.getCurrentContext();
if (!SESSION_MOCKS[context]) {
SESSION_MOCKS[context] = /* @__PURE__ */ new Set();
}
const networkInterception = await WebDriverInterception.initiate(url2, filterOptions || {}, this);
SESSION_MOCKS[context].add(networkInterception);
return networkInterception;
}
// src/commands/browser/mockClearAll.ts
import logger8 from "@wdio/logger";
var log8 = logger8("webdriverio:mockClearAll");
async function mockClearAll() {
for (const [handle, mocks] of Object.entries(SESSION_MOCKS)) {
log8.trace("Clearing mocks for ".concat(handle));
for (const mock2 of mocks) {
mock2.clear();
}
}
}
// src/commands/browser/mockRestoreAll.ts
import logger9 from "@wdio/logger";
var log9 = logger9("webdriverio:mockRestoreAll");
async function mockRestoreAll() {
for (const [handle, mocks] of Object.entries(SESSION_MOCKS)) {
log9.trace("Clearing mocks for ".concat(handle));
for (const mock2 of mocks) {
await mock2.restore();
}
}
}
// src/commands/browser/newWindow.ts
import { sleep } from "@wdio/utils";
// src/scripts/newWindow.ts
function newWindow(url2, windowName, windowFeatures) {
window.open(url2, windowName || "", windowFeatures || "");
}
// src/commands/browser/newWindow.ts
import logger10 from "@wdio/logger";
var log10 = logger10("webdriverio:newWindow");
var WAIT_FOR_NEW_HANDLE_TIMEOUT = 3e3;
async function newWindow2(url2, { type = "window", windowName = "", windowFeatures = "" } = {}) {
if (typeof url2 !== "string") {
throw new Error("number or type of arguments don't agree with newWindow command");
}
if (!["tab", "window"].includes(type)) {
throw new Error("Invalid type '".concat(type, "' provided to newWindow command. Use either 'tab' or 'window'"));
}
if (windowName || windowFeatures) {
log10.warn('The "windowName" and "windowFeatures" options are deprecated and only supported in WebDriver Classic sessions.');
}
if (this.isMobile) {
throw new Error("newWindow command is not supported on mobile platforms");
}
const tabsBefore = await this.getWindowHandles();
if (this.isBidi) {
const contextManager = getContextManager(this);
const { context } = await this.browsingContextCreate({ type });
contextManager.setCurrentContext(context);
await this.browsingContextNavigate({ context, url: url2 });
} else {
await this.execute(newWindow, url2, windowName, windowFeatures);
}
let tabsAfter = await this.getWindowHandles();
const now = Date.now();
while (Date.now() - now < WAIT_FOR_NEW_HANDLE_TIMEOUT) {
tabsAfter = await this.getWindowHandles();
if (tabsAfter.length > tabsBefore.length) {
break;
}
await sleep(100);
}
const newTab = tabsAfter.pop();
if (!newTab) {
throw new Error("No window handle was found to switch to");
}
await this.switchToWindow(newTab);
return { handle: newTab, type };
}
// src/commands/browser/pause.ts
function pause(milliseconds = 1e3) {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
}
// src/scripts/resq.ts
var waitToLoadReact = function waitToLoadReact2() {
window.resq.waitToLoadReact();
};
var react$ = function react$2(selector, props, state, reactElement) {
props = props || {};
state = state || {};
let element = window.resq.resq$(selector, reactElement);
if (Object.keys(props).length) {
element = element.byProps(props);
}
if (Object.keys(state).length) {
element = element.byState(state);
}
if (!element.name) {
return { message: 'React element with selector "'.concat(selector, "\" wasn't found") };
}
return element.isFragment && element.node ? element.node[0] : element.node;
};
var react$$ = function react$$2(selector, props, state, reactElement) {
let elements = window.resq.resq$$(selector, reactElement);
if (Object.keys(props).length) {
elements = elements.byProps(props);
}
if (Object.keys(state).length) {
elements = elements.byState(state);
}
if (!elements.length) {
return [];
}
let nodes = [];
elements.forEach(function(element) {
if (element.isFragment) {
nodes = nodes.concat(element.node || []);
} else if (element.node) {
nodes.push(element.node);
}
});
return [...nodes];
};
// src/commands/browser/react$$.ts
async function react$$3(selector, { props = {}, state = {} } = {}) {
await this.executeScript(resqScript, []);
await this.execute(waitToLoadReact);
const res = await this.execute(
react$$,
selector,
props,
state
);
const elements = await getElements.call(this, selector, res, { isReactElement: true });
return enhanceElementsArray(elements, this, selector, "react$$", [props, state]);
}
// src/commands/browser/react$.ts
async function react$3(selector, { props = {}, state = {} } = {}) {
await this.executeScript(resqScript.toString(), []);
await this.execute(waitToLoadReact);
const res = await this.execute(
react$,
selector,
props,
state
);
return getElement.call(this, selector, res, { isReactElement: true });
}
// src/commands/browser/reloadSession.ts
import logger13 from "@wdio/logger";
// src/session/polyfill.ts
import logger11 from "@wdio/logger";
function getPolyfillManager(browser) {
return SessionManager.getSessionManager(browser, PolyfillManager);
}
var log11 = logger11("webdriverio:PolyfillManager");
var _initialize, _browser5, _scriptsRegisteredInContexts, _registerScriptsListener, _PolyfillManager_instances, registerScripts_fn;
var _PolyfillManager = class _PolyfillManager extends SessionManager {
constructor(browser) {
super(browser, _PolyfillManager.name);
__privateAdd(this, _PolyfillManager_instances);
__privateAdd(this, _initialize);
__privateAdd(this, _browser5);
__privateAdd(this, _scriptsRegisteredInContexts, /* @__PURE__ */ new Set());
__privateAdd(this, _registerScriptsListener, __privateMethod(this, _PolyfillManager_instances, registerScripts_fn).bind(this));
__privateSet(this, _browser5, browser);
if (!this.isEnabled()) {
__privateSet(this, _initialize, Promise.resolve(true));
return;
}
__privateGet(this, _browser5).on("browsingContext.contextCreated", __privateGet(this, _registerScriptsListener));
__privateSet(this, _initialize, Promise.all([
__privateGet(this, _browser5).browsingContextGetTree({}).then(({ contexts }) => {
return Promise.all(contexts.map((context) => __privateMethod(this, _PolyfillManager_instances, registerScripts_fn).call(this, context)));
}),
__privateGet(this, _browser5).sessionSubscribe({
events: ["browsingContext.contextCreated"]
})
]).then(() => true, () => false));
}
removeListeners() {
super.removeListeners();
__privateGet(this, _browser5).off("browsingContext.contextCreated", __privateGet(this, _registerScriptsListener));
}
async initialize() {
return __privateGet(this, _initialize);
}
};
_initialize = new WeakMap();
_browser5 = new WeakMap();
_scriptsRegisteredInContexts = new WeakMap();
_registerScriptsListener = new WeakMap();
_PolyfillManager_instances = new WeakSet();
registerScripts_fn = function(context) {
if (__privateGet(this, _scriptsRegisteredInContexts).has(context.context)) {
return;
}
const functionDeclaration = createFunctionDeclarationFromString(polyfillFn);
log11.info("Adding polyfill script to context with id ".concat(context.context));
__privateGet(this, _scriptsRegisteredInContexts).add(context.context);
return Promise.all([
!context.parent ? __privateGet(this, _browser5).scriptAddPreloadScript({
functionDeclaration,
contexts: [context.context]
}).catch(() => {
}) : Promise.resolve(),
__privateGet(this, _browser5).scriptCallFunction({
functionDeclaration,
target: context,
awaitPromise: false
}).catch(() => {
})
]);
};
var PolyfillManager = _PolyfillManager;
// src/session/shadowRoot.ts
import logger12 from "@wdio/logger";
// src/scripts/customElement.ts
function customElementWrapper() {
const origFn = customElements.define.bind(customElements);
customElements.define = function(name, Constructor, options) {
const origConnectedCallback = Constructor.prototype.connectedCallback;
Constructor.prototype.connectedCallback = function() {
let parentNode = this;
while (parentNode.parentNode) {
parentNode = parentNode.parentNode;
}
console.debug("[WDIO]", "newShadowRoot", this, parentNode, parentNode === document, document.documentElement);
return origConnectedCallback == null ? void 0 : origConnectedCallback.call(this);
};
const origDisconnectedCallback = Constructor.prototype.disconnectedCallback;
Constructor.prototype.disconnectedCallback = function() {
console.debug("[WDIO]", "removeShadowRoot", this);
return origDisconnectedCallback == null ? void 0 : origDisconnectedCallback.call(this);
};
return origFn(name, Constructor, options);
};
const origAttachShadow = Element.prototype.attachShadow;
Element.prototype.attachShadow = function(init) {
const shadowRoot = origAttachShadow.call(this, init);
let parentNode = this;
while (parentNode.parentNode) {
parentNode = parentNode.parentNode;
}
console.debug("[WDIO]", "newShadowRoot", this, parentNode, parentNode === document, document.documentElement);
return shadowRoot;
};
}
// src/session/shadowRoot.ts
var log12 = logger12("webdriverio:ShadowRootManager");
function getShadowRootManager(browser) {
return SessionManager.getSessionManager(browser, ShadowRootManager);
}
var _browser6, _initialize2, _shadowRoots, _documentElement, _frameDepth, _handleLogEntryListener, _commandResultHandlerListener, _handleBidiCommandListener, _ShadowRootManager_instances, handleBidiCommand_fn, commandResultHandler_fn;
var _ShadowRootManager = class _ShadowRootManager extends SessionManager {
constructor(browser) {
super(browser, _ShadowRootManager.name);
__privateAdd(this, _ShadowRootManager_instances);
__privateAdd(this, _browser6);
__privateAdd(this, _initialize2);
__privateAdd(this, _shadowRoots, /* @__PURE__ */ new Map());
__privateAdd(this, _documentElement);
__privateAdd(this, _frameDepth, 0);
__privateAdd(this, _handleLogEntryListener, this.handleLogEntry.bind(this));
__privateAdd(this, _commandResultHandlerListener, __privateMethod(this, _ShadowRootManager_instances, commandResultHandler_fn).bind(this));
__privateAdd(this, _handleBidiCommandListener, __privateMethod(this, _ShadowRootManager_instances, handleBidiCommand_fn).bind(this));
__privateSet(this, _browser6, browser);
if (!this.isEnabled()) {
__privateSet(this, _initialize2, Promise.resolve(true));
return;
}
__privateSet(this, _initialize2, __privateGet(this, _browser6).sessionSubscribe({
events: ["log.entryAdded", "browsingContext.navigationStarted"]
}).then(() => true, () => false));
__privateGet(this, _browser6).on("log.entryAdded", __privateGet(this, _handleLogEntryListener));
__privateGet(this, _browser6).on("result", __privateGet(this, _commandResultHandlerListener));
__privateGet(this, _browser6).on("bidiCommand", __privateGet(this, _handleBidiCommandListener));
__privateGet(this, _browser6).scriptAddPreloadScript({
functionDeclaration: customElementWrapper.toString()
});
}
removeListeners() {
super.removeListeners();
__privateGet(this, _browser6).off("log.entryAdded", __privateGet(this, _handleLogEntryListener));
__privateGet(this, _browser6).off("result", __privateGet(this, _commandResultHandlerListener));
__privateGet(this, _browser6).off("bidiCommand", __privateGet(this, _handleBidiCommandListener));
}
async initialize() {
return __privateGet(this, _initialize2);
}
/**
* check if we are within a frame
* @returns {boolean} true if we are within a frame
*/
isWithinFrame() {
return __privateGet(this, _frameDepth) > 0;
}
/**
* capture shadow root elements propagated through console.debug
*/
handleLogEntry(logEntry) {
var _a, _b, _c, _d;
const args = "args" in logEntry && logEntry.level === "debug" ? logEntry.args : void 0;
if (!args || args[0].type !== "string" || args[0].value !== "[WDIO]" || args[1].type !== "string") {
return;
}
if (!logEntry.source.context) {
return;
}
const eventType = args[1].value;
if (eventType === "newShadowRoot" && args[2].type === "node" && args[3].type === "node") {
const [
/* [WDIO] */
,
/* newShadowRoot */
,
shadowElem,
rootElem,
isDocument,
documentElement
] = args;
if (!__privateGet(this, _shadowRoots).has(logEntry.source.context)) {
if (!rootElem.sharedId) {
throw new Error('Expected "sharedId" parameter from object '.concat(rootElem));
}
__privateGet(this, _shadowRoots).set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
} else if (isDocument.type === "boolean" && isDocument.value) {
if (!rootElem.sharedId) {
throw new Error('Expected "sharedId" parameter from object '.concat(rootElem));
}
const tree2 = __privateGet(this, _shadowRoots).get(logEntry.source.context);
if ((tree2 == null ? void 0 : tree2.element) !== rootElem.sharedId) {
__privateGet(this, _shadowRoots).set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
}
}
__privateSet(this, _documentElement, documentElement);
const tree = __privateGet(this, _shadowRoots).get(logEntry.source.context);
if (!tree) {
throw new Error("Couldn't find tree for context id ".concat(logEntry.source.context));
}
if (
// we expect an element id
!shadowElem.sharedId || // we expect the element to have a shadow root
!((_b = (_a = shadowElem.value) == null ? void 0 : _a.shadowRoot) == null ? void 0 : _b.sharedId) || // we expect the shadow root to have a proper type
((_c = shadowElem.value.shadowRoot.value) == null ? void 0 : _c.nodeType) !== 11
) {
return log12.warn("Expected element with shadow root but found <".concat((_d = shadowElem.value) == null ? void 0 : _d.localName, " />"));
}
log12.info("Registered new shadow root for element <".concat(shadowElem.value.localName, " /> with id ").concat(shadowElem.value.shadowRoot.sharedId));
const newTree = new ShadowRootTree(
shadowElem.sharedId,
shadowElem.value.shadowRoot.sharedId,
shadowElem.value.shadowRoot.value.mode
);
if (rootElem.sharedId) {
tree.addShadowElement(rootElem.sharedId, newTree);
} else {
tree.addShadowElement(newTree);
}
return;
}
if (eventType === "removeShadowRoot" && args[2].type === "node" && args[2].sharedId) {
const tree = __privateGet(this, _shadowRoots).get(logEntry.source.context);
if (!tree) {
return;
}
return tree.remove(args[2].sharedId);
}
throw new Error('Invalid parameters for "'.concat(eventType, '" event: ').concat(args.join(", ")));
}
getShadowElementsByContextId(contextId, scope) {
var _a;
let tree = __privateGet(this, _shadowRoots).get(contextId);
if (!tree) {
return [];
}
let documentElement;
if (scope) {
const subTree = tree.find(scope);
if (subTree) {
tree = subTree;
}
} else {
documentElement = (_a = __privateGet(this, _documentElement)) == null ? void 0 : _a.sharedId;
}
const elements = tree.getAllLookupScopes();
return [
...documentElement ? [documentElement] : [],
...new Set(elements).values()
];
}
getShadowElementPairsByContextId(contextId, scope) {
let tree = __privateGet(this, _shadowRoots).get(contextId);
if (!tree) {
return [];
}
if (scope) {
const subTree = tree.find(scope);
if (subTree) {
tree = subTree;
}
}
return tree.flat().map((tree2) => [tree2.element, tree2.shadowRoot]);
}
getShadowRootModeById(contextId, element) {
const tree = __privateGet(this, _shadowRoots).get(contextId);
if (!tree) {
return;
}
const shadowTree = tree.find(element);
if (!shadowTree) {
return;
}
return shadowTree.mode;
}
deleteShadowRoot(element, contextId) {
const tree = __privateGet(this, _shadowRoots).get(contextId);
if (!tree) {
return;
}
return tree.remove(element);
}
};
_browser6 = new WeakMap();
_initialize2 = new WeakMap();
_shadowRoots = new WeakMap();
_documentElement = new WeakMap();
_frameDepth = new WeakMap();
_handleLogEntryListener = new WeakMap();
_commandResultHandlerListener = new WeakMap();
_handleBidiCommandListener = new WeakMap();
_ShadowRootManager_instances = new WeakSet();
/**
* keep track of navigation events and remove shadow roots when they are no longer needed
*/
handleBidiCommand_fn = function(command) {
if (command.method !== "browsingContext.navigate") {
return;
}
const params = command.params;
__privateGet(this, _shadowRoots).delete(params.context);
};
/**
* keep track of frame depth
*/
commandResultHandler_fn = function(result) {
const noResultError = typeof result.result === "object" && result.result && "error" in result.result && !result.result.error;
if (result.command === "switchToFrame" && noResultError) {
__privateWrapper(this, _frameDepth)._++;
}
if (result.command === "switchToParentFrame" && noResultError) {
__privateSet(this, _frameDepth, Math.max(0, __privateGet(this, _frameDepth) - 1));
}
};
var ShadowRootManager = _ShadowRootManager;
var ShadowRootTree = class _ShadowRootTree {
constructor(element, shadowRoot, mode) {
__publicField(this, "element");
__publicField(this, "shadowRoot");
__publicField(this, "mode");
__publicField(this, "children", /* @__PURE__ */ new Set());
this.element = element;
this.shadowRoot = shadowRoot;
this.mode = mode;
}
addShadowElement(...args) {
const [scope, treeArg] = args;
if (!scope && !treeArg) {
throw new Error('Method "addShadowElement" expects at least 2 arguments');
}
if (scope instanceof _ShadowRootTree) {
this.children.add(scope);
return;
}
if (typeof scope === "string" && treeArg instanceof _ShadowRootTree) {
const tree = this.find(scope) || this.findByShadowId(scope);
if (!tree) {
return;
}
tree.addShadowElement(treeArg);
return;
}
throw new Error('Invalid arguments for "addShadowElement" method');
}
find(element) {
if (this.element === element) {
return this;
}
for (const child of this.children) {
const elem = child.find(element);
if (elem) {
return elem;
}
}
return void 0;
}
findByShadowId(shadowRoot) {
if (this.shadowRoot === shadowRoot) {
return this;
}
for (const child of this.children) {
const elem = child.findByShadowId(shadowRoot);
if (elem) {
return elem;
}
}
return void 0;
}
getAllLookupScopes() {
var _a;
return [
(_a = this.shadowRoot) != null ? _a : this.element,
...Array.from(this.children).map((tree) => tree.getAllLookupScopes())
].flat();
}
flat() {
return [this, ...Array.from(this.children).map((tree) => tree.flat())].flat();
}
remove(element) {
const childArray = Array.from(this.children);
for (let i = childArray.length - 1; i >= 0; i--) {
if (childArray[i].element === element) {
return this.children.delete(childArray[i]);
}
const wasFound = childArray[i].remove(element);
if (wasFound) {
return true;
}
}
return false;
}
};
// src/session/networkManager.ts
function getNetworkManager(browser) {
return SessionManager.getSessionManager(browser, NetworkManager);
}
var UNKNOWN_NAVIGATION_ID = "UNKNOWN_NAVIGATION_ID";
var SUPPORTED_NAVIGATION_PROTOCOLS = ["http", "https", "data", "file"];
var _browser7, _initialize3, _requests, _lastNetworkId, _navigationStartedListener2, _responseCompletedListener, _beforeRequestSentListener, _fetchErrorListener, _NetworkManager_instances, beforeRequestSent_fn, navigationStarted_fn2, fetchError_fn, findRootRequest_fn, responseCompleted_fn;
var _NetworkManager = class _NetworkManager extends SessionManager {
constructor(browser) {
super(browser, _NetworkManager.name);
__privateAdd(this, _NetworkManager_instances);
__privateAdd(this, _browser7);
__privateAdd(this, _initialize3);
__privateAdd(this, _requests, /* @__PURE__ */ new Map());
__privateAdd(this, _lastNetworkId);
__privateAdd(this, _navigationStartedListener2, __privateMethod(this, _NetworkManager_instances, navigationStarted_fn2).bind(this));
__privateAdd(this, _responseCompletedListener, __privateMethod(this, _NetworkManager_instances, responseCompleted_fn).bind(this));
__privateAdd(this, _beforeRequestSentListener, __privateMethod(this, _NetworkManager_instances, beforeRequestSent_fn).bind(this));
__privateAdd(this, _fetchErrorListener, __privateMethod(this, _NetworkManager_instances, fetchError_fn).bind(this));
__privateSet(this, _browser7, browser);
if (!this.isEnabled()) {
__privateSet(this, _initialize3, Promise.resolve(true));
return;
}
__privateSet(this, _initialize3, __privateGet(this, _browser7).sessionSubscribe({
events: [
"browsingContext.navigationStarted",
"browsingContext.fragmentNavigated",
"network.responseCompleted",
"network.beforeRequestSent",
"network.fetchError"
]
}).then(() => true, () => false));
__privateGet(this, _browser7).on("browsingContext.navigationStarted", __privateGet(this, _navigationStartedListener2));
__privateGet(this, _browser7).on("browsingContext.fragmentNavigated", __privateGet(this, _navigationStartedListener2));
__privateGet(this, _browser7).on("network.responseCompleted", __privateGet(this, _responseCompletedListener));
__privateGet(this, _browser7).on("network.beforeRequestSent", __privateGet(this, _beforeRequestSentListener));
__privateGet(this, _browser7).on("network.fetchError", __privateGet(this, _fetchErrorListener));
}
removeListeners() {
super.removeListeners();
__privateGet(this, _browser7).off("browsingContext.navigationStarted", __privateGet(this, _navigationStartedListener2));
__privateGet(this, _browser7).off("browsingContext.fragmentNavigated", __privateGet(this, _navigationStartedListener2));
__privateGet(this, _browser7).off("network.responseCompleted", __privateGet(this, _responseCompletedListener));
__privateGet(this, _browser7).off("network.beforeRequestSent", __privateGet(this, _beforeRequestSentListener));
__privateGet(this, _browser7).off("network.fetchError", __privateGet(this, _fetchErrorListener));
}
async initialize() {
return __privateGet(this, _initialize3);
}
getRequestResponseData(navigationId) {
return __privateGet(this, _requests).get(navigationId);
}
/**
* Returns the number of requests that are currently pending.
* @param context browsing context id
* @returns the number of requests that are currently pending
*/
getPendingRequests(navigationId) {
const request = __privateGet(this, _requests).get(navigationId);
if (!request) {
throw new Error("Couldn't find request for navigation with id ".concat(navigationId));
}
const subRequests = request.children || [];
return subRequests.filter((child) => (
/**
* either the request has no response yet
*/
!child.response && /**
* and there was no request error
*/
!child.error
));
}
};
_browser7 = new WeakMap();
_initialize3 = new WeakMap();
_requests = new WeakMap();
_lastNetworkId = new WeakMap();
_navigationStartedListener2 = new WeakMap();
_responseCompletedListener = new WeakMap();
_beforeRequestSentListener = new WeakMap();
_fetchErrorListener = new WeakMap();
_NetworkManager_instances = new WeakSet();
beforeRequestSent_fn = function(log29) {
var _a;
if (log29.navigation) {
return;
}
const request = __privateMethod(this, _NetworkManager_instances, findRootRequest_fn).call(this, log29.navigation);
if (!request) {
return;
}
const { request: id, headers, cookies, url: url2 } = log29.request;
(_a = request.children) == null ? void 0 : _a.push({
id,
url: url2,
headers: headerListToObject(headers),
cookies: cookies.map((cookie) => ({
name: cookie.name,
value: cookie.value.type === "string" ? cookie.value.value : atob(cookie.value.value),
domain: cookie.domain,
path: cookie.path,
size: cookie.size,
httpOnly: cookie.httpOnly,
secure: cookie.secure,
sameSite: cookie.sameSite,
expiry: cookie.expiry
})),
timestamp: log29.timestamp
});
};
navigationStarted_fn2 = function(log29) {
if (
/**
* we need a navigation id to identify the request
*/
!log29.navigation || /**
* ignore urls that users wouldn't navigate to
*/
!SUPPORTED_NAVIGATION_PROTOCOLS.some((protocol) => log29.url.startsWith(protocol))
) {
if (log29.navigation === null && log29.url === "") {
__privateSet(this, _lastNetworkId, UNKNOWN_NAVIGATION_ID);
return __privateGet(this, _requests).set(UNKNOWN_NAVIGATION_ID, {
url: "",
headers: {},
timestamp: log29.timestamp,
redirectChain: [],
children: []
});
}
return;
}
__privateSet(this, _lastNetworkId, log29.navigation);
__privateGet(this, _requests).set(log29.navigation, {
url: log29.url,
headers: {},
timestamp: log29.timestamp,
navigation: log29.navigation,
redirectChain: [],
children: []
});
};
fetchError_fn = function(log29) {
var _a;
const response = __privateMethod(this, _NetworkManager_instances, findRootRequest_fn).call(this, log29.navigation);
if (!response) {
return;
}
const request = (_a = response.children) == null ? void 0 : _a.find((child) => child.id === log29.request.request);
if (!request) {
return;
}
request.error = log29.errorText;
};
findRootRequest_fn = function(navigationId) {
const response = __privateGet(this, _requests).get(navigationId || UNKNOWN_NAVIGATION_ID);
if (response) {
return response;
}
const firstRequest = __privateGet(this, _requests).values().next().value;
return __privateGet(this, _lastNetworkId) ? __privateGet(this, _requests).get(__privateGet(this, _lastNetworkId)) || firstRequest : firstRequest;
};
responseCompleted_fn = function(log29) {
var _a, _b, _c;
const response = __privateMethod(this, _NetworkManager_instances, findRootRequest_fn).call(this, log29.navigation);
if (!response) {
return;
}
if (!response.navigation && response.url === "") {
response.url = log29.request.url;
response.navigation = log29.navigation;
}
if (log29.navigation === response.navigation) {
if (response.url !== log29.response.url) {
(_a = response.redirectChain) == null ? void 0 : _a.push(response.url);
}
response.url = log29.response.url;
const { headers: requestHeaders } = log29.request;
const { fromCache, headers: responseHeaders, mimeType, status } = log29.response;
response.headers = headerListToObject(requestHeaders), response.response = {
fromCache,
headers: headerListToObject(responseHeaders),
mimeType,
status
};
return;
}
const request = (_b = response.children) == null ? void 0 : _b.find((child) => child.id === log29.request.request);
if (!request) {
return;
}
request.response = {
fromCache: log29.response.fromCache,
headers: headerListToObject(log29.response.headers),
mimeType: log29.response.mimeType,
status: log29.response.status
};
(_c = response.children) == null ? void 0 : _c.push(request);
};
var NetworkManager = _NetworkManager;
function headerListToObject(headers) {
return headers.reduce((acc, { name, value }) => {
acc[name] = value.value;
return acc;
}, {});
}
// src/session/dialog.ts
function getDialogManager(browser) {
return SessionManager.getSessionManager(browser, DialogManager);
}
var _browser8, _initialize4, _autoHandleDialog, _handleUserPromptListener, _DialogManager_instances, handleUserPrompt_fn, switchListenerFlag_fn;
var _DialogManager = class _DialogManager extends SessionManager {
constructor(browser) {
super(browser, _DialogManager.name);
__privateAdd(this, _DialogManager_instances);
__privateAdd(this, _browser8);
__privateAdd(this, _initialize4);
__privateAdd(this, _autoHandleDialog, true);
__privateAdd(this, _handleUserPromptListener, __privateMethod(this, _DialogManager_instances, handleUserPrompt_fn).bind(this));
__privateSet(this, _browser8, browser);
if (!this.isEnabled()) {
__privateSet(this, _initialize4, Promise.resolve(true));
return;
}
__privateSet(this, _initialize4, __privateGet(this, _browser8).sessionSubscribe({
events: ["browsingContext.userPromptOpened"]
}).then(() => true, () => false));
__privateGet(this, _browser8).on("_dialogListenerRegistered", () => __privateMethod(this, _DialogManager_instances, switchListenerFlag_fn).call(this, false));
__privateGet(this, _browser8).on("_dialogListenerRemoved", () => __privateMethod(this, _DialogManager_instances, switchListenerFlag_fn).call(this, true));
__privateGet(this, _browser8).on("browsingContext.userPromptOpened", __privateGet(this, _handleUserPromptListener));
}
removeListeners() {
super.removeListeners();
__privateGet(this, _browser8).off("browsingContext.userPromptOpened", __privateGet(this, _handleUserPromptListener));
__privateGet(this, _browser8).removeAllListeners("_dialogListenerRegistered");
__privateGet(this, _browser8).removeAllListeners("_dialogListenerRemoved");
}
async initialize() {
return __privateGet(this, _initialize4);
}
};
_browser8 = new WeakMap();
_initialize4 = new WeakMap();
_autoHandleDialog = new WeakMap();
_handleUserPromptListener = new WeakMap();
_DialogManager_instances = new WeakSet();
handleUserPrompt_fn = async function(log29) {
if (__privateGet(this, _autoHandleDialog)) {
try {
return await __privateGet(this, _browser8).browsingContextHandleUserPrompt({
accept: false,
context: log29.context
});
} catch (err) {
if (err instanceof Error && (err.message.includes("no such alert") || err.message.includes("no such frame"))) {
return;
}
throw err;
}
}
const dialog = new Dialog(log29, __privateGet(this, _browser8));
__privateGet(this, _browser8).emit("dialog", dialog);
};
/**
* Is called when a new dialog listener is registered with the `dialog` name.
* In these cases we set a flag to the `#listener` map to indicate that we
* are listening to dialog events for this page in this context.
*/
switchListenerFlag_fn = function(value) {
__privateSet(this, _autoHandleDialog, value);
};
var DialogManager = _DialogManager;
var _browser9, _context, _message, _defaultValue, _type2;
var Dialog = class {
constructor(event, browser) {
__privateAdd(this, _browser9);
__privateAdd(this, _context);
__privateAdd(this, _message);
__privateAdd(this, _defaultValue);
__privateAdd(this, _type2);
__privateSet(this, _message, event.message);
__privateSet(this, _defaultValue, event.defaultValue);
__privateSet(this, _type2, event.type);
__privateSet(this, _context, event.context);
__privateSet(this, _browser9, browser);
}
message() {
return __privateGet(this, _message);
}
defaultValue() {
return __privateGet(this, _defaultValue);
}
type() {
return __privateGet(this, _type2);
}
/**
* Returns when the dialog has been accepted.
*
* @alias dialog.accept
* @param {string=} promptText A text to enter into prompt. Does not cause any effects if the dialog's type is not prompt.
* @returns {Promise<void>}
*/
async accept(userText) {
const contextManager = getContextManager(__privateGet(this, _browser9));
const context = await contextManager.getCurrentContext();
if (__privateGet(this, _context) !== context) {
return;
}
await __privateGet(this, _browser9).browsingContextHandleUserPrompt({
accept: true,
context: __privateGet(this, _context),
userText
});
}
async dismiss() {
const contextManager = getContextManager(__privateGet(this, _browser9));
const context = await contextManager.getCurrentContext();
if (__privateGet(this, _context) !== context) {
return;
}
await __privateGet(this, _browser9).browsingContextHandleUserPrompt({
accept: false,
context: __privateGet(this, _context)
});
}
};
_browser9 = new WeakMap();
_context = new WeakMap();
_message = new WeakMap();
_defaultValue = new WeakMap();
_type2 = new WeakMap();
// src/session/index.ts
function registerSessionManager(instance) {
const initializationPromises = [
getContextManager(instance).initialize()
];
if (typeof instance.capabilities.webSocketUrl === "string") {
initializationPromises.push(
getPolyfillManager(instance).initialize(),
getShadowRootManager(instance).initialize(),
getNetworkManager(instance).initialize(),
getDialogManager(instance).initialize()
);
}
return Promise.all(initializationPromises);
}
// src/commands/browser/reloadSession.ts
var log13 = logger13("webdriverio");
async function reloadSession(newCapabilities) {
var _a;
const oldSessionId = this.sessionId;
const shutdownDriver = Boolean(newCapabilities == null ? void 0 : newCapabilities.browserName);
try {
await this.deleteSession({ shutdownDriver });
} catch (err) {
log13.warn("Suppressing error closing the session: ".concat(err.stack));
}
if ((_a = this.puppeteer) == null ? void 0 : _a.connected) {
this.puppeteer.disconnect();
log13.debug("Disconnected puppeteer session");
}
const ProtocolDriver = (await import(
/* @vite-ignore */
this.options.automationProtocol
)).default;
await ProtocolDriver.reloadSession(this, newCapabilities);
await registerSessionManager(this);
const options = this.options;
if (Array.isArray(options.onReload) && options.onReload.length) {
await Promise.all(options.onReload.map((hook) => hook(oldSessionId, this.sessionId)));
}
return this.sessionId;
}
// src/commands/browser/restore.ts
async function restore(scopes2) {
const scopeArray = !scopes2 || Array.isArray(scopes2) ? scopes2 : [scopes2];
const instanceRestoreFunctions = restoreFunctions.get(this);
if (!instanceRestoreFunctions) {
return;
}
await Promise.all(Array.from(instanceRestoreFunctions.entries()).map(async ([scope, restoreFunctionsList]) => {
if (!scopeArray || scopeArray.includes(scope)) {
await Promise.all(restoreFunctionsList.map((fn) => fn()));
instanceRestoreFunctions.set(scope, []);
}
}));
}
// src/commands/browser/savePDF.ts
async function savePDF(filepath, options) {
return environment.value.savePDF.call(this, filepath, options);
}
// src/commands/browser/saveRecordingScreen.ts
async function saveRecordingScreen(filepath) {
return environment.value.saveRecordingScreen.call(this, filepath);
}
// src/commands/browser/saveScreenshot.ts
async function saveScreenshot(filepath, options) {
return environment.value.saveScreenshot.call(this, filepath, options);
}
// src/commands/browser/scroll.ts
import logger14 from "@wdio/logger";
var log14 = logger14("webdriverio");
function scroll(x = 0, y = 0) {
if (!x && !y) {
log14.warn('"scroll" command was called with no parameters, skipping execution');
return Promise.resolve();
}
if (this.isMobile) {
return this.execute((x2, y2) => window.scrollBy(x2, y2), x, y);
}
return this.action("wheel").scroll({
deltaX: x,
deltaY: y,
duration: 0
}).perform();
}
// src/commands/browser/setCookies.ts
import logger15 from "@wdio/logger";
var log15 = logger15("webdriverio");
async function setCookies(cookieObjs) {
const cookieObjsList = !Array.isArray(cookieObjs) ? [cookieObjs] : cookieObjs;
if (cookieObjsList.some((obj) => typeof obj !== "object")) {
throw new Error("Invalid input (see https://webdriver.io/docs/api/browser/setCookies for documentation)");
}
if (!this.isBidi) {
await Promise.all(cookieObjsList.map((cookieObj) => this.addCookie(cookieObj)));
return;
}
let url2;
try {
url2 = new URL(await this.getUrl());
if (url2.origin === "null") {
await Promise.all(cookieObjsList.map((cookieObj) => this.addCookie(cookieObj)));
return;
}
} catch {
await Promise.all(cookieObjsList.map((cookieObj) => this.addCookie(cookieObj)));
return;
}
try {
await Promise.all(cookieObjsList.map((cookie) => this.storageSetCookie({
cookie: {
...cookie,
domain: cookie.domain || url2.hostname,
value: {
type: "string",
value: cookie.value
}
},
partition: {
type: "storageKey",
sourceOrigin: url2.origin
}
})));
} catch (err) {
log15.warn("BiDi setCookies failed, falling back to classic: ".concat(err.message));
await Promise.all(cookieObjsList.map((cookieObj) => this.addCookie(cookieObj)));
}
return;
}
// src/commands/browser/setTimeout.ts
async function setTimeout2(timeouts) {
if (typeof timeouts !== "object") {
throw new Error('Parameter for "setTimeout" command needs to be an object');
}
const timeoutValues = Object.values(timeouts);
if (timeoutValues.length && timeoutValues.every((timeout) => typeof timeout !== "number" || timeout < 0 || timeout > Number.MAX_SAFE_INTEGER)) {
throw new Error("Specified timeout values are not valid integer (see https://webdriver.io/docs/api/browser/setTimeout for documentation).");
}
const implicit = timeouts.implicit;
const pageLoad = timeouts["page load"] || timeouts.pageLoad;
const script = timeouts.script;
const setTimeouts = this.setTimeouts.bind(this);
return setTimeouts(implicit, pageLoad, script);
}
// src/commands/browser/setViewport.ts
var minWindowSize = 0;
var maxWindowSize = Number.MAX_SAFE_INTEGER;
async function setViewport(options) {
if (typeof options.width !== "number" || typeof options.height !== "number") {
throw new Error("setViewport expects width and height of type number");
}
if (options.width < minWindowSize || options.width > maxWindowSize || options.height < minWindowSize || options.height > maxWindowSize) {
throw new Error("setViewport expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
}
if (options.devicePixelRatio && (typeof options.devicePixelRatio !== "number" || options.devicePixelRatio < 0)) {
throw new Error("setViewport expects devicePixelRatio to be a number in the 0 to 2^31 \u2212 1 range");
}
const contextManager = getContextManager(this);
const context = await contextManager.getCurrentContext();
await this.browsingContextSetViewport({
context,
devicePixelRatio: options.devicePixelRatio || 1,
viewport: {
width: options.width,
height: options.height
}
});
}
// src/commands/browser/setWindowSize.ts
import { getBrowserObject as getBrowserObject7 } from "@wdio/utils";
var minWindowSize2 = 0;
var maxWindowSize2 = Number.MAX_SAFE_INTEGER;
async function setWindowSize(width, height) {
if (typeof width !== "number" || typeof height !== "number") {
throw new Error("setWindowSize expects width and height of type number");
}
if (width < minWindowSize2 || width > maxWindowSize2 || height < minWindowSize2 || height > maxWindowSize2) {
throw new Error("setWindowSize expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
}
const browser = getBrowserObject7(this);
await browser.setWindowRect(null, null, width, height);
}
// src/commands/browser/switchWindow.ts
async function switchWindow(matcher) {
if (typeof matcher !== "string" && !(matcher instanceof RegExp)) {
throw new Error('Unsupported parameter for switchWindow, required is "string" or a RegExp');
}
const contextManager = getContextManager(this);
const tabs = await this.getWindowHandles();
if (typeof matcher === "string" && tabs.includes(matcher)) {
if (matcher === contextManager.getCurrentWindowHandle()) {
return matcher;
}
await this.switchToWindow(matcher);
contextManager.setCurrentContext(matcher);
return matcher;
}
const matchesTarget = (target) => {
if (typeof matcher === "string") {
return target.includes(matcher);
}
return matcher.test(target);
};
for (const tab of tabs) {
await this.switchToWindow(tab);
contextManager.setCurrentContext(tab);
const url2 = await this.getUrl();
if (matchesTarget(url2)) {
return tab;
}
const title = await this.getTitle();
if (matchesTarget(title)) {
return tab;
}
const windowName = await this.execute(
/* istanbul ignore next */
() => window.name
);
if (windowName && matchesTarget(windowName)) {
return tab;
}
}
throw new Error('No window found with title, url, name or window handle matching "'.concat(matcher, '"'));
}
// src/commands/browser/switchFrame.ts
import logger16 from "@wdio/logger";
import { ELEMENT_KEY as ELEMENT_KEY8 } from "webdriver";
// src/scripts/shadowDom.ts
function findIframeInShadowDOM(fragment) {
function findIframe(root) {
const allElements = Array.from(root.querySelectorAll("iframe"));
for (const el of allElements) {
if (el instanceof HTMLIFrameElement && el.src.includes(fragment)) {
return el;
}
}
const shadowHosts = Array.from(root.querySelectorAll("*"));
for (const host of shadowHosts) {
const maybeShadowRoot = host.shadowRoot;
if (maybeShadowRoot) {
const result = findIframe(maybeShadowRoot);
if (result) {
return result;
}
}
}
return null;
}
return findIframe(document);
}
// src/commands/browser/switchFrame.ts
var log16 = logger16("webdriverio:switchFrame");
async function switchFrame(context) {
var _a;
function isPossiblyUnresolvedElement(input) {
return Boolean(input) && typeof input === "object" && typeof input.getElement === "function";
}
if (!this.isBidi) {
if (typeof context === "function") {
throw new Error("Cannot use a function to fetch a context in WebDriver Classic");
}
if (typeof context === "string") {
throw new Error("Cannot use a string to fetch a context in WebDriver Classic");
}
if (isPossiblyUnresolvedElement(context)) {
const element = await context.getElement();
await element.waitForExist({
timeoutMsg: "Can't switch to frame with selector ".concat(element.selector, " because it doesn't exist")
});
return switchToFrame(this, element);
}
return switchToFrame(this, context);
}
const sessionContext = getContextManager(this);
if (context === null) {
const handle = await this.getWindowHandle();
switchToFrameHelper(this, handle);
await switchToFrame(this, context);
return handle;
}
if (typeof context === "string") {
const newContextId = await this.waitUntil(async () => {
const tree = await this.browsingContextGetTree({});
const urlContext = sessionContext.findContext(context, tree.contexts, "byUrl") || /**
* In case the user provides an url without `/` at the end, e.g. `https://example.com`,
* the `browsingContextGetTree` command may return a context with the url `https://example.com/`.
*/
sessionContext.findContext("".concat(context, "/"), tree.contexts, "byUrl");
const urlContextContaining = sessionContext.findContext(context, tree.contexts, "byUrlContaining");
const contextIdContext = sessionContext.findContext(context, tree.contexts, "byContextId");
if (urlContext) {
log16.info('Found context by url "'.concat(urlContext.url, '" with context id "').concat(urlContext.context, '"'));
return urlContext.context;
} else if (urlContextContaining) {
log16.info('Found context by url containing "'.concat(urlContextContaining.url, '" with context id "').concat(urlContextContaining.context, '"'));
return urlContextContaining.context;
} else if (contextIdContext) {
log16.info('Found context by id "'.concat(contextIdContext, '" with url "').concat(contextIdContext.url, '"'));
return contextIdContext.context;
}
return false;
}, {
timeout: this.options.waitforTimeout,
interval: this.options.waitforInterval,
timeoutMsg: 'No frame with url or id "'.concat(context, '" found within the timeout')
});
const currentContext = await sessionContext.getCurrentContext();
const allContexts = await sessionContext.getFlatContextTree();
const allFrames = (await Promise.all(Object.keys(allContexts).map(async (id) => {
const { nodes } = await this.browsingContextLocateNodes({
locator: { type: "css", value: "iframe, frame" },
context: id
}).catch(() => ({ nodes: [] }));
return Promise.all(nodes.map(async (node) => {
var _a2;
const html = "<iframe".concat(Object.entries(((_a2 = node.value) == null ? void 0 : _a2.attributes) || {}).reduce((acc, [key, value]) => "".concat(acc, " ").concat(key, '="').concat(value, '"'), " "), "></iframe>");
const args = [{ [ELEMENT_KEY8]: node.sharedId }];
const userScript = (iframe) => iframe.contentWindow;
const functionDeclaration = new Function("\n return (".concat(SCRIPT_PREFIX).concat(userScript.toString()).concat(SCRIPT_SUFFIX, ").apply(this, arguments);\n ")).toString();
const params = {
functionDeclaration,
awaitPromise: false,
arguments: args.map((arg) => LocalValue.getArgument(arg)),
target: { context: id }
};
const result = await this.scriptCallFunction(params).catch((err) => log16.warn("Failed to identify frame context id: ".concat(err.message)));
if (!result) {
return [];
}
const { context: context2 } = parseScriptResult(params, result);
return {
/**
* the actual frame context we need to switch WebDriver Bidi commands to
*/
context: context2,
/**
* the element reference of the iframe so we can call `switchToFrame` to
* switch context for WebDriver Classic commands
*/
frameElement: { [ELEMENT_KEY8]: node.sharedId },
/**
* the context id in which the iframe was found
*/
parentContext: id,
/**
* an HTML representation of the iframe for a good error message in case
* we can't find the desired frame from this list
*/
html
};
}));
}))).flat(Infinity);
if (allFrames.length === 0) {
const urlFragment = typeof context === "string" ? (_a = context.split("/").pop()) != null ? _a : "" : "";
const iframeFound = await this.execute(findIframeInShadowDOM, urlFragment);
if (iframeFound && typeof iframeFound === "object" && iframeFound[ELEMENT_KEY8]) {
const iframeElement = await this.$(iframeFound);
if (iframeElement) {
return this.switchFrame(iframeElement);
}
}
log16.warn('Shadow DOM iframe with src containing "'.concat(urlFragment, '" found, but could not be resolved into a WebdriverIO element.'));
}
let desiredFrame;
let desiredContext = newContextId;
const contextQueue = [];
log16.info("Available frames to switch to: ".concat(allFrames.length, ", desired context to switch: ").concat(desiredContext));
while (desiredContext !== currentContext) {
desiredFrame = allFrames.find(({ context: context2 }) => context2 === desiredContext);
if (!desiredFrame) {
break;
}
log16.info(
contextQueue.length === 0 ? "Found desired frame with element id ".concat(desiredFrame.frameElement[ELEMENT_KEY8]) : "to switch to desired frame, we need to switch to ".concat(desiredFrame.context, " first")
);
contextQueue.unshift(desiredFrame);
desiredContext = desiredFrame.parentContext;
}
if (contextQueue.length === 0) {
throw new Error('Frame with url or context id "'.concat(context, '" not found, available frames to switch to:\n - ').concat(allFrames.map(({ html }) => html).join("\n - ")));
}
for (const contextToSwitch of contextQueue) {
switchToFrameHelper(this, contextToSwitch.context);
await switchToFrame(this, contextToSwitch.frameElement);
}
sessionContext.setCurrentContext(newContextId);
return newContextId;
}
if (isPossiblyUnresolvedElement(context)) {
const element = await context.getElement();
await element.waitForExist({
timeoutMsg: "Can't switch to frame with selector ".concat(element.selector, " because it doesn't exist")
});
return switchToFrameUsingElement(this, element);
}
if (typeof context === "function") {
const foundContextId = await this.waitUntil(async () => {
const allContexts = await sessionContext.getFlatContextTree();
const allContextIds = Object.keys(allContexts);
for (const contextId of allContextIds) {
const functionDeclaration = new Function("\n return (".concat(SCRIPT_PREFIX).concat(context.toString()).concat(SCRIPT_SUFFIX, ").apply(this, arguments);\n ")).toString();
const params = {
functionDeclaration,
awaitPromise: false,
arguments: [],
target: { context: contextId }
};
const result = await this.scriptCallFunction(params).catch((err) => {
log16.warn("switchFrame context callback threw error: ".concat(err.message));
return void 0;
});
if (result && result.type === "success" && result.result.type === "boolean" && result.result.value) {
return contextId;
}
}
return false;
}, {
timeout: this.options.waitforTimeout,
interval: this.options.waitforInterval,
timeoutMsg: "Could not find the desired frame within the timeout"
});
await this.switchFrame(null);
await this.switchFrame(foundContextId);
return foundContextId;
}
throw new Error(
"Invalid type for context parameter: ".concat(typeof context, ", expected one of number, string or null. ") + "Check out our docs: https://webdriver.io/docs/api/browser/switchFrame.html"
);
}
function switchToFrameHelper(browser, context) {
const sessionContext = getContextManager(browser);
sessionContext.setCurrentContext(context);
}
async function switchToFrameUsingElement(browser, element) {
const frame = await browser.execute(
(iframe) => iframe.contentWindow,
element
);
switchToFrameHelper(browser, frame.context);
const elementId = element[ELEMENT_KEY8];
await switchToFrame(browser, { [ELEMENT_KEY8]: elementId });
return frame.context;
}
function switchToFrame(browser, frame) {
toggleDisableDeprecationWarning();
return browser.switchToFrame(frame).finally(toggleDisableDeprecationWarning);
}
function toggleDisableDeprecationWarning() {
if (typeof process !== "undefined" && process.env) {
process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS = process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS ? void 0 : "true";
}
}
// src/commands/browser/throttle.ts
import logger17 from "@wdio/logger";
import { getBrowserObject as getBrowserObject8 } from "@wdio/utils";
var log17 = logger17("webdriverio:throttle");
async function throttle(params) {
log17.warn('Command "throttle" is deprecated and will be removed with the next major version release! Use `throttleNetwork` instead.');
const browser = getBrowserObject8(this);
await browser.throttleNetwork(params);
}
// src/commands/browser/throttleCPU.ts
async function throttleCPU(factor) {
if (typeof factor !== "number") {
throw new Error('Invalid factor for "throttleCPU". Expected it to be a number (int)');
}
const failedConnectionMessage = "No Puppeteer connection could be established which is required to use this command";
await this.getPuppeteer();
if (!this.puppeteer) {
throw new Error(failedConnectionMessage);
}
const pages = await this.puppeteer.pages();
if (!pages.length) {
throw new Error(failedConnectionMessage);
}
const client = await pages[0].target().createCDPSession();
await client.send("Emulation.setCPUThrottlingRate", { rate: factor });
}
// src/commands/browser/throttleNetwork.ts
import { getBrowserObject as getBrowserObject9 } from "@wdio/utils";
var NETWORK_PRESETS = {
"offline": {
offline: true,
downloadThroughput: 0,
uploadThroughput: 0,
latency: 1
},
"GPRS": {
offline: false,
downloadThroughput: 50 * 1024 / 8,
uploadThroughput: 20 * 1024 / 8,
latency: 500
},
"Regular2G": {
offline: false,
downloadThroughput: 250 * 1024 / 8,
uploadThroughput: 50 * 1024 / 8,
latency: 300
},
"Good2G": {
offline: false,
downloadThroughput: 450 * 1024 / 8,
uploadThroughput: 150 * 1024 / 8,
latency: 150
},
"Regular3G": {
offline: false,
downloadThroughput: 750 * 1024 / 8,
uploadThroughput: 250 * 1024 / 8,
latency: 100
},
"Good3G": {
offline: false,
downloadThroughput: 1.5 * 1024 * 1024 / 8,
uploadThroughput: 750 * 1024 / 8,
latency: 40
},
"Regular4G": {
offline: false,
downloadThroughput: 4 * 1024 * 1024 / 8,
uploadThroughput: 3 * 1024 * 1024 / 8,
latency: 20
},
"DSL": {
offline: false,
downloadThroughput: 2 * 1024 * 1024 / 8,
uploadThroughput: 1 * 1024 * 1024 / 8,
latency: 5
},
"WiFi": {
offline: false,
downloadThroughput: 30 * 1024 * 1024 / 8,
uploadThroughput: 15 * 1024 * 1024 / 8,
latency: 2
},
"online": {
offline: false,
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1
}
};
var NETWORK_PRESET_TYPES = Object.keys(NETWORK_PRESETS);
async function throttleNetwork(params) {
if (
/**
* check string parameter
*/
(typeof params !== "string" || !NETWORK_PRESET_TYPES.includes(params)) && /**
* check object parameter
*/
typeof params !== "object"
) {
throw new Error('Invalid parameter for "throttleNetwork". Expected it to be typeof object or one of the following values: '.concat(NETWORK_PRESET_TYPES.join(", "), ' but found "').concat(params, '"'));
}
if (this.isSauce) {
const browser = getBrowserObject9(this);
await browser.sauceThrottleNetwork(params);
return;
}
const failedConnectionMessage = "No Puppeteer connection could be established which is required to use this command";
await this.getPuppeteer();
if (!this.puppeteer) {
throw new Error(failedConnectionMessage);
}
const pages = await this.puppeteer.pages();
if (!pages.length) {
throw new Error(failedConnectionMessage);
}
const client = await pages[0].target().createCDPSession();
await client.send(
"Network.emulateNetworkConditions",
typeof params === "string" ? NETWORK_PRESETS[params] : params
);
return;
}
// src/commands/browser/touchAction.ts
function touchAction2(actions2) {
return touchAction.call(this, actions2);
}
// src/commands/browser/uploadFile.ts
async function uploadFile(localPath) {
return environment.value.uploadFile.call(this, localPath);
}
// src/commands/browser/url.ts
var DEFAULT_NETWORK_IDLE_TIMEOUT = 5e3;
var DEFAULT_WAIT_STATE = "complete";
async function url(path, options = {}) {
if (typeof path !== "string") {
throw new Error('Parameter for "url" command needs to be type of string');
}
if (typeof this.options.baseUrl === "string" && this.options.baseUrl) {
path = new URL(path, this.options.baseUrl).href;
}
if (this.isBidi && path.startsWith("http")) {
let resetPreloadScript;
const contextManager = getContextManager(this);
const context = await contextManager.getCurrentContext();
if (options.onBeforeLoad) {
if (typeof options.onBeforeLoad !== "function") {
throw new Error('Option "onBeforeLoad" must be a function, but received: '.concat(typeof options.onBeforeLoad));
}
resetPreloadScript = await this.addInitScript(options.onBeforeLoad);
}
if (options.auth) {
options.headers = {
...options.headers || {},
Authorization: "Basic ".concat(btoa("".concat(options.auth.user, ":").concat(options.auth.pass)))
};
}
let mock2;
if (options.headers) {
mock2 = await this.mock(path);
mock2.requestOnce({ headers: options.headers });
}
const classicPageLoadStrategy = this.capabilities.pageLoadStrategy === "none" ? "none" : this.capabilities.pageLoadStrategy === "normal" ? "complete" : this.capabilities.pageLoadStrategy === "eager" ? "interactive" : void 0;
const wait = options.wait === "networkIdle" ? "complete" : options.wait || classicPageLoadStrategy || DEFAULT_WAIT_STATE;
const navigation = await this.browsingContextNavigate({
context,
url: path,
wait
}).catch((err) => {
if (
// Chrome error message
err.message.includes("navigation canceled by concurrent navigation") || // Firefox error message
err.message.includes("failed with error: unknown error") || // Race condition where the context is destroyed before navigation
err.message.includes("no such frame")
) {
return this.navigateTo(validateUrl(path));
}
throw err;
});
if (mock2) {
await mock2.restore();
}
const network = getNetworkManager(this);
if (options.wait === "networkIdle") {
const timeout = options.timeout || DEFAULT_NETWORK_IDLE_TIMEOUT;
await this.waitUntil(async () => {
return network.getPendingRequests(context).length === 0;
}, {
timeout,
timeoutMsg: "Navigation to '".concat(path, "' timed out after ").concat(timeout, "ms with ").concat(network.getPendingRequests(context).length, " (").concat(network.getPendingRequests(context).map((r) => r.url).join(", "), ") pending requests")
});
}
if (resetPreloadScript) {
await resetPreloadScript.remove();
}
if (!navigation) {
return;
}
const request = await this.waitUntil(
() => network.getRequestResponseData(navigation.navigation),
/**
* set a short interval to immediately return once the first request payload comes in
*/
{
interval: 1,
timeoutMsg: "Navigation to '".concat(path, "' timed out as no request payload was received")
}
);
return request;
}
if (Object.keys(options).length > 0) {
throw new Error("Setting url options is only supported when automating browser using WebDriver Bidi protocol");
}
await this.navigateTo(validateUrl(path));
}
// src/commands/browser/waitUntil.ts
import { getBrowserObject as getBrowserObject10 } from "@wdio/utils";
function waitUntil(condition, {
timeout = this.options.waitforTimeout,
interval = this.options.waitforInterval,
timeoutMsg
} = {}) {
if (typeof condition !== "function") {
throw new Error("Condition is not a function");
}
if (typeof timeout !== "number") {
timeout = this.options.waitforTimeout;
}
if (typeof interval !== "number") {
interval = this.options.waitforInterval;
}
const browser = getBrowserObject10(this);
const abort = new AbortController();
const abortOnSessionEnd = (result) => {
if (result.command === "deleteSession") {
abort.abort();
}
};
browser.on("result", abortOnSessionEnd);
const fn = condition.bind(this);
const timer = new Timer_default(interval, timeout, fn, true, abort.signal);
return timer.catch((e) => {
if (e.message === "timeout") {
if (typeof timeoutMsg === "string") {
throw new Error(timeoutMsg);
}
throw new Error("waitUntil condition timed out after ".concat(timeout, "ms"));
}
const err = new Error("waitUntil condition failed with the following reason: ".concat(e && e.message || e));
const origStack = e.stack;
if (!origStack || !err.stack) {
throw err;
}
const [errMsg, ...waitUntilErrorStackLines] = err.stack.split("\n");
err.stack = [
errMsg,
...origStack.split("\n").slice(1),
" ---",
...waitUntilErrorStackLines
].filter((errorLine) => !errorLine.includes("/node_modules/webdriverio/") && !errorLine.includes("/node_modules/@wdio/")).join("\n");
throw err;
}).finally(() => {
browser.off("result", abortOnSessionEnd);
});
}
// src/commands/mobile/swipe.ts
import logger18 from "@wdio/logger";
// src/types.ts
var MobileScrollDirection = /* @__PURE__ */ ((MobileScrollDirection2) => {
MobileScrollDirection2["Down"] = "down";
MobileScrollDirection2["Up"] = "up";
MobileScrollDirection2["Left"] = "left";
MobileScrollDirection2["Right"] = "right";
return MobileScrollDirection2;
})(MobileScrollDirection || {});
// src/commands/mobile/swipe.ts
var log18 = logger18("webdriverio");
var SWIPE_DEFAULTS = {
DIRECTION: "up" /* Up */,
DURATION: 1500,
PERCENT: 0.95
};
async function swipe(options) {
const browser = this;
if (!browser.isNativeContext) {
throw new Error("The swipe command is only available for mobile platforms in the NATIVE context.");
}
let { scrollableElement, from, to } = options || {};
if (scrollableElement && (from || to)) {
log18.warn("`scrollableElement` is provided, so `from` and `to` will be ignored.");
}
if (!from || !to) {
scrollableElement = scrollableElement || await getScrollableElement(browser);
({ from, to } = await calculateFromTo({
browser,
direction: (options == null ? void 0 : options.direction) || SWIPE_DEFAULTS.DIRECTION,
percentage: options == null ? void 0 : options.percent,
scrollableElement
}));
}
return w3cSwipe({ browser, duration: (options == null ? void 0 : options.duration) || SWIPE_DEFAULTS.DURATION, from, to });
}
async function calculateFromTo({
browser,
direction,
percentage,
scrollableElement
}) {
let swipePercentage = SWIPE_DEFAULTS.PERCENT;
if (percentage !== void 0) {
if (isNaN(percentage)) {
log18.warn("The percentage to swipe should be a number.");
} else if (percentage < 0 || percentage > 1) {
log18.warn("The percentage to swipe should be a number between 0 and 1.");
} else {
swipePercentage = percentage;
}
}
const { x, y, width, height } = await browser.getElementRect(await (scrollableElement == null ? void 0 : scrollableElement.elementId));
const verticalOffset = height - height * swipePercentage;
const horizontalOffset = width - width * swipePercentage;
const scrollRectangles = {
top: { x: Math.round(x + width / 2), y: Math.round(y + verticalOffset / 2) },
right: { x: Math.round(x + width - horizontalOffset / 2), y: Math.round(y + height / 2) },
bottom: { x: Math.round(x + width / 2), y: Math.round(y + height - verticalOffset / 2) },
left: { x: Math.round(x + horizontalOffset / 2), y: Math.round(y + height / 2) }
};
let from;
let to;
switch (direction) {
case "down" /* Down */:
from = scrollRectangles.top;
to = scrollRectangles.bottom;
break;
case "left" /* Left */:
from = scrollRectangles.right;
to = scrollRectangles.left;
break;
case "right" /* Right */:
from = scrollRectangles.left;
to = scrollRectangles.right;
break;
case "up" /* Up */:
from = scrollRectangles.bottom;
to = scrollRectangles.top;
break;
default:
throw new Error("Unknown direction: ".concat(direction));
}
return { from, to };
}
async function getScrollableElement(browser) {
const defaultAndroidSelector = "//android.widget.ScrollView";
const defaultIosSelector = '-ios predicate string:type == "XCUIElementTypeApplication"';
const selector = browser.isIOS ? (
// For iOS, we need to find the application element, if we can't find it, we should throw an error
defaultIosSelector
) : (
// There is always a scrollview for Android or, if this fails we should throw an error
defaultAndroidSelector
);
const scrollableElements = await browser.$$(
selector
);
if (scrollableElements.length > 0) {
return scrollableElements[0];
}
throw new Error(
"Default scrollable element '".concat(browser.isIOS ? defaultIosSelector : defaultAndroidSelector, "' was not found. Our advice is to provide a scrollable element like this:\n\nawait browser.swipe({ scrollableElement: $('#scrollable') });\n\n ")
);
}
async function w3cSwipe({ browser, duration, from, to }) {
await browser.action("pointer", {
parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
}).move(from.x, from.y).down().pause(10).move({ duration, x: to.x, y: to.y }).up().perform();
return browser.pause(500);
}
// src/commands/mobile/tap.ts
import logger19 from "@wdio/logger";
import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
var log19 = logger19("webdriver");
async function tap(options) {
const isElement2 = this.selector !== void 0;
const element = isElement2 ? this : null;
const browser = isElement2 ? getBrowserObject11(this) : this;
if (!browser.isMobile) {
throw new Error("The tap command is only available for mobile platforms.");
}
validateTapOptions(options);
if (element) {
return await elementTap(browser, element, options);
}
if (!options || options.x === void 0 || options.y === void 0) {
throw new Error("The tap command requires x and y coordinates to be set for screen taps.");
}
return await screenTap(browser, options);
}
function validateTapOptions(options) {
if (options) {
if (typeof options !== "object" || Array.isArray(options)) {
throw new TypeError("Options must be an object.");
}
const { x, y, ...otherArgs } = options;
if (x === void 0 !== (y === void 0)) {
throw new TypeError("If ".concat(x !== void 0 ? "x" : "y", " is set, then ").concat(x !== void 0 ? "y" : "x", " must also be set."));
}
if (x !== void 0 && y !== void 0 && Object.keys(otherArgs).length > 0) {
throw new TypeError("If x and y are provided, no other arguments are allowed. Found: ".concat(Object.keys(otherArgs).join(", ")));
}
const invalidCoordinates = [];
if (x !== void 0 && x < 0) {
invalidCoordinates.push("x");
}
if (y !== void 0 && y < 0) {
invalidCoordinates.push("y");
}
if (invalidCoordinates.length > 0) {
throw new TypeError("The ".concat(invalidCoordinates.join(" and "), " value").concat(invalidCoordinates.length > 1 ? "s" : "", " must be positive."));
}
}
}
async function elementTap(browser, element, options) {
if (browser.isNativeContext) {
return await nativeTap(element, browser, options);
}
if (options) {
log19.warn("The options object is not supported in Web environments and will be ignored.");
}
return await webTap(element);
}
async function webTap(element) {
return element.click();
}
async function executeNativeTap(browser, options) {
return await browser.execute(
"mobile: ".concat(browser.isIOS ? "tap" : "clickGesture"),
{ ...browser.isIOS ? { x: 0, y: 0 } : {}, ...options }
);
}
async function nativeTap(element, browser, options = {}) {
try {
if (!element.elementId) {
throw new Error("no such element");
}
return await executeNativeTap(browser, { elementId: element.elementId });
} catch (error) {
let err = error;
if (typeof error === "string") {
err = new Error(error);
}
if (!err.message.includes("no such element")) {
throw err;
}
const scrollIntoViewOptions = Object.fromEntries(
Object.entries({
direction: options == null ? void 0 : options.direction,
maxScrolls: options == null ? void 0 : options.maxScrolls,
scrollableElement: options == null ? void 0 : options.scrollableElement
}).filter(([_, value]) => value !== void 0)
);
try {
await element.scrollIntoView(scrollIntoViewOptions);
return await executeNativeTap(browser, { elementId: element.elementId });
} catch (scrollError) {
let err2 = scrollError;
if (typeof scrollError === "string") {
err2 = new Error(scrollError);
}
if (err2.message.includes("Element not found within scroll limit of")) {
throw new Error("Element not found within the automatic 'tap' scroll limit of ".concat((scrollIntoViewOptions == null ? void 0 : scrollIntoViewOptions.maxScrolls) || "10", ' scrolls by scrolling "').concat((scrollIntoViewOptions == null ? void 0 : scrollIntoViewOptions.direction) || "down", '". ') + "The 'tap' methods will automatically scroll if it can't find the element. It might be that 'direction|maxScrolls|scrollableElement' are not correct. You can change change them like this:\n\nawait elem.tap({\n direction: 'left' // possible options are: 'up|down|left|right'\n maxScrolls: 15,\n scrollableElement: $('#scrollable'),\n});\n\n ");
} else if (err2.message.includes("Default scrollable element")) {
const match = err2.message.match(/Default scrollable element '(.*?)' was not found/);
const scrollableElement = (match == null ? void 0 : match[1]) || "unknown-scrollable-element";
throw new Error("The 'tap' method tried to automatically scroll to the element but couldn't find the default scrollable element. '".concat(scrollableElement, "' ") + "If needed you can provide a custom scrollable element, together with the 'direction' and the 'maxScrolls' like this:\n\nawait elem.tap({\n scrollableElement: $('#scrollable'),\n});\n\n ");
}
throw err2;
}
}
}
async function screenTap(browser, options) {
const { x, y } = options;
if (browser.isNativeContext) {
return await executeNativeTap(browser, options);
}
return await browser.action(
"pointer",
{
parameters: { pointerType: "touch" }
}
).move({ x, y }).down({ button: 0 }).pause(10).up({ button: 0 }).perform();
}
// src/commands/mobile/getContext.ts
import logger20 from "@wdio/logger";
var log20 = logger20("webdriver");
async function getContext(options) {
const browser = this;
if (!browser.isMobile) {
throw new Error("The `getContext` command is only available for mobile platforms.");
}
const currentAppiumContext = await browser.getAppiumContext();
if (!options || !(options == null ? void 0 : options.returnDetailedContext) || currentAppiumContext === "NATIVE_APP") {
return currentAppiumContext;
}
delete options.returnDetailedContext;
return getDetailedContext(browser, currentAppiumContext, options);
}
async function getDetailedContext(browser, currentAppiumContext, options) {
const detailedContexts = await browser.getContexts({
...options,
// Defaults
returnDetailedContexts: true,
// We want to get back the detailed context information
isAndroidWebviewVisible: true,
// We only want to get back the visible webviews
filterByCurrentAndroidApp: true,
// We only want to get back the webviews that are attached to the current app
returnAndroidDescriptionData: false
// We don't want to get back the Android Webview description data
});
const parsedContexts = detailedContexts.filter((context) => context.id === currentAppiumContext);
if (parsedContexts.length > 1) {
log20.warn("We found more than 1 detailed context for the current context '".concat(currentAppiumContext, "'. We will return the first context."));
return parsedContexts[0];
} else if (parsedContexts.length === 0) {
log20.warn("We did not get back any detailed context for the current context '".concat(currentAppiumContext, "'. We will return the current context as a string."));
return currentAppiumContext;
}
return parsedContexts[0];
}
// src/commands/mobile/getContexts.ts
import logger21 from "@wdio/logger";
var log21 = logger21("webdriver");
async function getContexts(options) {
const browser = this;
if (!browser.isMobile) {
throw new Error("The `getContexts` command is only available for mobile platforms.");
}
if (!options || !options.returnDetailedContexts) {
log21.info("The standard Appium `contexts` method is used. If you want to get more detailed data, you can set `returnDetailedContexts` to `true`.");
return browser.getAppiumContexts();
}
const defaultOptions = {
androidWebviewConnectionRetryTime: 500,
androidWebviewConnectTimeout: 5e3,
filterByCurrentAndroidApp: false,
isAndroidWebviewVisible: true,
returnAndroidDescriptionData: false
};
return getCurrentContexts({ browser, ...{ ...defaultOptions, ...options } });
}
var CHROME_PACKAGE_NAME = "com.android.chrome";
async function parsedAndroidContexts({
contexts,
filterByCurrentAndroidApp,
isAttachedAndVisible,
packageName
}) {
const currentWebviewName = "WEBVIEW_".concat(packageName);
let parsedContexts = contexts;
if (filterByCurrentAndroidApp) {
parsedContexts = contexts.filter((context) => context.webviewName === currentWebviewName);
}
const result = [{ id: "NATIVE_APP" }];
if (!parsedContexts || parsedContexts.length < 1) {
return result;
}
parsedContexts.forEach(
(context) => {
var _a;
return (_a = context.pages) == null ? void 0 : _a.filter((page) => {
if (packageName === CHROME_PACKAGE_NAME) {
return true;
}
if (page.type === "page" && page.description) {
let descriptionObj;
try {
descriptionObj = JSON.parse(page.description);
} catch (e) {
return false;
}
return isAttachedAndVisible ? descriptionObj.attached === true && descriptionObj.visible === true : true;
}
return !isAttachedAndVisible;
}).forEach((page) => {
const {
attached = false,
empty = false,
height = 0,
never_attached: neverAttached = false,
screenX = 0,
screenY = 0,
visible = false,
width = 0
} = JSON.parse(page.description || "{}");
const pageData = {
androidWebviewData: {
attached,
empty,
height,
neverAttached,
screenX,
screenY,
visible,
width
},
id: context.webviewName,
title: page.title,
url: page.url,
packageName: context.info["Android-Package"],
webviewPageId: page.id
};
result.push(pageData);
});
}
);
return result;
}
async function getCurrentContexts({
browser,
androidWebviewConnectionRetryTime,
androidWebviewConnectTimeout,
filterByCurrentAndroidApp,
isAndroidWebviewVisible,
returnAndroidDescriptionData,
waitForWebviewMs
}) {
const contexts = await (waitForWebviewMs !== void 0 ? browser.execute("mobile: getContexts", { waitForWebviewMs }) : browser.execute("mobile: getContexts"));
if (browser.isIOS) {
return contexts;
}
const packageName = await browser.getCurrentPackage();
const startTime = Date.now();
const retryInterval = androidWebviewConnectionRetryTime;
let isPackageNameMissing = false;
while (Date.now() - startTime < androidWebviewConnectTimeout) {
const parsedContexts = await parsedAndroidContexts({
contexts,
filterByCurrentAndroidApp,
isAttachedAndVisible: isAndroidWebviewVisible,
packageName
});
const matchingContexts = parsedContexts.filter((context) => context.packageName === packageName);
isPackageNameMissing = matchingContexts.length === 0;
const hasAndroidWebviewData = matchingContexts.some((context) => Boolean(context.androidWebviewData));
const isAndroidWebviewDataMissing = matchingContexts.length > 0 && !hasAndroidWebviewData;
const hasNonEmptyAndroidWebviewData = matchingContexts.some((context) => context.androidWebviewData && !context.androidWebviewData.empty);
const isAndroidWebviewDataEmpty = matchingContexts.length > 0 && hasAndroidWebviewData && !hasNonEmptyAndroidWebviewData;
if (packageName === CHROME_PACKAGE_NAME) {
return parsedContexts;
}
if (!isPackageNameMissing && !isAndroidWebviewDataMissing && !isAndroidWebviewDataEmpty) {
if (!returnAndroidDescriptionData) {
parsedContexts.forEach((context) => {
if ("androidWebviewData" in context) {
delete context.androidWebviewData;
}
});
}
return parsedContexts;
}
await new Promise((resolve) => setTimeout(resolve, retryInterval));
}
throw new Error(
"The packageName '".concat(packageName, "' ").concat(isPackageNameMissing ? "could not be found!" : "matches, but no webview with pages was loaded in this response: " + JSON.stringify(contexts) + "'")
);
}
// src/commands/mobile/switchContext.ts
import logger22 from "@wdio/logger";
var log22 = logger22("webdriver");
async function switchContext(options) {
const browser = this;
if (!browser.isMobile) {
throw new Error("The `switchContext` command is only available for mobile platforms.");
}
if (!options) {
throw new Error("You need to provide at least a context name to switch to. See https://webdriver.io/docs/api/mobile/switchContext for more information.");
}
if (typeof options === "string") {
log22.info("The standard Appium `context`-method is used. If you want to switch to a webview with a specific title or url, please provide an object with the `title` or `url` property. See https://webdriver.io/docs/api/mobile/switchContext for more information.");
return browser.switchAppiumContext(options);
}
if (!options.title && !options.url) {
throw new Error("You need to provide at least a `title` or `url` property to use full potential of the `switchContext` command. See https://webdriver.io/docs/api/mobile/switchContext for more information.");
}
return switchToContext({ browser, options });
}
async function switchToContext({ browser, options }) {
var _a;
const getContextsOptions = {
returnDetailedContexts: true,
filterByCurrentAndroidApp: false,
isAndroidWebviewVisible: false,
returnAndroidDescriptionData: true,
...(options == null ? void 0 : options.androidWebviewConnectionRetryTime) && { androidWebviewConnectionRetryTime: options.androidWebviewConnectionRetryTime },
...(options == null ? void 0 : options.androidWebviewConnectTimeout) && { androidWebviewConnectTimeout: options.androidWebviewConnectTimeout }
};
const contexts = await browser.getContexts(getContextsOptions);
let identifier;
if (options.appIdentifier) {
identifier = options.appIdentifier;
} else {
identifier = browser.isIOS ? (_a = await browser.execute("mobile: activeAppInfo")) == null ? void 0 : _a.bundleId : await browser.getCurrentPackage();
}
const { matchingContext, reasons } = findMatchingContext({ browser, contexts, identifier, ...(options == null ? void 0 : options.title) && { title: options.title }, ...(options == null ? void 0 : options.url) && { url: options.url } });
if (!matchingContext) {
throw new Error(reasons.join("\n"));
}
log22.info("WebdriverIO found a matching context:", JSON.stringify(matchingContext, null, 2));
if (!browser.isIOS) {
const webviewName = "WEBVIEW_".concat(identifier);
await browser.switchAppiumContext(webviewName);
}
const switchFunction = browser.isIOS ? browser.switchAppiumContext.bind(browser) : browser.switchToWindow.bind(browser);
const matchingContextId = browser.isIOS ? matchingContext.id : matchingContext.webviewPageId;
return switchFunction(matchingContextId);
}
function findMatchingContext({
browser: { isIOS },
contexts,
identifier,
title,
url: url2
}) {
const reasons = [];
reasons.push("We parsed a total of ".concat(contexts.length, " Webviews but did not find a matching context. The reasons are:"));
const matchingContext = contexts.find((context, index) => {
var _a, _b, _c, _d;
reasons.push("- Webview ".concat(index + 1, ": '").concat(context.id, "'"));
if (context.id === "NATIVE_APP") {
reasons.push(" - Skipped context because it is NATIVE_APP");
return false;
}
const idMatch = isIOS ? context.bundleId === identifier : context.packageName === identifier;
const titleMatches = title ? title instanceof RegExp ? title.test(context.title || "") : (_a = context.title) == null ? void 0 : _a.includes(title) : true;
const urlMatches = url2 ? url2 instanceof RegExp ? url2.test(context.url || "") : (_b = context.url) == null ? void 0 : _b.includes(url2) : true;
const additionalAndroidChecks = isIOS ? true : ((_c = context.androidWebviewData) == null ? void 0 : _c.attached) && ((_d = context.androidWebviewData) == null ? void 0 : _d.visible);
if (!idMatch) {
reasons.push(" - App ".concat(isIOS ? "bundleId" : "packageName", " '").concat(identifier, "' did not match: '").concat(context.id, "'"));
}
if (!titleMatches) {
reasons.push(" - Title '".concat(title, "' did not match: '").concat(context.title, "'"));
}
if (!urlMatches) {
reasons.push(" - URL '".concat(url2, "' did not match: '").concat(context.url, "'"));
}
if (!additionalAndroidChecks) {
reasons.push(" - Additional Android checks failed");
}
return idMatch && titleMatches && urlMatches && additionalAndroidChecks;
});
return { matchingContext, reasons };
}
// src/commands/mobile/relaunchActiveApp.ts
async function relaunchActiveApp() {
const browser = this;
if (!browser.isMobile) {
throw new Error("The `relaunchActiveApp` command is only available for mobile platforms.");
}
if (browser.isIOS) {
const { bundleId, processArguments: { args, env } } = await browser.execute("mobile: activeAppInfo");
const iOSLaunchOptions = {
bundleId,
...args.length > 0 && { arguments: args },
...Object.keys(env).length > 0 && { environment: env }
};
await browser.execute("mobile: terminateApp", { bundleId });
return browser.execute("mobile:launchApp", iOSLaunchOptions);
}
const packageName = await browser.getCurrentPackage();
await browser.execute("mobile: terminateApp", { appId: packageName });
return browser.execute("mobile: activateApp", { appId: packageName });
}
// src/commands/mobile/deepLink.ts
async function deepLink(link, appIdentifier) {
const browser = this;
if (!browser.isMobile) {
throw new Error("The `deepLink` command is only available for mobile platforms.");
}
if (!isDeepLinkUrl(link)) {
throw new Error("The provided link is not a valid deep link URL.".concat(browser.isIOS ? " If your url is a `universal deep link` then use the `url` command instead." : ""));
}
if (!appIdentifier) {
const mobileOS = browser.isIOS ? "iOS" : "Android";
const identifierValue = browser.isIOS ? "bundleId" : "package";
throw new Error("When using a deep link URL for ".concat(mobileOS, ", you need to provide the `").concat(identifierValue, "` of the app that the deep link should open."));
}
return browser.execute("mobile:deepLink", {
url: link,
[browser.isIOS ? "bundleId" : "package"]: appIdentifier
});
}
function isDeepLinkUrl(link) {
const deepLinkRegex = /^(?!https?:\/\/)[a-zA-Z][\w+\-.]*:\/\//;
return deepLinkRegex.test(link);
}
// src/commands/element.ts
var element_exports = {};
__export(element_exports, {
$: () => $2,
$$: () => $$2,
addValue: () => addValue,
clearValue: () => clearValue,
click: () => click,
custom$: () => custom$2,
custom$$: () => custom$$2,
doubleClick: () => doubleClick,
dragAndDrop: () => dragAndDrop,
execute: () => execute2,
executeAsync: () => executeAsync2,
getAttribute: () => getAttribute,
getCSSProperty: () => getCSSProperty,
getComputedLabel: () => getComputedLabel,
getComputedRole: () => getComputedRole,
getElement: () => getElement2,
getHTML: () => getHTML2,
getLocation: () => getLocation,
getProperty: () => getProperty,
getSize: () => getSize,
getTagName: () => getTagName,
getText: () => getText,
getValue: () => getValue,
isClickable: () => isClickable,
isDisplayed: () => isDisplayed,
isEnabled: () => isEnabled,
isEqual: () => isEqual,
isExisting: () => isExisting,
isFocused: () => isFocused2,
isSelected: () => isSelected,
isStable: () => isStable,
longPress: () => longPress,
moveTo: () => moveTo,
nextElement: () => nextElement,
parentElement: () => parentElement,
pinch: () => pinch,
previousElement: () => previousElement,
react$: () => react$4,
react$$: () => react$$4,
saveScreenshot: () => saveScreenshot2,
scrollIntoView: () => scrollIntoView,
selectByAttribute: () => selectByAttribute,
selectByIndex: () => selectByIndex,
selectByVisibleText: () => selectByVisibleText,
setValue: () => setValue,
shadow$: () => shadow$,
shadow$$: () => shadow$$,
tap: () => tap,
touchAction: () => touchAction3,
waitForClickable: () => waitForClickable,
waitForDisplayed: () => waitForDisplayed,
waitForEnabled: () => waitForEnabled,
waitForExist: () => waitForExist,
waitForStable: () => waitForStable,
waitUntil: () => waitUntil2,
zoom: () => zoom
});
// src/commands/element/$$.ts
var $$2 = $$;
// src/commands/element/$.ts
var $2 = $;
// src/commands/element/addValue.ts
import { CommandRuntimeOptions } from "webdriver";
var VALID_TYPES = ["string", "number"];
function addValue(value, options) {
if (!VALID_TYPES.includes(typeof value)) {
throw new Error(
'The setValue/addValue command only take string or number values. If you like to use special characters, use the "keys" command.'
);
}
if (options) {
return this.elementSendKeys(this.elementId, value.toString(), new CommandRuntimeOptions(options));
}
return this.elementSendKeys(this.elementId, value.toString());
}
// src/commands/element/clearValue.ts
function clearValue() {
return this.elementClear(this.elementId);
}
// src/commands/element/click.ts
import logger23 from "@wdio/logger";
import { getBrowserObject as getBrowserObject12 } from "@wdio/utils";
var log23 = logger23("webdriver");
function click(options) {
if (typeof options !== "undefined") {
if (typeof options !== "object" || Array.isArray(options)) {
throw new TypeError("Options must be an object");
}
return actionClick(this, options);
}
return elementClick(this);
}
async function workaround(element) {
await element.scrollIntoView({ block: "center", inline: "center" });
}
async function elementClick(element) {
try {
return await element.elementClick(element.elementId);
} catch (error) {
let err = error;
if (typeof error === "string") {
err = new Error(error);
}
if (!err.message.includes("element click intercepted")) {
throw err;
}
await workaround(element);
return element.elementClick(element.elementId);
}
}
async function actionClick(element, options) {
const defaultOptions = {
button: 0,
x: 0,
y: 0,
skipRelease: false,
duration: 0
};
const { button, x, y, skipRelease, duration } = { ...defaultOptions, ...options };
if (typeof x !== "number" || typeof y !== "number" || !Number.isInteger(x) || !Number.isInteger(y)) {
throw new TypeError("Coordinates must be integers");
}
if (!buttonValue.includes(button)) {
throw new Error("Button type not supported.");
}
const browser = getBrowserObject12(element);
if (x || y) {
const { width, height } = await browser.getElementRect(element.elementId);
if (x && x < -Math.floor(width / 2) || x && x > Math.floor(width / 2)) {
log23.warn("x would cause a out of bounds error as it goes outside of element");
}
if (y && y < -Math.floor(height / 2) || y && y > Math.floor(height / 2)) {
log23.warn("y would cause a out of bounds error as it goes outside of element");
}
}
const clickNested = async () => {
await browser.action("pointer", {
parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
}).move({ origin: element, x, y }).down({ button }).pause(duration).up({ button }).perform(skipRelease);
};
try {
return await clickNested();
} catch {
await workaround(element);
return clickNested();
}
}
// src/commands/element/custom$$.ts
import { ELEMENT_KEY as ELEMENT_KEY9 } from "webdriver";
import { getBrowserObject as getBrowserObject13 } from "@wdio/utils";
async function custom$$2(strategyName, ...strategyArguments) {
const browserObject = getBrowserObject13(this);
const strategy = browserObject.strategies.get(strategyName);
if (!strategy) {
throw Error("No strategy found for " + strategyName);
}
if (!this.elementId) {
throw Error("Can't call custom$ on element with selector \"".concat(this.selector, "\" because element wasn't found"));
}
const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
let res = await browserObject.execute(strategy, ...strategyArguments, this);
if (!Array.isArray(res)) {
res = [res];
}
res = res.filter((el) => !!el && typeof el[ELEMENT_KEY9] === "string");
const elements = res.length ? await getElements.call(this, strategyRef, res) : [];
return enhanceElementsArray(elements, this, strategyName, "custom$$", strategyArguments);
}
// src/commands/element/custom$.ts
import { ELEMENT_KEY as ELEMENT_KEY10 } from "webdriver";
import { getBrowserObject as getBrowserObject14 } from "@wdio/utils";
async function custom$2(strategyName, ...strategyArguments) {
const browserObject = getBrowserObject14(this);
const strategy = browserObject.strategies.get(strategyName);
if (!strategy) {
throw Error("No strategy found for " + strategyName);
}
if (!this.elementId) {
throw Error("Can't call custom$ on element with selector \"".concat(this.selector, "\" because element wasn't found"));
}
const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
let res = await browserObject.execute(strategy, ...strategyArguments, this);
if (Array.isArray(res)) {
res = res[0];
}
if (res && typeof res[ELEMENT_KEY10] === "string") {
return await getElement.call(this, strategyRef, res);
}
return await getElement.call(this, strategyRef, new Error("no such element"));
}
// src/commands/element/doubleClick.ts
import { getBrowserObject as getBrowserObject15 } from "@wdio/utils";
async function doubleClick() {
const browser = getBrowserObject15(this);
return browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: this }).down().up().pause(10).down().up().perform();
}
// src/commands/element/dragAndDrop.ts
import { ELEMENT_KEY as ELEMENT_KEY11 } from "webdriver";
import { getBrowserObject as getBrowserObject16 } from "@wdio/utils";
async function dragAndDrop(target, options = {}) {
const moveToCoordinates = target;
const moveToElement = await target;
if (
/**
* no target was specified
*/
!moveToElement || /**
* target is not from type element
*/
moveToElement.constructor.name !== "Element" && /**
* and is also not an object with x and y number parameters
*/
(typeof moveToCoordinates.x !== "number" || typeof moveToCoordinates.y !== "number")
) {
throw new Error('command dragAndDrop requires an WebdriverIO Element or and object with "x" and "y" variables as first parameter');
}
const ACTION_BUTTON = 0;
const browser = getBrowserObject16(this);
const defaultOptions = { duration: browser.isMobile ? 250 : 10 };
const { duration } = { ...defaultOptions, ...options };
const isMovingToElement = moveToElement.constructor.name === "Element";
const sourceRef = { [ELEMENT_KEY11]: this[ELEMENT_KEY11] };
const targetRef = { [ELEMENT_KEY11]: moveToElement[ELEMENT_KEY11] };
const origin = sourceRef;
const targetOrigin = isMovingToElement ? targetRef : "pointer";
const targetX = isMovingToElement ? 0 : moveToCoordinates.x;
const targetY = isMovingToElement ? 0 : moveToCoordinates.y;
return browser.action("pointer", {
parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
}).move({ duration: 0, origin, x: 0, y: 0 }).down({ button: ACTION_BUTTON }).pause(10).move({ duration, origin: targetOrigin, x: targetX, y: targetY }).up({ button: ACTION_BUTTON }).perform();
}
// src/commands/element/execute.ts
import { getBrowserObject as getBrowserObject17 } from "@wdio/utils";
async function execute2(script, ...args) {
const scope = this;
const browser = getBrowserObject17(scope);
await scope.waitForExist();
return browser.execute(script, scope, ...args);
}
// src/commands/element/executeAsync.ts
import { getBrowserObject as getBrowserObject18 } from "@wdio/utils";
async function executeAsync2(script, ...args) {
const scope = this;
const browser = getBrowserObject18(scope);
return browser.executeAsync(script, scope, ...args);
}
// src/commands/element/getAttribute.ts
function getAttribute(attributeName) {
return this.getElementAttribute(this.elementId, attributeName);
}
// src/commands/element/getCSSProperty.ts
import cssShorthandProps from "css-shorthand-properties";
import { getBrowserObject as getBrowserObject19 } from "@wdio/utils";
async function getCSSProperty(cssProperty, pseudoElement) {
const getCSSProperty2 = cssShorthandProps.isShorthand(cssProperty) ? getShorthandPropertyCSSValue : getPropertyCSSValue;
const cssValue2 = await getCSSProperty2.call(
this,
{
cssProperty,
pseudoElement
}
);
return parseCSS(cssValue2, cssProperty);
}
async function getShorthandPropertyCSSValue(options) {
const { pseudoElement, cssProperty } = options;
const properties = getShorthandProperties(cssProperty);
if (pseudoElement) {
const cssValues2 = await Promise.all(
properties.map((prop) => getPseudoElementCSSValue(
this,
{
pseudoElement,
cssProperty: prop
}
))
);
return mergeEqualSymmetricalValue(cssValues2);
}
const cssValues = await Promise.all(
properties.map((prop) => this.getElementCSSValue(this.elementId, prop))
);
return mergeEqualSymmetricalValue(cssValues);
}
async function getPropertyCSSValue(options) {
const { pseudoElement, cssProperty } = options;
if (pseudoElement) {
return await getPseudoElementCSSValue(
this,
{
pseudoElement,
cssProperty
}
);
}
return await this.getElementCSSValue(this.elementId, cssProperty);
}
function getShorthandProperties(cssProperty) {
return cssShorthandProps.expand(cssProperty);
}
function mergeEqualSymmetricalValue(cssValues) {
let newCssValues = [...cssValues];
while (newCssValues.length % 2 === 0) {
const mergedValues = [
newCssValues.slice(0, newCssValues.length / 2).join(" "),
newCssValues.slice(newCssValues.length / 2).join(" ")
];
const hasEqualProperties = mergedValues.every((v) => v === mergedValues[0]);
if (!hasEqualProperties) {
break;
}
newCssValues = newCssValues.slice(0, newCssValues.length / 2);
}
return newCssValues.join(" ");
}
async function getPseudoElementCSSValue(elem, options) {
const browser = getBrowserObject19(elem);
const { cssProperty, pseudoElement } = options;
const cssValue2 = await browser.execute(
(elem2, pseudoElement2, cssProperty2) => {
if (typeof elem2.isConnected === "boolean" && !elem2.isConnected) {
throw new Error("stale element reference: element is not attached to the page document");
}
return window.getComputedStyle(elem2, pseudoElement2)[cssProperty2];
},
elem,
pseudoElement,
cssProperty
);
return cssValue2;
}
// src/commands/element/getComputedRole.ts
function getComputedRole() {
return this.getElementComputedRole(this.elementId);
}
// src/commands/element/getComputedLabel.ts
function getComputedLabel() {
return this.getElementComputedLabel(this.elementId);
}
// src/commands/element/getElement.ts
async function getElement2() {
return this;
}
// src/commands/element/getHTML.ts
import { ELEMENT_KEY as ELEMENT_KEY12 } from "webdriver";
import { prettify as prettifyFn } from "htmlfy";
import { getBrowserObject as getBrowserObject20 } from "@wdio/utils";
// src/scripts/getHTML.ts
function getHTML(element, includeSelectorTag) {
return element[includeSelectorTag ? "outerHTML" : "innerHTML"];
}
// src/scripts/getHTMLShadow.ts
function getHTMLShadow(element, includeSelectorTag, shadowElementIds = []) {
shadowElementIds.map(([id, elem]) => {
if (typeof elem.setAttribute !== "function") {
return;
}
elem.setAttribute("data-wdio-shadow-id", id);
});
const shadowElementHTML = shadowElementIds.map(([id, elem, shadow]) => {
if (!shadow) {
const html2 = elem[includeSelectorTag ? "outerHTML" : "innerHTML"];
return { id, html: html2 };
}
const styles = Array.from(shadow.adoptedStyleSheets || []).map(({ cssRules }) => Array.from(cssRules)).flat().map(({ cssText }) => cssText);
const html = shadow.innerHTML;
return { id, html, styles };
});
return {
html: element[includeSelectorTag ? "outerHTML" : "innerHTML"],
shadowElementHTML
};
}
// src/commands/element/getHTML.ts
var SHADOW_ID_ATTR_NAME = "data-wdio-shadow-id";
var SHADOW_ID_ATTR = "[".concat(SHADOW_ID_ATTR_NAME, "]");
async function getHTML2(options = {}) {
const browser = getBrowserObject20(this);
if (typeof options !== "object" && typeof options === "boolean") {
options = { includeSelectorTag: options };
} else if (typeof options !== "object") {
throw new Error("The `getHTML` options parameter must be an object");
}
const { includeSelectorTag, pierceShadowRoot, removeCommentNodes, prettify, excludeElements } = Object.assign({
includeSelectorTag: true,
pierceShadowRoot: true,
removeCommentNodes: true,
prettify: true,
excludeElements: []
}, options);
const basicGetHTML = (elementId, includeSelectorTag2) => {
return browser.execute(getHTML, {
[ELEMENT_KEY12]: elementId,
// w3c compatible
ELEMENT: elementId
// jsonwp compatible
}, includeSelectorTag2);
};
if (pierceShadowRoot && this.isBidi) {
if (globalThis.wdio) {
return globalThis.wdio.executeWithScope(
"getHTML",
this.elementId,
{ includeSelectorTag, pierceShadowRoot, removeCommentNodes, prettify }
);
}
const { load } = await import("cheerio");
const shadowRootManager = getShadowRootManager(browser);
const contextManager = getContextManager(browser);
const context = await contextManager.getCurrentContext();
const shadowRootElementPairs = shadowRootManager.getShadowElementPairsByContextId(context, this.elementId);
const elementsWithShadowRootAndIdVerified = (await Promise.all(
shadowRootElementPairs.map(([elemId, elem]) => browser.execute((elem2) => elem2.tagName, { [ELEMENT_KEY12]: elemId }).then(
() => [elemId, elem],
() => void 0
))
)).filter(Boolean).map(([elemId, shadowId]) => [
elemId,
{ [ELEMENT_KEY12]: elemId },
shadowId ? { [ELEMENT_KEY12]: shadowId } : void 0
]);
const { html, shadowElementHTML } = await this.execute(
getHTMLShadow,
includeSelectorTag,
elementsWithShadowRootAndIdVerified
);
const $3 = load(html);
populateHTML($3, shadowElementHTML.map(({ id, ...props }) => ({
...props,
id,
mode: shadowRootManager.getShadowRootModeById(context, id) || "open"
})));
return sanitizeHTML($3, { removeCommentNodes, prettify, excludeElements });
}
const returnHTML = await basicGetHTML(this.elementId, includeSelectorTag);
return sanitizeHTML(returnHTML, { removeCommentNodes, prettify });
}
function populateHTML($3, shadowElementHTML) {
const shadowElements = $3(SHADOW_ID_ATTR);
if (shadowElements.length === 0) {
return;
}
for (const elem of shadowElements) {
const id = elem.attribs[SHADOW_ID_ATTR_NAME];
const shadowReference = shadowElementHTML.find(({ id: shadowRootId }) => id === shadowRootId);
if (!shadowReference) {
continue;
}
$3("[".concat(SHADOW_ID_ATTR_NAME, '="').concat(id, '"]')).append([
'<template shadowrootmode="'.concat(shadowReference.mode, '">'),
shadowReference.styles && shadowReference.styles.length > 0 ? " <style>".concat(shadowReference.styles.join("\n"), "</style>") : "",
" ".concat(shadowReference.html),
"</template>"
].join("\n"));
delete elem.attribs[SHADOW_ID_ATTR_NAME];
}
populateHTML($3, shadowElementHTML);
}
function sanitizeHTML($3, options = {}) {
const isCheerioObject = $3 && typeof $3 !== "string";
if (isCheerioObject) {
for (const elemToRemove of options.excludeElements || []) {
$3(elemToRemove).remove();
}
if (options.removeCommentNodes) {
$3("*").contents().filter(function() {
return this.type === "comment";
}).remove();
}
}
let returnHTML = isCheerioObject ? $3("body").html() : $3;
if (!isCheerioObject && options.removeCommentNodes && returnHTML) {
returnHTML = returnHTML.replace(/<!--[\s\S]*?-->/g, "");
}
return options.prettify ? prettifyFn(returnHTML) : returnHTML;
}
// src/commands/element/getLocation.ts
async function getLocation(prop) {
const { x, y } = await getElementRect(this);
const location = { x, y };
if (prop === "x" || prop === "y") {
return location[prop];
}
return location;
}
// src/commands/element/getProperty.ts
function getProperty(property) {
return this.getElementProperty(this.elementId, property);
}
// src/commands/element/getSize.ts
async function getSize(prop) {
const rect = await getElementRect(this);
if (prop && typeof rect[prop] === "number") {
return rect[prop];
}
return {
width: rect.width,
height: rect.height
};
}
// src/commands/element/getTagName.ts
function getTagName() {
return this.getElementTagName(this.elementId);
}
// src/commands/element/getText.ts
function getText() {
return this.getElementText(this.elementId);
}
// src/commands/element/getValue.ts
function getValue() {
const value = this.isW3C && !this.isMobile ? this.getElementProperty(this.elementId, "value") : this.getElementAttribute(this.elementId, "value");
return value.then((res) => typeof res === "string" ? res : "");
}
// src/commands/element/isClickable.ts
import { ELEMENT_KEY as ELEMENT_KEY13 } from "webdriver";
import { getBrowserObject as getBrowserObject21 } from "@wdio/utils";
// src/scripts/isElementClickable.ts
function isElementClickable(elem) {
if (!elem.getBoundingClientRect || !elem.scrollIntoView || !elem.contains || !elem.getClientRects || !document.elementFromPoint) {
return false;
}
const isOldEdge = !!window.StyleMedia;
const scrollIntoViewFullSupport = !(window.safari || isOldEdge);
function getOverlappingElement(elem2, context) {
context = context || document;
const elemDimension = elem2.getBoundingClientRect();
const x = elemDimension.left + elem2.clientWidth / 2;
const y = elemDimension.top + elem2.clientHeight / 2;
return context.elementFromPoint(x, y);
}
function getOverlappingRects(elem2, context) {
context = context || document;
const rects = elem2.getClientRects();
const rect = rects[0];
const x = rect.left + rect.width / 2;
const y = rect.top + rect.height / 2;
return [context.elementFromPoint(x, y)];
}
function getOverlappingElements(elem2, context) {
return [getOverlappingElement(elem2, context)].concat(getOverlappingRects(elem2, context));
}
function nodeContains(elem2, otherNode) {
if (isOldEdge) {
let tmpElement = otherNode;
while (tmpElement) {
if (tmpElement === elem2) {
return true;
}
tmpElement = tmpElement.parentNode;
if (tmpElement && tmpElement.nodeType === 11 && tmpElement.host) {
tmpElement = tmpElement.host;
}
}
return false;
}
return elem2.contains(otherNode);
}
function isOverlappingElementMatch(elementsFromPoint, elem2) {
if (elementsFromPoint.some(function(elementFromPoint) {
return elementFromPoint === elem2 || nodeContains(elem2, elementFromPoint);
})) {
return true;
}
let elemsWithShadowRoot = [].concat(elementsFromPoint);
elemsWithShadowRoot = elemsWithShadowRoot.filter(function(x) {
return x && x.shadowRoot && x.shadowRoot.elementFromPoint;
});
let shadowElementsFromPoint = [];
for (let i = 0; i < elemsWithShadowRoot.length; ++i) {
const shadowElement = elemsWithShadowRoot[i];
shadowElementsFromPoint = shadowElementsFromPoint.concat(
getOverlappingElements(elem2, shadowElement.shadowRoot)
);
}
shadowElementsFromPoint = [].concat(shadowElementsFromPoint);
shadowElementsFromPoint = shadowElementsFromPoint.filter(function(x) {
return !elementsFromPoint.includes(x);
});
if (shadowElementsFromPoint.length === 0) {
return false;
}
return isOverlappingElementMatch(shadowElementsFromPoint, elem2);
}
function isElementInViewport2(elem2) {
if (!elem2.getBoundingClientRect) {
return false;
}
const rect = elem2.getBoundingClientRect();
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
const vertInView = rect.top < windowHeight && rect.top + rect.height > 0;
const horInView = rect.left < windowWidth && rect.left + rect.width > 0;
return vertInView && horInView;
}
function isEnabled2(elem2) {
return elem2.disabled !== true;
}
function hasOverlaps(elem2) {
return !isOverlappingElementMatch(getOverlappingElements(elem2), elem2);
}
function isFullyDisplayedInViewport(elem2) {
return isElementInViewport2(elem2) && !hasOverlaps(elem2);
}
function getViewportScrollPositions() {
return {
x: window.scrollX !== null && window.scrollX !== void 0 ? window.scrollX : window.pageXOffset,
y: window.scrollY !== null && window.scrollY !== void 0 ? window.scrollY : window.pageYOffset
};
}
let _isFullyDisplayedInViewport = isFullyDisplayedInViewport(elem);
if (!_isFullyDisplayedInViewport) {
const { x: originalX, y: originalY } = getViewportScrollPositions();
elem.scrollIntoView(scrollIntoViewFullSupport ? { block: "center", inline: "center" } : false);
_isFullyDisplayedInViewport = isFullyDisplayedInViewport(elem);
const { x: currentX, y: currentY } = getViewportScrollPositions();
if (currentX !== originalX || currentY !== originalY) {
window.scroll(originalX, originalY);
}
}
return _isFullyDisplayedInViewport && isEnabled2(elem);
}
// src/commands/element/isClickable.ts
async function isClickable() {
if (!await this.isDisplayed()) {
return false;
}
if (this.isMobile && this.isNativeContext) {
throw new Error("Method not supported in mobile native environment. It is unlikely that you need to use this command.");
}
const browser = getBrowserObject21(this);
return browser.execute(isElementClickable, {
[ELEMENT_KEY13]: this.elementId,
// w3c compatible
ELEMENT: this.elementId
// jsonwp compatible
});
}
// src/commands/element/isDisplayed.ts
import { getBrowserObject as getBrowserObject22 } from "@wdio/utils";
// src/scripts/isElementDisplayed.ts
function isElementDisplayed(element) {
function nodeIsElement(node) {
if (!node) {
return false;
}
switch (node.nodeType) {
case Node.ELEMENT_NODE:
case Node.DOCUMENT_NODE:
case Node.DOCUMENT_FRAGMENT_NODE:
return true;
default:
return false;
}
}
function parentElementForElement(element2) {
if (!element2) {
return null;
}
return enclosingNodeOrSelfMatchingPredicate(element2.parentNode, nodeIsElement);
}
function enclosingNodeOrSelfMatchingPredicate(targetNode, predicate) {
for (let node = targetNode; node && node !== targetNode.ownerDocument; node = node.parentNode) {
if (predicate(node)) {
return node;
}
}
return null;
}
function enclosingElementOrSelfMatchingPredicate(targetElement, predicate) {
for (let element2 = targetElement; element2 && element2 !== targetElement.ownerDocument; element2 = parentElementForElement(element2)) {
if (predicate(element2)) {
return element2;
}
}
return null;
}
function cascadedStylePropertyForElement(element2, property) {
if (!element2 || !property) {
return null;
}
if ("ShadowRoot" in window && element2 instanceof window.ShadowRoot) {
element2 = element2.host;
}
const computedStyle = window.getComputedStyle(element2);
const computedStyleProperty = computedStyle.getPropertyValue(property);
if (computedStyleProperty && computedStyleProperty !== "inherit") {
return computedStyleProperty;
}
const parentElement2 = parentElementForElement(element2);
return cascadedStylePropertyForElement(parentElement2, property);
}
function elementHasBoundingBox(element2) {
const boundingBox = element2.getBoundingClientRect();
return boundingBox.width > 0 && boundingBox.height > 0;
}
function elementSubtreeHasNonZeroDimensions(element2) {
if (elementHasBoundingBox(element2)) {
return true;
}
const boundingBox = element2.getBoundingClientRect();
if (element2.tagName.toUpperCase() === "PATH" && boundingBox.width + boundingBox.height > 0) {
const strokeWidth = cascadedStylePropertyForElement(element2, "stroke-width");
return !!strokeWidth && parseInt(strokeWidth, 10) > 0;
}
const cascadedOverflow = cascadedStylePropertyForElement(element2, "overflow");
if (cascadedOverflow === "hidden") {
return false;
}
return [].some.call(element2.childNodes, function(childNode) {
if (childNode.nodeType === Node.TEXT_NODE) {
return true;
}
if (nodeIsElement(childNode)) {
return elementSubtreeHasNonZeroDimensions(childNode);
}
return false;
});
}
function elementOverflowsContainer(element2) {
const cascadedOverflow = cascadedStylePropertyForElement(element2, "overflow");
if (cascadedOverflow !== "hidden") {
return false;
}
return true;
}
function isElementSubtreeHiddenByOverflow(element2) {
if (!element2) {
return false;
}
if (!elementOverflowsContainer(element2)) {
return false;
}
if (!element2.childNodes.length) {
return false;
}
return [].every.call(element2.childNodes, function(childNode) {
if (childNode.nodeType === Node.TEXT_NODE) {
return false;
}
if (!nodeIsElement(childNode)) {
return true;
}
if (!elementSubtreeHasNonZeroDimensions(childNode)) {
return true;
}
return isElementSubtreeHiddenByOverflow(childNode);
});
}
function isElementInsideShadowRoot(element2) {
if (!element2) {
return false;
}
if (element2.parentNode && element2.parentNode.host) {
return true;
}
return isElementInsideShadowRoot(element2.parentNode);
}
if (!isElementInsideShadowRoot(element) && (typeof document.contains === "function" ? !document.contains(element) : !document.body.contains(element))) {
return false;
}
switch (element.tagName.toUpperCase()) {
case "BODY":
return true;
case "SCRIPT":
case "NOSCRIPT":
return false;
case "OPTGROUP":
case "OPTION": {
const enclosingSelectElement = enclosingNodeOrSelfMatchingPredicate(element, function(e) {
return e.tagName.toUpperCase() === "SELECT";
});
return isElementDisplayed(enclosingSelectElement);
}
case "INPUT":
if (element.type === "hidden") {
return false;
}
break;
case "MAP":
break;
default:
break;
}
if (cascadedStylePropertyForElement(element, "visibility") !== "visible") {
return false;
}
const hasAncestorWithZeroOpacity = !!enclosingElementOrSelfMatchingPredicate(element, function(e) {
return Number(cascadedStylePropertyForElement(e, "opacity")) === 0;
});
const hasAncestorWithDisplayNone = !!enclosingElementOrSelfMatchingPredicate(element, function(e) {
return cascadedStylePropertyForElement(e, "display") === "none";
});
if (hasAncestorWithZeroOpacity || hasAncestorWithDisplayNone) {
return false;
}
if (!elementSubtreeHasNonZeroDimensions(element)) {
return false;
}
if (isElementSubtreeHiddenByOverflow(element) && !elementHasBoundingBox(element)) {
return false;
}
return true;
}
// src/scripts/isElementInViewport.ts
function isElementInViewport(elem) {
if (!elem.getBoundingClientRect) {
return false;
}
const rect = elem.getBoundingClientRect();
const windowHeight = window.innerHeight || document.documentElement.clientHeight;
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
const vertInView = rect.top < windowHeight && rect.top + rect.height > 0;
const horInView = rect.left < windowWidth && rect.left + rect.width > 0;
return vertInView && horInView;
}
// src/commands/element/isDisplayed.ts
async function isDisplayed(commandParams = DEFAULT_PARAMS) {
const browser = getBrowserObject22(this);
if (!await hasElementId(this)) {
return false;
}
if (browser.isMobile && (browser.isNativeContext || browser.isWindowsApp || browser.isMacApp)) {
if (commandParams == null ? void 0 : commandParams.withinViewport) {
throw new Error(
"Cannot determine element visibility within viewport for native mobile apps as it is not feasible to determine full vertical and horizontal application bounds. In most cases a basic visibility check should suffice."
);
}
return await this.isElementDisplayed(this.elementId);
}
let hadToFallback = false;
const [isDisplayed2, displayProperty] = await Promise.all([
browser.execute(function checkVisibility(elem, params) {
if (typeof elem.checkVisibility === "function") {
return elem.checkVisibility(params);
}
return null;
}, this, {
...DEFAULT_PARAMS,
...commandParams
}).then((result) => {
if (result === null) {
hadToFallback = true;
return browser.execute(isElementDisplayed, this);
}
return result;
}),
browser.execute(function(elem) {
var _a;
try {
const style = window.getComputedStyle(elem);
return { value: (_a = style == null ? void 0 : style.display) != null ? _a : "" };
} catch {
if (typeof elem.isConnected === "boolean" && !elem.isConnected) {
throw new Error("stale element reference: element is not attached to the page document");
}
return { value: "" };
}
}, this)
]);
const hasDisplayContentsCSSProperty = displayProperty.value === "contents";
const shouldRecheckContentVisibility = !hadToFallback && hasDisplayContentsCSSProperty;
const finalResponse = shouldRecheckContentVisibility ? await browser.execute(isElementDisplayed, this).catch(() => false) : isDisplayed2;
if (finalResponse && (commandParams == null ? void 0 : commandParams.withinViewport)) {
return browser.execute(isElementInViewport, this);
}
return finalResponse;
}
var DEFAULT_PARAMS = {
withinViewport: false,
contentVisibilityAuto: true,
opacityProperty: true,
visibilityProperty: true
};
// src/commands/element/isEnabled.ts
function isEnabled() {
return this.isElementEnabled(this.elementId);
}
// src/commands/element/isEqual.ts
import { ELEMENT_KEY as ELEMENT_KEY14 } from "webdriver";
import { getBrowserObject as getBrowserObject23 } from "@wdio/utils";
var getWebElement = (el) => ({
[ELEMENT_KEY14]: el.elementId,
// w3c compatible
ELEMENT: el.elementId
// jsonwp compatible
});
async function isEqual(el) {
const browser = getBrowserObject23(this);
if (browser.isMobile) {
const context = await browser.getContext().catch(() => void 0);
const contextId = typeof context === "string" ? context : context == null ? void 0 : context.id;
if (contextId && contextId.toLowerCase().includes("native")) {
return this.elementId === el.elementId;
}
}
let result;
try {
result = await browser.execute(
/* istanbul ignore next */
function(el1, el2) {
return el1 === el2;
},
getWebElement(this),
getWebElement(el)
);
} catch {
result = false;
}
return result;
}
// src/commands/element/isExisting.ts
async function isExisting() {
if (!this.selector) {
return this.getElementTagName(this.elementId).then(
() => true,
() => false
);
}
const command = this.isReactElement ? this.parent.react$$.bind(this.parent) : this.isShadowElement ? this.shadow$$.bind(this.parent) : this.parent.$$.bind(this.parent);
return command(this.selector).getElements().then((res) => res.length > 0);
}
// src/commands/element/isFocused.ts
import { ELEMENT_KEY as ELEMENT_KEY15 } from "webdriver";
import { getBrowserObject as getBrowserObject24 } from "@wdio/utils";
// src/scripts/isFocused.ts
function isFocused(elem) {
return elem === document.activeElement;
}
// src/commands/element/isFocused.ts
async function isFocused2() {
const browser = await getBrowserObject24(this);
return browser.execute(isFocused, {
[ELEMENT_KEY15]: this.elementId,
// w3c compatible
ELEMENT: this.elementId
// jsonwp compatible
});
}
// src/commands/element/isSelected.ts
function isSelected() {
return this.isElementSelected(this.elementId);
}
// src/commands/element/isStable.ts
import { ELEMENT_KEY as ELEMENT_KEY16 } from "webdriver";
import { getBrowserObject as getBrowserObject25 } from "@wdio/utils";
// src/scripts/isElementStable.ts
function isElementStable(elem, done) {
if (document.visibilityState === "hidden") {
throw Error("You are checking for animations on an inactive tab, animations do not run for inactive tabs");
}
try {
const previousPosition = elem.getBoundingClientRect();
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const currentPosition = elem.getBoundingClientRect();
for (const prop in previousPosition) {
if (previousPosition[prop] !== currentPosition[prop]) {
done(false);
}
}
done(true);
});
});
} catch {
done(false);
}
}
// src/commands/element/isStable.ts
async function isStable() {
const browser = getBrowserObject25(this);
if (browser.isMobile && browser.isNativeContext) {
throw new Error("The `isStable` command is only available for desktop and mobile browsers.");
}
return await browser.executeAsync(isElementStable, {
[ELEMENT_KEY16]: this.elementId,
// w3c compatible
ELEMENT: this.elementId
// jsonwp compatible
});
}
// src/commands/element/moveTo.ts
import logger24 from "@wdio/logger";
import { getBrowserObject as getBrowserObject26 } from "@wdio/utils";
var log24 = logger24("webdriver");
async function moveTo({ xOffset, yOffset } = {}) {
const browser = getBrowserObject26(this);
if (xOffset || yOffset) {
const { width, height } = await browser.getElementRect(this.elementId);
if (xOffset && xOffset < -Math.floor(width / 2) || xOffset && xOffset > Math.floor(width / 2)) {
log24.warn("xOffset would cause a out of bounds error as it goes outside of element");
}
if (yOffset && yOffset < -Math.floor(height / 2) || yOffset && yOffset > Math.floor(height / 2)) {
log24.warn("yOffset would cause a out of bounds error as it goes outside of element");
}
}
const moveToNested = async () => {
await browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: this, x: xOffset || 0, y: yOffset || 0 }).perform();
};
try {
await moveToNested();
} catch {
await this.scrollIntoView({ block: "center", inline: "center" });
await moveToNested();
}
}
// src/commands/element/nextElement.ts
function nextElement() {
return this.$(
/* istanbul ignore next */
function nextElement2() {
return this.nextElementSibling;
}
);
}
// src/commands/element/parentElement.ts
function parentElement() {
return this.$(
/* istanbul ignore next */
function parentElement2() {
return this.parentElement;
}
);
}
// src/commands/element/previousElement.ts
function previousElement() {
return this.$(
/* istanbul ignore next */
function previousElement2() {
return this.previousElementSibling;
}
);
}
// src/commands/element/react$$.ts
import { getBrowserObject as getBrowserObject27 } from "@wdio/utils";
async function react$$4(selector, { props = {}, state = {} } = {}) {
const browser = await getBrowserObject27(this);
await this.executeScript(resqScript.toString(), []);
await browser.execute(waitToLoadReact);
const res = await browser.execute(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
react$$,
selector,
props,
state,
this
);
const elements = await getElements.call(this, selector, res, { isReactElement: true });
return enhanceElementsArray(elements, this, selector, "react$$", [props, state]);
}
// src/commands/element/react$.ts
import { getBrowserObject as getBrowserObject28 } from "@wdio/utils";
async function react$4(selector, { props = {}, state = {} } = {}) {
const browser = await getBrowserObject28(this);
await this.executeScript(resqScript.toString(), []);
await browser.execute(waitToLoadReact);
const res = await browser.execute(
react$,
selector,
props,
state,
this
);
return getElement.call(this, selector, res, { isReactElement: true });
}
// src/commands/element/saveScreenshot.ts
async function saveScreenshot2(filepath) {
return environment.value.saveElementScreenshot.call(this, filepath);
}
// src/commands/element/scrollIntoView.ts
import logger25 from "@wdio/logger";
import { ELEMENT_KEY as ELEMENT_KEY17 } from "webdriver";
import { getBrowserObject as getBrowserObject29 } from "@wdio/utils";
var log25 = logger25("webdriverio");
async function scrollIntoView(options = { block: "start", inline: "nearest" }) {
const browser = getBrowserObject29(this);
if (browser.isMobile) {
if (await browser.isNativeContext) {
return nativeMobileScrollIntoView({
browser,
element: this,
options: options || {}
});
}
return scrollIntoViewWeb.call(this, options);
}
try {
const elemRect = await browser.getElementRect(this.elementId);
const viewport = await browser.getWindowSize();
let [scrollX, scrollY] = await browser.execute(() => [
window.scrollX,
window.scrollY
]);
scrollX = elemRect.x <= viewport.width ? elemRect.x : viewport.width / 2;
scrollY = elemRect.y <= viewport.height ? elemRect.y : viewport.height / 2;
const deltaByOption = {
start: { y: elemRect.y - elemRect.height, x: elemRect.x - elemRect.width },
center: { y: elemRect.y - Math.round((viewport.height - elemRect.height) / 2), x: elemRect.x - Math.round((viewport.width - elemRect.width) / 2) },
end: { y: elemRect.y - (viewport.height - elemRect.height), x: elemRect.x - (viewport.width - elemRect.width) }
};
let [deltaX, deltaY] = [deltaByOption.start.x, deltaByOption.start.y];
if (options === true) {
options = { block: "start", inline: "nearest" };
}
if (options === false) {
options = { block: "end", inline: "nearest" };
}
if (options && typeof options === "object") {
const { block, inline } = options;
if (block === "nearest") {
const nearestYDistance = Math.min(...Object.values(deltaByOption).map((delta) => delta.y));
deltaY = Object.values(deltaByOption).find((delta) => delta.y === nearestYDistance).y;
} else if (block) {
deltaY = deltaByOption[block].y;
}
if (inline === "nearest") {
const nearestXDistance = Math.min(...Object.values(deltaByOption).map((delta) => delta.x));
deltaX = Object.values(deltaByOption).find((delta) => delta.x === nearestXDistance).x;
} else if (inline) {
deltaX = deltaByOption[inline].x;
}
}
deltaX = Math.round(deltaX - scrollX);
deltaY = Math.round(deltaY - scrollY);
await browser.action("wheel").scroll({ duration: 0, x: deltaX, y: deltaY, origin: this }).perform();
} catch (err) {
log25.warn(
'Failed to execute "scrollIntoView" using WebDriver Actions API: '.concat(err.message, "!\n") + "Re-attempting using `Element.scrollIntoView` via Web API."
);
await scrollIntoViewWeb.call(this, options);
}
}
async function mobileScrollUntilVisible({
browser,
direction,
duration,
element,
maxScrolls,
percent,
scrollableElement
}) {
let isVisible = false;
let hasScrolled = false;
let scrolls = 0;
while (!isVisible && scrolls < maxScrolls) {
try {
isVisible = await element.isDisplayed();
} catch {
isVisible = false;
}
if (isVisible) {
break;
}
await browser.swipe({
direction,
...duration ? { duration } : {},
...percent ? { percent } : {},
...scrollableElement ? { scrollableElement } : {}
});
hasScrolled = true;
scrolls++;
}
return { hasScrolled, isVisible };
}
async function nativeMobileScrollIntoView({
browser,
element,
options
}) {
const defaultOptions = {
maxScrolls: 10,
direction: "up" /* Up */
};
const mobileOptions = {
...defaultOptions,
...options || {}
};
const { hasScrolled, isVisible } = await mobileScrollUntilVisible({
browser,
element,
maxScrolls: mobileOptions.maxScrolls,
direction: mobileOptions.direction,
...(mobileOptions == null ? void 0 : mobileOptions.duration) ? { duration: mobileOptions.duration } : {},
...(mobileOptions == null ? void 0 : mobileOptions.percent) ? { percent: mobileOptions.percent } : {},
...(mobileOptions == null ? void 0 : mobileOptions.scrollableElement) ? { scrollableElement: mobileOptions.scrollableElement } : {}
});
if (hasScrolled && isVisible) {
return browser.pause(1e3);
} else if (isVisible) {
return;
}
throw new Error("Element not found within scroll limit of ".concat(mobileOptions.maxScrolls, ' scrolls by scrolling "').concat(mobileOptions.direction, '". ') + "Are you sure the element is within the scrollable element or the direction is correct? You can change the scrollable element or direction like this:\n\nawait elem.scrollIntoView({\n direction: 'left' // possible options are: 'up|down|left|right'\n scrollableElement: $('#scrollable'),\n});\n\n ");
}
function scrollIntoViewWeb(options = { block: "start", inline: "nearest" }) {
const browser = getBrowserObject29(this);
return browser.execute(
(elem, options2) => elem.scrollIntoView(options2),
{
[ELEMENT_KEY17]: this.elementId,
// w3c compatible
ELEMENT: this.elementId
// jsonwp compatible
},
options
);
}
// src/commands/element/selectByAttribute.ts
import { ELEMENT_KEY as ELEMENT_KEY18 } from "webdriver";
async function selectByAttribute(attribute, value) {
value = typeof value === "number" ? value.toString() : value;
const normalized = "[normalize-space(@".concat(attribute.trim(), ') = "').concat(value.trim(), '"]');
let optionElement;
await this.waitUntil(async () => {
optionElement = await this.findElementFromElement(
this.elementId,
"xpath",
"./option".concat(normalized, "|./optgroup/option").concat(normalized)
);
return ELEMENT_KEY18 in optionElement;
}, {
timeoutMsg: 'Option with attribute "'.concat(attribute, "=").concat(value, '" not found.')
});
return this.elementClick(getElementFromResponse(optionElement));
}
// src/commands/element/selectByIndex.ts
async function selectByIndex(index) {
if (index < 0) {
throw new Error("Index needs to be 0 or any other positive number");
}
const fetchOptionElements = async () => {
return this.findElementsFromElement(this.elementId, "css selector", "option");
};
let optionElements = [];
await this.waitUntil(async () => {
optionElements = await fetchOptionElements();
return optionElements.length > 0;
}, {
timeoutMsg: "Select element doesn't contain any option element"
});
await this.waitUntil(async () => {
optionElements = await fetchOptionElements();
return typeof optionElements[index] !== "undefined";
}, {
timeoutMsg: 'Option with index "'.concat(index, '" not found. Select element only contains ').concat(optionElements.length, " option elements")
});
return this.elementClick(getElementFromResponse(optionElements[index]));
}
// src/commands/element/selectByVisibleText.ts
async function selectByVisibleText(text) {
text = typeof text === "number" ? text.toString() : text;
const normalized = text.trim().replace(/\s+/, " ");
const formatted = /"/.test(normalized) ? 'concat("' + normalized.split('"').join('", \'"\', "') + '")' : '"'.concat(normalized, '"');
const dotFormat = "[. = ".concat(formatted, "]");
const spaceFormat = "[normalize-space(text()) = ".concat(formatted, "]");
const selections = [
"./option".concat(dotFormat),
"./option".concat(spaceFormat),
"./optgroup/option".concat(dotFormat),
"./optgroup/option".concat(spaceFormat)
];
const optionElement = await this.$(selections.join("|"));
await optionElement.waitForExist({
timeoutMsg: 'Option with text "'.concat(text, '" not found.')
});
return this.elementClick(getElementFromResponse(optionElement));
}
// src/commands/element/setValue.ts
async function setValue(value, options) {
await this.clearValue();
return this.addValue(value, options);
}
// src/commands/element/shadow$$.ts
import logger26 from "@wdio/logger";
import { getBrowserObject as getBrowserObject30 } from "@wdio/utils";
import { SHADOW_ELEMENT_KEY } from "webdriver";
// src/scripts/shadowFnFactory.ts
var shadowFnFactory = function(elementSelector, qsAll = false) {
const strFn = (
/*js*/
"\n (function() {\n // element has a shadowRoot property\n if (this.shadowRoot) {\n return this.shadowRoot.querySelector".concat(qsAll ? "All" : "", "('").concat(elementSelector, "')\n }\n // fall back to querying the element directly if not\n return this.querySelector").concat(qsAll ? "All" : "", "('").concat(elementSelector, "')\n })")
);
return (0, eval)(strFn);
};
// src/utils/findStrategy.ts
import { roleElements } from "aria-query";
var DEFAULT_STRATEGY = "css selector";
var DIRECT_SELECTOR_REGEXP = /^(id|css selector|xpath|link text|partial link text|name|tag name|class name|-android uiautomator|-android datamatcher|-android viewmatcher|-android viewtag|-ios uiautomation|-ios predicate string|-ios class chain|accessibility id):(.+)/;
var XPATH_SELECTORS_START = [
"/",
"(",
"../",
"./",
"*/"
];
var NAME_MOBILE_SELECTORS_START = [
"uia",
"xcuielementtype",
"android.widget",
"cyi",
"android.view"
];
var XPATH_SELECTOR_REGEXP = [
// HTML tag
/^([a-z0-9|-]*)/,
// optional . or # + class or id
/(?:(\.|#)(-?[_a-zA-Z]+[_a-zA-Z0-9-]*))?/,
// optional [attribute-name="attribute-selector"]
/(?:\[(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)(?:=(?:"|')([a-zA-Z0-9\-_. ]+)(?:"|'))?\])?/,
// optional case insensitive
/(\.)?/,
// *=query or =query
/(\*)?=(.+)$/
];
var IMAGEPATH_MOBILE_SELECTORS_ENDSWITH = [
".jpg",
".jpeg",
".gif",
".png",
".bmp",
".svg"
];
var defineStrategy = function(selector) {
if (typeof selector === "object") {
if (JSON.stringify(selector).indexOf("test.espresso.matcher.ViewMatchers") < 0) {
return "-android datamatcher";
}
return "-android viewmatcher";
}
const stringSelector = selector;
if (DIRECT_SELECTOR_REGEXP.test(stringSelector)) {
return "directly";
}
if (IMAGEPATH_MOBILE_SELECTORS_ENDSWITH.some((path) => {
const selector2 = stringSelector.toLowerCase();
return selector2.endsWith(path) && selector2 !== path;
})) {
return "-image";
}
if (XPATH_SELECTORS_START.some((option) => stringSelector.startsWith(option))) {
return "xpath";
}
if (stringSelector.startsWith("=")) {
return "link text";
}
if (stringSelector.startsWith("*=")) {
return "partial link text";
}
if (stringSelector.startsWith("id=")) {
return "id";
}
if (stringSelector.startsWith(DEEP_SELECTOR)) {
return "shadow";
}
if (stringSelector.startsWith(ARIA_SELECTOR)) {
return "aria";
}
if (stringSelector.startsWith("android=")) {
return "-android uiautomator";
}
if (stringSelector.startsWith("ios=")) {
return "-ios uiautomation";
}
if (stringSelector.startsWith("~")) {
return "accessibility id";
}
if (NAME_MOBILE_SELECTORS_START.some((option) => stringSelector.toLowerCase().startsWith(option))) {
return "class name";
}
if (stringSelector.search(/<[0-9a-zA-Z-]+( \/)*>/g) >= 0) {
return "tag name";
}
if (stringSelector.search(/^\[name=(?:"(.[^"]*)"|'(.[^']*)')]$/) >= 0) {
return "name";
}
if (selector === ".." || selector === ".") {
return "xpath";
}
if (stringSelector.match(new RegExp(XPATH_SELECTOR_REGEXP.map((rx) => rx.source).join("")))) {
return "xpath extended";
}
if (/^\[role=[A-Za-z]+]$/.test(stringSelector)) {
return "role";
}
};
var findStrategy = function(selector, isW3C, isMobile) {
const stringSelector = selector;
let using = DEFAULT_STRATEGY;
let value = selector;
switch (defineStrategy(selector)) {
// user has specified locator strategy directly
case "directly": {
const match = stringSelector.match(DIRECT_SELECTOR_REGEXP);
if (!match) {
throw new Error("InvalidSelectorStrategy");
}
using = match[1];
value = match[2];
break;
}
case "xpath": {
using = "xpath";
break;
}
case "id": {
using = "id";
value = stringSelector.slice(3);
break;
}
case "link text": {
using = "link text";
value = stringSelector.slice(1);
break;
}
case "partial link text": {
using = "partial link text";
value = stringSelector.slice(2);
break;
}
case "shadow":
using = "shadow";
value = stringSelector.slice(DEEP_SELECTOR.length);
break;
case "aria": {
const label = stringSelector.slice(ARIA_SELECTOR.length);
const conditions = [
// aria label is recevied by other element with aria-labelledBy
// https://www.w3.org/TR/accname-1.1/#step2B
'.//*[@aria-labelledby=(//*[normalize-space(text()) = "'.concat(label, '"]/@id)]'),
// aria label is recevied by other element with aria-labelledBy
// https://www.w3.org/TR/accname-1.1/#step2B
'.//*[@aria-describedby=(//*[normalize-space(text()) = "'.concat(label, '"]/@id)]'),
// element has direct aria label
// https://www.w3.org/TR/accname-1.1/#step2C
'.//*[@aria-label = "'.concat(label, '"]'),
// input and textarea with a label
// https://www.w3.org/TR/accname-1.1/#step2D
'.//input[@id = (//label[normalize-space() = "'.concat(label, '"]/@for)]'),
'.//textarea[@id = (//label[normalize-space() = "'.concat(label, '"]/@for)]'),
// input and textarea with a label as parent
// https://www.w3.org/TR/accname-1.1/#step2D
'.//input[ancestor::label[normalize-space(text()) = "'.concat(label, '"]]'),
'.//textarea[ancestor::label[normalize-space(text()) = "'.concat(label, '"]]'),
// aria label is received by a placeholder
// https://www.w3.org/TR/accname-1.1/#step2D
'.//input[@placeholder="'.concat(label, '"]'),
'.//textarea[@placeholder="'.concat(label, '"]'),
// aria label is received by a aria-placeholder
// https://www.w3.org/TR/accname-1.1/#step2D
'.//input[@aria-placeholder="'.concat(label, '"]'),
'.//textarea[@aria-placeholder="'.concat(label, '"]'),
// aria label is received by a title
// https://www.w3.org/TR/accname-1.1/#step2D
'.//*[not(self::label)][@title="'.concat(label, '"]'),
// images with an alt tag
// https://www.w3.org/TR/accname-1.1/#step2D
'.//img[@alt="'.concat(label, '"]'),
// aria label is received from element text content
// https://www.w3.org/TR/accname-1.1/#step2G
'.//*[not(self::label)][normalize-space(text()) = "'.concat(label, '"]')
];
using = "xpath";
value = conditions.join(" | ");
break;
}
case "-android uiautomator": {
using = "-android uiautomator";
value = stringSelector.slice(8);
break;
}
case "-android datamatcher": {
using = "-android datamatcher";
value = JSON.stringify(value);
break;
}
case "-android viewmatcher": {
using = "-android viewmatcher";
value = JSON.stringify(value);
break;
}
case "-ios uiautomation": {
using = "-ios uiautomation";
value = stringSelector.slice(4);
break;
}
case "accessibility id": {
using = "accessibility id";
value = stringSelector.slice(1);
break;
}
case "class name": {
using = "class name";
break;
}
case "tag name": {
using = "tag name";
value = stringSelector.replace(/<|>|\/|\s/g, "");
break;
}
case "name": {
if (isMobile || !isW3C) {
const match = stringSelector.match(/^\[name=(?:"(.[^"]*)"|'(.[^']*)')]$/);
if (!match) {
throw new Error("InvalidSelectorMatch. Strategy 'name' has failed to match '".concat(stringSelector, "'"));
}
using = "name";
value = match[1] || match[2];
}
break;
}
case "xpath extended": {
using = "xpath";
const match = stringSelector.match(new RegExp(XPATH_SELECTOR_REGEXP.map((rx) => rx.source).join("")));
if (!match) {
throw new Error("InvalidSelectorMatch: Strategy 'xpath extended' has failed to match '".concat(stringSelector, "'"));
}
const PREFIX_NAME = { ".": "class", "#": "id" };
const conditions = [];
const [
tag,
prefix,
name,
attrName,
attrValue,
insensitive,
partial,
query
] = match.slice(1);
if (prefix) {
if (prefix === ".") {
conditions.push('contains(concat(" ",@'.concat(PREFIX_NAME[prefix], '," "), " ').concat(name, ' ")'));
} else {
conditions.push("contains(@".concat(PREFIX_NAME[prefix], ', "').concat(name, '")'));
}
}
if (attrName) {
conditions.push(
attrValue ? "contains(@".concat(attrName, ', "').concat(attrValue, '")') : "@".concat(attrName)
);
}
const partialNot = " and not(".concat(".//".concat(tag || "*").concat(conditions.length ? "[".concat(conditions.join(" and "), "]") : ""), ")");
if (insensitive) {
conditions.push(
partial ? 'contains(translate(., "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz"), "'.concat(query.toLowerCase(), '")').concat(partialNot) : 'normalize-space(translate(text(), "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz")) = "'.concat(query.toLowerCase(), '"')
);
} else {
conditions.push(partial ? 'contains(., "'.concat(query, '")').concat(partialNot) : 'normalize-space(text()) = "'.concat(query, '"'));
}
const getValue2 = () => ".//".concat(tag || "*", "[").concat(conditions.join(" and "), "]");
value = getValue2();
if (!partial) {
conditions.pop();
conditions.push(
"not(".concat(value, ")"),
'normalize-space() = "'.concat(insensitive ? query.toLowerCase() : query, '"')
);
value = value + " | " + getValue2();
}
break;
}
case "-image": {
using = "-image";
value = environment.value.readFileSync(stringSelector, { encoding: "base64" });
break;
}
case "role": {
const match = stringSelector.match(/^\[role=(.+)\]/);
if (!match) {
throw new Error("InvalidSelectorMatch. Strategy 'role' has failed to match '".concat(stringSelector, "'"));
}
using = "css selector";
value = createRoleBaseXpathSelector(match[1]);
break;
}
}
return { using, value };
};
var createRoleBaseXpathSelector = (role) => {
var _a;
const locatorArr = [];
(_a = roleElements.get(role)) == null ? void 0 : _a.forEach((value) => {
let locator;
let tagAttribute, tagAttributevalue;
const tagname = value.name;
if (value.attributes instanceof Array) {
value.attributes.forEach((val) => {
tagAttribute = val.name;
tagAttributevalue = val.value;
});
}
if (!tagAttribute) {
locator = tagname;
} else if (!tagAttributevalue) {
locator = "".concat(tagname, "[").concat(tagAttribute, "]");
} else {
locator = "".concat(tagname, "[").concat(tagAttribute, '="').concat(tagAttributevalue, '"]');
}
locatorArr.push(locator);
});
let xpathLocator = '[role="'.concat(role, '"]');
locatorArr.forEach((loc) => {
xpathLocator += "," + loc;
});
return xpathLocator;
};
// src/commands/element/shadow$$.ts
var log26 = logger26("webdriverio");
async function shadow$$(selector) {
const browser = getBrowserObject30(this);
try {
const shadowRoot = await browser.getElementShadowRoot(this.elementId);
const { using, value } = findStrategy(selector, this.isW3C, this.isMobile);
const res = await browser.findElementsFromShadowRoot(shadowRoot[SHADOW_ELEMENT_KEY], using, value);
const elements = await getElements.call(this, selector, res, { isShadowElement: true });
return enhanceElementsArray(elements, this, selector);
} catch (err) {
log26.warn(
"Failed to fetch element within shadow DOM using WebDriver command: ".concat(err.message, "!\n") + "Falling back to JavaScript shim."
);
return await this.$$(shadowFnFactory(selector, true));
}
}
// src/commands/element/shadow$.ts
import logger27 from "@wdio/logger";
import { SHADOW_ELEMENT_KEY as SHADOW_ELEMENT_KEY2 } from "webdriver";
import { getBrowserObject as getBrowserObject31 } from "@wdio/utils";
var log27 = logger27("webdriverio");
async function shadow$(selector) {
const browser = getBrowserObject31(this);
try {
const shadowRoot = await browser.getElementShadowRoot(this.elementId);
const { using, value } = findStrategy(selector, this.isW3C, this.isMobile);
const res = await browser.findElementFromShadowRoot(shadowRoot[SHADOW_ELEMENT_KEY2], using, value);
return getElement.call(this, selector, res, { isShadowElement: true });
} catch (err) {
log27.warn(
"Failed to fetch element within shadow DOM using WebDriver command: ".concat(err.message, "!\n") + "Falling back to JavaScript shim."
);
return this.$(shadowFnFactory(selector));
}
}
// src/commands/element/touchAction.ts
function touchAction3(actions2) {
return touchAction.call(this, actions2);
}
// src/commands/element/waitForClickable.ts
import { getBrowserObject as getBrowserObject32 } from "@wdio/utils";
async function waitForClickable({
timeout = this.options.waitforTimeout,
interval = this.options.waitforInterval,
reverse = false,
timeoutMsg = 'element ("'.concat(this.selector, '") still ').concat(reverse ? "" : "not ", "clickable after ").concat(timeout, "ms")
} = {}) {
var _a;
const browser = getBrowserObject32(this);
if (browser.isMobile && browser.isNativeContext && !((_a = browser.capabilities) == null ? void 0 : _a.browserName)) {
throw new Error("The `waitForClickable` command is only available for desktop and mobile browsers.");
}
return this.waitUntil(
async () => reverse !== await this.isClickable(),
{ timeout, timeoutMsg, interval }
);
}
// src/commands/element/waitForDisplayed.ts
function waitForDisplayed({
timeout = this.options.waitforTimeout,
interval = this.options.waitforInterval,
reverse = false,
withinViewport = false,
contentVisibilityAuto = true,
opacityProperty = true,
visibilityProperty = true,
timeoutMsg = 'element ("'.concat(this.selector, '") still ').concat(reverse ? "" : "not ", "displayed").concat(withinViewport ? " within viewport" : "", " after ").concat(timeout, "ms")
} = {}) {
return this.waitUntil(
async () => reverse !== await this.isDisplayed({ withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty }),
{ timeout, interval, timeoutMsg }
);
}
// src/commands/element/waitForEnabled.ts
async function waitForEnabled({
timeout = this.options.waitforTimeout,
interval = this.options.waitforInterval,
reverse = false,
timeoutMsg = 'element ("'.concat(this.selector, '") still ').concat(reverse ? "" : "not ", "enabled after ").concat(timeout, "ms")
} = {}) {
if (!this.elementId && !reverse) {
await this.waitForExist({ timeout, interval, timeoutMsg });
}
return this.waitUntil(
async () => reverse !== await this.isEnabled(),
{ timeout, interval, timeoutMsg }
);
}
// src/commands/element/waitForExist.ts
import { ELEMENT_KEY as ELEMENT_KEY19 } from "webdriver";
async function waitForExist({
timeout = this.options.waitforTimeout,
interval = this.options.waitforInterval,
reverse = false,
timeoutMsg = 'element ("'.concat(this.selector, '") still ').concat(reverse ? "" : "not ", "existing after ").concat(timeout, "ms")
} = {}) {
const isExisting2 = await this.waitUntil(
async () => reverse !== await this.isExisting(),
{ timeout, interval, timeoutMsg }
);
if (!reverse && isExisting2 && typeof this.selector === "string") {
let isCurrentIdValid = false;
if (this.elementId) {
try {
await this.getElementTagName(this.elementId);
isCurrentIdValid = true;
} catch {
}
}
if (!isCurrentIdValid) {
let element;
if (this.index !== void 0) {
const elements = this.isShadowElement ? await this.parent.shadow$$(this.selector) : await this.parent.$$(this.selector);
element = elements[this.index];
} else {
element = this.isShadowElement ? this.parent.shadow$(this.selector) : this.parent.$(this.selector);
}
this.elementId = await element.elementId;
this[ELEMENT_KEY19] = this.elementId;
}
delete this.error;
}
return isExisting2;
}
// src/commands/element/waitForStable.ts
import { getBrowserObject as getBrowserObject33 } from "@wdio/utils";
async function waitForStable({
timeout = this.options.waitforTimeout,
interval = this.options.waitforInterval,
reverse = false,
timeoutMsg = 'element ("'.concat(this.selector, '") still ').concat(reverse ? "" : "not ", "stable after ").concat(timeout, "ms")
} = {}) {
let errorMsg;
const browser = getBrowserObject33(this);
if (browser.isMobile && browser.isNativeContext) {
throw new Error("The `waitForStable` command is only available for desktop and mobile browsers.");
}
await this.waitUntil(
async () => {
try {
return reverse !== await this.isStable();
} catch (error) {
if (error instanceof Error) {
errorMsg = error.message;
} else if (typeof error === "string") {
errorMsg = error;
} else {
errorMsg = "The waitForStable command got an unknown error";
}
return !reverse;
}
},
{ timeout, interval, timeoutMsg }
);
if (errorMsg) {
throw Error(errorMsg);
}
}
// src/commands/element/waitUntil.ts
var waitUntil2 = waitUntil;
// src/commands/mobile/longPress.ts
import { getBrowserObject as getBrowserObject34 } from "@wdio/utils";
function longPress(options) {
const browser = getBrowserObject34(this);
if (!browser.isMobile) {
throw new Error("The longPress command is only available for mobile platforms.");
}
if (typeof options !== "undefined" && (typeof options !== "object" || Array.isArray(options))) {
throw new TypeError("Options must be an object");
}
const defaultOptions = {
duration: 1500,
x: 0,
y: 0
};
const { duration, x, y } = { ...defaultOptions, ...options };
if (!browser.isNativeContext && browser.isIOS) {
return browser.execute(
(el, duration2) => {
const touchStart = new TouchEvent("touchstart", {
touches: [new Touch({ identifier: 0, target: el, clientX: 0, clientY: 0 })],
bubbles: true,
cancelable: true
});
el.dispatchEvent(touchStart);
setTimeout(() => {
const touchEnd = new TouchEvent("touchend", {
changedTouches: [new Touch({ identifier: 0, target: el, clientX: 0, clientY: 0 })],
bubbles: true,
cancelable: true
});
el.dispatchEvent(touchEnd);
}, duration2);
},
this,
duration
);
}
return this.click({ duration, x, y });
}
// src/commands/mobile/pinch.ts
import { getBrowserObject as getBrowserObject35 } from "@wdio/utils";
async function pinch(options = {}) {
const browser = getBrowserObject35(this);
if (!browser.isMobile) {
throw new Error("The pinch command is only available for mobile platforms.");
}
const { duration, scale } = validatePinchAndZoomOptions({ browser, gesture: "pinch", options });
const gestureConfig = browser.isIOS ? {
elementId: await this.elementId,
scale,
velocity: -Math.abs(duration)
// Velocity is always negative for iOS pinch
} : {
elementId: await this.elementId,
percent: scale,
speed: calculateAndroidPinchAndZoomSpeed({ browser, duration, scale })
};
return browser.execute(browser.isIOS ? "mobile: pinch" : "mobile: pinchCloseGesture", gestureConfig);
}
// src/commands/mobile/zoom.ts
import { getBrowserObject as getBrowserObject36 } from "@wdio/utils";
async function zoom(options = {}) {
const browser = getBrowserObject36(this);
if (!browser.isMobile) {
throw new Error("The zoom command is only available for mobile platforms.");
}
const { duration, scale } = validatePinchAndZoomOptions({ browser, gesture: "zoom", options });
const gestureConfig = browser.isIOS ? {
elementId: await this.elementId,
scale,
velocity: duration
} : {
elementId: await this.elementId,
percent: scale,
speed: calculateAndroidPinchAndZoomSpeed({ browser, duration, scale })
};
return browser.execute(browser.isIOS ? "mobile: pinch" : "mobile: pinchOpenGesture", gestureConfig);
}
// src/scripts/elementContains.ts
function elementContains(scope, element) {
function isInDocument(element2) {
let currentElement = element2;
while (currentElement && currentElement.parentNode) {
if (currentElement.parentNode === scope || currentElement.parentNode.host === scope) {
return true;
} else if (currentElement.parentNode instanceof DocumentFragment) {
currentElement = currentElement.parentNode.host;
} else {
currentElement = currentElement.parentNode;
}
}
return false;
}
return isInDocument(element);
}
// src/utils/thirdParty/querySelectorShadowDom.ts
function querySelectorAllDeep(findMany, s, r) {
function normalizeSelector(sel) {
function saveUnmatched() {
if (unmatched) {
if (tokens.length > 0 && /^[~+>]$/.test(tokens[tokens.length - 1])) {
tokens.push(" ");
}
tokens.push(unmatched);
}
}
const tokens = [], state = [0], not_escaped_pattern = /(?:[^\\]|(?:^|[^\\])(?:\\\\)+)$/, whitespace_pattern = /^\s+$/, state_patterns = [
/\s+|\/\*|["'>~+[(]/g,
// general
/\s+|\/\*|["'[\]()]/g,
// [..] set
/\s+|\/\*|["'[\]()]/g,
// (..) set
null,
// string literal (placeholder)
/\*\//g
// comment
];
let match, unmatched, regex, next_match_idx = 0, prev_match_idx;
sel = sel.trim();
while (true) {
unmatched = "";
regex = state_patterns[state[state.length - 1]];
regex.lastIndex = next_match_idx;
match = regex.exec(sel);
if (match) {
prev_match_idx = next_match_idx;
next_match_idx = regex.lastIndex;
if (prev_match_idx < next_match_idx - match[0].length) {
unmatched = sel.substring(
prev_match_idx,
next_match_idx - match[0].length
);
}
if (state[state.length - 1] < 3) {
saveUnmatched();
if (match[0] === "[") {
state.push(1);
} else if (match[0] === "(") {
state.push(2);
} else if (/^["']$/.test(match[0])) {
state.push(3);
state_patterns[3] = new RegExp(match[0], "g");
} else if (match[0] === "/*") {
state.push(4);
} else if (/^[\])]$/.test(match[0]) && state.length > 0) {
state.pop();
} else if (/^(?:\s+|[~+>])$/.test(match[0])) {
if (tokens.length > 0 && !whitespace_pattern.test(tokens[tokens.length - 1]) && state[state.length - 1] === 0) {
tokens.push(" ");
}
if (state[state.length - 1] === 1 && tokens.length === 5 && tokens[2].charAt(tokens[2].length - 1) === "=") {
tokens[4] = " " + tokens[4];
}
if (whitespace_pattern.test(match[0])) {
continue;
}
}
tokens.push(match[0]);
} else {
tokens[tokens.length - 1] += unmatched;
if (not_escaped_pattern.test(tokens[tokens.length - 1])) {
if (state[state.length - 1] === 4) {
if (tokens.length < 2 || whitespace_pattern.test(tokens[tokens.length - 2])) {
tokens.pop();
} else {
tokens[tokens.length - 1] = " ";
}
match[0] = "";
}
state.pop();
}
tokens[tokens.length - 1] += match[0];
}
} else {
unmatched = sel.substr(next_match_idx);
saveUnmatched();
break;
}
}
return tokens.join("").trim();
}
function _querySelectorDeep(selector, root, allElements = null) {
selector = normalizeSelector(selector);
const lightElement = root.querySelector(selector);
if (document.head.createShadowRoot || document.head.attachShadow) {
if (!findMany && lightElement) {
return lightElement;
}
const selectionsToMake = splitByCharacterUnlessQuoted(selector, ",");
return selectionsToMake.reduce((acc, minimalSelector) => {
if (!findMany && acc) {
return acc;
}
const splitSelector = splitByCharacterUnlessQuoted(minimalSelector.replace(/^\s+/g, "").replace(/\s*([>+~]+)\s*/g, "$1"), " ").filter((entry) => !!entry).map((entry) => splitByCharacterUnlessQuoted(entry, ">"));
const possibleElementsIndex = splitSelector.length - 1;
const lastSplitPart = splitSelector[possibleElementsIndex][splitSelector[possibleElementsIndex].length - 1];
const possibleElements = collectAllElementsDeep(lastSplitPart, root, allElements);
const findElements2 = findMatchingElement(splitSelector, possibleElementsIndex, root);
if (findMany) {
acc = acc.concat(possibleElements.filter(findElements2));
return acc;
}
acc = possibleElements.find(findElements2);
return acc || null;
}, findMany ? [] : null);
}
return !findMany ? lightElement : root.querySelectorAll(selector);
}
function findMatchingElement(splitSelector, possibleElementsIndex, root) {
return (element) => {
let position = possibleElementsIndex;
let parent = element;
let foundElement = false;
while (parent && !isDocumentNode(parent)) {
let foundMatch = true;
if (splitSelector[position].length === 1) {
foundMatch = parent.matches(splitSelector[position]);
} else {
const reversedParts = [].concat(splitSelector[position]).reverse();
let newParent = parent;
for (const part of reversedParts) {
if (!newParent || !newParent.matches(part)) {
foundMatch = false;
break;
}
newParent = findParentOrHost(newParent, root);
}
}
if (foundMatch && position === 0) {
foundElement = true;
break;
}
if (foundMatch) {
position--;
}
parent = findParentOrHost(parent, root);
}
return foundElement;
};
}
function splitByCharacterUnlessQuoted(selector, character) {
return selector.match(/\\?.|^$/g).reduce((p, c) => {
if (c === '"' && !p.sQuote) {
p.quote ^= 1;
p.a[p.a.length - 1] += c;
} else if (c === "'" && !p.quote) {
p.sQuote ^= 1;
p.a[p.a.length - 1] += c;
} else if (!p.quote && !p.sQuote && c === character) {
p.a.push("");
} else {
p.a[p.a.length - 1] += c;
}
return p;
}, { a: [""] }).a;
}
function isDocumentNode(node) {
return node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.DOCUMENT_NODE;
}
function findParentOrHost(element, root) {
const parentNode = element.parentNode;
return parentNode && parentNode.host && parentNode.nodeType === 11 ? parentNode.host : parentNode === root ? null : parentNode;
}
function collectAllElementsDeep(selector = null, root, cachedElements = null) {
let allElements = [];
if (cachedElements) {
allElements = cachedElements;
} else {
const findAllElements = function(nodes) {
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
allElements.push(el);
if (el.shadowRoot) {
findAllElements(el.shadowRoot.querySelectorAll("*"));
}
}
};
const shadowRoot = root.shadowRoot;
if (shadowRoot) {
findAllElements(shadowRoot.querySelectorAll("*"));
}
findAllElements(root.querySelectorAll("*"));
}
return selector ? allElements.filter((el) => el.matches(selector)) : allElements;
}
return _querySelectorDeep(s, r || document);
}
// src/utils/index.ts
var log28 = logger28("webdriverio");
var INVALID_SELECTOR_ERROR = "selector needs to be typeof `string` or `function`";
var IGNORED_COMMAND_FILE_EXPORTS = ["SESSION_MOCKS", "CDP_SESSIONS"];
var scopes = {
browser: browser_exports,
element: element_exports
};
var applyScopePrototype = (prototype, scope) => {
Object.entries(scopes[scope]).filter(([exportName]) => !IGNORED_COMMAND_FILE_EXPORTS.includes(exportName)).forEach(([commandName, command]) => {
prototype[commandName] = { value: command };
});
};
var getPrototype = (scope) => {
const prototype = {
/**
* used to store the puppeteer instance in the browser scope
*/
puppeteer: { value: null, writable: true }
};
if (scope === "browser") {
prototype.isNativeContext = {
get: function() {
const context = getContextManager(this);
return context.isNativeContext;
}
};
prototype.mobileContext = {
get: function() {
const context = getContextManager(this);
return context.mobileContext;
}
};
}
applyScopePrototype(prototype, scope);
prototype.strategies = { value: /* @__PURE__ */ new Map() };
return prototype;
};
var getElementFromResponse = (res) => {
if (!res) {
return null;
}
if (res.ELEMENT) {
return res.ELEMENT;
}
if (res[ELEMENT_KEY20]) {
return res[ELEMENT_KEY20];
}
return null;
};
function sanitizeCSS(value) {
if (!value) {
return value;
}
return value.trim().replace(/'/g, "").replace(/"/g, "").toLowerCase();
}
function parseCSS(cssPropertyValue, cssProperty) {
var _a;
const parsedValue = {
property: cssProperty,
value: cssPropertyValue.toLowerCase().trim(),
parsed: {}
};
if (((_a = parsedValue.value) == null ? void 0 : _a.indexOf("rgb")) === 0) {
parsedValue.value = parsedValue.value.replace(/\s/g, "");
const color = parsedValue.value;
parsedValue.parsed = rgb2hex(parsedValue.value);
parsedValue.parsed.type = "color";
const colorType = /[rgba]+/g.exec(color) || [];
parsedValue.parsed[colorType[0]] = color;
} else if (parsedValue.property === "font-family") {
const font = cssValue(cssPropertyValue);
const string = parsedValue.value;
const value = cssPropertyValue.split(/,/).map(sanitizeCSS);
parsedValue.value = sanitizeCSS(font[0].value || font[0].string);
parsedValue.parsed = { value, type: "font", string };
} else {
try {
const value = cssValue(cssPropertyValue);
if (value.length === 1) {
parsedValue.parsed = value[0];
}
if (parsedValue.parsed.type && parsedValue.parsed.type === "number" && parsedValue.parsed.unit === "") {
parsedValue.value = parsedValue.parsed.value;
}
} catch {
}
}
return parsedValue;
}
function checkUnicode(value) {
if (value === Key.Ctrl) {
return [value];
}
if (!Object.prototype.hasOwnProperty.call(UNICODE_CHARACTERS2, value)) {
return new GraphemeSplitter().splitGraphemes(value);
}
return [UNICODE_CHARACTERS2[value]];
}
function fetchElementByJSFunction(selector, scope, referenceId) {
if (!("elementId" in scope)) {
return scope.execute(selector, referenceId);
}
const script = (function(elem, id) {
return selector.call(elem, id);
}).toString().replace("selector", "(".concat(selector.toString(), ")"));
const args = [scope];
if (referenceId) {
args.push(referenceId);
}
return getBrowserObject37(scope).executeScript("return (".concat(script, ").apply(null, arguments)"), args);
}
function isElement(o) {
return typeof HTMLElement === "object" ? o instanceof HTMLElement : o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string";
}
function isStaleElementError(err) {
return (
// Chrome
err.message.includes("stale element reference") || // Firefox
err.message.includes("is no longer attached to the DOM") || // Safari
err.message.toLowerCase().includes("stale element found") || // Chrome through JS execution
err.message.includes("stale element not found in the current frame") || // BIDI
err.message.includes("belongs to different document")
);
}
function transformClassicToBidiSelector(using, value) {
if (using === "css selector" || using === "tag name") {
return { type: "css", value };
}
if (using === "xpath") {
return { type: "xpath", value };
}
if (using === "link text") {
return { type: "innerText", value };
}
if (using === "partial link text") {
return { type: "innerText", value, matchType: "partial" };
}
throw new Error("Can't transform classic selector ".concat(using, " to Bidi selector"));
}
async function findDeepElement(selector) {
const browser = getBrowserObject37(this);
const shadowRootManager = getShadowRootManager(browser);
const contextManager = getContextManager(browser);
const context = await contextManager.getCurrentContext();
const shadowRoots = shadowRootManager.getShadowElementsByContextId(
context,
this.elementId
);
const { using, value } = findStrategy(selector, this.isW3C, this.isMobile);
if (using === "xpath" && (value.startsWith("./") || value.startsWith("..")) && this.elementId) {
return this.findElementFromElement(this.elementId, using, value);
}
const locator = transformClassicToBidiSelector(using, value);
const startNodes = shadowRoots.length > 0 ? shadowRoots.map((shadowRootNodeId) => ({ sharedId: shadowRootNodeId })) : this.elementId ? [{ sharedId: this.elementId }] : void 0;
const deepElementResult = await browser.browsingContextLocateNodes({ locator, context, startNodes }).then(async (result) => {
let nodes = result.nodes.filter((node) => Boolean(node.sharedId)).map((node) => ({
[ELEMENT_KEY20]: node.sharedId,
locator
}));
nodes = returnUniqueNodes(nodes);
if (!this.elementId) {
return nodes[0];
}
const scopedNodes = await Promise.all(nodes.map(async (node) => {
const isIn = await browser.execute(
elementContains,
{ [ELEMENT_KEY20]: this.elementId },
node
);
return [isIn, node];
})).then((elems) => elems.filter(([isIn]) => isIn).map(([, elem]) => elem));
return scopedNodes[0];
}, (err) => {
log28.warn("Failed to execute browser.browsingContextLocateNodes({ ... }) due to ".concat(err, ", falling back to regular WebDriver Classic command"));
return this && "elementId" in this && this.elementId ? this.findElementFromElement(this.elementId, using, value) : browser.findElement(using, value);
});
return deepElementResult;
}
async function findDeepElements(selector) {
const browser = getBrowserObject37(this);
const shadowRootManager = getShadowRootManager(browser);
const contextManager = getContextManager(browser);
const context = await contextManager.getCurrentContext();
const shadowRoots = shadowRootManager.getShadowElementsByContextId(
context,
this.elementId
);
const { using, value } = findStrategy(selector, this.isW3C, this.isMobile);
if (using === "xpath" && (value.startsWith("./") || value.startsWith("..")) && this.elementId) {
return this.findElementsFromElement(this.elementId, using, value);
}
const locator = transformClassicToBidiSelector(using, value);
const startNodes = shadowRoots.length > 0 ? shadowRoots.map((shadowRootNodeId) => ({ sharedId: shadowRootNodeId })) : this.elementId ? [{ sharedId: this.elementId }] : void 0;
const deepElementResult = await browser.browsingContextLocateNodes({ locator, context, startNodes }).then(async (result) => {
let nodes = result.nodes.filter((node) => Boolean(node.sharedId)).map((node) => ({
[ELEMENT_KEY20]: node.sharedId,
locator
}));
nodes = returnUniqueNodes(nodes);
if (!this.elementId) {
return nodes;
}
const scopedNodes = await Promise.all(nodes.map(async (node) => {
const isIn = await browser.execute(
elementContains,
{ [ELEMENT_KEY20]: this.elementId },
node
);
return [isIn, node];
})).then((elems) => elems.filter(([isIn]) => isIn).map(([, elem]) => elem));
return scopedNodes;
}, (err) => {
log28.warn("Failed to execute browser.browsingContextLocateNodes({ ... }) due to ".concat(err, ", falling back to regular WebDriver Classic command"));
return this && "elementId" in this && this.elementId ? this.findElementsFromElement(this.elementId, using, value) : browser.findElements(using, value);
});
return deepElementResult;
}
function returnUniqueNodes(nodes) {
const ids = /* @__PURE__ */ new Set();
return nodes.filter((node) => !ids.has(node[ELEMENT_KEY20]) && ids.add(node[ELEMENT_KEY20]));
}
async function findElement(selector) {
const browserObject = getBrowserObject37(this);
const shadowRootManager = getShadowRootManager(browserObject);
if (this.isBidi && typeof selector === "string" && !selector.startsWith(DEEP_SELECTOR) && !shadowRootManager.isWithinFrame()) {
const notFoundError = new Error("Couldn't find element with selector \"".concat(selector, '"'));
const elem = await findDeepElement.call(this, selector);
return getElementFromResponse(elem) ? elem : notFoundError;
}
if (typeof selector === "string" && selector.startsWith(DEEP_SELECTOR)) {
const notFoundError = new Error('shadow selector "'.concat(selector.slice(DEEP_SELECTOR.length), '" did not return an HTMLElement'));
let elem = await browserObject.execute(
querySelectorAllDeep,
false,
selector.slice(DEEP_SELECTOR.length),
// hard conversion from element id to Element is done by browser driver
this.elementId ? this : void 0
);
elem = Array.isArray(elem) ? elem[0] : elem;
return getElementFromResponse(elem) ? elem : notFoundError;
}
if (selector && typeof selector === "object" && typeof selector.strategy === "function") {
const { strategy, strategyName, strategyArguments } = selector;
const notFoundError = new Error('Custom Strategy "'.concat(strategyName, '" did not return an HTMLElement'));
let elem = await browserObject.execute(strategy, ...strategyArguments);
elem = Array.isArray(elem) ? elem[0] : elem;
return getElementFromResponse(elem) ? elem : notFoundError;
}
if (typeof selector === "string" || isPlainObject(selector)) {
const { using, value } = findStrategy(selector, this.isW3C, this.isMobile);
return this.elementId ? this.findElementFromElement(this.elementId, using, value) : this.findElement(using, value);
}
if (typeof selector === "function") {
const notFoundError = new Error('Function selector "'.concat(selector.toString(), '" did not return an HTMLElement'));
let elem = await fetchElementByJSFunction(selector, this);
elem = Array.isArray(elem) ? elem[0] : elem;
return getElementFromResponse(elem) ? elem : notFoundError;
}
if (isElement(selector)) {
if (!window.__wdio_element) {
window.__wdio_element = {};
}
const notFoundError = new Error("DOM Node couldn't be found anymore");
const uid = Math.random().toString().slice(2);
window.__wdio_element[uid] = selector;
selector = ((id) => window.__wdio_element[id]);
let elem = await fetchElementByJSFunction(selector, this, uid).catch((err) => {
if (isStaleElementError(err)) {
return void 0;
}
throw err;
});
elem = Array.isArray(elem) ? elem[0] : elem;
return getElementFromResponse(elem) ? elem : notFoundError;
}
throw new Error("".concat(INVALID_SELECTOR_ERROR, ", but found: `").concat(typeof selector, "`"));
}
async function findElements(selector) {
const browserObject = getBrowserObject37(this);
if (typeof selector === "string" && selector.startsWith(DEEP_SELECTOR)) {
const elems = await browserObject.execute(
querySelectorAllDeep,
true,
selector.slice(DEEP_SELECTOR.length),
// hard conversion from element id to Element is done by browser driver
this.elementId ? this : void 0
);
const elemArray = Array.isArray(elems) ? elems : [elems];
return elemArray.filter((elem) => elem && getElementFromResponse(elem));
}
if (isPlainObject(selector) && typeof selector.strategy === "function") {
const { strategy, strategyArguments } = selector;
const elems = await browserObject.execute(strategy, ...strategyArguments);
const elemArray = Array.isArray(elems) ? elems : [elems];
return elemArray.filter((elem) => elem && getElementFromResponse(elem));
}
if (typeof selector === "string" || isPlainObject(selector)) {
const { using, value } = findStrategy(selector, this.isW3C, this.isMobile);
return this.elementId ? this.findElementsFromElement(this.elementId, using, value) : this.findElements(using, value);
}
if (typeof selector === "function") {
const elems = await fetchElementByJSFunction(selector, this);
const elemArray = Array.isArray(elems) ? elems : [elems];
return elemArray.filter((elem) => elem && getElementFromResponse(elem));
}
throw new Error("".concat(INVALID_SELECTOR_ERROR, ", but found: `").concat(typeof selector, "`"));
}
function verifyArgsAndStripIfElement(args) {
function verify(arg) {
if (arg && typeof arg === "object" && arg.constructor.name === "Element") {
const elem = arg;
if (!elem.elementId) {
throw new Error('The element with selector "'.concat(elem.selector, "\" you are trying to pass into the execute method wasn't found"));
}
return {
[ELEMENT_KEY20]: elem.elementId,
ELEMENT: elem.elementId
};
}
return arg;
}
return !Array.isArray(args) ? verify(args) : args.map(verify);
}
async function getElementRect(scope) {
const rect = await scope.getElementRect(scope.elementId);
const defaults = { x: 0, y: 0, width: 0, height: 0 };
if (Object.keys(defaults).some((key) => rect[key] === void 0)) {
const rectJs = await getBrowserObject37(scope).execute(function(el) {
if (!el || !el.getBoundingClientRect) {
return;
}
const { left, top, width, height } = el.getBoundingClientRect();
return {
x: left + this.scrollX,
y: top + this.scrollY,
width,
height
};
}, scope);
Object.keys(defaults).forEach((key) => {
if (typeof rect[key] !== "undefined") {
return;
}
if (rectJs && typeof rectJs[key] === "number") {
rect[key] = Math.floor(rectJs[key]);
} else {
log28.error("getElementRect", { rect, rectJs, key });
throw new Error("Failed to receive element rects via execute command");
}
});
}
return rect;
}
function validateUrl(url2, origError) {
try {
const urlObject = new URL(url2);
return urlObject.href;
} catch {
if (origError) {
throw origError;
}
return validateUrl("http://".concat(url2), new Error("Invalid URL: ".concat(url2)));
}
}
async function hasElementId(element) {
if (!element.elementId) {
const command = element.isReactElement ? element.parent.react$.bind(element.parent) : element.isShadowElement ? element.parent.shadow$.bind(element.parent) : element.parent.$.bind(element.parent);
element.elementId = (await command(element.selector).getElement()).elementId;
}
if (!element.elementId) {
return false;
}
return true;
}
function addLocatorStrategyHandler(scope) {
return (name, func) => {
if (scope.strategies.get(name)) {
throw new Error("Strategy ".concat(name, " already exists"));
}
scope.strategies.set(name, func);
};
}
var enhanceElementsArray = (elements, parent, selector, foundWith = "$$", props = []) => {
const elementArray = elements;
if (!Array.isArray(selector)) {
elementArray.selector = selector;
}
const elems = selector;
if (Array.isArray(selector) && elems.length && elems.every((elem) => elem.selector && elem.selector === elems[0].selector)) {
elementArray.selector = elems[0].selector;
}
for (const [name, fn] of Object.entries(asyncIterators)) {
elementArray[name] = fn.bind(null, elementArray);
}
elementArray.parent = parent;
elementArray.foundWith = foundWith;
elementArray.props = props;
elementArray.getElements = async () => elementArray;
return elementArray;
};
var isStub = (automationProtocol) => automationProtocol === "./protocol-stub.js";
function createFunctionDeclarationFromString(userScript) {
if (typeof userScript === "string") {
return "(".concat(SCRIPT_PREFIX, "function () {\n").concat(userScript.toString(), "\n}").concat(SCRIPT_SUFFIX, ").apply(this, arguments);");
}
return new Function("return (".concat(SCRIPT_PREFIX).concat(userScript.toString()).concat(SCRIPT_SUFFIX, ").apply(this, arguments);")).toString();
}
// src/middlewares.ts
var IMPLICIT_WAIT_EXCLUSION_LIST = ["getElement", "getElements", "emit"];
var elementErrorHandler = (fn) => (commandName, commandFn) => {
return function elementErrorHandlerCallback(...args) {
return fn(commandName, async function elementErrorHandlerCallbackFn() {
var _a, _b;
if (IMPLICIT_WAIT_EXCLUSION_LIST.includes(commandName)) {
return fn(commandName, commandFn).apply(this, args);
}
const element = await implicitWait(this, commandName);
this.elementId = element.elementId;
this[ELEMENT_KEY21] = element.elementId;
try {
const result = await fn(commandName, commandFn).apply(this, args);
const caps = getBrowserObject38(this).capabilities;
if ((caps == null ? void 0 : caps.browserName) === "safari" && (result == null ? void 0 : result.error) === "no such element") {
const errorName = "stale element reference";
const err = new Error(errorName);
err.name = errorName;
throw err;
}
return result;
} catch (_err) {
const err = _err;
if (err.name === "element not interactable") {
try {
await element.waitForClickable();
return await fn(commandName, commandFn).apply(this, args);
} catch {
const elementHTML = await element.getHTML();
err.name = "webdriverio(middleware): element did not become interactable";
err.message = "Element ".concat(elementHTML, " did not become interactable");
err.stack = (_b = (_a = err.stack) != null ? _a : Error.captureStackTrace(err)) != null ? _b : "";
}
}
if (err.name === "stale element reference" || isStaleElementError(err)) {
const element2 = await refetchElement(this, commandName);
this.elementId = element2.elementId;
this.parent = element2.parent;
return await fn(commandName, commandFn).apply(this, args);
}
throw err;
}
}).apply(this);
};
};
var multiremoteHandler = (wrapCommand4) => (commandName) => {
return wrapCommand4(commandName, function(...args) {
const commandResults = this.instances.map((instanceName) => {
return this[instanceName][commandName](...args);
});
return Promise.all(commandResults);
});
};
// src/multiremote.ts
var MultiRemote = class _MultiRemote {
constructor() {
__publicField(this, "instances", {});
__publicField(this, "baseInstance");
__publicField(this, "sessionId");
}
/**
* add instance to multibrowser instance
*/
async addInstance(browserName, client) {
this.instances[browserName] = client;
return this.instances[browserName];
}
/**
* modifier for multibrowser instance
*/
modifier(wrapperClient) {
const propertiesObject = {};
propertiesObject.commandList = { value: wrapperClient.commandList };
propertiesObject.options = { value: wrapperClient.options };
propertiesObject.getInstance = {
value: (browserName) => this.instances[browserName]
};
for (const commandName of wrapperClient.commandList) {
propertiesObject[commandName] = {
value: this.commandWrapper(commandName),
configurable: true
};
}
propertiesObject.__propertiesObject__ = {
value: propertiesObject
};
this.baseInstance = new MultiRemoteDriver(this.instances, propertiesObject);
const client = Object.create(this.baseInstance, propertiesObject);
for (const [identifier, instance] of Object.entries(this.instances)) {
client[identifier] = instance;
}
return client;
}
/**
* helper method to generate element objects from results, so that we can call, e.g.
*
* ```
* const elem = $('#elem')
* elem.getHTML()
* ```
*
* or in case multiremote is used
*
* ```
* const elems = $$('div')
* elems[0].getHTML()
* ```
*/
static elementWrapper(instances, result, propertiesObject, scope) {
const prototype = { ...propertiesObject, ...clone2(getPrototype("element")), scope: { value: "element" } };
const element = webdriverMonad2({}, (client) => {
for (const [i, identifier] of Object.entries(Object.keys(instances))) {
client[identifier] = result[i];
}
client.instances = Object.keys(instances);
client.isMultiremote = true;
client.selector = Array.isArray(result) && result[0] ? result[0].selector : null;
delete client.sessionId;
return client;
}, prototype);
return element(this.sessionId, multiremoteHandler(scope.commandWrapper.bind(scope)));
}
/**
* handle commands for multiremote instances
*/
commandWrapper(commandName) {
const instances = this.instances;
const self = this;
if (commandName === "getInstance") {
return function(browserName) {
if (!this[browserName]) {
throw new Error('Multiremote object has no instance named "'.concat(browserName, '"'));
}
return this[browserName];
};
}
return wrapCommand2(commandName, async function(...args) {
const mElem = this;
const scope = this.selector ? Object.entries(mElem.instances.reduce((ins, instanceName) => (
// @ts-expect-error ToDo(Christian): deprecate
{ ...ins, [instanceName]: mElem[instanceName] }
), {})) : Object.entries(instances);
const result = await Promise.all(
scope.map(
([, instance]) => instance[commandName](...args)
)
);
if (commandName === "$") {
const elem = _MultiRemote.elementWrapper(instances, result, this.__propertiesObject__, self);
return elem;
} else if (commandName === "$$") {
const zippedResult = zip(...result);
return zippedResult.map((singleResult) => _MultiRemote.elementWrapper(instances, singleResult, this.__propertiesObject__, self));
}
return result;
});
}
};
var MultiRemoteDriver = class {
constructor(instances, propertiesObject) {
__publicField(this, "instances");
__publicField(this, "isMultiremote", true);
__publicField(this, "__propertiesObject__");
this.instances = Object.keys(instances);
this.__propertiesObject__ = propertiesObject;
}
on(eventName, emitter) {
this.instances.forEach((instanceName) => this.getInstance(instanceName).on(eventName, emitter));
return void 0;
}
once(eventName, emitter) {
this.instances.forEach((instanceName) => this.getInstance(instanceName).once(eventName, emitter));
return void 0;
}
emit(eventName, emitter) {
return this.instances.map(
(instanceName) => this.getInstance(instanceName).emit(eventName, emitter)
).some(Boolean);
}
eventNames() {
return this.instances.map(
(instanceName) => this.getInstance(instanceName).eventNames()
);
}
getMaxListeners() {
return this.instances.map(
(instanceName) => this.getInstance(instanceName).getMaxListeners()
);
}
listenerCount(eventName) {
return this.instances.map(
(instanceName) => this.getInstance(instanceName).listenerCount(eventName)
);
}
listeners(eventName) {
return this.instances.map(
(instanceName) => this.getInstance(instanceName).listeners(eventName)
).reduce((prev, cur) => {
prev.concat(cur);
return prev;
}, []);
}
removeListener(eventName, emitter) {
this.instances.forEach((instanceName) => this.getInstance(instanceName).removeListener(eventName, emitter));
return void 0;
}
removeAllListeners(eventName) {
this.instances.forEach((instanceName) => this.getInstance(instanceName).removeAllListeners(eventName));
return void 0;
}
};
// src/utils/SevereServiceError.ts
var SevereServiceError = class extends Error {
constructor(message = "Severe Service Error occurred.") {
super(message);
this.name = "SevereServiceError";
}
};
// src/utils/detectBackend.ts
var DEFAULT_HOSTNAME = "127.0.0.1";
var DEFAULT_PORT = 4444;
var DEFAULT_PROTOCOL = "http";
var DEFAULT_PATH = "/";
var LEGACY_PATH = "/wd/hub";
var REGION_MAPPING = {
"us": "us-west-1.",
// default endpoint
"eu": "eu-central-1.",
"eu-central-1": "eu-central-1.",
"us-east-4": "us-east-4."
};
function getSauceEndpoint(region, { isRDC, isVisual } = {}) {
const shortRegion = REGION_MAPPING[region] ? region : "us";
if (isRDC) {
return "".concat(shortRegion, "1.appium.testobject.com");
} else if (isVisual) {
return "hub.screener.io";
}
return "ondemand.".concat(REGION_MAPPING[shortRegion], "saucelabs.com");
}
function detectBackend(options = {}) {
var _a;
const { port, hostname, user, key, protocol, region, path, capabilities } = options;
if (typeof user === "string" && typeof key === "string" && key.length === 20) {
return {
protocol: protocol || "https",
hostname: hostname || "hub-cloud.browserstack.com",
port: port || 443,
path: path || LEGACY_PATH
};
}
if (typeof user === "string" && typeof key === "string" && key.length === 32) {
return {
protocol: protocol || "https",
hostname: hostname || "hub.testingbot.com",
port: port || 443,
path: path || LEGACY_PATH
};
}
const isVisual = Boolean(!Array.isArray(capabilities) && capabilities && ((_a = capabilities["sauce:visual"]) == null ? void 0 : _a.apiKey));
if (typeof user === "string" && typeof key === "string" && key.length === 36 || // Or only RDC or visual
isVisual) {
const sauceRegion = region;
return {
protocol: protocol || "https",
hostname: hostname || getSauceEndpoint(sauceRegion, { isVisual }),
port: port || 443,
path: path || LEGACY_PATH
};
}
if (typeof user === "string" && typeof key === "string" && key.length === 50) {
return {
protocol: protocol || DEFAULT_PROTOCOL,
hostname: hostname || "hub.lambdatest.com",
port: port || 80,
path: path || LEGACY_PATH
};
}
if (
/**
* user and key are set in config
*/
(typeof user === "string" || typeof key === "string") && /**
* but no custom WebDriver endpoint was configured
*/
!hostname
) {
throw new Error(
'A "user" or "key" was provided but could not be connected to a known cloud service (Sauce Labs, Browerstack, Testingbot or Lambdatest). Please check if given user and key properties are correct!'
);
}
if (hostname || port || protocol || path) {
return {
hostname: hostname || DEFAULT_HOSTNAME,
port: port || DEFAULT_PORT,
protocol: protocol || DEFAULT_PROTOCOL,
path: path || DEFAULT_PATH
};
}
return { hostname, port, protocol, path };
}
// src/protocol-stub.ts
import { capabilitiesEnvironmentDetector } from "@wdio/utils";
var NOOP2 = () => {
};
var ProtocolStub = class {
static async newSession(options) {
const capabilities = emulateSessionCapabilities(options.capabilities);
const browser = {
options,
capabilities,
requestedCapabilities: capabilities,
customCommands: [],
// internally used to transfer custom commands to the actual protocol instance
overwrittenCommands: [],
// internally used to transfer overwritten commands to the actual protocol instance
commandList: [],
getWindowHandle: NOOP2,
on: NOOP2,
off: NOOP2,
addCommand: NOOP2,
overwriteCommand: NOOP2,
...capabilitiesEnvironmentDetector(capabilities)
};
browser.addCommand = (...args) => browser.customCommands.push(args);
browser.overwriteCommand = (...args) => browser.overwrittenCommands.push(args);
return browser;
}
/**
* added just in case user wants to somehow reload webdriver before it was started.
*/
static reloadSession() {
throw new Error("Protocol Stub: Make sure to start the session before reloading it.");
}
static attachToSession(options, modifier) {
if (options || !modifier) {
throw new Error("You are trying to attach to a protocol stub, this should never occur, please file an issue.");
}
return modifier({
commandList: []
});
}
};
function emulateSessionCapabilities(caps) {
const capabilities = {};
Object.entries(caps).forEach(([key, value]) => {
const newKey = key.replace("appium:", "");
capabilities[newKey] = value;
});
const c = "alwaysMatch" in caps ? caps.alwaysMatch : caps;
if (c.browserName && c.browserName.toLowerCase() === "chrome") {
capabilities["goog:chromeOptions"] = {};
}
return capabilities;
}
// src/utils/driver.ts
var webdriverImport;
async function getProtocolDriver(options) {
if (isStub(options.automationProtocol)) {
return { Driver: ProtocolStub, options };
}
if (typeof options.user === "string" && typeof options.key === "string") {
Object.assign(options, detectBackend(options));
}
const Driver = webdriverImport || (await import(
/* @vite-ignore */
options.automationProtocol || "webdriver"
)).default;
return { Driver, options };
}
// src/index.ts
var Key2 = Key;
var SevereServiceError2 = SevereServiceError;
var remote = async function(params, remoteModifier) {
const keysToKeep = Object.keys(environment.value.variables.WDIO_WORKER_ID ? params : DEFAULTS);
const config = validateConfig(WDIO_DEFAULTS, params, keysToKeep);
await enableFileLogging(config.outputDir);
logger29.setLogLevelsConfig(config.logLevels, config.logLevel);
const modifier = (client, options2) => {
Object.assign(options2, Object.entries(config).reduce((a, [k, v]) => typeof v === "undefined" ? a : { ...a, [k]: v }, {}));
if (typeof remoteModifier === "function") {
client = remoteModifier(client, options2);
}
return client;
};
const { Driver, options } = await getProtocolDriver({ ...params, ...config });
const prototype = getPrototype("browser");
const instance = await Driver.newSession(options, modifier, prototype, wrapCommand3, IMPLICIT_WAIT_EXCLUSION_LIST);
if (params.framework && !isStub(params.automationProtocol)) {
instance.addCommand = instance.addCommand.bind(instance);
instance.overwriteCommand = instance.overwriteCommand.bind(instance);
}
instance.addLocatorStrategy = addLocatorStrategyHandler(instance);
if (!isStub(options.automationProtocol)) {
await registerSessionManager(instance);
}
return instance;
};
var attach = async function(attachOptions) {
const params = {
automationProtocol: "webdriver",
...detectBackend(attachOptions.options),
...attachOptions.options,
...attachOptions,
capabilities: attachOptions.capabilities || {},
requestedCapabilities: attachOptions.requestedCapabilities || {}
};
const prototype = getPrototype("browser");
const { Driver } = await getProtocolDriver(params);
const driver = Driver.attachToSession(
params,
void 0,
prototype,
wrapCommand3
);
driver.addLocatorStrategy = addLocatorStrategyHandler(driver);
if (isBidi(driver.capabilities) && "_bidiHandler" in driver) {
await driver["_bidiHandler"].waitForConnected();
}
await registerSessionManager(driver);
return driver;
};
var multiremote = async function(params, { automationProtocol } = {}) {
const multibrowser = new MultiRemote();
const browserNames = Object.keys(params);
await Promise.all(
browserNames.map(async (browserName) => {
const instance = await remote(params[browserName]);
return multibrowser.addInstance(browserName, instance);
})
);
const prototype = getPrototype("browser");
const sessionParams = isStub(automationProtocol) ? void 0 : {
sessionId: "",
isW3C: multibrowser.instances[browserNames[0]].isW3C,
logLevel: multibrowser.instances[browserNames[0]].options.logLevel
};
const ProtocolDriver = typeof automationProtocol === "string" ? (await import(
/* @vite-ignore */
automationProtocol
)).default : WebDriver;
const driver = ProtocolDriver.attachToSession(
sessionParams,
multibrowser.modifier.bind(multibrowser),
prototype,
wrapCommand3
);
if (!isStub(automationProtocol)) {
const origAddCommand = driver.addCommand.bind(driver);
driver.addCommand = function(name, fn, attachToElementOrOptions) {
const options = typeof attachToElementOrOptions === "object" && attachToElementOrOptions !== null ? attachToElementOrOptions : { attachToElement: attachToElementOrOptions };
driver.instances.forEach(
(instanceName) => driver.getInstance(instanceName).addCommand(name, fn, options)
);
return origAddCommand(
name,
fn,
{
attachToElement: options.attachToElement,
proto: Object.getPrototypeOf(multibrowser.baseInstance),
instances: multibrowser.instances
}
);
};
const origOverwriteCommand = driver.overwriteCommand.bind(driver);
driver.overwriteCommand = (name, fn, attachToElement) => {
return origOverwriteCommand(
name,
fn,
attachToElement,
Object.getPrototypeOf(multibrowser.baseInstance),
multibrowser.instances
);
};
}
driver.addLocatorStrategy = addLocatorStrategyHandler(driver);
return driver;
};
export {
Key2 as Key,
KeyAction,
MobileScrollDirection,
PointerAction,
SevereServiceError2 as SevereServiceError,
WheelAction,
attach,
buttonValue,
multiremote,
remote
};
/*!
* ensure that timeout and interval are set properly
*/