webdriverio 9.5.2 → 9.5.4

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,48 +3130,53 @@ var ContextManager = class {
3075
3130
  isAndroid: this.#browser.isAndroid,
3076
3131
  isNativeContext: this.#isNativeContext
3077
3132
  });
3078
- this.#browser.on("result", (event) => {
3079
- if (event.command === "closeWindow") {
3080
- this.#currentContext = event.result.value[0];
3081
- return this.#browser.switchToWindow(this.#currentContext);
3082
- }
3083
- });
3084
- if (!this.#isEnabled()) {
3133
+ this.#browser.on("result", this.#onCommandResultBidiAndClassic.bind(this));
3134
+ if (!this.isEnabled()) {
3085
3135
  return;
3086
3136
  }
3087
- this.#browser.on("command", (event) => {
3088
- if (event.command === "switchToWindow") {
3089
- this.setCurrentContext(event.body.handle);
3090
- }
3091
- if (event.command === "switchToParentFrame" || event.command === "refresh") {
3092
- this.#currentContext = void 0;
3093
- }
3094
- if (this.#browser.isMobile && event.command === "switchContext") {
3095
- this.#mobileContext = event.body.name;
3096
- }
3097
- });
3098
- this.#browser.on("result", (event) => {
3099
- if (this.#browser.isMobile) {
3100
- if (event.command === "getContext") {
3101
- this.setCurrentContext(event.result.value);
3102
- }
3103
- if (event.command === "switchContext" && event.result.value === null && this.#mobileContext) {
3104
- this.setCurrentContext(this.#mobileContext);
3105
- }
3106
- }
3107
- });
3137
+ this.#browser.on("command", this.#onCommand.bind(this));
3138
+ if (this.#browser.isMobile) {
3139
+ this.#browser.on("result", this.#onCommandResultMobile.bind(this));
3140
+ }
3108
3141
  }
3109
- /**
3110
- * Only run this session helper if BiDi is enabled and we're not in unit tests.
3111
- */
3112
- #isEnabled() {
3113
- 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
+ }
3114
3174
  }
3115
3175
  /**
3116
3176
  * set context at the start of the session
3117
3177
  */
