webdriverio 9.5.1 → 9.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js CHANGED
@@ -2995,9 +2995,69 @@ var ReferenceValue = class {
2995
2995
  }
2996
2996
  };
2997
2997
 
2998
- // src/context.ts
2998
+ // src/session/context.ts
2999
2999
  import logger4 from "@wdio/logger";
3000
3000
 
3001
+ // src/session/session.ts
3002
+ var sessionManager = /* @__PURE__ */ new Map();
3003
+ var SessionManager = class {
3004
+ #browser;
3005
+ #scope;
3006
+ /**
3007
+ * SessionManager constructor
3008
+ * Logic in here should be executed for all session singletons, e.g. remove instance
3009
+ * of itself when a session was deleted.
3010
+ * @param browser WebdriverIO.Browser
3011
+ * @param scope scope of the session manager, e.g. context, network etc.
3012
+ */
3013
+ constructor(browser, scope) {
3014
+ this.#browser = browser;
3015
+ this.#browser.on("command", this.#onCommand.bind(this));
3016
+ this.#scope = scope;
3017
+ }
3018
+ #onCommand(ev) {
3019
+ if (ev.command === "deleteSession") {
3020
+ const sessionManagerInstances = sessionManager.get(this.#scope);
3021
+ const sessionManagerInstance = sessionManagerInstances?.get(this.#browser);
3022
+ if (sessionManagerInstance && sessionManagerInstances) {
3023
+ sessionManagerInstance.removeListeners();
3024
+ sessionManagerInstances.delete(this.#browser);
3025
+ }
3026
+ }
3027
+ }
3028
+ removeListeners() {
3029
+ this.#browser.off("result", this.#onCommand.bind(this));
3030
+ }
3031
+ initialize() {
3032
+ return void 0;
3033
+ }
3034
+ /**
3035
+ * check if session manager should be enabled, if
3036
+ */
3037
+ isEnabled() {
3038
+ return (
3039
+ // we are in a Bidi session
3040
+ this.#browser.isBidi && // we are not running unit tests
3041
+ !process.env.WDIO_UNIT_TESTS && // we are running a WebDriver session
3042
+ this.#browser.options?.automationProtocol === "webdriver"
3043
+ );
3044
+ }
3045
+ static getSessionManager(browser, Manager) {
3046
+ const scope = Manager.name;
3047
+ let sessionManagerInstances = sessionManager.get(scope);
3048
+ if (!sessionManagerInstances) {
3049
+ sessionManagerInstances = /* @__PURE__ */ new Map();
3050
+ sessionManager.set(scope, sessionManagerInstances);
3051
+ }
3052
+ let sessionManagerInstance = sessionManagerInstances.get(browser);
3053
+ if (!sessionManagerInstance) {
3054
+ sessionManagerInstance = new Manager(browser);
3055
+ sessionManagerInstances.set(browser, sessionManagerInstance);
3056
+ }
3057
+ return sessionManagerInstance;
3058
+ }
3059
+ };
3060
+
3001
3061
  // src/utils/mobile.ts
3002
3062
  var appiumKeys = ["app", "bundleId", "appPackage", "appActivity", "appWaitActivity", "appWaitPackage"];
3003
3063
  function getNativeContext({ capabilities, isMobile }) {
@@ -3049,24 +3109,19 @@ function validatePinchAndZoomOptions({ browser, gesture, options }) {
3049
3109
  };
3050
3110
  }
3051
3111
 
3052
- // src/context.ts
3053
- var contextManager = /* @__PURE__ */ new Map();
3112
+ // src/session/context.ts
3054
3113
  var log4 = logger4("webdriverio:context");
3114
+ var COMMANDS_REQUIRING_RESET = ["deleteSession", "refresh", "switchToParentFrame"];
3055
3115
  function getContextManager(browser) {
3056
- const existingContextManager = contextManager.get(browser);
3057
- if (existingContextManager) {
3058
- return existingContextManager;
3059
- }
3060
- const newContext = new ContextManager(browser);
3061
- contextManager.set(browser, newContext);
3062
- return newContext;
3116
+ return SessionManager.getSessionManager(browser, ContextManager);
3063
3117
  }
3064
- var ContextManager = class {
3118
+ var ContextManager = class _ContextManager extends SessionManager {
3065
3119
  #browser;
3066
3120
  #currentContext;
3067
3121
  #mobileContext;
3068
3122
  #isNativeContext;
3069
3123
  constructor(browser) {
3124
+ super(browser, _ContextManager.name);
3070
3125
  this.#browser = browser;
3071
3126
  const capabilities = this.#browser.capabilities;
3072
3127
  this.#isNativeContext = getNativeContext({ capabilities, isMobile: this.#browser.isMobile });
@@ -3075,46 +3130,53 @@ var ContextManager = class {
3075
3130
  isAndroid: this.#browser.isAndroid,
3076
3131
  isNativeContext: this.#isNativeContext
3077
3132
  });
3078
- if (!this.#isEnabled()) {
3133
+ this.#browser.on("result", this.#onCommandResultBidiAndClassic.bind(this));
3134
+ if (!this.isEnabled()) {
3079
3135
  return;
3080
3136
  }
3081
- this.#browser.on("command", (event) => {
3082
- if (event.command === "switchToWindow") {
3083
- this.setCurrentContext(event.body.handle);
3084
- }
3085
- if (event.command === "switchToParentFrame" || event.command === "refresh") {
3086
- this.#currentContext = void 0;
3087
- }
3088
- if (this.#browser.isMobile && event.command === "switchContext") {
3089
- this.#mobileContext = event.body.name;
3090
- }
3091
- });
3092
- this.#browser.on("result", (event) => {
3093
- if (event.command === "closeWindow") {
3094
- this.#currentContext = event.result.value[0];
3095
- return this.#browser.switchToWindow(this.#currentContext);
3096
- }
3097
- if (this.#browser.isMobile) {
3098
- if (event.command === "getContext") {
3099
- this.setCurrentContext(event.result.value);
3100
- }
3101
- if (event.command === "switchContext" && event.result.value === null && this.#mobileContext) {
3102
- this.setCurrentContext(this.#mobileContext);
3103
- }
3104
- }
3105
- });
3137
+ this.#browser.on("command", this.#onCommand.bind(this));
3138
+ if (this.#browser.isMobile) {
3139
+ this.#browser.on("result", this.#onCommandResultMobile.bind(this));
3140
+ }
3106
3141
  }
3107
- /**
3108
- * Only run this session helper if BiDi is enabled and we're not in unit tests.
3109
- */
3110
- #isEnabled() {
3111
- return !process.env.WDIO_UNIT_TESTS && (this.#browser.isBidi || this.#browser.isMobile);
3142
+ removeListeners() {
3143
+ super.removeListeners();
3144
+ this.#browser.off("result", this.#onCommandResultBidiAndClassic.bind(this));
3145
+ this.#browser.off("command", this.#onCommand.bind(this));
3146
+ if (this.#browser.isMobile) {
3147
+ this.#browser.off("result", this.#onCommandResultMobile.bind(this));
3148
+ }
3149
+ }
3150
+ #onCommandResultBidiAndClassic(event) {
3151
+ if (event.command === "closeWindow") {
3152
+ this.#currentContext = event.result.value[0];
3153
+ return this.#browser.switchToWindow(this.#currentContext);
3154
+ }
3155
+ }
3156
+ #onCommand(event) {
3157
+ if (event.command === "switchToWindow") {
3158
+ this.setCurrentContext(event.body.handle);
3159
+ }
3160
+ if (COMMANDS_REQUIRING_RESET.includes(event.command)) {
3161
+ this.#currentContext = void 0;
3162
+ }
3163
+ if (this.#browser.isMobile && event.command === "switchContext") {
3164
+ this.#mobileContext = event.body.name;
3165
+ }
3166
+ }
3167
+ #onCommandResultMobile(event) {
3168
+ if (event.command === "getContext") {
3169
+ this.setCurrentContext(event.result.value);
3170
+ }
3171
+ if (event.command === "switchContext" && event.result.value === null && this.#mobileContext) {
3172
+ this.setCurrentContext(this.#mobileContext);
3173
+ }
3112
3174
  }
3113
3175
  /**
3114
3176
  * set context at the start of the session
3115
3177
  */