3118
3178
  async initialize() {
3119
- if (!this.#isEnabled()) {
3179
+ if (process.env.WDIO_UNIT_TESTS) {
3120
3180
  return "";
3121
3181
  }
3122
3182
  if (this.#browser.isMobile && !this.#isNativeContext && !this.#mobileContext) {
@@ -3156,24 +3216,18 @@ Requested WebDriver capabilities: ${JSON.stringify(this.#browser.requestedCapabi
3156
3216
  }
3157
3217
  };
3158
3218
 
3159
- // src/polyfill.ts
3219
+ // src/session/polyfill.ts
3160
3220
  import logger5 from "@wdio/logger";
3161
- var polyfillManager = /* @__PURE__ */ new Map();
3162
- var log5 = logger5("webdriverio:PolyfillManager");
3163
- 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;";
3164
3221
  function getPolyfillManager(browser) {
3165
- const existingPolyfillManager = polyfillManager.get(browser);
3166
- if (existingPolyfillManager) {
3167
- return existingPolyfillManager;
3168
- }
3169
- const newContext = new PolyfillManager(browser);
3170
- polyfillManager.set(browser, newContext);
3171
- return newContext;
3222
+ return SessionManager.getSessionManager(browser, PolyfillManager);
3172
3223
  }
3173
- 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 {
3174
3227
  #initialize;
3175
3228
  constructor(browser) {
3176
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
3229
+ super(browser, _PolyfillManager.name);
3230
+ if (!this.isEnabled()) {
3177
3231
  this.#initialize = Promise.resolve(true);
3178
3232
  return;
3179
3233
  }
@@ -3201,8 +3255,8 @@ async function execute(script, ...args) {
3201
3255
  }
3202
3256
  if (this.isBidi && !this.isMultiremote) {
3203
3257
  const browser = getBrowserObject3(this);
3204
- const contextManager2 = getContextManager(browser);
3205
- const context = await contextManager2.getCurrentContext();
3258
+ const contextManager = getContextManager(browser);
3259
+ const context = await contextManager.getCurrentContext();
3206
3260
  const userScript = typeof script === "string" ? new Function(script) : script;
3207
3261
  const functionDeclaration = new Function(`
3208
3262
  return (${SCRIPT_PREFIX}${userScript.toString()}${SCRIPT_SUFFIX}).apply(this, arguments);
@@ -3235,8 +3289,8 @@ async function executeAsync(script, ...args) {
3235
3289
  }
3236
3290
  if (this.isBidi && !this.isMultiremote) {
3237
3291
  const browser = getBrowserObject4(this);
3238
- const contextManager2 = getContextManager(browser);
3239
- const context = await contextManager2.getCurrentContext();
3292
+ const contextManager = getContextManager(browser);
3293
+ const context = await contextManager.getCurrentContext();
3240
3294
  const userScript = typeof script === "string" ? new Function(script) : script;
3241
3295
  const functionDeclaration = new Function(`
3242
3296
  const args = Array.from(arguments)
@@ -3936,8 +3990,8 @@ async function mock(url6, filterOptions) {
3936
3990
  throw new Error("Mocking is only supported when running tests using WebDriver Bidi");
3937
3991
  }
3938
3992
  const browser = getBrowserObject6(this);
3939
- const contextManager2 = getContextManager(browser);
3940
- const context = await contextManager2.getCurrentContext();
3993
+ const contextManager = getContextManager(browser);
3994
+ const context = await contextManager.getCurrentContext();
3941
3995
  if (!SESSION_MOCKS[context]) {
3942
3996
  SESSION_MOCKS[context] = /* @__PURE__ */ new Set();
3943
3997
  }
@@ -3991,9 +4045,9 @@ async function newWindow(url6, { type = "window", windowName = "", windowFeature
3991
4045
  }
3992
4046
  const tabsBefore = await this.getWindowHandles();
3993
4047
  if (this.isBidi) {
3994
- const contextManager2 = getContextManager(this);
4048
+ const contextManager = getContextManager(this);
3995
4049
  const { context } = await this.browsingContextCreate({ type });
3996
- contextManager2.setCurrentContext(context);
4050
+ contextManager.setCurrentContext(context);
3997
4051
  await this.browsingContextNavigate({ context, url: url6 });
3998
4052
  } else {
3999
4053
  await this.execute(newWindowHelper, url6, windowName, windowFeatures);
@@ -4066,306 +4120,873 @@ async function react$(selector, { props = {}, state = {} } = {}) {
4066
4120
  }
4067
4121
 
4068
4122
  // src/commands/browser/reloadSession.ts
4123
+ import logger13 from "@wdio/logger";
4124
+
4125
+ // src/session/shadowRoot.ts
4069
4126
  import logger12 from "@wdio/logger";
4070
- var log12 = logger12("webdriverio");
4071
- async function reloadSession(newCapabilities) {
4072
- const oldSessionId = this.sessionId;
4073
- const shutdownDriver = Boolean(newCapabilities?.browserName);
4074
- try {
4075
- await this.deleteSession({ shutdownDriver });
4076
- } catch (err) {
4077
- log12.warn(`Suppressing error closing the session: ${err.stack}`);
4078
- }
4079
- if (this.puppeteer?.connected) {
4080
- this.puppeteer.disconnect();
4081
- log12.debug("Disconnected puppeteer session");
4082
- }
4083
- const ProtocolDriver = (await import(
4084
- /* @vite-ignore */
4085
- this.options.automationProtocol
4086
- )).default;
4087
- await ProtocolDriver.reloadSession(this, newCapabilities);
4088
- const options = this.options;
4089
- if (Array.isArray(options.onReload) && options.onReload.length) {
4090
- await Promise.all(options.onReload.map((hook) => hook(oldSessionId, this.sessionId)));
4091
- }
4092
- 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);
4093
4131
  }
4094
-
4095
- // src/commands/browser/restore.ts
4096
- async function restore(scopes2) {
4097
- const scopeArray = !scopes2 || Array.isArray(scopes2) ? scopes2 : [scopes2];
4098
- const instanceRestoreFunctions = restoreFunctions.get(this);
4099
- if (!instanceRestoreFunctions) {
4100
- return;
4101
- }
4102
- await Promise.all(Array.from(instanceRestoreFunctions.entries()).map(async ([scope, restoreFunctionsList]) => {
4103
- if (!scopeArray || scopeArray.includes(scope)) {
4104
- await Promise.all(restoreFunctionsList.map((fn) => fn()));
4105
- 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;
4106
4144
  }
4107
- }));
4108
- }
4109
-
4110
- // src/commands/browser/savePDF.ts
4111
- import fs4 from "node:fs";
4112
- async function savePDF(filepath, options) {
4113
- if (typeof filepath !== "string" || !filepath.endsWith(".pdf")) {
4114
- throw new Error('savePDF expects a filepath of type string and ".pdf" file ending');
4115
- }
4116
- const absoluteFilepath = getAbsoluteFilepath(filepath);
4117
- await assertDirectoryExists(absoluteFilepath);
4118
- const pdf = await this.printPage(
4119
- options?.orientation,
4120
- options?.scale,
4121
- options?.background,
4122
- options?.width,
4123
- options?.height,
4124
- options?.top,
4125
- options?.bottom,
4126
- options?.left,
4127
- options?.right,
4128
- options?.shrinkToFit,
4129
- options?.pageRanges
4130
- );
4131
- const page = Buffer.from(pdf, "base64");
4132
- fs4.writeFileSync(absoluteFilepath, page);
4133
- return page;
4134
- }
4135
-
4136
- // src/commands/browser/saveRecordingScreen.ts
4137
- import fs5 from "node:fs";
4138
- async function saveRecordingScreen(filepath) {
4139
- if (typeof filepath !== "string") {
4140
- throw new Error("saveRecordingScreen expects a filepath");
4141
- }
4142
- const absoluteFilepath = getAbsoluteFilepath(filepath);
4143
- await assertDirectoryExists(absoluteFilepath);
4144
- const videoBuffer = await this.stopRecordingScreen();
4145
- const video = Buffer.from(videoBuffer, "base64");
4146
- fs5.writeFileSync(absoluteFilepath, video);
4147
- return video;
4148
- }
4149
-
4150
- // src/commands/browser/saveScreenshot.ts
4151
- import fs6 from "node:fs";
4152
- import { getBrowserObject as getBrowserObject7 } from "@wdio/utils";
4153
- async function saveScreenshot(filepath) {
4154
- if (typeof filepath !== "string" || !filepath.endsWith(".png")) {
4155
- throw new Error('saveScreenshot expects a filepath of type string and ".png" file ending');
4156
- }
4157
- const absoluteFilepath = getAbsoluteFilepath(filepath);
4158
- await assertDirectoryExists(absoluteFilepath);
4159
- let screenBuffer;
4160
- if (this.isBidi) {
4161
- const browser = getBrowserObject7(this);
4162
- const contextManager2 = getContextManager(browser);
4163
- const context = await contextManager2.getCurrentContext();
4164
- const { data } = await this.browsingContextCaptureScreenshot({ context });
4165
- screenBuffer = data;
4166
- } else {
4167
- screenBuffer = await this.takeScreenshot();
4168
- }
4169
- const screenshot = Buffer.from(screenBuffer, "base64");
4170
- fs6.writeFileSync(absoluteFilepath, screenshot);
4171
- return screenshot;
4172
- }
4173
-
4174
- // src/commands/browser/scroll.ts
4175
- import logger13 from "@wdio/logger";
4176
- var log13 = logger13("webdriverio");
4177
- function scroll(x = 0, y = 0) {
4178
- if (!x && !y) {
4179
- 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
+ });
4180
4154
  }
4181
- if (this.isMobile) {
4182
- 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));
4183
4160
  }
4184
- return this.action("wheel").scroll({
4185
- deltaX: x,
4186
- deltaY: y,
4187
- duration: 0
4188
- }).perform();
4189
- }
4190
-
4191
- // src/commands/browser/setCookies.ts
4192
- async function setCookies(cookieObjs) {
4193
- const cookieObjsList = !Array.isArray(cookieObjs) ? [cookieObjs] : cookieObjs;
4194
- if (cookieObjsList.some((obj) => typeof obj !== "object")) {
4195
- throw new Error("Invalid input (see https://webdriver.io/docs/api/browser/setCookies for documentation)");
4161
+ async initialize() {
4162
+ return this.#initialize;
4196
4163
  }
4197
- if (!this.isBidi) {
4198
- for (const cookieObj of cookieObjsList) {
4199
- 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;
4200
4170
  }
4201
- return;
4202
- }
4203
- let url6 = new URL("http://localhost");
4204
- if (cookieObjsList.some((cookie) => typeof cookie.domain !== "string")) {
4205
- url6 = new URL(await this.getUrl());
4171
+ const params = command.params;
4172
+ this.#shadowRoots.delete(params.context);
4206
4173
  }
4207
- await Promise.all(cookieObjsList.map((cookie) => this.storageSetCookie({
4208
- cookie: {
4209
- ...cookie,
4210
- domain: cookie.domain || url6.hostname,
4211
- value: {
4212
- type: "string",
4213
- value: cookie.value
4214
- }
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);
4215
4184
  }
4216
- })));
4217
- return;
4218
- }
4219
-
4220
- // src/commands/browser/setTimeout.ts
4221
- async function setTimeout2(timeouts) {
4222
- if (typeof timeouts !== "object") {
4223
- throw new Error('Parameter for "setTimeout" command needs to be an object');
4224
- }
4225
- const timeoutValues = Object.values(timeouts);
4226
- if (timeoutValues.length && timeoutValues.every((timeout) => typeof timeout !== "number" || timeout < 0 || timeout > Number.MAX_SAFE_INTEGER)) {
4227
- throw new Error("Specified timeout values are not valid integer (see https://webdriver.io/docs/api/browser/setTimeout for documentation).");
4228
4185
  }
4229
- const implicit = timeouts.implicit;
4230
- const pageLoad = timeouts["page load"] || timeouts.pageLoad;
4231
- const script = timeouts.script;
4232
- const setTimeouts = this.setTimeouts.bind(this);
4233
- return setTimeouts(implicit, pageLoad, script);
4234
- }
4235
-
4236
- // src/commands/browser/setViewport.ts
4237
- var minWindowSize = 0;
4238
- var maxWindowSize = Number.MAX_SAFE_INTEGER;
4239
- async function setViewport(options) {
4240
- if (typeof options.width !== "number" || typeof options.height !== "number") {
4241
- throw new Error("setViewport expects width and height of type number");
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;
4242
4192
  }
4243
- if (options.width < minWindowSize || options.width > maxWindowSize || options.height < minWindowSize || options.height > maxWindowSize) {
4244
- throw new Error("setViewport expects width and height to be a number in the 0 to 2^31 \u2212 1 range");
4245
- }
4246
- if (options.devicePixelRatio && (typeof options.devicePixelRatio !== "number" || options.devicePixelRatio < 0)) {
4247
- 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(", ")}`);
4248
4264
  }
4249
- const contextManager2 = getContextManager(this);
4250
- const context = await contextManager2.getCurrentContext();
4251
- await this.browsingContextSetViewport({
4252
- context,
4253
- devicePixelRatio: options.devicePixelRatio || 1,
4254
- viewport: {
4255
- width: options.width,
4256
- height: options.height
4265
+ getShadowElementsByContextId(contextId, scope) {
4266
+ let tree = this.#shadowRoots.get(contextId);
4267
+ if (!tree) {
4268
+ return [];
4257
4269
  }
4258
- });
4259
- }
4260
-
4261
- // src/commands/browser/setWindowSize.ts
4262
- import { getBrowserObject as getBrowserObject8 } from "@wdio/utils";
4263
- var minWindowSize2 = 0;
4264
- var maxWindowSize2 = Number.MAX_SAFE_INTEGER;
4265
- async function setWindowSize(width, height) {
4266
- if (typeof width !== "number" || typeof height !== "number") {
4267
- 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
+ ];
4268
4284
  }
4269
- if (width < minWindowSize2 || width > maxWindowSize2 || height < minWindowSize2 || height > maxWindowSize2) {
4270
- 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]);
4271
4297
  }
4272
- const browser = getBrowserObject8(this);
4273
- await browser.setWindowRect(null, null, width, height);
4274
- }
4275
-
4276
- // src/commands/browser/switchWindow.ts
4277
- async function switchWindow(matcher) {
4278
- if (typeof matcher !== "string" && !(matcher instanceof RegExp)) {
4279
- 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;
4280
4308
  }
4281
- const currentWindow = await this.getWindowHandle();
4282
- if (typeof matcher === "string" && currentWindow === matcher) {
4283
- return currentWindow;
4309
+ deleteShadowRoot(element, contextId) {
4310
+ const tree = this.#shadowRoots.get(contextId);
4311
+ if (!tree) {
4312
+ return;
4313
+ }
4314
+ return tree.remove(element);
4284
4315
  }
4285
- const contextManager2 = getContextManager(this);
4286
- const tabs = await this.getWindowHandles();
4287
- if (typeof matcher === "string" && tabs.includes(matcher)) {
4288
- await this.switchToWindow(matcher);
4289
- contextManager2.setCurrentContext(matcher);
4290
- 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;
4291
4326
  }
4292
- const matchesTarget = (target) => {
4293
- if (typeof matcher === "string") {
4294
- return target.includes(matcher);
4295
- }
4296
- return !!target.match(matcher);
4297
- };
4298
- for (const tab of tabs) {
4299
- await this.switchToWindow(tab);
4300
- contextManager2.setCurrentContext(tab);
4301
- const url6 = await this.getUrl();
4302
- if (matchesTarget(url6)) {
4303
- 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');
4304
4331
  }
4305
- const title = await this.getTitle();
4306
- if (matchesTarget(title)) {
4307
- return tab;
4332
+ if (scope instanceof _ShadowRootTree) {
4333
+ this.children.add(scope);
4334
+ return;
4308
4335
  }
4309
- const windowName = await this.execute(
4310
- /* istanbul ignore next */
4311
- () => window.name
4312
- );
4313
- if (windowName && matchesTarget(windowName)) {
4314
- 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;
4315
4343
  }
4344
+ throw new Error('Invalid arguments for "addShadowElement" method');
4316
4345
  }
4317
- await this.switchToWindow(currentWindow);
4318
- throw new Error(`No window found with title, url, name or window handle matching "${matcher}"`);
4319
- }
4320
-
4321
- // src/commands/browser/switchFrame.ts
4322
- import logger14 from "@wdio/logger";
4323
- import { ELEMENT_KEY as ELEMENT_KEY8 } from "webdriver";
4324
- var log14 = logger14("webdriverio:switchFrame");
4325
- async function switchFrame(context) {
4326
- function isPossiblyUnresolvedElement(input) {
4327
- return Boolean(input) && typeof input === "object" && typeof input.getElement === "function";
4328
- }
4329
- if (!this.isBidi) {
4330
- if (typeof context === "function") {
4331
- 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;
4332
4349
  }
4333
- if (typeof context === "string") {
4334
- 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
+ }
4335
4355
  }
4336
- if (isPossiblyUnresolvedElement(context)) {
4337
- const element = await context.getElement();
4338
- await element.waitForExist({
4339
- timeoutMsg: `Can't switch to frame with selector ${element.selector} because it doesn't exist`
4340
- });
4341
- return switchToFrame(this, element);
4356
+ return void 0;
4357
+ }
4358
+ findByShadowId(shadowRoot) {
4359
+ if (this.shadowRoot === shadowRoot) {
4360
+ return this;
4342
4361
  }
4343
- 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;
4344
4369
  }
4345
- if (context === null) {
4346
- const handle = await this.getWindowHandle();
4347
- switchToFrameHelper(this, handle);
4348
- await switchToFrame(this, context);
4349
- return handle;
4370
+ getAllLookupScopes() {
4371
+ return [
4372
+ this.shadowRoot ?? this.element,
4373
+ ...Array.from(this.children).map((tree) => tree.getAllLookupScopes())
4374
+ ].flat();
4350
4375
  }
4351
- if (typeof context === "string") {
4352
- const tree = await this.browsingContextGetTree({});
4353
- let newContextId;
4354
- const urlContext = findContext(context, tree.contexts, byUrl) || /**
4355
- * In case the user provides an url without `/` at the end, e.g. `https://example.com`,
4356
- * the `browsingContextGetTree` command may return a context with the url `https://example.com/`.
4357
- */
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
+ */
4358
4979
  findContext(`${context}/`, tree.contexts, byUrl);
4359
4980
  const urlContextContaining = findContext(context, tree.contexts, byUrlContaining);
4360
4981
  const contextIdContext = findContext(context, tree.contexts, byContextId);
4361
4982
  if (urlContext) {
4362
- 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}"`);
4363
4984
  newContextId = urlContext.context;
4364
4985
  } else if (urlContextContaining) {
4365
- 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}"`);
4366
4987
  newContextId = urlContextContaining.context;
4367
4988
  } else if (contextIdContext) {
4368
- log14.info(`Found context by id "${contextIdContext}" with url "${contextIdContext.url}"`);
4989
+ log15.info(`Found context by id "${contextIdContext}" with url "${contextIdContext.url}"`);
4369
4990
  newContextId = contextIdContext.context;
4370
4991
  }
4371
4992
  if (!newContextId) {
@@ -4392,7 +5013,7 @@ async function switchFrame(context) {
4392
5013
  arguments: args.map((arg) => LocalValue.getArgument(arg)),
4393
5014
  target: { context: id }
4394
5015
  };
4395
- 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}`));
4396
5017
  if (!result) {
4397
5018
  return [];
4398
5019
  }
@@ -4422,13 +5043,13 @@ async function switchFrame(context) {
4422
5043
  let desiredFrame;
4423
5044
  let desiredContext = newContextId;
4424
5045
  const contextQueue = [];
4425
- 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}`);
4426
5047
  while (desiredContext !== currentContext) {
4427
5048
  desiredFrame = allFrames.find(({ context: context2 }) => context2 === desiredContext);
4428
5049
  if (!desiredFrame) {
4429
5050
  break;
4430
5051
  }
4431
- log14.info(
5052
+ log15.info(
4432
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`
4433
5054
  );
4434
5055
  contextQueue.unshift(desiredFrame);
@@ -4464,7 +5085,7 @@ async function switchFrame(context) {
4464
5085
  arguments: [],
4465
5086
  target: { context: contextId }
4466
5087
  };
4467
- 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}`));
4468
5089
  if (!result || result.type !== "success" || result.result.type !== "boolean" || !result.result.value) {
4469
5090
  continue;
4470
5091
  }
@@ -4474,7 +5095,7 @@ async function switchFrame(context) {
4474
5095
  throw new Error("Could not find the desired frame");
4475
5096
  }
4476
5097
  throw new Error(
4477
- `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`
4478
5099
  );
4479
5100
  }
4480
5101
  function switchToFrameHelper(browser, context) {
@@ -4535,11 +5156,11 @@ function switchToFrame(browser, frame) {
4535
5156
  }
4536
5157
 
4537
5158
  // src/commands/browser/throttle.ts
4538
- import logger15 from "@wdio/logger";
5159
+ import logger16 from "@wdio/logger";
4539
5160
  import { getBrowserObject as getBrowserObject9 } from "@wdio/utils";
4540
- var log15 = logger15("webdriverio:throttle");
5161
+ var log16 = logger16("webdriverio:throttle");
4541
5162
  async function throttle(params) {
4542
- 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.');
4543
5164
  const browser = getBrowserObject9(this);
4544
5165
  await browser.throttleNetwork(params);
4545
5166
  }
@@ -4675,199 +5296,13 @@ async function uploadFile(localPath) {
4675
5296
  throw new Error("number or type of arguments don't agree with uploadFile command");
4676
5297
  }
4677
5298
  if (typeof this.file !== "function") {
4678
- throw new Error(`The uploadFile command is not available in ${this.capabilities.browserName}`);
4679
- }
4680
- const zipData = [];
4681
- const source = fs7.createReadStream(localPath);
4682
- return new Promise((resolve5, reject) => {
4683
- 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();
4684
- });
4685
- }
4686
-
4687
- // src/networkManager.ts
4688
- var networkManager = /* @__PURE__ */ new Map();
4689
- function getNetworkManager(browser) {
4690
- const existingNetworkManager = networkManager.get(browser);
4691
- if (existingNetworkManager) {
4692
- return existingNetworkManager;
4693
- }
4694
- const newContext = new NetworkManager(browser);
4695
- networkManager.set(browser, newContext);
4696
- return newContext;
4697
- }
4698
- var UNKNOWN_NAVIGATION_ID = "UNKNOWN_NAVIGATION_ID";
4699
- var SUPPORTED_NAVIGATION_PROTOCOLS = ["http", "https", "data", "file"];
4700
- var NetworkManager = class {
4701
- #browser;
4702
- #initialize;
4703
- #requests = /* @__PURE__ */ new Map();
4704
- #lastNetworkId;
4705
- constructor(browser) {
4706
- this.#browser = browser;
4707
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
4708
- this.#initialize = Promise.resolve(true);
4709
- return;
4710
- }
4711
- this.#initialize = this.#browser.sessionSubscribe({
4712
- events: [
4713
- "browsingContext.navigationStarted",
4714
- "network.responseCompleted",
4715
- "network.beforeRequestSent",
4716
- "network.fetchError"
4717
- ]
4718
- }).then(() => true, () => false);
4719
- this.#browser.on("browsingContext.navigationStarted", this.#navigationStarted.bind(this));
4720
- this.#browser.on("network.responseCompleted", this.#responseCompleted.bind(this));
4721
- this.#browser.on("network.beforeRequestSent", this.#beforeRequestSent.bind(this));
4722
- this.#browser.on("network.fetchError", this.#fetchError.bind(this));
4723
- }
4724
- async initialize() {
4725
- return this.#initialize;
4726
- }
4727
- #beforeRequestSent(log25) {
4728
- if (log25.navigation) {
4729
- return;
4730
- }
4731
- const request = this.#findRootRequest(log25.navigation);
4732
- if (!request) {
4733
- return;
4734
- }
4735
- const { request: id, headers, cookies, url: url6 } = log25.request;
4736
- request.children?.push({
4737
- id,
4738
- url: url6,
4739
- headers: headerListToObject(headers),
4740
- cookies: cookies.map((cookie) => ({
4741
- name: cookie.name,
4742
- value: cookie.value.type === "string" ? cookie.value.value : atob(cookie.value.value),
4743
- domain: cookie.domain,
4744
- path: cookie.path,
4745
- size: cookie.size,
4746
- httpOnly: cookie.httpOnly,
4747
- secure: cookie.secure,
4748
- sameSite: cookie.sameSite,
4749
- expiry: cookie.expiry
4750
- })),
4751
- timestamp: log25.timestamp
4752
- });
4753
- }
4754
- #navigationStarted(log25) {
4755
- if (
4756
- /**
4757
- * we need a navigation id to identify the request
4758
- */
4759
- !log25.navigation || /**
4760
- * ignore urls that users wouldn't navigate to
4761
- */
4762
- !SUPPORTED_NAVIGATION_PROTOCOLS.some((protocol) => log25.url.startsWith(protocol))
4763
- ) {
4764
- if (log25.navigation === null && log25.url === "") {
4765
- this.#lastNetworkId = UNKNOWN_NAVIGATION_ID;
4766
- return this.#requests.set(UNKNOWN_NAVIGATION_ID, {
4767
- url: "",
4768
- headers: {},
4769
- timestamp: log25.timestamp,
4770
- redirectChain: [],
4771
- children: []
4772
- });
4773
- }
4774
- return;
4775
- }
4776
- this.#lastNetworkId = log25.navigation;
4777
- this.#requests.set(log25.navigation, {
4778
- url: log25.url,
4779
- headers: {},
4780
- timestamp: log25.timestamp,
4781
- navigation: log25.navigation,
4782
- redirectChain: [],
4783
- children: []
4784
- });
4785
- }
4786
- #fetchError(log25) {
4787
- const response = this.#findRootRequest(log25.navigation);
4788
- if (!response) {
4789
- return;
4790
- }
4791
- const request = response.children?.find((child) => child.id === log25.request.request);
4792
- if (!request) {
4793
- return;
4794
- }
4795
- request.error = log25.errorText;
4796
- }
4797
- #findRootRequest(navigationId) {
4798
- const response = this.#requests.get(navigationId || UNKNOWN_NAVIGATION_ID);
4799
- if (response) {
4800
- return response;
4801
- }
4802
- const firstRequest = this.#requests.values().next().value;
4803
- return this.#lastNetworkId ? this.#requests.get(this.#lastNetworkId) || firstRequest : firstRequest;
4804
- }
4805
- #responseCompleted(log25) {
4806
- const response = this.#findRootRequest(log25.navigation);
4807
- if (!response) {
4808
- return;
4809
- }
4810
- if (!response.navigation && response.url === "") {
4811
- response.url = log25.request.url;
4812
- response.navigation = log25.navigation;
4813
- }
4814
- if (log25.navigation === response.navigation) {
4815
- if (response.url !== log25.response.url) {
4816
- response.redirectChain?.push(response.url);
4817
- }
4818
- response.url = log25.response.url;
4819
- const { headers: requestHeaders } = log25.request;
4820
- const { fromCache, headers: responseHeaders, mimeType, status } = log25.response;
4821
- response.headers = headerListToObject(requestHeaders), response.response = {
4822
- fromCache,
4823
- headers: headerListToObject(responseHeaders),
4824
- mimeType,
4825
- status
4826
- };
4827
- return;
4828
- }
4829
- const request = response.children?.find((child) => child.id === log25.request.request);
4830
- if (!request) {
4831
- return;
4832
- }
4833
- request.response = {
4834
- fromCache: log25.response.fromCache,
4835
- headers: headerListToObject(log25.response.headers),
4836
- mimeType: log25.response.mimeType,
4837
- status: log25.response.status
4838
- };
4839
- response.children?.push(request);
4840
- }
4841
- getRequestResponseData(navigationId) {
4842
- return this.#requests.get(navigationId);
4843
- }
4844
- /**
4845
- * Returns the number of requests that are currently pending.
4846
- * @param context browsing context id
4847
- * @returns the number of requests that are currently pending
4848
- */
4849
- getPendingRequests(navigationId) {
4850
- const request = this.#requests.get(navigationId);
4851
- if (!request) {
4852
- throw new Error(`Couldn't find request for navigation with id ${navigationId}`);
4853
- }
4854
- const subRequests = request.children || [];
4855
- return subRequests.filter((child) => (
4856
- /**
4857
- * either the request has no response yet
4858
- */
4859
- !child.response && /**
4860
- * and there was no request error
4861
- */
4862
- !child.error
4863
- ));
5299
+ throw new Error(`The uploadFile command is not available in ${this.capabilities.browserName}`);
4864
5300
  }
4865
- };
4866
- function headerListToObject(headers) {
4867
- return headers.reduce((acc, { name, value }) => {
4868
- acc[name] = value.value;
4869
- return acc;
4870
- }, {});
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
+ });
4871
5306
  }
4872
5307
 
4873
5308
  // src/commands/browser/url.ts
@@ -4882,8 +5317,8 @@ async function url3(path4, options = {}) {
4882
5317
  }
4883
5318
  if (this.isBidi) {
4884
5319
  let resetPreloadScript;
4885
- const contextManager2 = getContextManager(this);
4886
- const context = await contextManager2.getCurrentContext();
5320
+ const contextManager = getContextManager(this);
5321
+ const context = await contextManager.getCurrentContext();
4887
5322
  if (options.onBeforeLoad) {
4888
5323
  if (typeof options.onBeforeLoad !== "function") {
4889
5324
  throw new Error(`Option "onBeforeLoad" must be a function, but received: ${typeof options.onBeforeLoad}`);
@@ -4930,7 +5365,8 @@ async function url3(path4, options = {}) {
4930
5365
  * set a short interval to immediately return once the first request payload comes in
4931
5366
  */
4932
5367
  {
4933
- interval: 1
5368
+ interval: 1,
5369
+ timeoutMsg: `Navigation to '${path4}' timed out as no request payload was received`
4934
5370
  }
4935
5371
  );
4936
5372
  return request;
@@ -4982,7 +5418,7 @@ function waitUntil(condition, {
4982
5418
  }
4983
5419
 
4984
5420
  // src/commands/mobile/swipe.ts
4985
- import logger16 from "@wdio/logger";
5421
+ import logger17 from "@wdio/logger";
4986
5422
 
4987
5423
  // src/types.ts
4988
5424
  var MobileScrollDirection = /* @__PURE__ */ ((MobileScrollDirection2) => {
@@ -4994,7 +5430,7 @@ var MobileScrollDirection = /* @__PURE__ */ ((MobileScrollDirection2) => {
4994
5430
  })(MobileScrollDirection || {});
4995
5431
 
4996
5432
  // src/commands/mobile/swipe.ts
4997
- var log16 = logger16("webdriverio");
5433
+ var log17 = logger17("webdriverio");
4998
5434
  var SWIPE_DEFAULTS = {
4999
5435
  DIRECTION: "up" /* Up */,
5000
5436
  DURATION: 1500,
@@ -5007,7 +5443,7 @@ async function swipe(options) {
5007
5443
  }
5008
5444
  let { scrollableElement, from, to } = options || {};
5009
5445
  if (scrollableElement && (from || to)) {
5010
- 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.");
5011
5447
  }
5012
5448
  if (!from || !to) {
5013
5449
  scrollableElement = scrollableElement || await getScrollableElement(browser);
@@ -5029,9 +5465,9 @@ async function calculateFromTo({
5029
5465
  let swipePercentage = SWIPE_DEFAULTS.PERCENT;
5030
5466
  if (percentage !== void 0) {
5031
5467
  if (isNaN(percentage)) {
5032
- log16.warn("The percentage to swipe should be a number.");
5468
+ log17.warn("The percentage to swipe should be a number.");
5033
5469
  } else if (percentage < 0 || percentage > 1) {
5034
- 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.");
5035
5471
  } else {
5036
5472
  swipePercentage = percentage;
5037
5473
  }
@@ -5057,802 +5493,531 @@ async function calculateFromTo({
5057
5493
  case "right" /* Right */:
5058
5494
  from = scrollRectangles.left;
5059
5495
  to = scrollRectangles.right;
5060
- break;
5061
- case "up" /* Up */:
5062
- from = scrollRectangles.bottom;
5063
- to = scrollRectangles.top;
5064
- break;
5065
- default:
5066
- throw new Error(`Unknown direction: ${direction}`);
5067
- }
5068
- return { from, to };
5069
- }
5070
- async function getScrollableElement(browser) {
5071
- const defaultAndroidSelector = "//android.widget.ScrollView";
5072
- const defaultIosSelector = '-ios predicate string:type == "XCUIElementTypeApplication"';
5073
- const selector = browser.isIOS ? (
5074
- // For iOS, we need to find the application element, if we can't find it, we should throw an error
5075
- defaultIosSelector
5076
- ) : (
5077
- // There is always a scrollview for Android or, if this fails we should throw an error
5078
- defaultAndroidSelector
5079
- );
5080
- const scrollableElements = await browser.$$(
5081
- selector
5082
- );
5083
- if (scrollableElements.length > 0) {
5084
- return scrollableElements[0];
5085
- }
5086
- throw new Error(
5087
- `Default scrollable element '${browser.isIOS ? defaultIosSelector : defaultAndroidSelector}' was not found. Our advice is to provide a scrollable element like this:
5088
-
5089
- await browser.swipe({ scrollableElement: $('#scrollable') });
5090
-
5091
- `
5092
- );
5093
- }
5094
- async function w3cSwipe({ browser, duration, from, to }) {
5095
- await browser.action("pointer", {
5096
- parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5097
- }).move(from.x, from.y).down().pause(10).move({ duration, x: to.x, y: to.y }).up().perform();
5098
- return browser.pause(500);
5099
- }
5100
-
5101
- // src/commands/mobile/tap.ts
5102
- import logger17 from "@wdio/logger";
5103
- import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
5104
- var log17 = logger17("webdriver");
5105
- async function tap(options) {
5106
- const isElement2 = this.selector !== void 0;
5107
- const element = isElement2 ? this : null;
5108
- const browser = isElement2 ? getBrowserObject11(this) : this;
5109
- if (!browser.isMobile) {
5110
- throw new Error("The tap command is only available for mobile platforms.");
5111
- }
5112
- validateTapOptions(options);
5113
- if (element) {
5114
- return await elementTap(browser, element, options);
5115
- }
5116
- if (!options || options.x === void 0 || options.y === void 0) {
5117
- throw new Error("The tap command requires x and y coordinates to be set for screen taps.");
5118
- }
5119
- return await screenTap(browser, options);
5120
- }
5121
- function validateTapOptions(options) {
5122
- if (options) {
5123
- if (typeof options !== "object" || Array.isArray(options)) {
5124
- throw new TypeError("Options must be an object.");
5125
- }
5126
- const { x, y, ...otherArgs } = options;
5127
- if (x === void 0 !== (y === void 0)) {
5128
- throw new TypeError(`If ${x !== void 0 ? "x" : "y"} is set, then ${x !== void 0 ? "y" : "x"} must also be set.`);
5129
- }
5130
- if (x !== void 0 && y !== void 0 && Object.keys(otherArgs).length > 0) {
5131
- throw new TypeError(`If x and y are provided, no other arguments are allowed. Found: ${Object.keys(otherArgs).join(", ")}`);
5132
- }
5133
- const invalidCoordinates = [];
5134
- if (x !== void 0 && x < 0) {
5135
- invalidCoordinates.push("x");
5136
- }
5137
- if (y !== void 0 && y < 0) {
5138
- invalidCoordinates.push("y");
5139
- }
5140
- if (invalidCoordinates.length > 0) {
5141
- throw new TypeError(`The ${invalidCoordinates.join(" and ")} value${invalidCoordinates.length > 1 ? "s" : ""} must be positive.`);
5142
- }
5143
- }
5144
- }
5145
- async function elementTap(browser, element, options) {
5146
- if (browser.isNativeContext) {
5147
- return await nativeTap(element, browser, options);
5148
- }
5149
- if (options) {
5150
- log17.warn("The options object is not supported in Web environments and will be ignored.");
5151
- }
5152
- return await webTap(element);
5153
- }
5154
- async function webTap(element) {
5155
- return element.click();
5156
- }
5157
- async function executeNativeTap(browser, options) {
5158
- return await browser.execute(
5159
- `mobile: ${browser.isIOS ? "tap" : "clickGesture"}`,
5160
- { ...browser.isIOS ? { x: 0, y: 0 } : {}, ...options }
5161
- );
5162
- }
5163
- async function nativeTap(element, browser, options = {}) {
5164
- try {
5165
- if (!element.elementId) {
5166
- throw new Error("no such element");
5167
- }
5168
- return await executeNativeTap(browser, { elementId: element.elementId });
5169
- } catch (error) {
5170
- let err = error;
5171
- if (typeof error === "string") {
5172
- err = new Error(error);
5173
- }
5174
- if (!err.message.includes("no such element")) {
5175
- throw err;
5176
- }
5177
- const scrollIntoViewOptions = Object.fromEntries(
5178
- Object.entries({
5179
- direction: options?.direction,
5180
- maxScrolls: options?.maxScrolls,
5181
- scrollableElement: options?.scrollableElement
5182
- }).filter(([_, value]) => value !== void 0)
5183
- );
5184
- try {
5185
- await element.scrollIntoView(scrollIntoViewOptions);
5186
- return await executeNativeTap(browser, { elementId: element.elementId });
5187
- } catch (scrollError) {
5188
- let err2 = scrollError;
5189
- if (typeof scrollError === "string") {
5190
- err2 = new Error(scrollError);
5191
- }
5192
- if (err2.message.includes("Element not found within scroll limit of")) {
5193
- 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:
5194
-
5195
- await elem.tap({
5196
- direction: 'left' // possible options are: 'up|down|left|right'
5197
- maxScrolls: 15,
5198
- scrollableElement: $('#scrollable'),
5199
- });
5200
-
5201
- `);
5202
- } else if (err2.message.includes("Default scrollable element")) {
5203
- const match = err2.message.match(/Default scrollable element '(.*?)' was not found/);
5204
- const scrollableElement = match?.[1] || "unknown-scrollable-element";
5205
- 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:
5206
-
5207
- await elem.tap({
5208
- scrollableElement: $('#scrollable'),
5209
- });
5210
-
5211
- `);
5212
- }
5213
- throw err2;
5214
- }
5215
- }
5216
- }
5217
- async function screenTap(browser, options) {
5218
- const { x, y } = options;
5219
- if (browser.isNativeContext) {
5220
- return await executeNativeTap(browser, options);
5221
- }
5222
- return await browser.action(
5223
- "pointer",
5224
- {
5225
- parameters: { pointerType: "touch" }
5226
- }
5227
- ).move({ x, y }).down({ button: 0 }).pause(10).up({ button: 0 }).perform();
5228
- }
5229
-
5230
- // src/commands/element.ts
5231
- var element_exports = {};
5232
- __export(element_exports, {
5233
- $: () => $2,
5234
- $$: () => $$2,
5235
- addValue: () => addValue,
5236
- clearValue: () => clearValue,
5237
- click: () => click,
5238
- custom$: () => custom$2,
5239
- custom$$: () => custom$$2,
5240
- doubleClick: () => doubleClick,
5241
- dragAndDrop: () => dragAndDrop,
5242
- execute: () => execute2,
5243
- executeAsync: () => executeAsync2,
5244
- getAttribute: () => getAttribute,
5245
- getCSSProperty: () => getCSSProperty,
5246
- getComputedLabel: () => getComputedLabel,
5247
- getComputedRole: () => getComputedRole,
5248
- getElement: () => getElement2,
5249
- getHTML: () => getHTML,
5250
- getLocation: () => getLocation,
5251
- getProperty: () => getProperty,
5252
- getSize: () => getSize,
5253
- getTagName: () => getTagName,
5254
- getText: () => getText,
5255
- getValue: () => getValue,
5256
- isClickable: () => isClickable,
5257
- isDisplayed: () => isDisplayed,
5258
- isEnabled: () => isEnabled,
5259
- isEqual: () => isEqual,
5260
- isExisting: () => isExisting,
5261
- isFocused: () => isFocused,
5262
- isSelected: () => isSelected,
5263
- isStable: () => isStable,
5264
- longPress: () => longPress,
5265
- moveTo: () => moveTo,
5266
- nextElement: () => nextElement,
5267
- parentElement: () => parentElement,
5268
- pinch: () => pinch,
5269
- previousElement: () => previousElement,
5270
- react$: () => react$2,
5271
- react$$: () => react$$2,
5272
- saveScreenshot: () => saveScreenshot2,
5273
- scrollIntoView: () => scrollIntoView,
5274
- selectByAttribute: () => selectByAttribute,
5275
- selectByIndex: () => selectByIndex,
5276
- selectByVisibleText: () => selectByVisibleText,
5277
- setValue: () => setValue,
5278
- shadow$: () => shadow$,
5279
- shadow$$: () => shadow$$,
5280
- tap: () => tap,
5281
- touchAction: () => touchAction3,
5282
- waitForClickable: () => waitForClickable,
5283
- waitForDisplayed: () => waitForDisplayed,
5284
- waitForEnabled: () => waitForEnabled,
5285
- waitForExist: () => waitForExist,
5286
- waitForStable: () => waitForStable,
5287
- waitUntil: () => waitUntil2,
5288
- zoom: () => zoom
5289
- });
5290
-
5291
- // src/commands/element/$$.ts
5292
- var $$2 = $$;
5293
-
5294
- // src/commands/element/$.ts
5295
- var $2 = $;
5296
-
5297
- // src/commands/element/addValue.ts
5298
- var VALID_TYPES = ["string", "number"];
5299
- function addValue(value) {
5300
- if (!VALID_TYPES.includes(typeof value)) {
5301
- throw new Error(
5302
- 'The setValue/addValue command only take string or number values. If you like to use special characters, use the "keys" command.'
5303
- );
5496
+ break;
5497
+ case "up" /* Up */:
5498
+ from = scrollRectangles.bottom;
5499
+ to = scrollRectangles.top;
5500
+ break;
5501
+ default:
5502
+ throw new Error(`Unknown direction: ${direction}`);
5304
5503
  }
5305
- return this.elementSendKeys(this.elementId, value.toString());
5504
+ return { from, to };
5306
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:
5307
5524
 
5308
- // src/commands/element/clearValue.ts
5309
- function clearValue() {
5310
- 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);
5311
5535
  }
5312
5536
 
5313
- // src/commands/element/click.ts
5537
+ // src/commands/mobile/tap.ts
5314
5538
  import logger18 from "@wdio/logger";
5315
- import { getBrowserObject as getBrowserObject12 } from "@wdio/utils";
5539
+ import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
5316
5540
  var log18 = logger18("webdriver");
5317
- function click(options) {
5318
- if (typeof options !== "undefined") {
5319
- if (typeof options !== "object" || Array.isArray(options)) {
5320
- throw new TypeError("Options must be an object");
5321
- }
5322
- 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.");
5323
5547
  }
5324
- return elementClick(this);
5325
- }
5326
- async function workaround(element) {
5327
- 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);
5328
5556
  }
5329
- async function elementClick(element) {
5330
- try {
5331
- return await element.elementClick(element.elementId);
5332
- } catch (error) {
5333
- let err = error;
5334
- if (typeof error === "string") {
5335
- 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.");
5336
5561
  }
5337
- if (!err.message.includes("element click intercepted")) {
5338
- 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.`);
5339
5565
  }
5340
- await workaround(element);
5341
- return element.elementClick(element.elementId);
5342
- }
5343
- }
5344
- async function actionClick(element, options) {
5345
- const defaultOptions = {
5346
- button: 0,
5347
- x: 0,
5348
- y: 0,
5349
- skipRelease: false,
5350
- duration: 0
5351
- };
5352
- const { button, x, y, skipRelease, duration } = { ...defaultOptions, ...options };
5353
- if (typeof x !== "number" || typeof y !== "number" || !Number.isInteger(x) || !Number.isInteger(y)) {
5354
- throw new TypeError("Coordinates must be integers");
5355
- }
5356
- if (!buttonValue.includes(button)) {
5357
- throw new Error("Button type not supported.");
5358
- }
5359
- const browser = getBrowserObject12(element);
5360
- if (x || y) {
5361
- const { width, height } = await browser.getElementRect(element.elementId);
5362
- if (x && x < -Math.floor(width / 2) || x && x > Math.floor(width / 2)) {
5363
- 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(", ")}`);
5364
5568
  }
5365
- if (y && y < -Math.floor(height / 2) || y && y > Math.floor(height / 2)) {
5366
- 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.`);
5367
5578
  }
5368
5579
  }
5369
- const clickNested = async () => {
5370
- await browser.action("pointer", {
5371
- parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5372
- }).move({ origin: element, x, y }).down({ button }).pause(duration).up({ button }).perform(skipRelease);
5373
- };
5374
- try {
5375
- return await clickNested();
5376
- } catch {
5377
- await workaround(element);
5378
- return clickNested();
5379
- }
5380
- }
5381
-
5382
- // src/commands/element/custom$$.ts
5383
- import { ELEMENT_KEY as ELEMENT_KEY9 } from "webdriver";
5384
- import { getBrowserObject as getBrowserObject13 } from "@wdio/utils";
5385
- async function custom$$2(strategyName, ...strategyArguments) {
5386
- const browserObject = getBrowserObject13(this);
5387
- const strategy = browserObject.strategies.get(strategyName);
5388
- if (!strategy) {
5389
- throw Error("No strategy found for " + strategyName);
5390
- }
5391
- if (!this.elementId) {
5392
- throw Error(`Can't call custom$ on element with selector "${this.selector}" because element wasn't found`);
5393
- }
5394
- const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
5395
- let res = await browserObject.execute(strategy, ...strategyArguments, this);
5396
- if (!Array.isArray(res)) {
5397
- res = [res];
5398
- }
5399
- res = res.filter((el) => !!el && typeof el[ELEMENT_KEY9] === "string");
5400
- const elements = res.length ? await getElements.call(this, strategyRef, res) : [];
5401
- return enhanceElementsArray(elements, this, strategyName, "custom$$", strategyArguments);
5402
5580
  }
5403
-
5404
- // src/commands/element/custom$.ts
5405
- import { ELEMENT_KEY as ELEMENT_KEY10 } from "webdriver";
5406
- import { getBrowserObject as getBrowserObject14 } from "@wdio/utils";
5407
- async function custom$2(strategyName, ...strategyArguments) {
5408
- const browserObject = getBrowserObject14(this);
5409
- const strategy = browserObject.strategies.get(strategyName);
5410
- if (!strategy) {
5411
- throw Error("No strategy found for " + strategyName);
5412
- }
5413
- if (!this.elementId) {
5414
- throw Error(`Can't call custom$ on element with selector "${this.selector}" because element wasn't found`);
5415
- }
5416
- const strategyRef = { strategy, strategyName, strategyArguments: [...strategyArguments, this] };
5417
- let res = await browserObject.execute(strategy, ...strategyArguments, this);
5418
- if (Array.isArray(res)) {
5419
- res = res[0];
5581
+ async function elementTap(browser, element, options) {
5582
+ if (browser.isNativeContext) {
5583
+ return await nativeTap(element, browser, options);
5420
5584
  }
5421
- if (res && typeof res[ELEMENT_KEY10] === "string") {
5422
- 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.");
5423
5587
  }
5424
- return await getElement.call(this, strategyRef, new Error("no such element"));
5425
- }
5426
-
5427
- // src/commands/element/doubleClick.ts
5428
- import { getBrowserObject as getBrowserObject15 } from "@wdio/utils";
5429
- async function doubleClick() {
5430
- const browser = getBrowserObject15(this);
5431
- return browser.action("pointer", { parameters: { pointerType: "mouse" } }).move({ origin: this }).down().up().pause(10).down().up().perform();
5588
+ return await webTap(element);
5432
5589
  }
5433
-
5434
- // src/commands/element/dragAndDrop.ts
5435
- import { ELEMENT_KEY as ELEMENT_KEY11 } from "webdriver";
5436
- import { getBrowserObject as getBrowserObject16 } from "@wdio/utils";
5437
- async function dragAndDrop(target, options = {}) {
5438
- const moveToCoordinates = target;
5439
- const moveToElement = target;
5440
- if (
5441
- /**
5442
- * no target was specified
5443
- */
5444
- !target || /**
5445
- * target is not from type element
5446
- */
5447
- target.constructor.name !== "Element" && /**
5448
- * and is also not an object with x and y number parameters
5449
- */
5450
- (typeof moveToCoordinates.x !== "number" || typeof moveToCoordinates.y !== "number")
5451
- ) {
5452
- throw new Error('command dragAndDrop requires an WebdriverIO Element or and object with "x" and "y" variables as first parameter');
5453
- }
5454
- const ACTION_BUTTON = 0;
5455
- const browser = getBrowserObject16(this);
5456
- const defaultOptions = { duration: browser.isMobile ? 250 : 10 };
5457
- const { duration } = { ...defaultOptions, ...options };
5458
- const isMovingToElement = target.constructor.name === "Element";
5459
- const sourceRef = { [ELEMENT_KEY11]: this[ELEMENT_KEY11] };
5460
- const targetRef = { [ELEMENT_KEY11]: moveToElement[ELEMENT_KEY11] };
5461
- const origin = sourceRef;
5462
- const targetOrigin = isMovingToElement ? targetRef : "pointer";
5463
- const targetX = isMovingToElement ? 0 : moveToCoordinates.x;
5464
- const targetY = isMovingToElement ? 0 : moveToCoordinates.y;
5465
- return browser.action("pointer", {
5466
- parameters: { pointerType: browser.isMobile ? "touch" : "mouse" }
5467
- }).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();
5590
+ async function webTap(element) {
5591
+ return element.click();
5468
5592
  }
5469
-
5470
- // src/commands/element/execute.ts
5471
- import { getBrowserObject as getBrowserObject17 } from "@wdio/utils";
5472
- async function execute2(script, ...args) {
5473
- const browser = getBrowserObject17(this);
5474
- return browser.execute(script, this, ...args);
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
+ );
5475
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:
5476
5630
 
5477
- // src/commands/element/executeAsync.ts
5478
- import { getBrowserObject as getBrowserObject18 } from "@wdio/utils";
5479
- async function executeAsync2(script, ...args) {
5480
- const browser = getBrowserObject18(this);
5481
- return browser.executeAsync(script, this, ...args);
5482
- }
5631
+ await elem.tap({
5632
+ direction: 'left' // possible options are: 'up|down|left|right'
5633
+ maxScrolls: 15,
5634
+ scrollableElement: $('#scrollable'),
5635
+ });
5483
5636
 
5484
- // src/commands/element/getAttribute.ts
5485
- function getAttribute(attributeName) {
5486
- return this.getElementAttribute(this.elementId, attributeName);
5487
- }
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:
5488
5642
 
5489
- // src/commands/element/getCSSProperty.ts
5490
- import cssShorthandProps from "css-shorthand-properties";
5491
- import { getBrowserObject as getBrowserObject19 } from "@wdio/utils";
5492
- async function getCSSProperty(cssProperty, pseudoElement) {
5493
- const getCSSProperty2 = cssShorthandProps.isShorthand(cssProperty) ? getShorthandPropertyCSSValue : getPropertyCSSValue;
5494
- const cssValue2 = await getCSSProperty2.call(
5495
- this,
5496
- {
5497
- cssProperty,
5498
- pseudoElement
5643
+ await elem.tap({
5644
+ scrollableElement: $('#scrollable'),
5645
+ });
5646
+
5647
+ `);
5648
+ }
5649
+ throw err2;
5499
5650
  }
5500
- );
5501
- return parseCSS(cssValue2, cssProperty);
5502
- }
5503
- async function getShorthandPropertyCSSValue(options) {
5504
- const { pseudoElement, cssProperty } = options;
5505
- const properties = getShorthandProperties(cssProperty);
5506
- if (pseudoElement) {
5507
- const cssValues2 = await Promise.all(
5508
- properties.map((prop) => getPseudoElementCSSValue(
5509
- this,
5510
- {
5511
- pseudoElement,
5512
- cssProperty: prop
5513
- }
5514
- ))
5515
- );
5516
- return mergeEqualSymmetricalValue(cssValues2);
5517
5651
  }
5518
- const cssValues = await Promise.all(
5519
- properties.map((prop) => this.getElementCSSValue(this.elementId, prop))
5520
- );
5521
- return mergeEqualSymmetricalValue(cssValues);
5522
5652
  }
5523
- async function getPropertyCSSValue(options) {
5524
- const { pseudoElement, cssProperty } = options;
5525
- if (pseudoElement) {
5526
- return await getPseudoElementCSSValue(
5527
- this,
5528
- {
5529
- pseudoElement,
5530
- cssProperty
5531
- }
5532
- );
5653
+ async function screenTap(browser, options) {
5654
+ const { x, y } = options;
5655
+ if (browser.isNativeContext) {
5656
+ return await executeNativeTap(browser, options);
5533
5657
  }
5534
- return await this.getElementCSSValue(this.elementId, cssProperty);
5535
- }
5536
- function getShorthandProperties(cssProperty) {
5537
- return cssShorthandProps.expand(cssProperty);
5538
- }
5539
- function mergeEqualSymmetricalValue(cssValues) {
5540
- let newCssValues = [...cssValues];
5541
- while (newCssValues.length % 2 === 0) {
5542
- const mergedValues = [
5543
- newCssValues.slice(0, newCssValues.length / 2).join(" "),
5544
- newCssValues.slice(newCssValues.length / 2).join(" ")
5545
- ];
5546
- const hasEqualProperties = mergedValues.every((v) => v === mergedValues[0]);
5547
- if (!hasEqualProperties) {
5548
- break;
5658
+ return await browser.action(
5659
+ "pointer",
5660
+ {
5661
+ parameters: { pointerType: "touch" }
5549
5662
  }
5550
- newCssValues = newCssValues.slice(0, newCssValues.length / 2);
5551
- }
5552
- return newCssValues.join(" ");
5553
- }
5554
- async function getPseudoElementCSSValue(elem, options) {
5555
- const browser = getBrowserObject19(elem);
5556
- const { cssProperty, pseudoElement } = options;
5557
- const cssValue2 = await browser.execute(
5558
- (elem2, pseudoElement2, cssProperty2) => window.getComputedStyle(elem2, pseudoElement2)[cssProperty2],
5559
- elem,
5560
- pseudoElement,
5561
- cssProperty
5562
- );
5563
- return cssValue2;
5663
+ ).move({ x, y }).down({ button: 0 }).pause(10).up({ button: 0 }).perform();
5564
5664
  }
5565
5665
 
5566
- // src/commands/element/getComputedRole.ts
5567
- function getComputedRole() {
5568
- return this.getElementComputedRole(this.elementId);
5569
- }
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
+ });
5570
5726
 
5571
- // src/commands/element/getComputedLabel.ts
5572
- function getComputedLabel() {
5573
- return this.getElementComputedLabel(this.elementId);
5574
- }
5727
+ // src/commands/element/$$.ts
5728
+ var $$2 = $$;
5575
5729
 
5576
- // src/commands/element/getElement.ts
5577
- async function getElement2() {
5578
- 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());
5579
5742
  }
5580
5743
 
5581
- // src/commands/element/getHTML.ts
5582
- import { ELEMENT_KEY as ELEMENT_KEY12 } from "webdriver";
5583
- import { prettify as prettifyFn } from "htmlfy";
5584
- import { getBrowserObject as getBrowserObject20 } from "@wdio/utils";
5744
+ // src/commands/element/clearValue.ts
5745
+ function clearValue() {
5746
+ return this.elementClear(this.elementId);
5747
+ }
5585
5748
 
5586
- // src/shadowRoot.ts
5749
+ // src/commands/element/click.ts
5587
5750
  import logger19 from "@wdio/logger";
5588
- import customElementWrapper from "./scripts/customElement.js";
5589
- var shadowRootManager = /* @__PURE__ */ new Map();
5590
- var log19 = logger19("webdriverio:ShadowRootManager");
5591
- function getShadowRootManager(browser) {
5592
- const existingShadowRootManager = shadowRootManager.get(browser);
5593
- if (existingShadowRootManager) {
5594
- return existingShadowRootManager;
5595
- }
5596
- const newContext = new ShadowRootManager(browser);
5597
- shadowRootManager.set(browser, newContext);
5598
- return newContext;
5599
- }
5600
- var ShadowRootManager = class {
5601
- #browser;
5602
- #initialize;
5603
- #shadowRoots = /* @__PURE__ */ new Map();
5604
- #documentElement;
5605
- #frameDepth = 0;
5606
- constructor(browser) {
5607
- this.#browser = browser;
5608
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
5609
- this.#initialize = Promise.resolve(true);
5610
- return;
5611
- }
5612
- this.#initialize = this.#browser.sessionSubscribe({
5613
- events: ["log.entryAdded", "browsingContext.navigationStarted"]
5614
- }).then(() => true, () => false);
5615
- this.#browser.on("log.entryAdded", this.handleLogEntry.bind(this));
5616
- this.#browser.on("result", this.#commandResultHandler.bind(this));
5617
- this.#browser.on("bidiCommand", this.#handleBidiCommand.bind(this));
5618
- browser.scriptAddPreloadScript({
5619
- functionDeclaration: customElementWrapper.toString()
5620
- });
5621
- }
5622
- async initialize() {
5623
- return this.#initialize;
5624
- }
5625
- /**
5626
- * keep track of navigation events and remove shadow roots when they are no longer needed
5627
- */
5628
- #handleBidiCommand(command) {
5629
- if (command.method !== "browsingContext.navigate") {
5630
- 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");
5631
5757
  }
5632
- const params = command.params;
5633
- this.#shadowRoots.delete(params.context);
5758
+ return actionClick(this, options);
5634
5759
  }
5635
- /**
5636
- * keep track of frame depth
5637
- */
5638
- #commandResultHandler(result) {
5639
- const noResultError = typeof result.result === "object" && result.result && "error" in result.result && !result.result.error;
5640
- if (result.command === "switchToFrame" && noResultError) {
5641
- 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);
5642
5772
  }
5643
- if (result.command === "switchToParentFrame" && noResultError) {
5644
- this.#frameDepth = Math.max(0, this.#frameDepth - 1);
5773
+ if (!err.message.includes("element click intercepted")) {
5774
+ throw err;
5645
5775
  }
5776
+ await workaround(element);
5777
+ return element.elementClick(element.elementId);
5646
5778
  }
5647
- /**
5648
- * check if we are within a frame
5649
- * @returns {boolean} true if we are within a frame
5650
- */
5651
- isWithinFrame() {
5652
- 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");
5653
5791
  }
5654
- /**
5655
- * capture shadow root elements propagated through console.debug
5656
- */
5657
- handleLogEntry(logEntry) {
5658
- const args = "args" in logEntry && logEntry.level === "debug" ? logEntry.args : void 0;
5659
- if (!args || args[0].type !== "string" || args[0].value !== "[WDIO]" || args[1].type !== "string") {
5660
- return;
5661
- }
5662
- if (!logEntry.source.context) {
5663
- return;
5664
- }
5665
- const eventType = args[1].value;
5666
- if (eventType === "newShadowRoot" && args[2].type === "node" && args[3].type === "node") {
5667
- const [
5668
- /* [WDIO] */
5669
- ,
5670
- /* newShadowRoot */
5671
- ,
5672
- shadowElem,
5673
- rootElem,
5674
- isDocument,
5675
- documentElement
5676
- ] = args;
5677
- if (!this.#shadowRoots.has(logEntry.source.context)) {
5678
- if (!rootElem.sharedId) {
5679
- throw new Error(`Expected "sharedId" parameter from object ${rootElem}`);
5680
- }
5681
- this.#shadowRoots.set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
5682
- } else if (isDocument.type === "boolean" && isDocument.value) {
5683
- if (!rootElem.sharedId) {
5684
- throw new Error(`Expected "sharedId" parameter from object ${rootElem}`);
5685
- }
5686
- const tree2 = this.#shadowRoots.get(logEntry.source.context);
5687
- if (tree2?.element !== rootElem.sharedId) {
5688
- this.#shadowRoots.set(logEntry.source.context, new ShadowRootTree(rootElem.sharedId));
5689
- }
5690
- }
5691
- this.#documentElement = documentElement;
5692
- const tree = this.#shadowRoots.get(logEntry.source.context);
5693
- if (!tree) {
5694
- throw new Error(`Couldn't find tree for context id ${logEntry.source.context}`);
5695
- }
5696
- if (
5697
- // we expect an element id
5698
- !shadowElem.sharedId || // we expect the element to have a shadow root
5699
- !shadowElem.value?.shadowRoot?.sharedId || // we expect the shadow root to have a proper type
5700
- shadowElem.value.shadowRoot.value?.nodeType !== 11
5701
- ) {
5702
- return log19.warn(`Expected element with shadow root but found <${shadowElem.value?.localName} />`);
5703
- }
5704
- log19.info(`Registered new shadow root for element <${shadowElem.value.localName} /> with id ${shadowElem.value.shadowRoot.sharedId}`);
5705
- const newTree = new ShadowRootTree(
5706
- shadowElem.sharedId,
5707
- shadowElem.value.shadowRoot.sharedId,
5708
- shadowElem.value.shadowRoot.value.mode
5709
- );
5710
- if (rootElem.sharedId) {
5711
- tree.addShadowElement(rootElem.sharedId, newTree);
5712
- } else {
5713
- tree.addShadowElement(newTree);
5714
- }
5715
- return;
5716
- }
5717
- if (eventType === "removeShadowRoot" && args[2].type === "node" && args[2].sharedId) {
5718
- const tree = this.#shadowRoots.get(logEntry.source.context);
5719
- if (!tree) {
5720
- return;
5721
- }
5722
- return tree.remove(args[2].sharedId);
5723
- }
5724
- throw new Error(`Invalid parameters for "${eventType}" event: ${args.join(", ")}`);
5792
+ if (!buttonValue.includes(button)) {
5793
+ throw new Error("Button type not supported.");
5725
5794
  }
5726
- getShadowElementsByContextId(contextId, scope) {
5727
- let tree = this.#shadowRoots.get(contextId);
5728
- if (!tree) {
5729
- 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");
5730
5800
  }
5731
- let documentElement;
5732
- if (scope) {
5733
- const subTree = tree.find(scope);
5734
- if (subTree) {
5735
- tree = subTree;
5736
- }
5737
- } else {
5738
- 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");
5739
5803
  }
5740
- const elements = tree.getAllLookupScopes();
5741
- return [
5742
- ...documentElement ? [documentElement] : [],
5743
- ...new Set(elements).values()
5744
- ];
5745
5804
  }
5746
- getShadowElementPairsByContextId(contextId, scope) {
5747
- let tree = this.#shadowRoots.get(contextId);
5748
- if (!tree) {
5749
- return [];
5750
- }
5751
- if (scope) {
5752
- const subTree = tree.find(scope);
5753
- if (subTree) {
5754
- tree = subTree;
5755
- }
5756
- }
5757
- 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();
5758
5815
  }
5759
- getShadowRootModeById(contextId, element) {
5760
- const tree = this.#shadowRoots.get(contextId);
5761
- if (!tree) {
5762
- return;
5763
- }
5764
- const shadowTree = tree.find(element);
5765
- if (!shadowTree) {
5766
- return;
5767
- }
5768
- 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);
5769
5826
  }
5770
- deleteShadowRoot(element, contextId) {
5771
- const tree = this.#shadowRoots.get(contextId);
5772
- if (!tree) {
5773
- return;
5774
- }
5775
- 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`);
5776
5829
  }
5777
- };
5778
- var ShadowRootTree = class _ShadowRootTree {
5779
- element;
5780
- shadowRoot;
5781
- mode;
5782
- children = /* @__PURE__ */ new Set();
5783
- constructor(element, shadowRoot, mode) {
5784
- this.element = element;
5785
- this.shadowRoot = shadowRoot;
5786
- 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];
5787
5834
  }
5788
- addShadowElement(...args) {
5789
- const [scope, treeArg] = args;
5790
- if (!scope && !treeArg) {
5791
- throw new Error('Method "addShadowElement" expects at least 2 arguments');
5792
- }
5793
- if (scope instanceof _ShadowRootTree) {
5794
- this.children.add(scope);
5795
- return;
5796
- }
5797
- if (typeof scope === "string" && treeArg instanceof _ShadowRootTree) {
5798
- const tree = this.find(scope) || this.findByShadowId(scope);
5799
- if (!tree) {
5800
- throw new Error(`Couldn't find element with id ${scope}`);
5801
- }
5802
- tree.addShadowElement(treeArg);
5803
- return;
5804
- }
5805
- 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);
5848
+ }
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 = await target;
5876
+ if (
5877
+ /**
5878
+ * no target was specified
5879
+ */
5880
+ !moveToElement || /**
5881
+ * target is not from type element
5882
+ */
5883
+ moveToElement.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');
5806
5889
  }