3116
3178
  async initialize() {
3117
- if (!this.#isEnabled()) {
3179
+ if (process.env.WDIO_UNIT_TESTS) {
3118
3180
  return "";
3119
3181
  }
3120
3182
  if (this.#browser.isMobile && !this.#isNativeContext && !this.#mobileContext) {
@@ -3154,24 +3216,18 @@ Requested WebDriver capabilities: ${JSON.stringify(this.#browser.requestedCapabi
3154
3216
  }
3155
3217
  };
3156
3218
 
3157
- // src/polyfill.ts
3219
+ // src/session/polyfill.ts
3158
3220
  import logger5 from "@wdio/logger";
3159
- var polyfillManager = /* @__PURE__ */ new Map();
3160
- var log5 = logger5("webdriverio:PolyfillManager");
3161
- var NAME_POLYFILL = "var __defProp = Object.defineProperty;var __name = function (target, value) { return __defProp(target, 'name', { value: value, configurable: true }); };var __globalThis = (typeof globalThis === 'object' && globalThis) || (typeof window === 'object' && window);__globalThis.__name = __name;";
3162
3221
  function getPolyfillManager(browser) {
3163
- const existingPolyfillManager = polyfillManager.get(browser);
3164
- if (existingPolyfillManager) {
3165
- return existingPolyfillManager;
3166
- }
3167
- const newContext = new PolyfillManager(browser);
3168
- polyfillManager.set(browser, newContext);
3169
- return newContext;
3222
+ return SessionManager.getSessionManager(browser, PolyfillManager);
3170
3223
  }
3171
- var PolyfillManager = class {
3224
+ var log5 = logger5("webdriverio:PolyfillManager");
3225
+ var NAME_POLYFILL = "var __defProp = Object.defineProperty;var __name = function (target, value) { return __defProp(target, 'name', { value: value, configurable: true }); };var __globalThis = (typeof globalThis === 'object' && globalThis) || (typeof window === 'object' && window);__globalThis.__name = __name;";
3226
+ var PolyfillManager = class _PolyfillManager extends SessionManager {
3172
3227
  #initialize;
3173
3228
  constructor(browser) {
3174
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
3229
+ super(browser, _PolyfillManager.name);
3230
+ if (!this.isEnabled()) {
3175
3231
  this.#initialize = Promise.resolve(true);
3176
3232
  return;
3177
3233
  }
@@ -3199,8 +3255,8 @@ async function execute(script, ...args) {
3199
3255
  }
3200
3256
  if (this.isBidi && !this.isMultiremote) {
3201
3257
  const browser = getBrowserObject3(this);
3202
- const contextManager2 = getContextManager(browser);
3203
- const context = await contextManager2.getCurrentContext();
3258
+ const contextManager = getContextManager(browser);
3259
+ const context = await contextManager.getCurrentContext();
3204
3260
  const userScript = typeof script === "string" ? new Function(script) : script;
3205
3261
  const functionDeclaration = new Function(`
3206
3262
  return (${SCRIPT_PREFIX}${userScript.toString()}${SCRIPT_SUFFIX}).apply(this, arguments);
@@ -3233,8 +3289,8 @@ async function executeAsync(script, ...args) {
3233
3289
  }
3234
3290
  if (this.isBidi && !this.isMultiremote) {
3235
3291
  const browser = getBrowserObject4(this);
3236
- const contextManager2 = getContextManager(browser);
3237
- const context = await contextManager2.getCurrentContext();
3292
+ const contextManager = getContextManager(browser);
3293
+ const context = await contextManager.getCurrentContext();
3238
3294
  const userScript = typeof script === "string" ? new Function(script) : script;
3239
3295
  const functionDeclaration = new Function(`
3240
3296
  const args = Array.from(arguments)
@@ -3934,8 +3990,8 @@ async function mock(url6, filterOptions) {
3934
3990
  throw new Error("Mocking is only supported when running tests using WebDriver Bidi");
3935
3991
  }
3936
3992
  const browser = getBrowserObject6(this);
3937
- const contextManager2 = getContextManager(browser);
3938
- const context = await contextManager2.getCurrentContext();
3993
+ const contextManager = getContextManager(browser);
3994
+ const context = await contextManager.getCurrentContext();
3939
3995
  if (!SESSION_MOCKS[context]) {
3940
3996
  SESSION_MOCKS[context] = /* @__PURE__ */ new Set();
3941
3997
  }
@@ -3989,9 +4045,9 @@ async function newWindow(url6, { type = "window", windowName = "", windowFeature
3989
4045
  }
3990
4046
  const tabsBefore = await this.getWindowHandles();
3991
4047
  if (this.isBidi) {
3992
- const contextManager2 = getContextManager(this);
4048
+ const contextManager = getContextManager(this);
3993
4049
  const { context } = await this.browsingContextCreate({ type });
3994
- contextManager2.setCurrentContext(context);
4050
+ contextManager.setCurrentContext(context);
3995
4051
  await this.browsingContextNavigate({ context, url: url6 });
3996
4052
  } else {
3997
4053
  await this.execute(newWindowHelper, url6, windowName, windowFeatures);
@@ -4064,306 +4120,873 @@ async function react$(selector, { props = {}, state = {} } = {}) {
4064
4120
  }
4065
4121
 
4066
4122
  // src/commands/browser/reloadSession.ts
4123
+ import logger13 from "@wdio/logger";
4124
+
4125
+ // src/session/shadowRoot.ts
4067
4126
  import logger12 from "@wdio/logger";
4068
- var log12 = logger12("webdriverio");
4069
- async function reloadSession(newCapabilities) {
4070
- const oldSessionId = this.sessionId;
4071
- const shutdownDriver = Boolean(newCapabilities?.browserName);
4072
- try {
4073
- await this.deleteSession({ shutdownDriver });
4074
- } catch (err) {
4075
- log12.warn(`Suppressing error closing the session: ${err.stack}`);
4076
- }
4077
- if (this.puppeteer?.connected) {
4078
- this.puppeteer.disconnect();
4079
- log12.debug("Disconnected puppeteer session");
4080
- }
4081
- const ProtocolDriver = (await import(
4082
- /* @vite-ignore */
4083
- this.options.automationProtocol
4084
- )).default;
4085
- await ProtocolDriver.reloadSession(this, newCapabilities);
4086
- const options = this.options;
4087
- if (Array.isArray(options.onReload) && options.onReload.length) {
4088
- await Promise.all(options.onReload.map((hook) => hook(oldSessionId, this.sessionId)));
4089
- }
4090
- return this.sessionId;
4127
+ import customElementWrapper from "./scripts/customElement.js";
4128
+ var log12 = logger12("webdriverio:ShadowRootManager");
4129
+ function getShadowRootManager(browser) {
4130
+ return SessionManager.getSessionManager(browser, ShadowRootManager);
4091
4131
  }
4092
-
4093
- // src/commands/browser/restore.ts
4094
- async function restore(scopes2) {
4095
- const scopeArray = !scopes2 || Array.isArray(scopes2) ? scopes2 : [scopes2];
4096
- const instanceRestoreFunctions = restoreFunctions.get(this);
4097
- if (!instanceRestoreFunctions) {
4098
- return;
4099
- }
4100
- await Promise.all(Array.from(instanceRestoreFunctions.entries()).map(async ([scope, restoreFunctionsList]) => {
4101
- if (!scopeArray || scopeArray.includes(scope)) {
4102
- await Promise.all(restoreFunctionsList.map((fn) => fn()));
4103
- instanceRestoreFunctions.set(scope, []);
4132
+ var ShadowRootManager = class _ShadowRootManager extends SessionManager {
4133
+ #browser;
4134
+ #initialize;
4135
+ #shadowRoots = /* @__PURE__ */ new Map();
4136
+ #documentElement;
4137
+ #frameDepth = 0;
4138
+ constructor(browser) {
4139
+ super(browser, _ShadowRootManager.name);
4140
+ this.#browser = browser;
4141
+ if (!this.isEnabled()) {
4142
+ this.#initialize = Promise.resolve(true);
4143
+ return;
4104
4144
  }
4105
- }));
4106
- }
4107
-
4108
- // src/commands/browser/savePDF.ts
4109
- import fs4 from "node:fs";
4110
- async function savePDF(filepath, options) {
4111
- if (typeof filepath !== "string" || !filepath.endsWith(".pdf")) {
4112
- throw new Error('savePDF expects a filepath of type string and ".pdf" file ending');
4113
- }
4114
- const absoluteFilepath = getAbsoluteFilepath(filepath);
4115
- await assertDirectoryExists(absoluteFilepath);
4116
- const pdf = await this.printPage(
4117
- options?.orientation,
4118
- options?.scale,
4119
- options?.background,
4120
- options?.width,
4121
- options?.height,
4122
- options?.top,
4123
- options?.bottom,
4124
- options?.left,
4125
- options?.right,
4126
- options?.shrinkToFit,
4127
- options?.pageRanges
4128
- );
4129
- const page = Buffer.from(pdf, "base64");
4130
- fs4.writeFileSync(absoluteFilepath, page);
4131
- return page;
4132
- }
4133
-
4134
- // src/commands/browser/saveRecordingScreen.ts
4135
- import fs5 from "node:fs";
4136
- async function saveRecordingScreen(filepath) {
4137
- if (typeof filepath !== "string") {
4138
- throw new Error("saveRecordingScreen expects a filepath");
4139
- }
4140
- const absoluteFilepath = getAbsoluteFilepath(filepath);
4141
- await assertDirectoryExists(absoluteFilepath);
4142
- const videoBuffer = await this.stopRecordingScreen();
4143
- const video = Buffer.from(videoBuffer, "base64");
4144
- fs5.writeFileSync(absoluteFilepath, video);
4145
- return video;
4146
- }
4147
-
4148
- // src/commands/browser/saveScreenshot.ts
4149
- import fs6 from "node:fs";
4150
- import { getBrowserObject as getBrowserObject7 } from "@wdio/utils";
4151
- async function saveScreenshot(filepath) {
4152
- if (typeof filepath !== "string" || !filepath.endsWith(".png")) {
4153
- throw new Error('saveScreenshot expects a filepath of type string and ".png" file ending');
4154
- }
4155
- const absoluteFilepath = getAbsoluteFilepath(filepath);
4156
- await assertDirectoryExists(absoluteFilepath);
4157
- let screenBuffer;
4158
- if (this.isBidi) {
4159
- const browser = getBrowserObject7(this);
4160
- const contextManager2 = getContextManager(browser);
4161
- const context = await contextManager2.getCurrentContext();
4162
- const { data } = await this.browsingContextCaptureScreenshot({ context });
4163
- screenBuffer = data;
4164
- } else {
4165
- screenBuffer = await this.takeScreenshot();
4166
- }
4167
- const screenshot = Buffer.from(screenBuffer, "base64");
4168
- fs6.writeFileSync(absoluteFilepath, screenshot);
4169
- return screenshot;
4170
- }
4171
-
4172
- // src/commands/browser/scroll.ts
4173
- import logger13 from "@wdio/logger";
4174
- var log13 = logger13("webdriverio");
4175
- function scroll(x = 0, y = 0) {
4176
- if (!x && !y) {
4177
- return log13.warn('"scroll" command was called with no parameters, skipping execution');
4145
+ this.#initialize = this.#browser.sessionSubscribe({
4146
+ events: ["log.entryAdded", "browsingContext.navigationStarted"]
4147
+ }).then(() => true, () => false);
4148
+ this.#browser.on("log.entryAdded", this.handleLogEntry.bind(this));
4149
+ this.#browser.on("result", this.#commandResultHandler.bind(this));
4150
+ this.#browser.on("bidiCommand", this.#handleBidiCommand.bind(this));
4151
+ this.#browser.scriptAddPreloadScript({
4152
+ functionDeclaration: customElementWrapper.toString()
4153
+ });
4178
4154
  }
4179
- if (this.isMobile) {
4180
- return this.execute((x2, y2) => window.scrollBy(x2, y2), x, y);
4155
+ removeListeners() {
4156
+ super.removeListeners();
4157
+ this.#browser.off("log.entryAdded", this.handleLogEntry.bind(this));
4158
+ this.#browser.off("result", this.#commandResultHandler.bind(this));
4159
+ this.#browser.off("bidiCommand", this.#handleBidiCommand.bind(this));
4181
4160
  }
4182
- return this.action("wheel").scroll({
4183
- deltaX: x,
4184
- deltaY: y,
4185
- duration: 0
4186
- }).perform();
4187
- }
4188
-
4189
- // src/commands/browser/setCookies.ts
4190
- async function setCookies(cookieObjs) {
4191
- const cookieObjsList = !Array.isArray(cookieObjs) ? [cookieObjs] : cookieObjs;
4192
- if (cookieObjsList.some((obj) => typeof obj !== "object")) {
4193
- throw new Error("Invalid input (see https://webdriver.io/docs/api/browser/setCookies for documentation)");
4161
+ async initialize() {
4162
+ return this.#initialize;
4194
4163
  }
4195
- if (!this.isBidi) {
4196
- for (const cookieObj of cookieObjsList) {
4197
- await this.addCookie(cookieObj);
4164
+ /**
4165
+ * keep track of navigation events and remove shadow roots when they are no longer needed
4166
+ */
4167
+ #handleBidiCommand(command) {
4168
+ if (command.method !== "browsingContext.navigate") {
4169
+ return;
4198
4170
  }
4199
- return;
4200
- }
4201
- let url6 = new URL("http://localhost");
4202
- if (cookieObjsList.some((cookie) => typeof cookie.domain !== "string")) {
4203
- url6 = new URL(await this.getUrl());
4171
+ const params = command.params;
4172
+ this.#shadowRoots.delete(params.context);
4204
4173
  }
4205
- await Promise.all(cookieObjsList.map((cookie) => this.storageSetCookie({
4206
- cookie: {
4207
- ...cookie,
4208
- domain: cookie.domain || url6.hostname,
4209
- value: {
4210
- type: "string",
4211
- value: cookie.value
4212
- }
4174
+ /**
4175
+ * keep track of frame depth
4176
+ */
4177
+ #commandResultHandler(result) {
4178
+ const noResultError = typeof result.result === "object" && result.result && "error" in result.result && !result.result.error;
4179
+ if (result.command === "switchToFrame" && noResultError) {
4180
+ this.#frameDepth++;
4181
+ }
4182
+ if (result.command === "switchToParentFrame" && noResultError) {
4183
+ this.#frameDepth = Math.max(0, this.#frameDepth - 1);
4213
4184
  }
4214
- })));
4215
- return;
4216
- }
4217
-
4218
- // src/commands/browser/setTimeout.ts
4219
- async function setTimeout2(timeouts) {
4220
- if (typeof timeouts !== "object") {
4221
- throw new Error('Parameter for "setTimeout" command needs to be an object');
4222
- }
4223
- const timeoutValues = Object.values(timeouts);
4224
- if (timeoutValues.length && timeoutValues.every((timeout) => typeof timeout !== "number" || timeout < 0 || timeout > Number.MAX_SAFE_INTEGER)) {
4225
- throw new Error("Specified timeout values are not valid integer (see https://webdriver.io/docs/api/browser/setTimeout for documentation).");
4226
- }
4227
- const implicit = timeouts.implicit;
4228
- const pageLoad = timeouts["page load"] || timeouts.pageLoad;
4229
- const script = timeouts.script;
4230
- const setTimeouts = this.setTimeouts.bind(this);
4231
- return setTimeouts(implicit, pageLoad, script);
4232
- }
4233
-
4234
- // src/commands/browser/setViewport.ts
4235
- var minWindowSize = 0;
4236
- var maxWindowSize = Number.MAX_SAFE_INTEGER;
4237
- async function setViewport(options) {
4238
- if (typeof options.width !== "number" || typeof options.height !== "number") {
4239
- throw new Error("setViewport expects width and height of type number");
4240
4185
  }
4241
- if (options.width < minWindowSize || options.width > maxWindowSize || options.height < minWindowSize || options.height > maxWindowSize) {
4242
- throw new Error("setViewport expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
4186
+ /**
4187
+ * check if we are within a frame
4188
+ * @returns {boolean} true if we are within a frame
4189
+ */
4190
+ isWithinFrame() {
4191
+ return this.#frameDepth > 0;
4243
4192
  }
4244
- if (options.devicePixelRatio && (typeof options.devicePixelRatio !== "number" || options.devicePixelRatio < 0)) {
4245
- throw new Error("setViewport expects devicePixelRatio to be a number in the 0 to 2^31 \u2212 1 range");
4193
+ /**
4194
+ * capture shadow root elements propagated through console.debug
4195
+ */
4196
+ handleLogEntry(logEntry) {
4197
+ const args = "args" in logEntry && logEntry.level === "debug" ? logEntry.args : void 0;
4198
+ if (!args || args[0].type !== "string" || args[0].value !== "[WDIO]" || args[1].type !== "string") {
4199
+ return;
4200
+ }
4201
+ if (!logEntry.source.context) {
4202
+ return;
4203
+ }
4204
+ const eventType = args[1].value;
4205
+ if (eventType === "newShadowRoot" && args[2].type === "node" && args[3].type === "node") {
4206
+ const [
4207
+ /* [WDIO] */
4208
+ ,
4209
+ /* newShadowRoot */
4210
+ ,
4211
+ shadowElem,
4212
+ rootElem,
4213
+ isDocument,
4214
+ documentElement
4215
+ ] = args;
4216
+ if (!this.#shadowRoots.has(logEntry.source.context)) {
4217
+ if (!rootElem.sharedId) {
4218
+ throw new Error(`Expected "sharedId" parameter from object ${rootElem}`);
4219
+ }
4220
+ this.#shadowRoots.set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
4221
+ } else if (isDocument.type === "boolean" && isDocument.value) {
4222
+ if (!rootElem.sharedId) {
4223
+ throw new Error(`Expected "sharedId" parameter from object ${rootElem}`);
4224
+ }
4225
+ const tree2 = this.#shadowRoots.get(logEntry.source.context);
4226
+ if (tree2?.element !== rootElem.sharedId) {
4227
+ this.#shadowRoots.set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
4228
+ }
4229
+ }
4230
+ this.#documentElement = documentElement;
4231
+ const tree = this.#shadowRoots.get(logEntry.source.context);
4232
+ if (!tree) {
4233
+ throw new Error(`Couldn't find tree for context id ${logEntry.source.context}`);
4234
+ }
4235
+ if (
4236
+ // we expect an element id
4237
+ !shadowElem.sharedId || // we expect the element to have a shadow root
4238
+ !shadowElem.value?.shadowRoot?.sharedId || // we expect the shadow root to have a proper type
4239
+ shadowElem.value.shadowRoot.value?.nodeType !== 11
4240
+ ) {
4241
+ return log12.warn(`Expected element with shadow root but found <${shadowElem.value?.localName} />`);
4242
+ }
4243
+ log12.info(`Registered new shadow root for element <${shadowElem.value.localName} /> with id ${shadowElem.value.shadowRoot.sharedId}`);
4244
+ const newTree = new ShadowRootTree(
4245
+ shadowElem.sharedId,
4246
+ shadowElem.value.shadowRoot.sharedId,
4247
+ shadowElem.value.shadowRoot.value.mode
4248
+ );
4249
+ if (rootElem.sharedId) {
4250
+ tree.addShadowElement(rootElem.sharedId, newTree);
4251
+ } else {
4252
+ tree.addShadowElement(newTree);
4253
+ }
4254
+ return;
4255
+ }
4256
+ if (eventType === "removeShadowRoot" && args[2].type === "node" && args[2].sharedId) {
4257
+ const tree = this.#shadowRoots.get(logEntry.source.context);
4258
+ if (!tree) {
4259
+ return;
4260
+ }
4261
+ return tree.remove(args[2].sharedId);
4262
+ }
4263
+ throw new Error(`Invalid parameters for "${eventType}" event: ${args.join(", ")}`);
4246
4264
  }
4247
- const contextManager2 = getContextManager(this);
4248
- const context = await contextManager2.getCurrentContext();
4249
- await this.browsingContextSetViewport({
4250
- context,
4251
- devicePixelRatio: options.devicePixelRatio || 1,
4252
- viewport: {
4253
- width: options.width,
4254
- height: options.height
4265
+ getShadowElementsByContextId(contextId, scope) {
4266
+ let tree = this.#shadowRoots.get(contextId);
4267
+ if (!tree) {
4268
+ return [];
4255
4269
  }
4256
- });
4257
- }
4258
-
4259
- // src/commands/browser/setWindowSize.ts
4260
- import { getBrowserObject as getBrowserObject8 } from "@wdio/utils";
4261
- var minWindowSize2 = 0;
4262
- var maxWindowSize2 = Number.MAX_SAFE_INTEGER;
4263
- async function setWindowSize(width, height) {
4264
- if (typeof width !== "number" || typeof height !== "number") {
4265
- throw new Error("setWindowSize expects width and height of type number");
4270
+ let documentElement;
4271
+ if (scope) {
4272
+ const subTree = tree.find(scope);
4273
+ if (subTree) {
4274
+ tree = subTree;
4275
+ }
4276
+ } else {
4277
+ documentElement = this.#documentElement?.sharedId;
4278
+ }
4279
+ const elements = tree.getAllLookupScopes();
4280
+ return [
4281
+ ...documentElement ? [documentElement] : [],
4282
+ ...new Set(elements).values()
4283
+ ];
4266
4284
  }
4267
- if (width < minWindowSize2 || width > maxWindowSize2 || height < minWindowSize2 || height > maxWindowSize2) {
4268
- throw new Error("setWindowSize expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
4285
+ getShadowElementPairsByContextId(contextId, scope) {
4286
+ let tree = this.#shadowRoots.get(contextId);
4287
+ if (!tree) {
4288
+ return [];
4289
+ }
4290
+ if (scope) {
4291
+ const subTree = tree.find(scope);
4292
+ if (subTree) {
4293
+ tree = subTree;
4294
+ }
4295
+ }
4296
+ return tree.flat().map((tree2) => [tree2.element, tree2.shadowRoot]);
4269
4297
  }
4270
- const browser = getBrowserObject8(this);
4271
- await browser.setWindowRect(null, null, width, height);
4272
- }
4273
-
4274
- // src/commands/browser/switchWindow.ts
4275
- async function switchWindow(matcher) {
4276
- if (typeof matcher !== "string" && !(matcher instanceof RegExp)) {
4277
- throw new Error('Unsupported parameter for switchWindow, required is "string" or a RegExp');
4298
+ getShadowRootModeById(contextId, element) {
4299
+ const tree = this.#shadowRoots.get(contextId);
4300
+ if (!tree) {
4301
+ return;
4302
+ }
4303
+ const shadowTree = tree.find(element);
4304
+ if (!shadowTree) {
4305
+ return;
4306
+ }
4307
+ return shadowTree.mode;
4278
4308
  }
4279
- const currentWindow = await this.getWindowHandle();
4280
- if (typeof matcher === "string" && currentWindow === matcher) {
4281
- return currentWindow;
4309
+ deleteShadowRoot(element, contextId) {
4310
+ const tree = this.#shadowRoots.get(contextId);
4311
+ if (!tree) {
4312
+ return;
4313
+ }
4314
+ return tree.remove(element);
4282
4315
  }
4283
- const contextManager2 = getContextManager(this);
4284
- const tabs = await this.getWindowHandles();
4285
- if (typeof matcher === "string" && tabs.includes(matcher)) {
4286
- await this.switchToWindow(matcher);
4287
- contextManager2.setCurrentContext(matcher);
4288
- return matcher;
4316
+ };
4317
+ var ShadowRootTree = class _ShadowRootTree {
4318
+ element;
4319
+ shadowRoot;
4320
+ mode;
4321
+ children = /* @__PURE__ */ new Set();
4322
+ constructor(element, shadowRoot, mode) {
4323
+ this.element = element;
4324
+ this.shadowRoot = shadowRoot;
4325
+ this.mode = mode;
4289
4326
  }
4290
- const matchesTarget = (target) => {
4291
- if (typeof matcher === "string") {
4292
- return target.includes(matcher);
4293
- }
4294
- return !!target.match(matcher);
4295
- };
4296
- for (const tab of tabs) {
4297
- await this.switchToWindow(tab);
4298
- contextManager2.setCurrentContext(tab);
4299
- const url6 = await this.getUrl();
4300
- if (matchesTarget(url6)) {
4301
- return tab;
4327
+ addShadowElement(...args) {
4328
+ const [scope, treeArg] = args;
4329
+ if (!scope && !treeArg) {
4330
+ throw new Error('Method "addShadowElement" expects at least 2 arguments');
4302
4331
  }
4303
- const title = await this.getTitle();
4304
- if (matchesTarget(title)) {
4305
- return tab;
4332
+ if (scope instanceof _ShadowRootTree) {
4333
+ this.children.add(scope);
4334
+ return;
4306
4335
  }
4307
- const windowName = await this.execute(
4308
- /* istanbul ignore next */
4309
- () => window.name
4310
- );
4311
- if (windowName && matchesTarget(windowName)) {
4312
- return tab;
4336
+ if (typeof scope === "string" && treeArg instanceof _ShadowRootTree) {
4337
+ const tree = this.find(scope) || this.findByShadowId(scope);
4338
+ if (!tree) {
4339
+ throw new Error(`Couldn't find element with id ${scope}`);
4340
+ }
4341
+ tree.addShadowElement(treeArg);
4342
+ return;
4313
4343
  }
4344
+ throw new Error('Invalid arguments for "addShadowElement" method');
4314
4345
  }
4315
- await this.switchToWindow(currentWindow);
4316
- throw new Error(`No window found with title, url, name or window handle matching "${matcher}"`);
4317
- }
4318
-
4319
- // src/commands/browser/switchFrame.ts
4320
- import logger14 from "@wdio/logger";
4321
- import { ELEMENT_KEY as ELEMENT_KEY8 } from "webdriver";
4322
- var log14 = logger14("webdriverio:switchFrame");
4323
- async function switchFrame(context) {
4324
- function isPossiblyUnresolvedElement(input) {
4325
- return Boolean(input) && typeof input === "object" && typeof input.getElement === "function";
4326
- }
4327
- if (!this.isBidi) {
4328
- if (typeof context === "function") {
4329
- throw new Error("Cannot use a function to fetch a context in WebDriver Classic");
4346
+ find(element) {
4347
+ if (this.element === element) {
4348
+ return this;
4330
4349
  }
4331
- if (typeof context === "string") {
4332
- throw new Error("Cannot use a string to fetch a context in WebDriver Classic");
4350
+ for (const child of this.children) {
4351
+ const elem = child.find(element);
4352
+ if (elem) {
4353
+ return elem;
4354
+ }
4333
4355
  }
4334
- if (isPossiblyUnresolvedElement(context)) {
4335
- const element = await context.getElement();
4336
- await element.waitForExist({
4337
- timeoutMsg: `Can't switch to frame with selector ${element.selector} because it doesn't exist`
4338
- });
4339
- return switchToFrame(this, element);
4356
+ return void 0;
4357
+ }
4358
+ findByShadowId(shadowRoot) {
4359
+ if (this.shadowRoot === shadowRoot) {
4360
+ return this;
4340
4361
  }
4341
- return switchToFrame(this, context);
4362
+ for (const child of this.children) {
4363
+ const elem = child.findByShadowId(shadowRoot);
4364
+ if (elem) {
4365
+ return elem;
4366
+ }
4367
+ }
4368
+ return void 0;
4342
4369
  }
4343
- if (context === null) {
4344
- const handle = await this.getWindowHandle();
4345
- switchToFrameHelper(this, handle);
4346
- await switchToFrame(this, context);
4347
- return handle;
4370
+ getAllLookupScopes() {
4371
+ return [
4372
+ this.shadowRoot ?? this.element,
4373
+ ...Array.from(this.children).map((tree) => tree.getAllLookupScopes())
4374
+ ].flat();
4348
4375
  }
4349
- if (typeof context === "string") {
4350
- const tree = await this.browsingContextGetTree({});
4351
- let newContextId;
4352
- const urlContext = findContext(context, tree.contexts, byUrl) || /**
4353
- * In case the user provides an url without `/` at the end, e.g. `https://example.com`,
4354
- * the `browsingContextGetTree` command may return a context with the url `https://example.com/`.
4355
- */
4356
- findContext(`${context}/`, tree.contexts, byUrl);
4376
+ flat() {
4377
+ return [this, ...Array.from(this.children).map((tree) => tree.flat())].flat();
4378
+ }
4379
+ remove(element) {
4380
+ const childArray = Array.from(this.children);
4381
+ for (let i = childArray.length - 1; i >= 0; i--) {
4382
+ if (childArray[i].element === element) {
4383
+ return this.children.delete(childArray[i]);
4384
+ }
4385
+ const wasFound = childArray[i].remove(element);
4386
+ if (wasFound) {
4387
+ return true;
4388
+ }
4389
+ }
4390
+ return false;
4391
+ }
4392
+ };
4393
+
4394
+ // src/session/networkManager.ts
4395
+ function getNetworkManager(browser) {
4396
+ return SessionManager.getSessionManager(browser, NetworkManager);
4397
+ }
4398
+ var UNKNOWN_NAVIGATION_ID = "UNKNOWN_NAVIGATION_ID";
4399
+ var SUPPORTED_NAVIGATION_PROTOCOLS = ["http", "https", "data", "file"];
4400
+ var NetworkManager = class _NetworkManager extends SessionManager {
4401
+ #browser;
4402
+ #initialize;
4403
+ #requests = /* @__PURE__ */ new Map();
4404
+ #lastNetworkId;
4405
+ constructor(browser) {
4406
+ super(browser, _NetworkManager.name);
4407
+ this.#browser = browser;
4408
+ if (!this.isEnabled()) {
4409
+ this.#initialize = Promise.resolve(true);
4410
+ return;
4411
+ }
4412
+ this.#initialize = this.#browser.sessionSubscribe({
4413
+ events: [
4414
+ "browsingContext.navigationStarted",
4415
+ "network.responseCompleted",
4416
+ "network.beforeRequestSent",
4417
+ "network.fetchError"
4418
+ ]
4419
+ }).then(() => true, () => false);
4420
+ this.#browser.on("browsingContext.navigationStarted", this.#navigationStarted.bind(this));
4421
+ this.#browser.on("network.responseCompleted", this.#responseCompleted.bind(this));
4422
+ this.#browser.on("network.beforeRequestSent", this.#beforeRequestSent.bind(this));
4423
+ this.#browser.on("network.fetchError", this.#fetchError.bind(this));
4424
+ }
4425
+ removeListeners() {
4426
+ super.removeListeners();
4427
+ this.#browser.off("browsingContext.navigationStarted", this.#navigationStarted.bind(this));
4428
+ this.#browser.off("network.responseCompleted", this.#responseCompleted.bind(this));
4429
+ this.#browser.off("network.beforeRequestSent", this.#beforeRequestSent.bind(this));
4430
+ this.#browser.off("network.fetchError", this.#fetchError.bind(this));
4431
+ }
4432
+ async initialize() {
4433
+ return this.#initialize;
4434
+ }
4435
+ #beforeRequestSent(log25) {
4436
+ if (log25.navigation) {
4437
+ return;
4438
+ }
4439
+ const request = this.#findRootRequest(log25.navigation);
4440
+ if (!request) {
4441
+ return;
4442
+ }
4443
+ const { request: id, headers, cookies, url: url6 } = log25.request;
4444
+ request.children?.push({
4445
+ id,
4446
+ url: url6,
4447
+ headers: headerListToObject(headers),
4448
+ cookies: cookies.map((cookie) => ({
4449
+ name: cookie.name,
4450
+ value: cookie.value.type === "string" ? cookie.value.value : atob(cookie.value.value),
4451
+ domain: cookie.domain,
4452
+ path: cookie.path,
4453
+ size: cookie.size,
4454
+ httpOnly: cookie.httpOnly,
4455
+ secure: cookie.secure,
4456
+ sameSite: cookie.sameSite,
4457
+ expiry: cookie.expiry
4458
+ })),
4459
+ timestamp: log25.timestamp
4460
+ });
4461
+ }
4462
+ #navigationStarted(log25) {
4463
+ if (
4464
+ /**
4465
+ * we need a navigation id to identify the request
4466
+ */
4467
+ !log25.navigation || /**
4468
+ * ignore urls that users wouldn't navigate to
4469
+ */
4470
+ !SUPPORTED_NAVIGATION_PROTOCOLS.some((protocol) => log25.url.startsWith(protocol))
4471
+ ) {
4472
+ if (log25.navigation === null && log25.url === "") {
4473
+ this.#lastNetworkId = UNKNOWN_NAVIGATION_ID;
4474
+ return this.#requests.set(UNKNOWN_NAVIGATION_ID, {
4475
+ url: "",
4476
+ headers: {},
4477
+ timestamp: log25.timestamp,
4478
+ redirectChain: [],
4479
+ children: []
4480
+ });
4481
+ }
4482
+ return;
4483
+ }
4484
+ this.#lastNetworkId = log25.navigation;
4485
+ this.#requests.set(log25.navigation, {
4486
+ url: log25.url,
4487
+ headers: {},
4488
+ timestamp: log25.timestamp,
4489
+ navigation: log25.navigation,
4490
+ redirectChain: [],
4491
+ children: []
4492
+ });
4493
+ }
4494
+ #fetchError(log25) {
4495
+ const response = this.#findRootRequest(log25.navigation);
4496
+ if (!response) {
4497
+ return;
4498
+ }
4499
+ const request = response.children?.find((child) => child.id === log25.request.request);
4500
+ if (!request) {
4501
+ return;
4502
+ }
4503
+ request.error = log25.errorText;
4504
+ }
4505
+ #findRootRequest(navigationId) {
4506
+ const response = this.#requests.get(navigationId || UNKNOWN_NAVIGATION_ID);
4507
+ if (response) {
4508
+ return response;
4509
+ }
4510
+ const firstRequest = this.#requests.values().next().value;
4511
+ return this.#lastNetworkId ? this.#requests.get(this.#lastNetworkId) || firstRequest : firstRequest;
4512
+ }
4513
+ #responseCompleted(log25) {
4514
+ const response = this.#findRootRequest(log25.navigation);
4515
+ if (!response) {
4516
+ return;
4517
+ }
4518
+ if (!response.navigation && response.url === "") {
4519
+ response.url = log25.request.url;
4520
+ response.navigation = log25.navigation;
4521
+ }
4522
+ if (log25.navigation === response.navigation) {
4523
+ if (response.url !== log25.response.url) {
4524
+ response.redirectChain?.push(response.url);
4525
+ }
4526
+ response.url = log25.response.url;
4527
+ const { headers: requestHeaders } = log25.request;
4528
+ const { fromCache, headers: responseHeaders, mimeType, status } = log25.response;
4529
+ response.headers = headerListToObject(requestHeaders), response.response = {
4530
+ fromCache,
4531
+ headers: headerListToObject(responseHeaders),
4532
+ mimeType,
4533
+ status
4534
+ };
4535
+ return;
4536
+ }
4537
+ const request = response.children?.find((child) => child.id === log25.request.request);
4538
+ if (!request) {
4539
+ return;
4540
+ }
4541
+ request.response = {
4542
+ fromCache: log25.response.fromCache,
4543
+ headers: headerListToObject(log25.response.headers),
4544
+ mimeType: log25.response.mimeType,
4545
+ status: log25.response.status
4546
+ };
4547
+ response.children?.push(request);
4548
+ }
4549
+ getRequestResponseData(navigationId) {
4550
+ return this.#requests.get(navigationId);
4551
+ }
4552
+ /**
4553
+ * Returns the number of requests that are currently pending.
4554
+ * @param context browsing context id
4555
+ * @returns the number of requests that are currently pending
4556
+ */
4557
+ getPendingRequests(navigationId) {
4558
+ const request = this.#requests.get(navigationId);
4559
+ if (!request) {
4560
+ throw new Error(`Couldn't find request for navigation with id ${navigationId}`);
4561
+ }
4562
+ const subRequests = request.children || [];
4563
+ return subRequests.filter((child) => (
4564
+ /**
4565
+ * either the request has no response yet
4566
+ */
4567
+ !child.response && /**
4568
+ * and there was no request error
4569
+ */
4570
+ !child.error
4571
+ ));
4572
+ }
4573
+ };
4574
+ function headerListToObject(headers) {
4575
+ return headers.reduce((acc, { name, value }) => {
4576
+ acc[name] = value.value;
4577
+ return acc;
4578
+ }, {});
4579
+ }
4580
+
4581
+ // src/session/dialog.ts
4582
+ function getDialogManager(browser) {
4583
+ return SessionManager.getSessionManager(browser, DialogManager);
4584
+ }
4585
+ var DialogManager = class _DialogManager extends SessionManager {
4586
+ #browser;
4587
+ #initialize;
4588
+ #autoHandleDialog = true;
4589
+ constructor(browser) {
4590
+ super(browser, _DialogManager.name);
4591
+ this.#browser = browser;
4592
+ if (!this.isEnabled()) {
4593
+ this.#initialize = Promise.resolve(true);
4594
+ return;
4595
+ }
4596
+ this.#initialize = this.#browser.sessionSubscribe({
4597
+ events: ["browsingContext.userPromptOpened"]
4598
+ }).then(() => true, () => false);
4599
+ this.#browser.on("_dialogListenerRegistered", () => this.#switchListenerFlag(false));
4600
+ this.#browser.on("_dialogListenerRemoved", () => this.#switchListenerFlag(true));
4601
+ this.#browser.on("browsingContext.userPromptOpened", this.#handleUserPrompt.bind(this));
4602
+ }
4603
+ removeListeners() {
4604
+ super.removeListeners();
4605
+ this.#browser.off("browsingContext.userPromptOpened", this.#handleUserPrompt.bind(this));
4606
+ this.#browser.removeAllListeners("_dialogListenerRegistered");
4607
+ this.#browser.removeAllListeners("_dialogListenerRemoved");
4608
+ }
4609
+ async initialize() {
4610
+ return this.#initialize;
4611
+ }
4612
+ /**
4613
+ * capture shadow root elements propagated through console.debug
4614
+ */
4615
+ async #handleUserPrompt(log25) {
4616
+ if (this.#autoHandleDialog) {
4617
+ return this.#browser.browsingContextHandleUserPrompt({
4618
+ accept: false,
4619
+ context: log25.context
4620
+ });
4621
+ }
4622
+ const dialog = new Dialog(log25, this.#browser);
4623
+ this.#browser.emit("dialog", dialog);
4624
+ }
4625
+ /**
4626
+ * Is called when a new dialog listener is registered with the `dialog` name.
4627
+ * In these cases we set a flag to the `#listener` map to indicate that we
4628
+ * are listening to dialog events for this page in this context.
4629
+ */
4630
+ #switchListenerFlag(value) {
4631
+ this.#autoHandleDialog = value;
4632
+ }
4633
+ };
4634
+ var Dialog = class {
4635
+ #browser;
4636
+ #context;
4637
+ #message;
4638
+ #defaultValue;
4639
+ #type;
4640
+ constructor(event, browser) {
4641
+ this.#message = event.message;
4642
+ this.#defaultValue = event.defaultValue;
4643
+ this.#type = event.type;
4644
+ this.#context = event.context;
4645
+ this.#browser = browser;
4646
+ }
4647
+ message() {
4648
+ return this.#message;
4649
+ }
4650
+ defaultValue() {
4651
+ return this.#defaultValue;
4652
+ }
4653
+ type() {
4654
+ return this.#type;
4655
+ }
4656
+ /**
4657
+ * Returns when the dialog has been accepted.
4658
+ *
4659
+ * @alias dialog.accept
4660
+ * @param {string=} promptText A text to enter into prompt. Does not cause any effects if the dialog's type is not prompt.
4661
+ * @returns {Promise<void>}
4662
+ */
4663
+ async accept(userText) {
4664
+ await this.#browser.browsingContextHandleUserPrompt({
4665
+ accept: true,
4666
+ context: this.#context,
4667
+ userText
4668
+ });
4669
+ }
4670
+ async dismiss() {
4671
+ await this.#browser.browsingContextHandleUserPrompt({
4672
+ accept: false,
4673
+ context: this.#context
4674
+ });
4675
+ }
4676
+ };
4677
+
4678
+ // src/session/index.ts
4679
+ function registerSessionManager(instance) {
4680
+ return Promise.all([
4681
+ getPolyfillManager(instance).initialize(),
4682
+ getShadowRootManager(instance).initialize(),
4683
+ getNetworkManager(instance).initialize(),
4684
+ getDialogManager(instance).initialize(),
4685
+ getContextManager(instance).initialize()
4686
+ ]);
4687
+ }
4688
+
4689
+ // src/commands/browser/reloadSession.ts
4690
+ var log13 = logger13("webdriverio");
4691
+ async function reloadSession(newCapabilities) {
4692
+ const oldSessionId = this.sessionId;
4693
+ const shutdownDriver = Boolean(newCapabilities?.browserName);
4694
+ try {
4695
+ await this.deleteSession({ shutdownDriver });
4696
+ } catch (err) {
4697
+ log13.warn(`Suppressing error closing the session: ${err.stack}`);
4698
+ }
4699
+ if (this.puppeteer?.connected) {
4700
+ this.puppeteer.disconnect();
4701
+ log13.debug("Disconnected puppeteer session");
4702
+ }
4703
+ const ProtocolDriver = (await import(
4704
+ /* @vite-ignore */
4705
+ this.options.automationProtocol
4706
+ )).default;
4707
+ await ProtocolDriver.reloadSession(this, newCapabilities);
4708
+ await registerSessionManager(this);
4709
+ const options = this.options;
4710
+ if (Array.isArray(options.onReload) && options.onReload.length) {
4711
+ await Promise.all(options.onReload.map((hook) => hook(oldSessionId, this.sessionId)));
4712
+ }
4713
+ return this.sessionId;
4714
+ }
4715
+
4716
+ // src/commands/browser/restore.ts
4717
+ async function restore(scopes2) {
4718
+ const scopeArray = !scopes2 || Array.isArray(scopes2) ? scopes2 : [scopes2];
4719
+ const instanceRestoreFunctions = restoreFunctions.get(this);
4720
+ if (!instanceRestoreFunctions) {
4721
+ return;
4722
+ }
4723
+ await Promise.all(Array.from(instanceRestoreFunctions.entries()).map(async ([scope, restoreFunctionsList]) => {
4724
+ if (!scopeArray || scopeArray.includes(scope)) {
4725
+ await Promise.all(restoreFunctionsList.map((fn) => fn()));
4726
+ instanceRestoreFunctions.set(scope, []);
4727
+ }
4728
+ }));
4729
+ }
4730
+
4731
+ // src/commands/browser/savePDF.ts
4732
+ import fs4 from "node:fs";
4733
+ async function savePDF(filepath, options) {
4734
+ if (typeof filepath !== "string" || !filepath.endsWith(".pdf")) {
4735
+ throw new Error('savePDF expects a filepath of type string and ".pdf" file ending');
4736
+ }
4737
+ const absoluteFilepath = getAbsoluteFilepath(filepath);
4738
+ await assertDirectoryExists(absoluteFilepath);
4739
+ const pdf = await this.printPage(
4740
+ options?.orientation,
4741
+ options?.scale,
4742
+ options?.background,
4743
+ options?.width,
4744
+ options?.height,
4745
+ options?.top,
4746
+ options?.bottom,
4747
+ options?.left,
4748
+ options?.right,
4749
+ options?.shrinkToFit,
4750
+ options?.pageRanges
4751
+ );
4752
+ const page = Buffer.from(pdf, "base64");
4753
+ fs4.writeFileSync(absoluteFilepath, page);
4754
+ return page;
4755
+ }
4756
+
4757
+ // src/commands/browser/saveRecordingScreen.ts
4758
+ import fs5 from "node:fs";
4759
+ async function saveRecordingScreen(filepath) {
4760
+ if (typeof filepath !== "string") {
4761
+ throw new Error("saveRecordingScreen expects a filepath");
4762
+ }
4763
+ const absoluteFilepath = getAbsoluteFilepath(filepath);
4764
+ await assertDirectoryExists(absoluteFilepath);
4765
+ const videoBuffer = await this.stopRecordingScreen();
4766
+ const video = Buffer.from(videoBuffer, "base64");
4767
+ fs5.writeFileSync(absoluteFilepath, video);
4768
+ return video;
4769
+ }
4770
+
4771
+ // src/commands/browser/saveScreenshot.ts
4772
+ import fs6 from "node:fs";
4773
+ import { getBrowserObject as getBrowserObject7 } from "@wdio/utils";
4774
+ async function saveScreenshot(filepath) {
4775
+ if (typeof filepath !== "string" || !filepath.endsWith(".png")) {
4776
+ throw new Error('saveScreenshot expects a filepath of type string and ".png" file ending');
4777
+ }
4778
+ const absoluteFilepath = getAbsoluteFilepath(filepath);
4779
+ await assertDirectoryExists(absoluteFilepath);
4780
+ let screenBuffer;
4781
+ if (this.isBidi) {
4782
+ const browser = getBrowserObject7(this);
4783
+ const contextManager = getContextManager(browser);
4784
+ const context = await contextManager.getCurrentContext();
4785
+ const { data } = await this.browsingContextCaptureScreenshot({ context });
4786
+ screenBuffer = data;
4787
+ } else {
4788
+ screenBuffer = await this.takeScreenshot();
4789
+ }
4790
+ const screenshot = Buffer.from(screenBuffer, "base64");
4791
+ fs6.writeFileSync(absoluteFilepath, screenshot);
4792
+ return screenshot;
4793
+ }
4794
+
4795
+ // src/commands/browser/scroll.ts
4796
+ import logger14 from "@wdio/logger";
4797
+ var log14 = logger14("webdriverio");
4798
+ function scroll(x = 0, y = 0) {
4799
+ if (!x && !y) {
4800
+ return log14.warn('"scroll" command was called with no parameters, skipping execution');
4801
+ }
4802
+ if (this.isMobile) {
4803
+ return this.execute((x2, y2) => window.scrollBy(x2, y2), x, y);
4804
+ }
4805
+ return this.action("wheel").scroll({
4806
+ deltaX: x,
4807
+ deltaY: y,
4808
+ duration: 0
4809
+ }).perform();
4810
+ }
4811
+
4812
+ // src/commands/browser/setCookies.ts
4813
+ async function setCookies(cookieObjs) {
4814
+ const cookieObjsList = !Array.isArray(cookieObjs) ? [cookieObjs] : cookieObjs;
4815
+ if (cookieObjsList.some((obj) => typeof obj !== "object")) {
4816
+ throw new Error("Invalid input (see https://webdriver.io/docs/api/browser/setCookies for documentation)");
4817
+ }
4818
+ if (!this.isBidi) {
4819
+ for (const cookieObj of cookieObjsList) {
4820
+ await this.addCookie(cookieObj);
4821
+ }
4822
+ return;
4823
+ }
4824
+ let url6 = new URL("http://localhost");
4825
+ if (cookieObjsList.some((cookie) => typeof cookie.domain !== "string")) {
4826
+ url6 = new URL(await this.getUrl());
4827
+ }
4828
+ await Promise.all(cookieObjsList.map((cookie) => this.storageSetCookie({
4829
+ cookie: {
4830
+ ...cookie,
4831
+ domain: cookie.domain || url6.hostname,
4832
+ value: {
4833
+ type: "string",
4834
+ value: cookie.value
4835
+ }
4836
+ }
4837
+ })));
4838
+ return;
4839
+ }
4840
+
4841
+ // src/commands/browser/setTimeout.ts
4842
+ async function setTimeout2(timeouts) {
4843
+ if (typeof timeouts !== "object") {
4844
+ throw new Error('Parameter for "setTimeout" command needs to be an object');
4845
+ }
4846
+ const timeoutValues = Object.values(timeouts);
4847
+ if (timeoutValues.length && timeoutValues.every((timeout) => typeof timeout !== "number" || timeout < 0 || timeout > Number.MAX_SAFE_INTEGER)) {
4848
+ throw new Error("Specified timeout values are not valid integer (see https://webdriver.io/docs/api/browser/setTimeout for documentation).");
4849
+ }
4850
+ const implicit = timeouts.implicit;
4851
+ const pageLoad = timeouts["page load"] || timeouts.pageLoad;
4852
+ const script = timeouts.script;
4853
+ const setTimeouts = this.setTimeouts.bind(this);
4854
+ return setTimeouts(implicit, pageLoad, script);
4855
+ }
4856
+
4857
+ // src/commands/browser/setViewport.ts
4858
+ var minWindowSize = 0;
4859
+ var maxWindowSize = Number.MAX_SAFE_INTEGER;
4860
+ async function setViewport(options) {
4861
+ if (typeof options.width !== "number" || typeof options.height !== "number") {
4862
+ throw new Error("setViewport expects width and height of type number");
4863
+ }
4864
+ if (options.width < minWindowSize || options.width > maxWindowSize || options.height < minWindowSize || options.height > maxWindowSize) {
4865
+ throw new Error("setViewport expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
4866
+ }
4867
+ if (options.devicePixelRatio && (typeof options.devicePixelRatio !== "number" || options.devicePixelRatio < 0)) {
4868
+ throw new Error("setViewport expects devicePixelRatio to be a number in the 0 to 2^31 \u2212 1 range");
4869
+ }
4870
+ const contextManager = getContextManager(this);
4871
+ const context = await contextManager.getCurrentContext();
4872
+ await this.browsingContextSetViewport({
4873
+ context,
4874
+ devicePixelRatio: options.devicePixelRatio || 1,
4875
+ viewport: {
4876
+ width: options.width,
4877
+ height: options.height
4878
+ }
4879
+ });
4880
+ }
4881
+
4882
+ // src/commands/browser/setWindowSize.ts
4883
+ import { getBrowserObject as getBrowserObject8 } from "@wdio/utils";
4884
+ var minWindowSize2 = 0;
4885
+ var maxWindowSize2 = Number.MAX_SAFE_INTEGER;
4886
+ async function setWindowSize(width, height) {
4887
+ if (typeof width !== "number" || typeof height !== "number") {
4888
+ throw new Error("setWindowSize expects width and height of type number");
4889
+ }
4890
+ if (width < minWindowSize2 || width > maxWindowSize2 || height < minWindowSize2 || height > maxWindowSize2) {
4891
+ throw new Error("setWindowSize expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
4892
+ }
4893
+ const browser = getBrowserObject8(this);
4894
+ await browser.setWindowRect(null, null, width, height);
4895
+ }
4896
+
4897
+ // src/commands/browser/switchWindow.ts
4898
+ async function switchWindow(matcher) {
4899
+ if (typeof matcher !== "string" && !(matcher instanceof RegExp)) {
4900
+ throw new Error('Unsupported parameter for switchWindow, required is "string" or a RegExp');
4901
+ }
4902
+ const currentWindow = await this.getWindowHandle();
4903
+ if (typeof matcher === "string" && currentWindow === matcher) {
4904
+ return currentWindow;
4905
+ }
4906
+ const contextManager = getContextManager(this);
4907
+ const tabs = await this.getWindowHandles();
4908
+ if (typeof matcher === "string" && tabs.includes(matcher)) {
4909
+ await this.switchToWindow(matcher);
4910
+ contextManager.setCurrentContext(matcher);
4911
+ return matcher;
4912
+ }
4913
+ const matchesTarget = (target) => {
4914
+ if (typeof matcher === "string") {
4915
+ return target.includes(matcher);
4916
+ }
4917
+ return !!target.match(matcher);
4918
+ };
4919
+ for (const tab of tabs) {
4920
+ await this.switchToWindow(tab);
4921
+ contextManager.setCurrentContext(tab);
4922
+ const url6 = await this.getUrl();
4923
+ if (matchesTarget(url6)) {
4924
+ return tab;
4925
+ }
4926
+ const title = await this.getTitle();
4927
+ if (matchesTarget(title)) {
4928
+ return tab;
4929
+ }
4930
+ const windowName = await this.execute(
4931
+ /* istanbul ignore next */
4932
+ () => window.name
4933
+ );
4934
+ if (windowName && matchesTarget(windowName)) {
4935
+ return tab;
4936
+ }
4937
+ }
4938
+ await this.switchToWindow(currentWindow);
4939
+ throw new Error(`No window found with title, url, name or window handle matching "${matcher}"`);
4940
+ }
4941
+
4942
+ // src/commands/browser/switchFrame.ts
4943
+ import logger15 from "@wdio/logger";
4944
+ import { ELEMENT_KEY as ELEMENT_KEY8 } from "webdriver";
4945
+ var log15 = logger15("webdriverio:switchFrame");
4946
+ async function switchFrame(context) {
4947
+ function isPossiblyUnresolvedElement(input) {
4948
+ return Boolean(input) && typeof input === "object" && typeof input.getElement === "function";
4949
+ }
4950
+ if (!this.isBidi) {
4951
+ if (typeof context === "function") {
4952
+ throw new Error("Cannot use a function to fetch a context in WebDriver Classic");
4953
+ }
4954
+ if (typeof context === "string") {
4955
+ throw new Error("Cannot use a string to fetch a context in WebDriver Classic");
4956
+ }
4957
+ if (isPossiblyUnresolvedElement(context)) {
4958
+ const element = await context.getElement();
4959
+ await element.waitForExist({
4960
+ timeoutMsg: `Can't switch to frame with selector ${element.selector} because it doesn't exist`
4961
+ });
4962
+ return switchToFrame(this, element);
4963
+ }
4964
+ return switchToFrame(this, context);
4965
+ }
4966
+ if (context === null) {
4967
+ const handle = await this.getWindowHandle();
4968
+ switchToFrameHelper(this, handle);
4969
+ await switchToFrame(this, context);
4970
+ return handle;
4971
+ }
4972
+ if (typeof context === "string") {
4973
+ const tree = await this.browsingContextGetTree({});
4974
+ let newContextId;
4975
+ const urlContext = findContext(context, tree.contexts, byUrl) || /**
4976
+ * In case the user provides an url without `/` at the end, e.g. `https://example.com`,
4977
+ * the `browsingContextGetTree` command may return a context with the url `https://example.com/`.
4978
+ */
4979
+ findContext(`${context}/`, tree.contexts, byUrl);
4357
4980
  const urlContextContaining = findContext(context, tree.contexts, byUrlContaining);
4358
4981
  const contextIdContext = findContext(context, tree.contexts, byContextId);
4359
4982
  if (urlContext) {
4360
- log14.info(`Found context by url "${urlContext.url}" with context id "${urlContext.context}"`);
4983
+ log15.info(`Found context by url "${urlContext.url}" with context id "${urlContext.context}"`);
4361
4984
  newContextId = urlContext.context;
4362
4985
  } else if (urlContextContaining) {
4363
- log14.info(`Found context by url containing "${urlContextContaining.url}" with context id "${urlContextContaining.context}"`);
4986
+ log15.info(`Found context by url containing "${urlContextContaining.url}" with context id "${urlContextContaining.context}"`);
4364
4987
  newContextId = urlContextContaining.context;
4365
4988
  } else if (contextIdContext) {
4366
- log14.info(`Found context by id "${contextIdContext}" with url "${contextIdContext.url}"`);
4989
+ log15.info(`Found context by id "${contextIdContext}" with url "${contextIdContext.url}"`);
4367
4990
  newContextId = contextIdContext.context;
4368
4991
  }
4369
4992
  if (!newContextId) {
@@ -4390,7 +5013,7 @@ async function switchFrame(context) {
4390
5013
  arguments: args.map((arg) => LocalValue.getArgument(arg)),
4391
5014
  target: { context: id }
4392
5015
  };
4393
- const result = await this.scriptCallFunction(params).catch((err) => log14.warn(`Failed to identify frame context id: ${err.message}`));
5016
+ const result = await this.scriptCallFunction(params).catch((err) => log15.warn(`Failed to identify frame context id: ${err.message}`));
4394
5017
  if (!result) {
4395
5018
  return [];
4396
5019
  }
@@ -4420,13 +5043,13 @@ async function switchFrame(context) {
4420
5043
  let desiredFrame;
4421
5044
  let desiredContext = newContextId;
4422
5045
  const contextQueue = [];
4423
- log14.info(`Available frames to switch to: ${allFrames.length}, desired context to switch: ${desiredContext}`);
5046
+ log15.info(`Available frames to switch to: ${allFrames.length}, desired context to switch: ${desiredContext}`);
4424
5047
  while (desiredContext !== currentContext) {
4425
5048
  desiredFrame = allFrames.find(({ context: context2 }) => context2 === desiredContext);
4426
5049
  if (!desiredFrame) {
4427
5050
  break;
4428
5051
  }
4429
- log14.info(
5052
+ log15.info(
4430
5053
  contextQueue.length === 0 ? `Found desired frame with element id ${desiredFrame.frameElement[ELEMENT_KEY8]}` : `to switch to desired frame, we need to switch to ${desiredFrame.context} first`
4431
5054
  );
4432
5055
  contextQueue.unshift(desiredFrame);
@@ -4462,7 +5085,7 @@ async function switchFrame(context) {
4462
5085
  arguments: [],
4463
5086
  target: { context: contextId }
4464
5087
  };
4465
- const result = await this.scriptCallFunction(params).catch((err) => log14.warn(`switchFrame context callback threw error: ${err.message}`));
5088
+ const result = await this.scriptCallFunction(params).catch((err) => log15.warn(`switchFrame context callback threw error: ${err.message}`));
4466
5089
  if (!result || result.type !== "success" || result.result.type !== "boolean" || !result.result.value) {
4467
5090
  continue;
4468
5091
  }
@@ -4472,7 +5095,7 @@ async function switchFrame(context) {
4472
5095
  throw new Error("Could not find the desired frame");
4473
5096
  }
4474
5097
  throw new Error(
4475
- `Invalid type for context parameter: ${typeof context}, expected one of number, string or null. Check out our docs: https://webdriver.io/docs/api/browser/switchToFrame.html`
5098
+ `Invalid type for context parameter: ${typeof context}, expected one of number, string or null. Check out our docs: https://webdriver.io/docs/api/browser/switchFrame.html`
4476
5099
  );
4477
5100
  }
4478
5101
  function switchToFrameHelper(browser, context) {
@@ -4533,11 +5156,11 @@ function switchToFrame(browser, frame) {
4533
5156
  }
4534
5157
 
4535
5158
  // src/commands/browser/throttle.ts
4536
- import logger15 from "@wdio/logger";
5159
+ import logger16 from "@wdio/logger";
4537
5160
  import { getBrowserObject as getBrowserObject9 } from "@wdio/utils";
4538
- var log15 = logger15("webdriverio:throttle");
5161
+ var log16 = logger16("webdriverio:throttle");
4539
5162
  async function throttle(params) {
4540
- log15.warn('Command "throttle" is deprecated and will be removed with the next major version release! Use `throttleNetwork` instead.');
5163
+ log16.warn('Command "throttle" is deprecated and will be removed with the next major version release! Use `throttleNetwork` instead.');
4541
5164
  const browser = getBrowserObject9(this);
4542
5165
  await browser.throttleNetwork(params);
4543
5166
  }
@@ -4667,205 +5290,19 @@ function touchAction2(actions2) {
4667
5290
  // src/commands/browser/uploadFile.ts
4668
5291
  import fs7 from "node:fs";
4669
5292
  import path2 from "node:path";
4670
- import archiver from "archiver";
4671
- async function uploadFile(localPath) {
4672
- if (typeof localPath !== "string") {
4673
- throw new Error("number or type of arguments don't agree with uploadFile command");
4674
- }
4675
- if (typeof this.file !== "function") {
4676
- throw new Error(`The uploadFile command is not available in ${this.capabilities.browserName}`);
4677
- }
4678
- const zipData = [];
4679
- const source = fs7.createReadStream(localPath);
4680
- return new Promise((resolve5, reject) => {
4681
- archiver("zip").on("error", (err) => reject(err)).on("data", (data) => zipData.push(data)).on("end", () => this.file(Buffer.concat(zipData).toString("base64")).then((localPath2) => resolve5(localPath2), reject)).append(source, { name: path2.basename(localPath) }).finalize();
4682
- });
4683
- }
4684
-
4685
- // src/networkManager.ts
4686
- var networkManager = /* @__PURE__ */ new Map();
4687
- function getNetworkManager(browser) {
4688
- const existingNetworkManager = networkManager.get(browser);
4689
- if (existingNetworkManager) {
4690
- return existingNetworkManager;
4691
- }
4692
- const newContext = new NetworkManager(browser);
4693
- networkManager.set(browser, newContext);
4694
- return newContext;
4695
- }
4696
- var UNKNOWN_NAVIGATION_ID = "UNKNOWN_NAVIGATION_ID";
4697
- var SUPPORTED_NAVIGATION_PROTOCOLS = ["http", "https", "data", "file"];
4698
- var NetworkManager = class {
4699
- #browser;
4700
- #initialize;
4701
- #requests = /* @__PURE__ */ new Map();
4702
- #lastNetworkId;
4703
- constructor(browser) {
4704
- this.#browser = browser;
4705
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
4706
- this.#initialize = Promise.resolve(true);
4707
- return;
4708
- }
4709
- this.#initialize = this.#browser.sessionSubscribe({
4710
- events: [
4711
- "browsingContext.navigationStarted",
4712
- "network.responseCompleted",
4713
- "network.beforeRequestSent",
4714
- "network.fetchError"
4715
- ]
4716
- }).then(() => true, () => false);
4717
- this.#browser.on("browsingContext.navigationStarted", this.#navigationStarted.bind(this));
4718
- this.#browser.on("network.responseCompleted", this.#responseCompleted.bind(this));
4719
- this.#browser.on("network.beforeRequestSent", this.#beforeRequestSent.bind(this));
4720
- this.#browser.on("network.fetchError", this.#fetchError.bind(this));
4721
- }
4722
- async initialize() {
4723
- return this.#initialize;
4724
- }
4725
- #beforeRequestSent(log25) {
4726
- if (log25.navigation) {
4727
- return;
4728
- }
4729
- const request = this.#findRootRequest(log25.navigation);
4730
- if (!request) {
4731
- return;
4732
- }
4733
- const { request: id, headers, cookies, url: url6 } = log25.request;
4734
- request.children?.push({
4735
- id,
4736
- url: url6,
4737
- headers: headerListToObject(headers),
4738
- cookies: cookies.map((cookie) => ({
4739
- name: cookie.name,
4740
- value: cookie.value.type === "string" ? cookie.value.value : atob(cookie.value.value),
4741
- domain: cookie.domain,
4742
- path: cookie.path,
4743
- size: cookie.size,
4744
- httpOnly: cookie.httpOnly,
4745
- secure: cookie.secure,
4746
- sameSite: cookie.sameSite,
4747
- expiry: cookie.expiry
4748
- })),
4749
- timestamp: log25.timestamp
4750
- });
4751
- }
4752
- #navigationStarted(log25) {
4753
- if (
4754
- /**
4755
- * we need a navigation id to identify the request
4756
- */
4757
- !log25.navigation || /**
4758
- * ignore urls that users wouldn't navigate to
4759
- */
4760
- !SUPPORTED_NAVIGATION_PROTOCOLS.some((protocol) => log25.url.startsWith(protocol))
4761
- ) {
4762
- if (log25.navigation === null && log25.url === "") {
4763
- this.#lastNetworkId = UNKNOWN_NAVIGATION_ID;
4764
- return this.#requests.set(UNKNOWN_NAVIGATION_ID, {
4765
- url: "",
4766
- headers: {},
4767
- timestamp: log25.timestamp,
4768
- redirectChain: [],
4769
- children: []
4770
- });
4771
- }
4772
- return;
4773
- }
4774
- this.#lastNetworkId = log25.navigation;
4775
- this.#requests.set(log25.navigation, {
4776
- url: log25.url,
4777
- headers: {},
4778
- timestamp: log25.timestamp,
4779
- navigation: log25.navigation,
4780
- redirectChain: [],
4781
- children: []
4782
- });
4783
- }
4784
- #fetchError(log25) {
4785
- const response = this.#findRootRequest(log25.navigation);
4786
- if (!response) {
4787
- return;
4788
- }
4789
- const request = response.children?.find((child) => child.id === log25.request.request);
4790
- if (!request) {
4791
- return;
4792
- }
4793
- request.error = log25.errorText;
4794
- }
4795
- #findRootRequest(navigationId) {
4796
- const response = this.#requests.get(navigationId || UNKNOWN_NAVIGATION_ID);
4797
- if (response) {
4798
- return response;
4799
- }
4800
- const firstRequest = this.#requests.values().next().value;
4801
- return this.#lastNetworkId ? this.#requests.get(this.#lastNetworkId) || firstRequest : firstRequest;
4802
- }
4803
- #responseCompleted(log25) {
4804
- const response = this.#findRootRequest(log25.navigation);
4805
- if (!response) {
4806
- return;
4807
- }
4808
- if (!response.navigation && response.url === "") {
4809
- response.url = log25.request.url;
4810
- response.navigation = log25.navigation;
4811
- }
4812
- if (log25.navigation === response.navigation) {
4813
- if (response.url !== log25.response.url) {
4814
- response.redirectChain?.push(response.url);
4815
- }
4816
- response.url = log25.response.url;
4817
- const { headers: requestHeaders } = log25.request;
4818
- const { fromCache, headers: responseHeaders, mimeType, status } = log25.response;
4819
- response.headers = headerListToObject(requestHeaders), response.response = {
4820
- fromCache,
4821
- headers: headerListToObject(responseHeaders),
4822
- mimeType,
4823
- status
4824
- };
4825
- return;
4826
- }
4827
- const request = response.children?.find((child) => child.id === log25.request.request);
4828
- if (!request) {
4829
- return;
4830
- }
4831
- request.response = {
4832
- fromCache: log25.response.fromCache,
4833
- headers: headerListToObject(log25.response.headers),
4834
- mimeType: log25.response.mimeType,
4835
- status: log25.response.status
4836
- };
4837
- response.children?.push(request);
4838
- }
4839
- getRequestResponseData(navigationId) {
4840
- return this.#requests.get(navigationId);
4841
- }
4842
- /**
4843
- * Returns the number of requests that are currently pending.
4844
- * @param context browsing context id
4845
- * @returns the number of requests that are currently pending
4846
- */
4847
- getPendingRequests(navigationId) {
4848
- const request = this.#requests.get(navigationId);
4849
- if (!request) {
4850
- throw new Error(`Couldn't find request for navigation with id ${navigationId}`);
4851
- }
4852
- const subRequests = request.children || [];
4853
- return subRequests.filter((child) => (
4854
- /**
4855
- * either the request has no response yet
4856
- */
4857
- !child.response && /**
4858
- * and there was no request error
4859
- */
4860
- !child.error
4861
- ));
5293
+ import archiver from "archiver";
5294
+ async function uploadFile(localPath) {
5295
+ if (typeof localPath !== "string") {
5296
+ throw new Error("number or type of arguments don't agree with uploadFile command");
4862
5297
  }
4863
- };
4864
- function headerListToObject(headers) {
4865
- return headers.reduce((acc, { name, value }) => {
4866
- acc[name] = value.value;
4867
- return acc;
4868
- }, {});
5298
+ if (typeof this.file !== "function") {
5299
+ throw new Error(`The uploadFile command is not available in ${this.capabilities.browserName}`);
5300
+ }
5301
+ const zipData = [];
5302
+ const source = fs7.createReadStream(localPath);
5303
+ return new Promise((resolve5, reject) => {
5304
+ archiver("zip").on("error", (err) => reject(err)).on("data", (data) => zipData.push(data)).on("end", () => this.file(Buffer.concat(zipData).toString("base64")).then((localPath2) => resolve5(localPath2), reject)).append(source, { name: path2.basename(localPath) }).finalize();
5305
+ });
4869
5306
  }
4870
5307
 
4871
5308
  // src/commands/browser/url.ts
@@ -4880,8 +5317,8 @@ async function url3(path4, options = {}) {
4880
5317
  }
4881
5318
  if (this.isBidi) {
4882
5319
  let resetPreloadScript;
4883
- const contextManager2 = getContextManager(this);
4884
- const context = await contextManager2.getCurrentContext();
5320
+ const contextManager = getContextManager(this);
5321
+ const context = await contextManager.getCurrentContext();
4885
5322
  if (options.onBeforeLoad) {
4886
5323
  if (typeof options.onBeforeLoad !== "function") {
4887
5324
  throw new Error(`Option "onBeforeLoad" must be a function, but received: ${typeof options.onBeforeLoad}`);
@@ -4928,7 +5365,8 @@ async function url3(path4, options = {}) {
4928
5365
  * set a short interval to immediately return once the first request payload comes in
4929
5366
  */
4930
5367
  {
4931
- interval: 1
5368
+ interval: 1,
5369
+ timeoutMsg: `Navigation to '${path4}' timed out as no request payload was received`
4932
5370
  }
4933
5371
  );
4934
5372
  return request;
@@ -4980,7 +5418,7 @@ function waitUntil(condition, {
4980
5418
  }
4981
5419
 
4982
5420
  // src/commands/mobile/swipe.ts
4983
- import logger16 from "@wdio/logger";
5421
+ import logger17 from "@wdio/logger";
4984
5422
 
4985
5423
  // src/types.ts
4986
5424
  var MobileScrollDirection = /* @__PURE__ */ ((MobileScrollDirection2) => {
@@ -4992,7 +5430,7 @@ var MobileScrollDirection = /* @__PURE__ */ ((MobileScrollDirection2) => {
4992
5430
  })(MobileScrollDirection || {});
4993
5431
 
4994
5432
  // src/commands/mobile/swipe.ts
4995
- var log16 = logger16("webdriverio");
5433
+ var log17 = logger17("webdriverio");
4996
5434
  var SWIPE_DEFAULTS = {
4997
5435
  DIRECTION: "up" /* Up */,
4998
5436
  DURATION: 1500,
@@ -5005,7 +5443,7 @@ async function swipe(options) {
5005
5443
  }
5006
5444
  let { scrollableElement, from, to } = options || {};
5007
5445
  if (scrollableElement && (from || to)) {
5008
- log16.warn("`scrollableElement` is provided, so `from` and `to` will be ignored.");
5446
+ log17.warn("`scrollableElement` is provided, so `from` and `to` will be ignored.");
5009
5447
  }
5010
5448
  if (!from || !to) {
5011
5449
  scrollableElement = scrollableElement || await getScrollableElement(browser);
@@ -5027,9 +5465,9 @@ async function calculateFromTo({
5027
5465
  let swipePercentage = SWIPE_DEFAULTS.PERCENT;
5028
5466
  if (percentage !== void 0) {
5029
5467
  if (isNaN(percentage)) {
5030
- log16.warn("The percentage to swipe should be a number.");
5468
+ log17.warn("The percentage to swipe should be a number.");
5031
5469
  } else if (percentage < 0 || percentage > 1) {
5032
- log16.warn("The percentage to swipe should be a number between 0 and 1.");
5470
+ log17.warn("The percentage to swipe should be a number between 0 and 1.");
5033
5471
  } else {
5034
5472
  swipePercentage = percentage;
5035
5473
  }
@@ -5061,796 +5499,525 @@ async function calculateFromTo({
5061
5499
  to = scrollRectangles.top;
5062
5500
  break;
5063
5501
  default:
5064
- throw new Error(`Unknown direction: ${direction}`);
5065
- }
5066
- return { from, to };
5067
- }
5068
- async function getScrollableElement(browser) {
5069
- const defaultAndroidSelector = "//android.widget.ScrollView";
5070
- const defaultIosSelector = '-ios predicate string:type == "XCUIElementTypeApplication"';
5071
- const selector = browser.isIOS ? (
5072
- // For iOS, we need to find the application element, if we can't find it, we should throw an error
5073
- defaultIosSelector
5074
- ) : (
5075
- // There is always a scrollview for Android or, if this fails we should throw an error
5076
- defaultAndroidSelector
5077
- );
5078
- const scrollableElements = await browser.$$(
5079
- selector
5080
- );
5081
- if (scrollableElements.length > 0) {
5082
- return scrollableElements[0];
5083
- }
5084
- throw new Error(
5085
- `Default scrollable element '${browser.isIOS ? defaultIosSelector : defaultAndroidSelector}' was not found. Our advice is to provide a scrollable element like this:
5086
-
5087
- await browser.swipe({ scrollableElement: $('#scrollable') });
5088
-
5089
- `
5090
- );
5091
- }
5092
- async function w3cSwipe({ browser, duration, from, to }) {
5093
- await browser.action("pointer", {
5094
- parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5095
- }).move(from.x, from.y).down().pause(10).move({ duration, x: to.x, y: to.y }).up().perform();
5096
- return browser.pause(500);
5097
- }
5098
-
5099
- // src/commands/mobile/tap.ts
5100
- import logger17 from "@wdio/logger";
5101
- import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
5102
- var log17 = logger17("webdriver");
5103
- async function tap(options) {
5104
- const isElement2 = this.selector !== void 0;
5105
- const element = isElement2 ? this : null;
5106
- const browser = isElement2 ? getBrowserObject11(this) : this;
5107
- if (!browser.isMobile) {
5108
- throw new Error("The tap command is only available for mobile platforms.");
5109
- }
5110
- validateTapOptions(options);
5111
- if (element) {
5112
- return await elementTap(browser, element, options);
5113
- }
5114
- if (!options || options.x === void 0 || options.y === void 0) {
5115
- throw new Error("The tap command requires x and y coordinates to be set for screen taps.");
5116
- }
5117
- return await screenTap(browser, options);
5118
- }
5119
- function validateTapOptions(options) {
5120
- if (options) {
5121
- if (typeof options !== "object" || Array.isArray(options)) {
5122
- throw new TypeError("Options must be an object.");
5123
- }
5124
- const { x, y, ...otherArgs } = options;
5125
- if (x === void 0 !== (y === void 0)) {
5126
- throw new TypeError(`If ${x !== void 0 ? "x" : "y"} is set, then ${x !== void 0 ? "y" : "x"} must also be set.`);
5127
- }
5128
- if (x !== void 0 && y !== void 0 && Object.keys(otherArgs).length > 0) {
5129
- throw new TypeError(`If x and y are provided, no other arguments are allowed. Found: ${Object.keys(otherArgs).join(", ")}`);
5130
- }
5131
- const invalidCoordinates = [];
5132
- if (x !== void 0 && x < 0) {
5133
- invalidCoordinates.push("x");
5134
- }
5135
- if (y !== void 0 && y < 0) {
5136
- invalidCoordinates.push("y");
5137
- }
5138
- if (invalidCoordinates.length > 0) {
5139
- throw new TypeError(`The ${invalidCoordinates.join(" and ")} value${invalidCoordinates.length > 1 ? "s" : ""} must be positive.`);
5140
- }
5141
- }
5142
- }
5143
- async function elementTap(browser, element, options) {
5144
- if (browser.isNativeContext) {
5145
- return await nativeTap(element, browser, options);
5146
- }
5147
- if (options) {
5148
- log17.warn("The options object is not supported in Web environments and will be ignored.");
5149
- }
5150
- return await webTap(element);
5151
- }
5152
- async function webTap(element) {
5153
- return element.click();
5154
- }
5155
- async function executeNativeTap(browser, options) {
5156
- return await browser.execute(
5157
- `mobile: ${browser.isIOS ? "tap" : "clickGesture"}`,
5158
- { ...browser.isIOS ? { x: 0, y: 0 } : {}, ...options }
5159
- );
5160
- }
5161
- async function nativeTap(element, browser, options = {}) {
5162
- try {
5163
- if (!element.elementId) {
5164
- throw new Error("no such element");
5165
- }
5166
- return await executeNativeTap(browser, { elementId: element.elementId });
5167
- } catch (error) {
5168
- let err = error;
5169
- if (typeof error === "string") {
5170
- err = new Error(error);
5171
- }
5172
- if (!err.message.includes("no such element")) {
5173
- throw err;
5174
- }
5175
- const scrollIntoViewOptions = Object.fromEntries(
5176
- Object.entries({
5177
- direction: options?.direction,
5178
- maxScrolls: options?.maxScrolls,
5179
- scrollableElement: options?.scrollableElement
5180
- }).filter(([_, value]) => value !== void 0)
5181
- );
5182
- try {
5183
- await element.scrollIntoView(scrollIntoViewOptions);
5184
- return await executeNativeTap(browser, { elementId: element.elementId });
5185
- } catch (scrollError) {
5186
- let err2 = scrollError;
5187
- if (typeof scrollError === "string") {
5188
- err2 = new Error(scrollError);
5189
- }
5190
- if (err2.message.includes("Element not found within scroll limit of")) {
5191
- throw new Error(`Element not found within the automatic 'tap' scroll limit of ${scrollIntoViewOptions?.maxScrolls || "10"} scrolls by scrolling "${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:
5192
-
5193
- await elem.tap({
5194
- direction: 'left' // possible options are: 'up|down|left|right'
5195
- maxScrolls: 15,
5196
- scrollableElement: $('#scrollable'),
5197
- });
5198
-
5199
- `);
5200
- } else if (err2.message.includes("Default scrollable element")) {
5201
- const match = err2.message.match(/Default scrollable element '(.*?)' was not found/);
5202
- const scrollableElement = match?.[1] || "unknown-scrollable-element";
5203
- throw new Error(`The 'tap' method tried to automatically scroll to the element but couldn't find the default scrollable element. '${scrollableElement}' If needed you can provide a custom scrollable element, together with the 'direction' and the 'maxScrolls' like this:
5204
-
5205
- await elem.tap({
5206
- scrollableElement: $('#scrollable'),
5207
- });
5208
-
5209
- `);
5210
- }
5211
- throw err2;
5212
- }
5213
- }
5214
- }
5215
- async function screenTap(browser, options) {
5216
- const { x, y } = options;
5217
- if (browser.isNativeContext) {
5218
- return await executeNativeTap(browser, options);
5219
- }
5220
- return await browser.action(
5221
- "pointer",
5222
- {
5223
- parameters: { pointerType: "touch" }
5224
- }
5225
- ).move({ x, y }).down({ button: 0 }).pause(10).up({ button: 0 }).perform();
5226
- }
5227
-
5228
- // src/commands/element.ts
5229
- var element_exports = {};
5230
- __export(element_exports, {
5231
- $: () => $2,
5232
- $$: () => $$2,
5233
- addValue: () => addValue,
5234
- clearValue: () => clearValue,
5235
- click: () => click,
5236
- custom$: () => custom$2,
5237
- custom$$: () => custom$$2,
5238
- doubleClick: () => doubleClick,
5239
- dragAndDrop: () => dragAndDrop,
5240
- execute: () => execute2,
5241
- executeAsync: () => executeAsync2,
5242
- getAttribute: () => getAttribute,
5243
- getCSSProperty: () => getCSSProperty,
5244
- getComputedLabel: () => getComputedLabel,
5245
- getComputedRole: () => getComputedRole,
5246
- getElement: () => getElement2,
5247
- getHTML: () => getHTML,
5248
- getLocation: () => getLocation,
5249
- getProperty: () => getProperty,
5250
- getSize: () => getSize,
5251
- getTagName: () => getTagName,
5252
- getText: () => getText,
5253
- getValue: () => getValue,
5254
- isClickable: () => isClickable,
5255
- isDisplayed: () => isDisplayed,
5256
- isEnabled: () => isEnabled,
5257
- isEqual: () => isEqual,
5258
- isExisting: () => isExisting,
5259
- isFocused: () => isFocused,
5260
- isSelected: () => isSelected,
5261
- isStable: () => isStable,
5262
- longPress: () => longPress,
5263
- moveTo: () => moveTo,
5264
- nextElement: () => nextElement,
5265
- parentElement: () => parentElement,
5266
- pinch: () => pinch,
5267
- previousElement: () => previousElement,
5268
- react$: () => react$2,
5269
- react$$: () => react$$2,
5270
- saveScreenshot: () => saveScreenshot2,
5271
- scrollIntoView: () => scrollIntoView,
5272
- selectByAttribute: () => selectByAttribute,
5273
- selectByIndex: () => selectByIndex,
5274
- selectByVisibleText: () => selectByVisibleText,
5275
- setValue: () => setValue,
5276
- shadow$: () => shadow$,
5277
- shadow$$: () => shadow$$,
5278
- tap: () => tap,
5279
- touchAction: () => touchAction3,
5280
- waitForClickable: () => waitForClickable,
5281
- waitForDisplayed: () => waitForDisplayed,
5282
- waitForEnabled: () => waitForEnabled,
5283
- waitForExist: () => waitForExist,
5284
- waitForStable: () => waitForStable,
5285
- waitUntil: () => waitUntil2,
5286
- zoom: () => zoom
5287
- });
5288
-
5289
- // src/commands/element/$$.ts
5290
- var $$2 = $$;
5291
-
5292
- // src/commands/element/$.ts
5293
- var $2 = $;
5294
-
5295
- // src/commands/element/addValue.ts
5296
- var VALID_TYPES = ["string", "number"];
5297
- function addValue(value) {
5298
- if (!VALID_TYPES.includes(typeof value)) {
5299
- throw new Error(
5300
- 'The setValue/addValue command only take string or number values. If you like to use special characters, use the "keys" command.'
5301
- );
5502
+ throw new Error(`Unknown direction: ${direction}`);
5302
5503
  }
5303
- return this.elementSendKeys(this.elementId, value.toString());
5504
+ return { from, to };
5304
5505
  }
5506
+ async function getScrollableElement(browser) {
5507
+ const defaultAndroidSelector = "//android.widget.ScrollView";
5508
+ const defaultIosSelector = '-ios predicate string:type == "XCUIElementTypeApplication"';
5509
+ const selector = browser.isIOS ? (
5510
+ // For iOS, we need to find the application element, if we can't find it, we should throw an error
5511
+ defaultIosSelector
5512
+ ) : (
5513
+ // There is always a scrollview for Android or, if this fails we should throw an error
5514
+ defaultAndroidSelector
5515
+ );
5516
+ const scrollableElements = await browser.$$(
5517
+ selector
5518
+ );
5519
+ if (scrollableElements.length > 0) {
5520
+ return scrollableElements[0];
5521
+ }
5522
+ throw new Error(
5523
+ `Default scrollable element '${browser.isIOS ? defaultIosSelector : defaultAndroidSelector}' was not found. Our advice is to provide a scrollable element like this:
5305
5524
 
5306
- // src/commands/element/clearValue.ts
5307
- function clearValue() {
5308
- return this.elementClear(this.elementId);
5525
+ await browser.swipe({ scrollableElement: $('#scrollable') });
5526
+
5527
+ `
5528
+ );
5529
+ }
5530
+ async function w3cSwipe({ browser, duration, from, to }) {
5531
+ await browser.action("pointer", {
5532
+ parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5533
+ }).move(from.x, from.y).down().pause(10).move({ duration, x: to.x, y: to.y }).up().perform();
5534
+ return browser.pause(500);
5309
5535
  }
5310
5536
 
5311
- // src/commands/element/click.ts
5537
+ // src/commands/mobile/tap.ts
5312
5538
  import logger18 from "@wdio/logger";
5313
- import { getBrowserObject as getBrowserObject12 } from "@wdio/utils";
5539
+ import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
5314
5540
  var log18 = logger18("webdriver");
5315
- function click(options) {
5316
- if (typeof options !== "undefined") {
5317
- if (typeof options !== "object" || Array.isArray(options)) {
5318
- throw new TypeError("Options must be an object");
5319
- }
5320
- return actionClick(this, options);
5541
+ async function tap(options) {
5542
+ const isElement2 = this.selector !== void 0;
5543
+ const element = isElement2 ? this : null;
5544
+ const browser = isElement2 ? getBrowserObject11(this) : this;
5545
+ if (!browser.isMobile) {
5546
+ throw new Error("The tap command is only available for mobile platforms.");
5321
5547
  }
5322
- return elementClick(this);
5323
- }
5324
- async function workaround(element) {
5325
- await element.scrollIntoView({ block: "center", inline: "center" });
5548
+ validateTapOptions(options);
5549
+ if (element) {
5550
+ return await elementTap(browser, element, options);
5551
+ }
5552
+ if (!options || options.x === void 0 || options.y === void 0) {
5553
+ throw new Error("The tap command requires x and y coordinates to be set for screen taps.");
5554
+ }
5555
+ return await screenTap(browser, options);
5326
5556
  }
5327
- async function elementClick(element) {
5328
- try {
5329
- return await element.elementClick(element.elementId);
5330
- } catch (error) {
5331
- let err = error;
5332
- if (typeof error === "string") {
5333
- err = new Error(error);
5557
+ function validateTapOptions(options) {
5558
+ if (options) {
5559
+ if (typeof options !== "object" || Array.isArray(options)) {
5560
+ throw new TypeError("Options must be an object.");
5334
5561
  }
5335
- if (!err.message.includes("element click intercepted")) {
5336
- throw err;
5562
+ const { x, y, ...otherArgs } = options;
5563
+ if (x === void 0 !== (y === void 0)) {
5564
+ throw new TypeError(`If ${x !== void 0 ? "x" : "y"} is set, then ${x !== void 0 ? "y" : "x"} must also be set.`);
5337
5565
  }
5338
- await workaround(element);
5339
- return element.elementClick(element.elementId);
5340
- }
5341
- }
5342
- async function actionClick(element, options) {
5343
- const defaultOptions = {
5344
- button: 0,
5345
- x: 0,
5346
- y: 0,
5347
- skipRelease: false,
5348
- duration: 0
5349
- };
5350
- const { button, x, y, skipRelease, duration } = { ...defaultOptions, ...options };
5351
- if (typeof x !== "number" || typeof y !== "number" || !Number.isInteger(x) || !Number.isInteger(y)) {
5352
- throw new TypeError("Coordinates must be integers");
5353
- }
5354
- if (!buttonValue.includes(button)) {
5355
- throw new Error("Button type not supported.");
5356
- }
5357
- const browser = getBrowserObject12(element);
5358
- if (x || y) {
5359
- const { width, height } = await browser.getElementRect(element.elementId);
5360
- if (x && x < -Math.floor(width / 2) || x && x > Math.floor(width / 2)) {
5361
- log18.warn("x would cause a out of bounds error as it goes outside of element");
5566
+ if (x !== void 0 && y !== void 0 && Object.keys(otherArgs).length > 0) {
5567
+ throw new TypeError(`If x and y are provided, no other arguments are allowed. Found: ${Object.keys(otherArgs).join(", ")}`);
5362
5568
  }
5363
- if (y && y < -Math.floor(height / 2) || y && y > Math.floor(height / 2)) {
5364
- log18.warn("y would cause a out of bounds error as it goes outside of element");
5569
+ const invalidCoordinates = [];
5570
+ if (x !== void 0 && x < 0) {
5571
+ invalidCoordinates.push("x");
5572
+ }
5573
+ if (y !== void 0 && y < 0) {
5574
+ invalidCoordinates.push("y");
5575
+ }
5576
+ if (invalidCoordinates.length > 0) {
5577
+ throw new TypeError(`The ${invalidCoordinates.join(" and ")} value${invalidCoordinates.length > 1 ? "s" : ""} must be positive.`);
5365
5578
  }
5366
5579
  }
5367
- const clickNested = async () => {
5368
- await browser.action("pointer", {
5369
- parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5370
- }).move({ origin: element, x, y }).down({ button }).pause(duration).up({ button }).perform(skipRelease);
5371
- };
5372
- try {
5373
- return await clickNested();
5374
- } catch {
5375
- await workaround(element);
5376
- return clickNested();
5377
- }
5378
- }
5379
-
5380
- // src/commands/element/custom$$.ts
5381
- import { ELEMENT_KEY as ELEMENT_KEY9 } from "webdriver";
5382
- import { getBrowserObject as getBrowserObject13 } from "@wdio/utils";
5383
- async function custom$$2(strategyName, ...strategyArguments) {
5384
- const browserObject = getBrowserObject13(this);
5385
- const strategy = browserObject.strategies.get(strategyName);
5386
- if (!strategy) {
5387
- throw Error("No strategy found for " + strategyName);
5388
- }
5389
- if (!this.elementId) {
5390
- throw Error(`Can't call custom$ on element with selector "${this.selector}" because element wasn't found`);
5391
- }
5392
- const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
5393
- let res = await browserObject.execute(strategy, ...strategyArguments, this);
5394
- if (!Array.isArray(res)) {
5395
- res = [res];
5396
- }
5397
- res = res.filter((el) => !!el && typeof el[ELEMENT_KEY9] === "string");
5398
- const elements = res.length ? await getElements.call(this, strategyRef, res) : [];
5399
- return enhanceElementsArray(elements, this, strategyName, "custom$$", strategyArguments);
5400
5580
  }
5401
-
5402
- // src/commands/element/custom$.ts
5403
- import { ELEMENT_KEY as ELEMENT_KEY10 } from "webdriver";
5404
- import { getBrowserObject as getBrowserObject14 } from "@wdio/utils";
5405
- async function custom$2(strategyName, ...strategyArguments) {
5406
- const browserObject = getBrowserObject14(this);
5407
- const strategy = browserObject.strategies.get(strategyName);
5408
- if (!strategy) {
5409
- throw Error("No strategy found for " + strategyName);
5410
- }
5411
- if (!this.elementId) {
5412
- throw Error(`Can't call custom$ on element with selector "${this.selector}" because element wasn't found`);
5413
- }
5414
- const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
5415
- let res = await browserObject.execute(strategy, ...strategyArguments, this);
5416
- if (Array.isArray(res)) {
5417
- res = res[0];
5581
+ async function elementTap(browser, element, options) {
5582
+ if (browser.isNativeContext) {
5583
+ return await nativeTap(element, browser, options);
5418
5584
  }
5419
- if (res && typeof res[ELEMENT_KEY10] === "string") {
5420
- return await getElement.call(this, strategyRef, res);
5585
+ if (options) {
5586
+ log18.warn("The options object is not supported in Web environments and will be ignored.");
5421
5587
  }
5422
- return await getElement.call(this, strategyRef, new Error("no such element"));
5588
+ return await webTap(element);
5423
5589
  }
5424
-
5425
- // src/commands/element/doubleClick.ts
5426
- import { getBrowserObject as getBrowserObject15 } from "@wdio/utils";
5427
- async function doubleClick() {
5428
- const browser = getBrowserObject15(this);
5429
- return browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: this }).down().up().pause(10).down().up().perform();
5590
+ async function webTap(element) {
5591
+ return element.click();
5430
5592
  }
5431
-
5432
- // src/commands/element/dragAndDrop.ts
5433
- import { ELEMENT_KEY as ELEMENT_KEY11 } from "webdriver";
5434
- import { getBrowserObject as getBrowserObject16 } from "@wdio/utils";
5435
- async function dragAndDrop(target, options = {}) {
5436
- const moveToCoordinates = target;
5437
- const moveToElement = target;
5438
- if (
5439
- /**
5440
- * no target was specified
5441
- */
5442
- !target || /**
5443
- * target is not from type element
5444
- */
5445
- target.constructor.name !== "Element" && /**
5446
- * and is also not an object with x and y number parameters
5447
- */
5448
- (typeof moveToCoordinates.x !== "number" || typeof moveToCoordinates.y !== "number")
5449
- ) {
5450
- throw new Error('command dragAndDrop requires an WebdriverIO Element or and object with "x" and "y" variables as first parameter');
5451
- }
5452
- const ACTION_BUTTON = 0;
5453
- const browser = getBrowserObject16(this);
5454
- const defaultOptions = { duration: browser.isMobile ? 250 : 10 };
5455
- const { duration } = { ...defaultOptions, ...options };
5456
- const isMovingToElement = target.constructor.name === "Element";
5457
- const sourceRef = { [ELEMENT_KEY11]: this[ELEMENT_KEY11] };
5458
- const targetRef = { [ELEMENT_KEY11]: moveToElement[ELEMENT_KEY11] };
5459
- const origin = sourceRef;
5460
- const targetOrigin = isMovingToElement ? targetRef : "pointer";
5461
- const targetX = isMovingToElement ? 0 : moveToCoordinates.x;
5462
- const targetY = isMovingToElement ? 0 : moveToCoordinates.y;
5463
- return browser.action("pointer", {
5464
- parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5465
- }).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();
5593
+ async function executeNativeTap(browser, options) {
5594
+ return await browser.execute(
5595
+ `mobile: ${browser.isIOS ? "tap" : "clickGesture"}`,
5596
+ { ...browser.isIOS ? { x: 0, y: 0 } : {}, ...options }
5597
+ );
5466
5598
  }
5599
+ async function nativeTap(element, browser, options = {}) {
5600
+ try {
5601
+ if (!element.elementId) {
5602
+ throw new Error("no such element");
5603
+ }
5604
+ return await executeNativeTap(browser, { elementId: element.elementId });
5605
+ } catch (error) {
5606
+ let err = error;
5607
+ if (typeof error === "string") {
5608
+ err = new Error(error);
5609
+ }
5610
+ if (!err.message.includes("no such element")) {
5611
+ throw err;
5612
+ }
5613
+ const scrollIntoViewOptions = Object.fromEntries(
5614
+ Object.entries({
5615
+ direction: options?.direction,
5616
+ maxScrolls: options?.maxScrolls,
5617
+ scrollableElement: options?.scrollableElement
5618
+ }).filter(([_, value]) => value !== void 0)
5619
+ );
5620
+ try {
5621
+ await element.scrollIntoView(scrollIntoViewOptions);
5622
+ return await executeNativeTap(browser, { elementId: element.elementId });
5623
+ } catch (scrollError) {
5624
+ let err2 = scrollError;
5625
+ if (typeof scrollError === "string") {
5626
+ err2 = new Error(scrollError);
5627
+ }
5628
+ if (err2.message.includes("Element not found within scroll limit of")) {
5629
+ throw new Error(`Element not found within the automatic 'tap' scroll limit of ${scrollIntoViewOptions?.maxScrolls || "10"} scrolls by scrolling "${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:
5467
5630
 
5468
- // src/commands/element/execute.ts
5469
- import { getBrowserObject as getBrowserObject17 } from "@wdio/utils";
5470
- async function execute2(script, ...args) {
5471
- const browser = getBrowserObject17(this);
5472
- return browser.execute(script, this, ...args);
5473
- }
5631
+ await elem.tap({
5632
+ direction: 'left' // possible options are: 'up|down|left|right'
5633
+ maxScrolls: 15,
5634
+ scrollableElement: $('#scrollable'),
5635
+ });
5474
5636
 
5475
- // src/commands/element/executeAsync.ts
5476
- import { getBrowserObject as getBrowserObject18 } from "@wdio/utils";
5477
- async function executeAsync2(script, ...args) {
5478
- const browser = getBrowserObject18(this);
5479
- return browser.executeAsync(script, this, ...args);
5480
- }
5637
+ `);
5638
+ } else if (err2.message.includes("Default scrollable element")) {
5639
+ const match = err2.message.match(/Default scrollable element '(.*?)' was not found/);
5640
+ const scrollableElement = match?.[1] || "unknown-scrollable-element";
5641
+ throw new Error(`The 'tap' method tried to automatically scroll to the element but couldn't find the default scrollable element. '${scrollableElement}' If needed you can provide a custom scrollable element, together with the 'direction' and the 'maxScrolls' like this:
5481
5642
 
5482
- // src/commands/element/getAttribute.ts
5483
- function getAttribute(attributeName) {
5484
- return this.getElementAttribute(this.elementId, attributeName);
5485
- }
5643
+ await elem.tap({
5644
+ scrollableElement: $('#scrollable'),
5645
+ });
5486
5646
 
5487
- // src/commands/element/getCSSProperty.ts
5488
- import cssShorthandProps from "css-shorthand-properties";
5489
- import { getBrowserObject as getBrowserObject19 } from "@wdio/utils";
5490
- async function getCSSProperty(cssProperty, pseudoElement) {
5491
- const getCSSProperty2 = cssShorthandProps.isShorthand(cssProperty) ? getShorthandPropertyCSSValue : getPropertyCSSValue;
5492
- const cssValue2 = await getCSSProperty2.call(
5493
- this,
5494
- {
5495
- cssProperty,
5496
- pseudoElement
5647
+ `);
5648
+ }
5649
+ throw err2;
5497
5650
  }
5498
- );
5499
- return parseCSS(cssValue2, cssProperty);
5500
- }
5501
- async function getShorthandPropertyCSSValue(options) {
5502
- const { pseudoElement, cssProperty } = options;
5503
- const properties = getShorthandProperties(cssProperty);
5504
- if (pseudoElement) {
5505
- const cssValues2 = await Promise.all(
5506
- properties.map((prop) => getPseudoElementCSSValue(
5507
- this,
5508
- {
5509
- pseudoElement,
5510
- cssProperty: prop
5511
- }
5512
- ))
5513
- );
5514
- return mergeEqualSymmetricalValue(cssValues2);
5515
5651
  }
5516
- const cssValues = await Promise.all(
5517
- properties.map((prop) => this.getElementCSSValue(this.elementId, prop))
5518
- );
5519
- return mergeEqualSymmetricalValue(cssValues);
5520
5652
  }
5521
- async function getPropertyCSSValue(options) {
5522
- const { pseudoElement, cssProperty } = options;
5523
- if (pseudoElement) {
5524
- return await getPseudoElementCSSValue(
5525
- this,
5526
- {
5527
- pseudoElement,
5528
- cssProperty
5529
- }
5530
- );
5653
+ async function screenTap(browser, options) {
5654
+ const { x, y } = options;
5655
+ if (browser.isNativeContext) {
5656
+ return await executeNativeTap(browser, options);
5531
5657
  }
5532
- return await this.getElementCSSValue(this.elementId, cssProperty);
5533
- }
5534
- function getShorthandProperties(cssProperty) {
5535
- return cssShorthandProps.expand(cssProperty);
5536
- }
5537
- function mergeEqualSymmetricalValue(cssValues) {
5538
- let newCssValues = [...cssValues];
5539
- while (newCssValues.length % 2 === 0) {
5540
- const mergedValues = [
5541
- newCssValues.slice(0, newCssValues.length / 2).join(" "),
5542
- newCssValues.slice(newCssValues.length / 2).join(" ")
5543
- ];
5544
- const hasEqualProperties = mergedValues.every((v) => v === mergedValues[0]);
5545
- if (!hasEqualProperties) {
5546
- break;
5658
+ return await browser.action(
5659
+ "pointer",
5660
+ {
5661
+ parameters: { pointerType: "touch" }
5547
5662
  }
5548
- newCssValues = newCssValues.slice(0, newCssValues.length / 2);
5549
- }
5550
- return newCssValues.join(" ");
5551
- }
5552
- async function getPseudoElementCSSValue(elem, options) {
5553
- const browser = getBrowserObject19(elem);
5554
- const { cssProperty, pseudoElement } = options;
5555
- const cssValue2 = await browser.execute(
5556
- (elem2, pseudoElement2, cssProperty2) => window.getComputedStyle(elem2, pseudoElement2)[cssProperty2],
5557
- elem,
5558
- pseudoElement,
5559
- cssProperty
5560
- );
5561
- return cssValue2;
5663
+ ).move({ x, y }).down({ button: 0 }).pause(10).up({ button: 0 }).perform();
5562
5664
  }
5563
5665
 
5564
- // src/commands/element/getComputedRole.ts
5565
- function getComputedRole() {
5566
- return this.getElementComputedRole(this.elementId);
5567
- }
5666
+ // src/commands/element.ts
5667
+ var element_exports = {};
5668
+ __export(element_exports, {
5669
+ $: () => $2,
5670
+ $$: () => $$2,
5671
+ addValue: () => addValue,
5672
+ clearValue: () => clearValue,
5673
+ click: () => click,
5674
+ custom$: () => custom$2,
5675
+ custom$$: () => custom$$2,
5676
+ doubleClick: () => doubleClick,
5677
+ dragAndDrop: () => dragAndDrop,
5678
+ execute: () => execute2,
5679
+ executeAsync: () => executeAsync2,
5680
+ getAttribute: () => getAttribute,
5681
+ getCSSProperty: () => getCSSProperty,
5682
+ getComputedLabel: () => getComputedLabel,
5683
+ getComputedRole: () => getComputedRole,
5684
+ getElement: () => getElement2,
5685
+ getHTML: () => getHTML,
5686
+ getLocation: () => getLocation,
5687
+ getProperty: () => getProperty,
5688
+ getSize: () => getSize,
5689
+ getTagName: () => getTagName,
5690
+ getText: () => getText,
5691
+ getValue: () => getValue,
5692
+ isClickable: () => isClickable,
5693
+ isDisplayed: () => isDisplayed,
5694
+ isEnabled: () => isEnabled,
5695
+ isEqual: () => isEqual,
5696
+ isExisting: () => isExisting,
5697
+ isFocused: () => isFocused,
5698
+ isSelected: () => isSelected,
5699
+ isStable: () => isStable,
5700
+ longPress: () => longPress,
5701
+ moveTo: () => moveTo,
5702
+ nextElement: () => nextElement,
5703
+ parentElement: () => parentElement,
5704
+ pinch: () => pinch,
5705
+ previousElement: () => previousElement,
5706
+ react$: () => react$2,
5707
+ react$$: () => react$$2,
5708
+ saveScreenshot: () => saveScreenshot2,
5709
+ scrollIntoView: () => scrollIntoView,
5710
+ selectByAttribute: () => selectByAttribute,
5711
+ selectByIndex: () => selectByIndex,
5712
+ selectByVisibleText: () => selectByVisibleText,
5713
+ setValue: () => setValue,
5714
+ shadow$: () => shadow$,
5715
+ shadow$$: () => shadow$$,
5716
+ tap: () => tap,
5717
+ touchAction: () => touchAction3,
5718
+ waitForClickable: () => waitForClickable,
5719
+ waitForDisplayed: () => waitForDisplayed,
5720
+ waitForEnabled: () => waitForEnabled,
5721
+ waitForExist: () => waitForExist,
5722
+ waitForStable: () => waitForStable,
5723
+ waitUntil: () => waitUntil2,
5724
+ zoom: () => zoom
5725
+ });
5568
5726
 
5569
- // src/commands/element/getComputedLabel.ts
5570
- function getComputedLabel() {
5571
- return this.getElementComputedLabel(this.elementId);
5572
- }
5727
+ // src/commands/element/$$.ts
5728
+ var $$2 = $$;
5573
5729
 
5574
- // src/commands/element/getElement.ts
5575
- async function getElement2() {
5576
- return this;
5730
+ // src/commands/element/$.ts
5731
+ var $2 = $;
5732
+
5733
+ // src/commands/element/addValue.ts
5734
+ var VALID_TYPES = ["string", "number"];
5735
+ function addValue(value) {
5736
+ if (!VALID_TYPES.includes(typeof value)) {
5737
+ throw new Error(
5738
+ 'The setValue/addValue command only take string or number values. If you like to use special characters, use the "keys" command.'
5739
+ );
5740
+ }
5741
+ return this.elementSendKeys(this.elementId, value.toString());
5577
5742
  }
5578
5743
 
5579
- // src/commands/element/getHTML.ts
5580
- import { ELEMENT_KEY as ELEMENT_KEY12 } from "webdriver";
5581
- import { prettify as prettifyFn } from "htmlfy";
5582
- import { getBrowserObject as getBrowserObject20 } from "@wdio/utils";
5744
+ // src/commands/element/clearValue.ts
5745
+ function clearValue() {
5746
+ return this.elementClear(this.elementId);
5747
+ }
5583
5748
 
5584
- // src/shadowRoot.ts
5749
+ // src/commands/element/click.ts
5585
5750
  import logger19 from "@wdio/logger";
5586
- import customElementWrapper from "./scripts/customElement.js";
5587
- var shadowRootManager = /* @__PURE__ */ new Map();
5588
- var log19 = logger19("webdriverio:ShadowRootManager");
5589
- function getShadowRootManager(browser) {
5590
- const existingShadowRootManager = shadowRootManager.get(browser);
5591
- if (existingShadowRootManager) {
5592
- return existingShadowRootManager;
5593
- }
5594
- const newContext = new ShadowRootManager(browser);
5595
- shadowRootManager.set(browser, newContext);
5596
- return newContext;
5597
- }
5598
- var ShadowRootManager = class {
5599
- #browser;
5600
- #initialize;
5601
- #shadowRoots = /* @__PURE__ */ new Map();
5602
- #documentElement;
5603
- #frameDepth = 0;
5604
- constructor(browser) {
5605
- this.#browser = browser;
5606
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
5607
- this.#initialize = Promise.resolve(true);
5608
- return;
5609
- }
5610
- this.#initialize = this.#browser.sessionSubscribe({
5611
- events: ["log.entryAdded", "browsingContext.navigationStarted"]
5612
- }).then(() => true, () => false);
5613
- this.#browser.on("log.entryAdded", this.handleLogEntry.bind(this));
5614
- this.#browser.on("result", this.#commandResultHandler.bind(this));
5615
- this.#browser.on("bidiCommand", this.#handleBidiCommand.bind(this));
5616
- browser.scriptAddPreloadScript({
5617
- functionDeclaration: customElementWrapper.toString()
5618
- });
5619
- }
5620
- async initialize() {
5621
- return this.#initialize;
5622
- }
5623
- /**
5624
- * keep track of navigation events and remove shadow roots when they are no longer needed
5625
- */
5626
- #handleBidiCommand(command) {
5627
- if (command.method !== "browsingContext.navigate") {
5628
- return;
5751
+ import { getBrowserObject as getBrowserObject12 } from "@wdio/utils";
5752
+ var log19 = logger19("webdriver");
5753
+ function click(options) {
5754
+ if (typeof options !== "undefined") {
5755
+ if (typeof options !== "object" || Array.isArray(options)) {
5756
+ throw new TypeError("Options must be an object");
5629
5757
  }
5630
- const params = command.params;
5631
- this.#shadowRoots.delete(params.context);
5758
+ return actionClick(this, options);
5632
5759
  }
5633
- /**
5634
- * keep track of frame depth
5635
- */
5636
- #commandResultHandler(result) {
5637
- const noResultError = typeof result.result === "object" && result.result && "error" in result.result && !result.result.error;
5638
- if (result.command === "switchToFrame" && noResultError) {
5639
- this.#frameDepth++;
5760
+ return elementClick(this);
5761
+ }
5762
+ async function workaround(element) {
5763
+ await element.scrollIntoView({ block: "center", inline: "center" });
5764
+ }
5765
+ async function elementClick(element) {
5766
+ try {
5767
+ return await element.elementClick(element.elementId);
5768
+ } catch (error) {
5769
+ let err = error;
5770
+ if (typeof error === "string") {
5771
+ err = new Error(error);
5640
5772
  }
5641
- if (result.command === "switchToParentFrame" && noResultError) {
5642
- this.#frameDepth = Math.max(0, this.#frameDepth - 1);
5773
+ if (!err.message.includes("element click intercepted")) {
5774
+ throw err;
5643
5775
  }
5776
+ await workaround(element);
5777
+ return element.elementClick(element.elementId);
5644
5778
  }
5645
- /**
5646
- * check if we are within a frame
5647
- * @returns {boolean} true if we are within a frame
5648
- */
5649
- isWithinFrame() {
5650
- return this.#frameDepth > 0;
5779
+ }
5780
+ async function actionClick(element, options) {
5781
+ const defaultOptions = {
5782
+ button: 0,
5783
+ x: 0,
5784
+ y: 0,
5785
+ skipRelease: false,
5786
+ duration: 0
5787
+ };
5788
+ const { button, x, y, skipRelease, duration } = { ...defaultOptions, ...options };
5789
+ if (typeof x !== "number" || typeof y !== "number" || !Number.isInteger(x) || !Number.isInteger(y)) {
5790
+ throw new TypeError("Coordinates must be integers");
5651
5791
  }
5652
- /**
5653
- * capture shadow root elements propagated through console.debug
5654
- */
5655
- handleLogEntry(logEntry) {
5656
- const args = "args" in logEntry && logEntry.level === "debug" ? logEntry.args : void 0;
5657
- if (!args || args[0].type !== "string" || args[0].value !== "[WDIO]" || args[1].type !== "string") {
5658
- return;
5659
- }
5660
- if (!logEntry.source.context) {
5661
- return;
5662
- }
5663
- const eventType = args[1].value;
5664
- if (eventType === "newShadowRoot" && args[2].type === "node" && args[3].type === "node") {
5665
- const [
5666
- /* [WDIO] */
5667
- ,
5668
- /* newShadowRoot */
5669
- ,
5670
- shadowElem,
5671
- rootElem,
5672
- isDocument,
5673
- documentElement
5674
- ] = args;
5675
- if (!this.#shadowRoots.has(logEntry.source.context)) {
5676
- if (!rootElem.sharedId) {
5677
- throw new Error(`Expected "sharedId" parameter from object ${rootElem}`);
5678
- }
5679
- this.#shadowRoots.set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
5680
- } else if (isDocument.type === "boolean" && isDocument.value) {
5681
- if (!rootElem.sharedId) {
5682
- throw new Error(`Expected "sharedId" parameter from object ${rootElem}`);
5683
- }
5684
- const tree2 = this.#shadowRoots.get(logEntry.source.context);
5685
- if (tree2?.element !== rootElem.sharedId) {
5686
- this.#shadowRoots.set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
5687
- }
5688
- }
5689
- this.#documentElement = documentElement;
5690
- const tree = this.#shadowRoots.get(logEntry.source.context);
5691
- if (!tree) {
5692
- throw new Error(`Couldn't find tree for context id ${logEntry.source.context}`);
5693
- }
5694
- if (
5695
- // we expect an element id
5696
- !shadowElem.sharedId || // we expect the element to have a shadow root
5697
- !shadowElem.value?.shadowRoot?.sharedId || // we expect the shadow root to have a proper type
5698
- shadowElem.value.shadowRoot.value?.nodeType !== 11
5699
- ) {
5700
- return log19.warn(`Expected element with shadow root but found <${shadowElem.value?.localName} />`);
5701
- }
5702
- log19.info(`Registered new shadow root for element <${shadowElem.value.localName} /> with id ${shadowElem.value.shadowRoot.sharedId}`);
5703
- const newTree = new ShadowRootTree(
5704
- shadowElem.sharedId,
5705
- shadowElem.value.shadowRoot.sharedId,
5706
- shadowElem.value.shadowRoot.value.mode
5707
- );
5708
- if (rootElem.sharedId) {
5709
- tree.addShadowElement(rootElem.sharedId, newTree);
5710
- } else {
5711
- tree.addShadowElement(newTree);
5712
- }
5713
- return;
5714
- }
5715
- if (eventType === "removeShadowRoot" && args[2].type === "node" && args[2].sharedId) {
5716
- const tree = this.#shadowRoots.get(logEntry.source.context);
5717
- if (!tree) {
5718
- return;
5719
- }
5720
- return tree.remove(args[2].sharedId);
5721
- }
5722
- throw new Error(`Invalid parameters for "${eventType}" event: ${args.join(", ")}`);
5792
+ if (!buttonValue.includes(button)) {
5793
+ throw new Error("Button type not supported.");
5723
5794
  }
5724
- getShadowElementsByContextId(contextId, scope) {
5725
- let tree = this.#shadowRoots.get(contextId);
5726
- if (!tree) {
5727
- return [];
5795
+ const browser = getBrowserObject12(element);
5796
+ if (x || y) {
5797
+ const { width, height } = await browser.getElementRect(element.elementId);
5798
+ if (x && x < -Math.floor(width / 2) || x && x > Math.floor(width / 2)) {
5799
+ log19.warn("x would cause a out of bounds error as it goes outside of element");
5728
5800
  }
5729
- let documentElement;
5730
- if (scope) {
5731
- const subTree = tree.find(scope);
5732
- if (subTree) {
5733
- tree = subTree;
5734
- }
5735
- } else {
5736
- documentElement = this.#documentElement?.sharedId;
5801
+ if (y && y < -Math.floor(height / 2) || y && y > Math.floor(height / 2)) {
5802
+ log19.warn("y would cause a out of bounds error as it goes outside of element");
5737
5803
  }
5738
- const elements = tree.getAllLookupScopes();
5739
- return [
5740
- ...documentElement ? [documentElement] : [],
5741
- ...new Set(elements).values()
5742
- ];
5743
5804
  }
5744
- getShadowElementPairsByContextId(contextId, scope) {
5745
- let tree = this.#shadowRoots.get(contextId);
5746
- if (!tree) {
5747
- return [];
5748
- }
5749
- if (scope) {
5750
- const subTree = tree.find(scope);
5751
- if (subTree) {
5752
- tree = subTree;
5753
- }
5754
- }
5755
- return tree.flat().map((tree2) => [tree2.element, tree2.shadowRoot]);
5805
+ const clickNested = async () => {
5806
+ await browser.action("pointer", {
5807
+ parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5808
+ }).move({ origin: element, x, y }).down({ button }).pause(duration).up({ button }).perform(skipRelease);
5809
+ };
5810
+ try {
5811
+ return await clickNested();
5812
+ } catch {
5813
+ await workaround(element);
5814
+ return clickNested();
5756
5815
  }
5757
- getShadowRootModeById(contextId, element) {
5758
- const tree = this.#shadowRoots.get(contextId);
5759
- if (!tree) {
5760
- return;
5761
- }
5762
- const shadowTree = tree.find(element);
5763
- if (!shadowTree) {
5764
- return;
5765
- }
5766
- return shadowTree.mode;
5816
+ }
5817
+
5818
+ // src/commands/element/custom$$.ts
5819
+ import { ELEMENT_KEY as ELEMENT_KEY9 } from "webdriver";
5820
+ import { getBrowserObject as getBrowserObject13 } from "@wdio/utils";
5821
+ async function custom$$2(strategyName, ...strategyArguments) {
5822
+ const browserObject = getBrowserObject13(this);
5823
+ const strategy = browserObject.strategies.get(strategyName);
5824
+ if (!strategy) {
5825
+ throw Error("No strategy found for " + strategyName);
5767
5826
  }
5768
- deleteShadowRoot(element, contextId) {
5769
- const tree = this.#shadowRoots.get(contextId);
5770
- if (!tree) {
5771
- return;
5772
- }
5773
- return tree.remove(element);
5827
+ if (!this.elementId) {
5828
+ throw Error(`Can't call custom$ on element with selector "${this.selector}" because element wasn't found`);
5774
5829
  }
5775
- };
5776
- var ShadowRootTree = class _ShadowRootTree {
5777
- element;
5778
- shadowRoot;
5779
- mode;
5780
- children = /* @__PURE__ */ new Set();
5781
- constructor(element, shadowRoot, mode) {
5782
- this.element = element;
5783
- this.shadowRoot = shadowRoot;
5784
- this.mode = mode;
5830
+ const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
5831
+ let res = await browserObject.execute(strategy, ...strategyArguments, this);
5832
+ if (!Array.isArray(res)) {
5833
+ res = [res];
5785
5834
  }
5786
- addShadowElement(...args) {
5787
- const [scope, treeArg] = args;
5788
- if (!scope && !treeArg) {
5789
- throw new Error('Method "addShadowElement" expects at least 2 arguments');
5790
- }
5791
- if (scope instanceof _ShadowRootTree) {
5792
- this.children.add(scope);
5793
- return;
5794
- }
5795
- if (typeof scope === "string" && treeArg instanceof _ShadowRootTree) {
5796
- const tree = this.find(scope) || this.findByShadowId(scope);
5797
- if (!tree) {
5798
- throw new Error(`Couldn't find element with id ${scope}`);
5799
- }
5800
- tree.addShadowElement(treeArg);
5801
- return;
5802
- }
5803
- throw new Error('Invalid arguments for "addShadowElement" method');
5835
+ res = res.filter((el) => !!el && typeof el[ELEMENT_KEY9] === "string");
5836
+ const elements = res.length ? await getElements.call(this, strategyRef, res) : [];
5837
+ return enhanceElementsArray(elements, this, strategyName, "custom$$", strategyArguments);
5838
+ }
5839
+
5840
+ // src/commands/element/custom$.ts
5841
+ import { ELEMENT_KEY as ELEMENT_KEY10 } from "webdriver";
5842
+ import { getBrowserObject as getBrowserObject14 } from "@wdio/utils";
5843
+ async function custom$2(strategyName, ...strategyArguments) {
5844
+ const browserObject = getBrowserObject14(this);
5845
+ const strategy = browserObject.strategies.get(strategyName);
5846
+ if (!strategy) {
5847
+ throw Error("No strategy found for " + strategyName);
5804
5848
  }
5805
- find(element) {
5806
- if (this.element === element) {
5807
- return this;
5808
- }
5809
- for (const child of this.children) {
5810
- const elem = child.find(element);
5811
- if (elem) {
5812
- return elem;
5813
- }
5814
- }
5815
- return void 0;
5849
+ if (!this.elementId) {
5850
+ throw Error(`Can't call custom$ on element with selector "${this.selector}" because element wasn't found`);
5851
+ }
5852
+ const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
5853
+ let res = await browserObject.execute(strategy, ...strategyArguments, this);
5854
+ if (Array.isArray(res)) {
5855
+ res = res[0];
5856
+ }
5857
+ if (res && typeof res[ELEMENT_KEY10] === "string") {
5858
+ return await getElement.call(this, strategyRef, res);
5859
+ }
5860
+ return await getElement.call(this, strategyRef, new Error("no such element"));
5861
+ }
5862
+
5863
+ // src/commands/element/doubleClick.ts
5864
+ import { getBrowserObject as getBrowserObject15 } from "@wdio/utils";
5865
+ async function doubleClick() {
5866
+ const browser = getBrowserObject15(this);
5867
+ return browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: this }).down().up().pause(10).down().up().perform();
5868
+ }
5869
+
5870
+ // src/commands/element/dragAndDrop.ts
5871
+ import { ELEMENT_KEY as ELEMENT_KEY11 } from "webdriver";
5872
+ import { getBrowserObject as getBrowserObject16 } from "@wdio/utils";
5873
+ async function dragAndDrop(target, options = {}) {
5874
+ const moveToCoordinates = target;
5875
+ const moveToElement = target;
5876
+ if (
5877
+ /**
5878
+ * no target was specified
5879
+ */
5880
+ !target || /**
5881
+ * target is not from type element
5882
+ */
5883
+ target.constructor.name !== "Element" && /**
5884
+ * and is also not an object with x and y number parameters
5885
+ */
5886
+ (typeof moveToCoordinates.x !== "number" || typeof moveToCoordinates.y !== "number")
5887
+ ) {
5888
+ throw new Error('command dragAndDrop requires an WebdriverIO Element or and object with "x" and "y" variables as first parameter');
5816
5889
  }
5817
- findByShadowId(shadowRoot) {
5818
- if (this.shadowRoot === shadowRoot) {
5819
- return this;
5820
- }
5821
- for (const child of this.children) {
5822
- const elem = child.findByShadowId(shadowRoot);
5823
- if (elem) {
5824
- return elem;
5825
- }
5890
+ const ACTION_BUTTON = 0;
5891
+ const browser = getBrowserObject16(this);
5892
+ const defaultOptions = { duration: browser.isMobile ? 250 : 10 };
5893
+ const { duration } = { ...defaultOptions, ...options };
5894
+ const isMovingToElement = target.constructor.name === "Element";
5895
+ const sourceRef = { [ELEMENT_KEY11]: this[ELEMENT_KEY11] };
5896
+ const targetRef = { [ELEMENT_KEY11]: moveToElement[ELEMENT_KEY11] };
5897
+ const origin = sourceRef;
5898
+ const targetOrigin = isMovingToElement ? targetRef : "pointer";
5899
+ const targetX = isMovingToElement ? 0 : moveToCoordinates.x;
5900
+ const targetY = isMovingToElement ? 0 : moveToCoordinates.y;
5901
+ return browser.action("pointer", {
5902
+ parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5903
+ }).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();
5904
+ }
5905
+
5906
+ // src/commands/element/execute.ts
5907
+ import { getBrowserObject as getBrowserObject17 } from "@wdio/utils";
5908
+ async function execute2(script, ...args) {
5909
+ const browser = getBrowserObject17(this);
5910
+ return browser.execute(script, this, ...args);
5911
+ }
5912
+
5913
+ // src/commands/element/executeAsync.ts
5914
+ import { getBrowserObject as getBrowserObject18 } from "@wdio/utils";
5915
+ async function executeAsync2(script, ...args) {
5916
+ const browser = getBrowserObject18(this);
5917
+ return browser.executeAsync(script, this, ...args);
5918
+ }
5919
+
5920
+ // src/commands/element/getAttribute.ts
5921
+ function getAttribute(attributeName) {
5922
+ return this.getElementAttribute(this.elementId, attributeName);
5923
+ }
5924
+
5925
+ // src/commands/element/getCSSProperty.ts
5926
+ import cssShorthandProps from "css-shorthand-properties";
5927
+ import { getBrowserObject as getBrowserObject19 } from "@wdio/utils";
5928
+ async function getCSSProperty(cssProperty, pseudoElement) {
5929
+ const getCSSProperty2 = cssShorthandProps.isShorthand(cssProperty) ? getShorthandPropertyCSSValue : getPropertyCSSValue;
5930
+ const cssValue2 = await getCSSProperty2.call(
5931
+ this,
5932
+ {
5933
+ cssProperty,
5934
+ pseudoElement
5826
5935
  }
5827
- return void 0;
5828
- }
5829
- getAllLookupScopes() {
5830
- return [
5831
- this.shadowRoot ?? this.element,
5832
- ...Array.from(this.children).map((tree) => tree.getAllLookupScopes())
5833
- ].flat();
5834
- }
5835
- flat() {
5836
- return [this, ...Array.from(this.children).map((tree) => tree.flat())].flat();
5936
+ );
5937
+ return parseCSS(cssValue2, cssProperty);
5938
+ }
5939
+ async function getShorthandPropertyCSSValue(options) {
5940
+ const { pseudoElement, cssProperty } = options;
5941
+ const properties = getShorthandProperties(cssProperty);
5942
+ if (pseudoElement) {
5943
+ const cssValues2 = await Promise.all(
5944
+ properties.map((prop) => getPseudoElementCSSValue(
5945
+ this,
5946
+ {
5947
+ pseudoElement,
5948
+ cssProperty: prop
5949
+ }
5950
+ ))
5951
+ );
5952
+ return mergeEqualSymmetricalValue(cssValues2);
5837
5953
  }
5838
- remove(element) {
5839
- const childArray = Array.from(this.children);
5840
- for (let i = childArray.length - 1; i >= 0; i--) {
5841
- if (childArray[i].element === element) {
5842
- return this.children.delete(childArray[i]);
5843
- }
5844
- const wasFound = childArray[i].remove(element);
5845
- if (wasFound) {
5846
- return true;
5954
+ const cssValues = await Promise.all(
5955
+ properties.map((prop) => this.getElementCSSValue(this.elementId, prop))
5956
+ );
5957
+ return mergeEqualSymmetricalValue(cssValues);
5958
+ }
5959
+ async function getPropertyCSSValue(options) {
5960
+ const { pseudoElement, cssProperty } = options;
5961
+ if (pseudoElement) {
5962
+ return await getPseudoElementCSSValue(
5963
+ this,
5964
+ {
5965
+ pseudoElement,
5966
+ cssProperty
5847
5967
  }
5968
+ );
5969
+ }
5970
+ return await this.getElementCSSValue(this.elementId, cssProperty);
5971
+ }
5972
+ function getShorthandProperties(cssProperty) {
5973
+ return cssShorthandProps.expand(cssProperty);
5974
+ }
5975
+ function mergeEqualSymmetricalValue(cssValues) {
5976
+ let newCssValues = [...cssValues];
5977
+ while (newCssValues.length % 2 === 0) {
5978
+ const mergedValues = [
5979
+ newCssValues.slice(0, newCssValues.length / 2).join(" "),
5980
+ newCssValues.slice(newCssValues.length / 2).join(" ")
5981
+ ];
5982
+ const hasEqualProperties = mergedValues.every((v) => v === mergedValues[0]);
5983
+ if (!hasEqualProperties) {
5984
+ break;
5848
5985
  }
5849
- return false;
5986
+ newCssValues = newCssValues.slice(0, newCssValues.length / 2);
5850
5987
  }
5851
- };
5988
+ return newCssValues.join(" ");
5989
+ }
5990
+ async function getPseudoElementCSSValue(elem, options) {
5991
+ const browser = getBrowserObject19(elem);
5992
+ const { cssProperty, pseudoElement } = options;
5993
+ const cssValue2 = await browser.execute(
5994
+ (elem2, pseudoElement2, cssProperty2) => window.getComputedStyle(elem2, pseudoElement2)[cssProperty2],
5995
+ elem,
5996
+ pseudoElement,
5997
+ cssProperty
5998
+ );
5999
+ return cssValue2;
6000
+ }
6001
+
6002
+ // src/commands/element/getComputedRole.ts
6003
+ function getComputedRole() {
6004
+ return this.getElementComputedRole(this.elementId);
6005
+ }
6006
+
6007
+ // src/commands/element/getComputedLabel.ts
6008
+ function getComputedLabel() {
6009
+ return this.getElementComputedLabel(this.elementId);
6010
+ }
6011
+
6012
+ // src/commands/element/getElement.ts
6013
+ async function getElement2() {
6014
+ return this;
6015
+ }
5852
6016
 
5853
6017
  // src/commands/element/getHTML.ts
6018
+ import { ELEMENT_KEY as ELEMENT_KEY12 } from "webdriver";
6019
+ import { prettify as prettifyFn } from "htmlfy";
6020
+ import { getBrowserObject as getBrowserObject20 } from "@wdio/utils";
5854
6021
  import getHTMLScript from "./scripts/getHTML.js";
5855
6022
  import getHTMLShadowScript from "./scripts/getHTMLShadow.js";
5856
6023
  var SHADOW_ID_ATTR_NAME = "data-wdio-shadow-id";
@@ -5886,10 +6053,10 @@ async function getHTML(options = {}) {
5886
6053
  );
5887
6054
  }
5888
6055
  const { load } = await import("cheerio");
5889
- const shadowRootManager2 = getShadowRootManager(browser);
5890
- const contextManager2 = getContextManager(browser);
5891
- const context = await contextManager2.getCurrentContext();
5892
- const shadowRootElementPairs = shadowRootManager2.getShadowElementPairsByContextId(context, this.elementId);
6056
+ const shadowRootManager = getShadowRootManager(browser);
6057
+ const contextManager = getContextManager(browser);
6058
+ const context = await contextManager.getCurrentContext();
6059
+ const shadowRootElementPairs = shadowRootManager.getShadowElementPairsByContextId(context, this.elementId);
5893
6060
  const elementsWithShadowRootAndIdVerified = (await Promise.all(
5894
6061
  shadowRootElementPairs.map(([elemId, elem]) => browser.execute((elem2) => elem2.tagName, { [ELEMENT_KEY12]: elemId }).then(
5895
6062
  () => [elemId, elem],
@@ -5910,7 +6077,7 @@ async function getHTML(options = {}) {
5910
6077
  populateHTML($3, shadowElementHTML.map(({ id, ...props }) => ({
5911
6078
  ...props,
5912
6079
  id,
5913
- mode: shadowRootManager2.getShadowRootModeById(context, id) || "open"
6080
+ mode: shadowRootManager.getShadowRootModeById(context, id) || "open"
5914
6081
  })));
5915
6082
  return sanitizeHTML($3, { removeCommentNodes, prettify, excludeElements });
5916
6083
  }
@@ -7313,10 +7480,10 @@ function transformClassicToBidiSelector(using, value) {
7313
7480
  }
7314
7481
  async function findDeepElement(selector) {
7315
7482
  const browser = getBrowserObject35(this);
7316
- const shadowRootManager2 = getShadowRootManager(browser);
7317
- const contextManager2 = getContextManager(browser);
7318
- const context = await contextManager2.getCurrentContext();
7319
- const shadowRoots = shadowRootManager2.getShadowElementsByContextId(
7483
+ const shadowRootManager = getShadowRootManager(browser);
7484
+ const contextManager = getContextManager(browser);
7485
+ const context = await contextManager.getCurrentContext();
7486
+ const shadowRoots = shadowRootManager.getShadowElementsByContextId(
7320
7487
  context,
7321
7488
  this.elementId
7322
7489
  );
@@ -7351,10 +7518,10 @@ async function findDeepElement(selector) {
7351
7518
  }
7352
7519
  async function findDeepElements(selector) {
7353
7520
  const browser = getBrowserObject35(this);
7354
- const shadowRootManager2 = getShadowRootManager(browser);
7355
- const contextManager2 = getContextManager(browser);
7356
- const context = await contextManager2.getCurrentContext();
7357
- const shadowRoots = shadowRootManager2.getShadowElementsByContextId(
7521
+ const shadowRootManager = getShadowRootManager(browser);
7522
+ const contextManager = getContextManager(browser);
7523
+ const context = await contextManager.getCurrentContext();
7524
+ const shadowRoots = shadowRootManager.getShadowElementsByContextId(
7358
7525
  context,
7359
7526
  this.elementId
7360
7527
  );
@@ -7386,8 +7553,8 @@ async function findDeepElements(selector) {
7386
7553
  }
7387
7554
  async function findElement(selector) {
7388
7555
  const browserObject = getBrowserObject35(this);
7389
- const shadowRootManager2 = getShadowRootManager(browserObject);
7390
- if (this.isBidi && typeof selector === "string" && !selector.startsWith(DEEP_SELECTOR) && !shadowRootManager2.isWithinFrame()) {
7556
+ const shadowRootManager = getShadowRootManager(browserObject);
7557
+ if (this.isBidi && typeof selector === "string" && !selector.startsWith(DEEP_SELECTOR) && !shadowRootManager.isWithinFrame()) {
7391
7558
  return findDeepElement.call(this, selector);
7392
7559
  }
7393
7560
  if (typeof selector === "string" && selector.startsWith(DEEP_SELECTOR)) {
@@ -7945,103 +8112,6 @@ async function getProtocolDriver(options) {
7945
8112
  return { Driver, options };
7946
8113
  }
7947
8114
 
7948
- // src/dialog.ts
7949
- var dialogManager = /* @__PURE__ */ new Map();
7950
- function getDialogManager(browser) {
7951
- const existingDialogManager = dialogManager.get(browser);
7952
- if (existingDialogManager) {
7953
- return existingDialogManager;
7954
- }
7955
- const newContext = new DialogManager(browser);
7956
- dialogManager.set(browser, newContext);
7957
- return newContext;
7958
- }
7959
- var DialogManager = class {
7960
- #browser;
7961
- #initialize;
7962
- #autoHandleDialog = true;
7963
- constructor(browser) {
7964
- this.#browser = browser;
7965
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
7966
- this.#initialize = Promise.resolve(true);
7967
- return;
7968
- }
7969
- this.#initialize = this.#browser.sessionSubscribe({
7970
- events: ["browsingContext.userPromptOpened"]
7971
- }).then(() => true, () => false);
7972
- this.#browser.on("_dialogListenerRegistered", () => this.#switchListenerFlag(false));
7973
- this.#browser.on("_dialogListenerRemoved", () => this.#switchListenerFlag(true));
7974
- this.#browser.on("browsingContext.userPromptOpened", this.#handleUserPrompt.bind(this));
7975
- }
7976
- async initialize() {
7977
- return this.#initialize;
7978
- }
7979
- /**
7980
- * capture shadow root elements propagated through console.debug
7981
- */
7982
- async #handleUserPrompt(log25) {
7983
- if (this.#autoHandleDialog) {
7984
- return this.#browser.browsingContextHandleUserPrompt({
7985
- accept: false,
7986
- context: log25.context
7987
- });
7988
- }
7989
- const dialog = new Dialog(log25, this.#browser);
7990
- this.#browser.emit("dialog", dialog);
7991
- }
7992
- /**
7993
- * Is called when a new dialog listener is registered with the `dialog` name.
7994
- * In these cases we set a flag to the `#listener` map to indicate that we
7995
- * are listening to dialog events for this page in this context.
7996
- */
7997
- #switchListenerFlag(value) {
7998
- this.#autoHandleDialog = value;
7999
- }
8000
- };
8001
- var Dialog = class {
8002
- #browser;
8003
- #context;
8004
- #message;
8005
- #defaultValue;
8006
- #type;
8007
- constructor(event, browser) {
8008
- this.#message = event.message;
8009
- this.#defaultValue = event.defaultValue;
8010
- this.#type = event.type;
8011
- this.#context = event.context;
8012
- this.#browser = browser;
8013
- }
8014
- message() {
8015
- return this.#message;
8016
- }
8017
- defaultValue() {
8018
- return this.#defaultValue;
8019
- }
8020
- type() {
8021
- return this.#type;
8022
- }
8023
- /**
8024
- * Returns when the dialog has been accepted.
8025
- *
8026
- * @alias dialog.accept
8027
- * @param {string=} promptText A text to enter into prompt. Does not cause any effects if the dialog's type is not prompt.
8028
- * @returns {Promise<void>}
8029
- */
8030
- async accept(userText) {
8031
- await this.#browser.browsingContextHandleUserPrompt({
8032
- accept: true,
8033
- context: this.#context,
8034
- userText
8035
- });
8036
- }
8037
- async dismiss() {
8038
- await this.#browser.browsingContextHandleUserPrompt({
8039
- accept: false,
8040
- context: this.#context
8041
- });
8042
- }
8043
- };
8044
-
8045
8115
  // src/index.ts
8046
8116
  var Key2 = Key;
8047
8117
  var SevereServiceError2 = SevereServiceError;
@@ -8067,13 +8137,7 @@ var remote = async function(params, remoteModifier) {
8067
8137
  instance.overwriteCommand = (name, fn, attachToElement) => origOverwriteCommand(name, fn, attachToElement);
8068
8138
  }
8069
8139
  instance.addLocatorStrategy = addLocatorStrategyHandler(instance);
8070
- await Promise.all([
8071
- getPolyfillManager(instance).initialize(),
8072
- getShadowRootManager(instance).initialize(),
8073
- getNetworkManager(instance).initialize(),
8074
- getDialogManager(instance).initialize(),
8075
- getContextManager(instance).initialize()
8076
- ]);
8140
+ await registerSessionManager(instance);
8077
8141
  return instance;
8078
8142
  };
8079
8143
  var attach = async function(attachOptions) {
@@ -8093,13 +8157,7 @@ var attach = async function(attachOptions) {
8093
8157
  wrapCommand3
8094
8158
  );
8095
8159
  driver.addLocatorStrategy = addLocatorStrategyHandler(driver);
8096
- await driver._bidiHandler?.connect().then(() => Promise.all([
8097
- getPolyfillManager(driver).initialize(),
8098
- getShadowRootManager(driver).initialize(),
8099
- getNetworkManager(driver).initialize(),
8100
- getDialogManager(driver).initialize(),
8101
- getContextManager(driver).initialize()
8102
- ]));
8160
+ await registerSessionManager(driver);
8103
8161
  return driver;
8104
8162
  };
8105
8163
  var multiremote = async function(params, { automationProtocol } = {}) {