5807
- find(element) {
5808
- if (this.element === element) {
5809
- return this;
5810
- }
5811
- for (const child of this.children) {
5812
- const elem = child.find(element);
5813
- if (elem) {
5814
- return elem;
5815
- }
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 = moveToElement.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
5816
5935
  }
5817
- return void 0;
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);
5818
5953
  }
5819
- findByShadowId(shadowRoot) {
5820
- if (this.shadowRoot === shadowRoot) {
5821
- return this;
5822
- }
5823
- for (const child of this.children) {
5824
- const elem = child.findByShadowId(shadowRoot);
5825
- if (elem) {
5826
- return elem;
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
5827
5967
  }
5828
- }
5829
- return void 0;
5830
- }
5831
- getAllLookupScopes() {
5832
- return [
5833
- this.shadowRoot ?? this.element,
5834
- ...Array.from(this.children).map((tree) => tree.getAllLookupScopes())
5835
- ].flat();
5836
- }
5837
- flat() {
5838
- return [this, ...Array.from(this.children).map((tree) => tree.flat())].flat();
5968
+ );
5839
5969
  }
5840
- remove(element) {
5841
- const childArray = Array.from(this.children);
5842
- for (let i = childArray.length - 1; i >= 0; i--) {
5843
- if (childArray[i].element === element) {
5844
- return this.children.delete(childArray[i]);
5845
- }
5846
- const wasFound = childArray[i].remove(element);
5847
- if (wasFound) {
5848
- return true;
5849
- }
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;
5850
5985
  }
5851
- return false;
5986
+ newCssValues = newCssValues.slice(0, newCssValues.length / 2);
5852
5987
  }
5853
- };
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
+ }
5854
6016
 
5855
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";
5856
6021
  import getHTMLScript from "./scripts/getHTML.js";
5857
6022
  import getHTMLShadowScript from "./scripts/getHTMLShadow.js";
5858
6023
  var SHADOW_ID_ATTR_NAME = "data-wdio-shadow-id";
@@ -5888,10 +6053,10 @@ async function getHTML(options = {}) {
5888
6053
  );
5889
6054
  }
5890
6055
  const { load } = await import("cheerio");
5891
- const shadowRootManager2 = getShadowRootManager(browser);
5892
- const contextManager2 = getContextManager(browser);
5893
- const context = await contextManager2.getCurrentContext();
5894
- 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);
5895
6060
  const elementsWithShadowRootAndIdVerified = (await Promise.all(
5896
6061
  shadowRootElementPairs.map(([elemId, elem]) => browser.execute((elem2) => elem2.tagName, { [ELEMENT_KEY12]: elemId }).then(
5897
6062
  () => [elemId, elem],
@@ -5912,7 +6077,7 @@ async function getHTML(options = {}) {
5912
6077
  populateHTML($3, shadowElementHTML.map(({ id, ...props }) => ({
5913
6078
  ...props,
5914
6079
  id,
5915
- mode: shadowRootManager2.getShadowRootModeById(context, id) || "open"
6080
+ mode: shadowRootManager.getShadowRootModeById(context, id) || "open"
5916
6081
  })));
5917
6082
  return sanitizeHTML($3, { removeCommentNodes, prettify, excludeElements });
5918
6083
  }
@@ -7315,10 +7480,10 @@ function transformClassicToBidiSelector(using, value) {
7315
7480
  }
7316
7481
  async function findDeepElement(selector) {
7317
7482
  const browser = getBrowserObject35(this);
7318
- const shadowRootManager2 = getShadowRootManager(browser);
7319
- const contextManager2 = getContextManager(browser);
7320
- const context = await contextManager2.getCurrentContext();
7321
- 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(
7322
7487
  context,
7323
7488
  this.elementId
7324
7489
  );
@@ -7353,10 +7518,10 @@ async function findDeepElement(selector) {
7353
7518
  }
7354
7519
  async function findDeepElements(selector) {
7355
7520
  const browser = getBrowserObject35(this);
7356
- const shadowRootManager2 = getShadowRootManager(browser);
7357
- const contextManager2 = getContextManager(browser);
7358
- const context = await contextManager2.getCurrentContext();
7359
- 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(
7360
7525
  context,
7361
7526
  this.elementId
7362
7527
  );
@@ -7388,8 +7553,8 @@ async function findDeepElements(selector) {
7388
7553
  }
7389
7554
  async function findElement(selector) {
7390
7555
  const browserObject = getBrowserObject35(this);
7391
- const shadowRootManager2 = getShadowRootManager(browserObject);
7392
- 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()) {
7393
7558
  return findDeepElement.call(this, selector);
7394
7559
  }
7395
7560
  if (typeof selector === "string" && selector.startsWith(DEEP_SELECTOR)) {
@@ -7947,103 +8112,6 @@ async function getProtocolDriver(options) {
7947
8112
  return { Driver, options };
7948
8113
  }
7949
8114
 
7950
- // src/dialog.ts
7951
- var dialogManager = /* @__PURE__ */ new Map();
7952
- function getDialogManager(browser) {
7953
- const existingDialogManager = dialogManager.get(browser);
7954
- if (existingDialogManager) {
7955
- return existingDialogManager;
7956
- }
7957
- const newContext = new DialogManager(browser);
7958
- dialogManager.set(browser, newContext);
7959
- return newContext;
7960
- }
7961
- var DialogManager = class {
7962
- #browser;
7963
- #initialize;
7964
- #autoHandleDialog = true;
7965
- constructor(browser) {
7966
- this.#browser = browser;
7967
- if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
7968
- this.#initialize = Promise.resolve(true);
7969
- return;
7970
- }
7971
- this.#initialize = this.#browser.sessionSubscribe({
7972
- events: ["browsingContext.userPromptOpened"]
7973
- }).then(() => true, () => false);
7974
- this.#browser.on("_dialogListenerRegistered", () => this.#switchListenerFlag(false));
7975
- this.#browser.on("_dialogListenerRemoved", () => this.#switchListenerFlag(true));
7976
- this.#browser.on("browsingContext.userPromptOpened", this.#handleUserPrompt.bind(this));
7977
- }
7978
- async initialize() {
7979
- return this.#initialize;
7980
- }
7981
- /**
7982
- * capture shadow root elements propagated through console.debug
7983
- */
7984
- async #handleUserPrompt(log25) {
7985
- if (this.#autoHandleDialog) {
7986
- return this.#browser.browsingContextHandleUserPrompt({
7987
- accept: false,
7988
- context: log25.context
7989
- });
7990
- }
7991
- const dialog = new Dialog(log25, this.#browser);
7992
- this.#browser.emit("dialog", dialog);
7993
- }
7994
- /**
7995
- * Is called when a new dialog listener is registered with the `dialog` name.
7996
- * In these cases we set a flag to the `#listener` map to indicate that we
7997
- * are listening to dialog events for this page in this context.
7998
- */
7999
- #switchListenerFlag(value) {
8000
- this.#autoHandleDialog = value;
8001
- }
8002
- };
8003
- var Dialog = class {
8004
- #browser;
8005
- #context;
8006
- #message;
8007
- #defaultValue;
8008
- #type;
8009
- constructor(event, browser) {
8010
- this.#message = event.message;
8011
- this.#defaultValue = event.defaultValue;
8012
- this.#type = event.type;
8013
- this.#context = event.context;
8014
- this.#browser = browser;
8015
- }
8016
- message() {
8017
- return this.#message;
8018
- }
8019
- defaultValue() {
8020
- return this.#defaultValue;
8021
- }
8022
- type() {
8023
- return this.#type;
8024
- }
8025
- /**
8026
- * Returns when the dialog has been accepted.
8027
- *
8028
- * @alias dialog.accept
8029
- * @param {string=} promptText A text to enter into prompt. Does not cause any effects if the dialog's type is not prompt.
8030
- * @returns {Promise<void>}
8031
- */
8032
- async accept(userText) {
8033
- await this.#browser.browsingContextHandleUserPrompt({
8034
- accept: true,
8035
- context: this.#context,
8036
- userText
8037
- });
8038
- }
8039
- async dismiss() {
8040
- await this.#browser.browsingContextHandleUserPrompt({
8041
- accept: false,
8042
- context: this.#context
8043
- });
8044
- }
8045
- };
8046
-
8047
8115
  // src/index.ts
8048
8116
  var Key2 = Key;
8049
8117
  var SevereServiceError2 = SevereServiceError;
@@ -8069,13 +8137,7 @@ var remote = async function(params, remoteModifier) {
8069
8137
  instance.overwriteCommand = (name, fn, attachToElement) => origOverwriteCommand(name, fn, attachToElement);
8070
8138
  }
8071
8139
  instance.addLocatorStrategy = addLocatorStrategyHandler(instance);
8072
- await Promise.all([
8073
- getPolyfillManager(instance).initialize(),
8074
- getShadowRootManager(instance).initialize(),
8075
- getNetworkManager(instance).initialize(),
8076
- getDialogManager(instance).initialize(),
8077
- getContextManager(instance).initialize()
8078
- ]);
8140
+ await registerSessionManager(instance);
8079
8141
  return instance;
8080
8142
  };
8081
8143
  var attach = async function(attachOptions) {
@@ -8095,13 +8157,7 @@ var attach = async function(attachOptions) {
8095
8157
  wrapCommand3
8096
8158
  );
8097
8159
  driver.addLocatorStrategy = addLocatorStrategyHandler(driver);
8098
- await driver._bidiHandler?.connect().then(() => Promise.all([
8099
- getPolyfillManager(driver).initialize(),
8100
- getShadowRootManager(driver).initialize(),
8101
- getNetworkManager(driver).initialize(),
8102
- getDialogManager(driver).initialize(),
8103
- getContextManager(driver).initialize()
8104
- ]));
8160
+ await registerSessionManager(driver);
8105
8161
  return driver;
8106
8162
  };
8107
8163
  var multiremote = async function(params, { automationProtocol } = {}) {