webdriverio 9.2.1 → 9.2.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
@@ -5,7 +5,7 @@ var __export = (target, all) => {
5
5
  };
6
6
 
7
7
  // src/index.ts
8
- import logger21 from "@wdio/logger";
8
+ import logger22 from "@wdio/logger";
9
9
  import WebDriver, { DEFAULTS } from "webdriver";
10
10
  import { validateConfig } from "@wdio/config";
11
11
  import { enableFileLogging, wrapCommand as wrapCommand3 } from "@wdio/utils";
@@ -16,7 +16,7 @@ import clone2 from "lodash.clonedeep";
16
16
  import { webdriverMonad as webdriverMonad2, wrapCommand as wrapCommand2 } from "@wdio/utils";
17
17
 
18
18
  // src/middlewares.ts
19
- import { ELEMENT_KEY as ELEMENT_KEY18 } from "webdriver";
19
+ import { ELEMENT_KEY as ELEMENT_KEY19 } from "webdriver";
20
20
  import { getBrowserObject as getBrowserObject31 } from "@wdio/utils";
21
21
 
22
22
  // src/utils/implicitWait.ts
@@ -78,9 +78,9 @@ import { URL as URL2 } from "node:url";
78
78
  import cssValue from "css-value";
79
79
  import rgb2hex from "rgb2hex";
80
80
  import GraphemeSplitter from "grapheme-splitter";
81
- import logger20 from "@wdio/logger";
81
+ import logger21 from "@wdio/logger";
82
82
  import isPlainObject from "is-plain-obj";
83
- import { ELEMENT_KEY as ELEMENT_KEY17 } from "webdriver";
83
+ import { ELEMENT_KEY as ELEMENT_KEY18 } from "webdriver";
84
84
  import { UNICODE_CHARACTERS as UNICODE_CHARACTERS2, asyncIterators, getBrowserObject as getBrowserObject30 } from "@wdio/utils";
85
85
 
86
86
  // src/commands/browser.ts
@@ -2974,9 +2974,7 @@ var ReferenceValue = class {
2974
2974
  };
2975
2975
 
2976
2976
  // src/context.ts
2977
- import logger4 from "@wdio/logger";
2978
2977
  var contextManager = /* @__PURE__ */ new Map();
2979
- var log4 = logger4("webdriverio:ContextManager");
2980
2978
  function getContextManager(browser) {
2981
2979
  const existingContextManager = contextManager.get(browser);
2982
2980
  if (existingContextManager) {
@@ -2988,53 +2986,47 @@ function getContextManager(browser) {
2988
2986
  }
2989
2987
  var ContextManager = class {
2990
2988
  #browser;
2991
- #initialize;
2992
2989
  #currentContext;
2993
2990
  constructor(browser) {
2994
2991
  this.#browser = browser;
2995
- if (!browser.isBidi || process.env.VITEST_WORKER_ID || browser.options?.automationProtocol !== "webdriver") {
2996
- this.#initialize = Promise.resolve(true);
2992
+ if (process.env.WDIO_UNIT_TESTS) {
2997
2993
  return;
2998
2994
  }
2999
- this.#initialize = this.#browser.sessionSubscribe({
3000
- events: ["browsingContext.navigationStarted"]
3001
- }).then(() => true, () => false);
3002
- this.#browser.on("browsingContext.navigationStarted", this.#handleNavigationStarted.bind(this));
3003
- }
3004
- async initialize() {
3005
- return this.#initialize;
2995
+ this.#browser.on("command", (event) => {
2996
+ if (event.command === "switchToWindow") {
2997
+ this.setCurrentContext(event.body.handle);
2998
+ }
2999
+ if (event.command === "switchToParentFrame") {
3000
+ this.#currentContext = void 0;
3001
+ }
3002
+ });
3006
3003
  }
3007
3004
  /**
3008
- * We use this handler to observe the current context which is used to navigate to a certain url.
3009
- * This is most likely the context that the user is using. However if a frame was loaded on the page
3010
- * then this handler is triggered with the context of the frame. To find out which context we are in
3011
- * we use the `getWindowHandle` method to validate our assumption before setting the `#currentContext`
3012
- * value.
3013
- *
3014
- * @param {local.BrowsingContextNavigationInfo} context browsing context used to navigate
3005
+ * set context at the start of the session
3015
3006
  */
3016
- async #handleNavigationStarted(context) {
3017
- const windowHandle = await this.#browser.getWindowHandle();
3018
- if (context.context === windowHandle && context.url !== "UNKNOWN") {
3019
- log4.info(`Update current context: ${context.context}`);
3020
- this.#currentContext = context.context;
3007
+ async initialize() {
3008
+ if (process.env.WDIO_UNIT_TESTS) {
3009
+ return "fake-context";
3021
3010
  }
3011
+ const windowHandle = await this.#browser.getWindowHandle();
3012
+ this.#currentContext = windowHandle;
3013
+ return windowHandle;
3022
3014
  }
3023
3015
  setCurrentContext(context) {
3024
3016
  this.#currentContext = context;
3025
3017
  }
3026
3018
  async getCurrentContext() {
3027
3019
  if (!this.#currentContext) {
3028
- this.#currentContext = await this.#browser.getWindowHandle();
3020
+ return this.initialize();
3029
3021
  }
3030
3022
  return this.#currentContext;
3031
3023
  }
3032
3024
  };
3033
3025
 
3034
3026
  // src/polyfill.ts
3035
- import logger5 from "@wdio/logger";
3027
+ import logger4 from "@wdio/logger";
3036
3028
  var polyfillManager = /* @__PURE__ */ new Map();
3037
- var log5 = logger5("webdriverio:PolyfillManager");
3029
+ var log4 = logger4("webdriverio:PolyfillManager");
3038
3030
  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;";
3039
3031
  function getPolyfillManager(browser) {
3040
3032
  const existingPolyfillManager = polyfillManager.get(browser);
@@ -3048,7 +3040,7 @@ function getPolyfillManager(browser) {
3048
3040
  var PolyfillManager = class {
3049
3041
  #initialize;
3050
3042
  constructor(browser) {
3051
- if (!browser.isBidi || process.env.VITEST_WORKER_ID || browser.options?.automationProtocol !== "webdriver") {
3043
+ if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
3052
3044
  this.#initialize = Promise.resolve(true);
3053
3045
  return;
3054
3046
  }
@@ -3060,7 +3052,7 @@ var PolyfillManager = class {
3060
3052
  browser.addInitScript(polyfill, NAME_POLYFILL),
3061
3053
  browser.execute(polyfill, NAME_POLYFILL)
3062
3054
  ]).then(() => {
3063
- log5.info("polyfill script added");
3055
+ log4.info("polyfill script added");
3064
3056
  return true;
3065
3057
  }, () => false);
3066
3058
  }
@@ -3145,8 +3137,8 @@ async function executeAsync(script, ...args) {
3145
3137
  }
3146
3138
 
3147
3139
  // src/commands/browser/getCookies.ts
3148
- import logger6 from "@wdio/logger";
3149
- var log6 = logger6("webdriverio");
3140
+ import logger5 from "@wdio/logger";
3141
+ var log5 = logger5("webdriverio");
3150
3142
  async function getCookies(filter) {
3151
3143
  const usesMultipleFilter = Array.isArray(filter) && filter.length > 1;
3152
3144
  if (!this.isBidi || usesMultipleFilter) {
@@ -3165,7 +3157,7 @@ async function getCookiesClassic(names) {
3165
3157
  }
3166
3158
  const usesMultipleFilter = Array.isArray(names) && names.length > 1;
3167
3159
  if (usesMultipleFilter) {
3168
- log6.warn(
3160
+ log5.warn(
3169
3161
  "Passing a string array as filter for `getCookies` is deprecated and its support will be removed in an upcoming version of WebdriverIO!"
3170
3162
  );
3171
3163
  const allCookies2 = await this.getAllCookies();
@@ -3184,7 +3176,7 @@ function getCookieFilter(names) {
3184
3176
  }
3185
3177
  return (Array.isArray(names) ? names : [names]).map((filter) => {
3186
3178
  if (typeof filter === "string") {
3187
- log6.warn("Passing string values into `getCookie` is deprecated and its support will be removed in an upcoming version of WebdriverIO!");
3179
+ log5.warn("Passing string values into `getCookie` is deprecated and its support will be removed in an upcoming version of WebdriverIO!");
3188
3180
  return { name: filter };
3189
3181
  }
3190
3182
  return filter;
@@ -3192,9 +3184,9 @@ function getCookieFilter(names) {
3192
3184
  }
3193
3185
 
3194
3186
  // src/commands/browser/getPuppeteer.ts
3195
- import logger7 from "@wdio/logger";
3187
+ import logger6 from "@wdio/logger";
3196
3188
  import { userImport } from "@wdio/utils";
3197
- var log7 = logger7("webdriverio");
3189
+ var log6 = logger6("webdriverio");
3198
3190
  var DEBUG_PIPE_FLAG = "remote-debugging-pipe";
3199
3191
  async function getPuppeteer() {
3200
3192
  const puppeteer = await userImport("puppeteer-core");
@@ -3204,7 +3196,7 @@ async function getPuppeteer() {
3204
3196
  );
3205
3197
  }
3206
3198
  if (this.puppeteer?.connected) {
3207
- log7.debug("Reusing existing puppeteer session");
3199
+ log6.debug("Reusing existing puppeteer session");
3208
3200
  return this.puppeteer;
3209
3201
  }
3210
3202
  const { headers } = this.options;
@@ -3315,7 +3307,7 @@ import { getBrowserObject as getBrowserObject5 } from "@wdio/utils";
3315
3307
 
3316
3308
  // src/utils/interception/index.ts
3317
3309
  import EventEmitter2 from "node:events";
3318
- import logger8 from "@wdio/logger";
3310
+ import logger7 from "@wdio/logger";
3319
3311
  import { URLPattern } from "urlpattern-polyfill";
3320
3312
 
3321
3313
  // src/utils/Timer.ts
@@ -3372,10 +3364,10 @@ var Timer = class {
3372
3364
  }
3373
3365
  _tick() {
3374
3366
  const result = this._fn();
3367
+ if (!result) {
3368
+ return this._checkCondition(new Error(TIMEOUT_ERROR));
3369
+ }
3375
3370
  if (typeof result.then !== "function") {
3376
- if (!result) {
3377
- return this._checkCondition(new Error(TIMEOUT_ERROR));
3378
- }
3379
3371
  return this._checkCondition(void 0, result);
3380
3372
  }
3381
3373
  result.then(
@@ -3481,7 +3473,7 @@ function getPatternParam(pattern, key) {
3481
3473
  }
3482
3474
 
3483
3475
  // src/utils/interception/index.ts
3484
- var log8 = logger8("WebDriverInterception");
3476
+ var log7 = logger7("WebDriverInterception");
3485
3477
  var hasSubscribedToEvents = false;
3486
3478
  var WebDriverInterception = class _WebDriverInterception {
3487
3479
  #pattern;
@@ -3510,7 +3502,7 @@ var WebDriverInterception = class _WebDriverInterception {
3510
3502
  "network.responseStarted"
3511
3503
  ]
3512
3504
  });
3513
- log8.info("subscribed to network events");
3505
+ log7.info("subscribed to network events");
3514
3506
  hasSubscribedToEvents = true;
3515
3507
  }
3516
3508
  const interception = await browser.networkAddIntercept({
@@ -3672,7 +3664,7 @@ var WebDriverInterception = class _WebDriverInterception {
3672
3664
  this.#respondOverwrites = [];
3673
3665
  this.#restored = true;
3674
3666
  const handle = await this.#browser.getWindowHandle();
3675
- log8.trace(`Restoring mock for ${handle}`);
3667
+ log7.trace(`Restoring mock for ${handle}`);
3676
3668
  SESSION_MOCKS[handle].delete(this);
3677
3669
  if (this.#mockId) {
3678
3670
  await this.#browser.networkRemoveIntercept({ intercept: this.#mockId });
@@ -3812,11 +3804,11 @@ async function mock(url6, filterOptions) {
3812
3804
  }
3813
3805
 
3814
3806
  // src/commands/browser/mockClearAll.ts
3815
- import logger9 from "@wdio/logger";
3816
- var log9 = logger9("webdriverio:mockClearAll");
3807
+ import logger8 from "@wdio/logger";
3808
+ var log8 = logger8("webdriverio:mockClearAll");
3817
3809
  async function mockClearAll() {
3818
3810
  for (const [handle, mocks] of Object.entries(SESSION_MOCKS)) {
3819
- log9.trace(`Clearing mocks for ${handle}`);
3811
+ log8.trace(`Clearing mocks for ${handle}`);
3820
3812
  for (const mock2 of mocks) {
3821
3813
  mock2.clear();
3822
3814
  }
@@ -3824,11 +3816,11 @@ async function mockClearAll() {
3824
3816
  }
3825
3817
 
3826
3818
  // src/commands/browser/mockRestoreAll.ts
3827
- import logger10 from "@wdio/logger";
3828
- var log10 = logger10("webdriverio:mockRestoreAll");
3819
+ import logger9 from "@wdio/logger";
3820
+ var log9 = logger9("webdriverio:mockRestoreAll");
3829
3821
  async function mockRestoreAll() {
3830
3822
  for (const [handle, mocks] of Object.entries(SESSION_MOCKS)) {
3831
- log10.trace(`Clearing mocks for ${handle}`);
3823
+ log9.trace(`Clearing mocks for ${handle}`);
3832
3824
  for (const mock2 of mocks) {
3833
3825
  await mock2.restore();
3834
3826
  }
@@ -3838,18 +3830,26 @@ async function mockRestoreAll() {
3838
3830
  // src/commands/browser/newWindow.ts
3839
3831
  import { sleep } from "@wdio/utils";
3840
3832
  import newWindowHelper from "./scripts/newWindow.js";
3833
+ import logger10 from "@wdio/logger";
3834
+ var log10 = logger10("webdriverio:newWindow");
3841
3835
  var WAIT_FOR_NEW_HANDLE_TIMEOUT = 3e3;
3842
- async function newWindow(url6, { windowName = "", windowFeatures = "" } = {}) {
3836
+ async function newWindow(url6, { type = "window", windowName = "", windowFeatures = "" } = {}) {
3843
3837
  if (typeof url6 !== "string") {
3844
3838
  throw new Error("number or type of arguments don't agree with newWindow command");
3845
3839
  }
3840
+ if (!["tab", "window"].includes(type)) {
3841
+ throw new Error(`Invalid type '${type}' provided to newWindow command. Use either 'tab' or 'window'`);
3842
+ }
3843
+ if (windowName || windowFeatures) {
3844
+ log10.warn('The "windowName" and "windowFeatures" options are deprecated and only supported in WebDriver Classic sessions.');
3845
+ }
3846
3846
  if (this.isMobile) {
3847
3847
  throw new Error("newWindow command is not supported on mobile platforms");
3848
3848
  }
3849
3849
  const tabsBefore = await this.getWindowHandles();
3850
3850
  if (this.isBidi) {
3851
3851
  const contextManager2 = getContextManager(this);
3852
- const { context } = await this.browsingContextCreate({ type: "window" });
3852
+ const { context } = await this.browsingContextCreate({ type });
3853
3853
  contextManager2.setCurrentContext(context);
3854
3854
  await this.browsingContextNavigate({ context, url: url6 });
3855
3855
  } else {
@@ -3869,7 +3869,7 @@ async function newWindow(url6, { windowName = "", windowFeatures = "" } = {}) {
3869
3869
  throw new Error("No window handle was found to switch to");
3870
3870
  }
3871
3871
  await this.switchToWindow(newTab);
3872
- return newTab;
3872
+ return { handle: newTab, type };
3873
3873
  }
3874
3874
 
3875
3875
  // src/commands/browser/pause.ts
@@ -4135,6 +4135,7 @@ async function switchWindow(matcher) {
4135
4135
  if (typeof matcher !== "string" && !(matcher instanceof RegExp)) {
4136
4136
  throw new Error('Unsupported parameter for switchWindow, required is "string" or an RegExp');
4137
4137
  }
4138
+ const currentWindow = await this.getWindowHandle();
4138
4139
  const tabs = await this.getWindowHandles();
4139
4140
  const matchesTarget = (target) => {
4140
4141
  if (typeof matcher === "string") {
@@ -4162,60 +4163,150 @@ async function switchWindow(matcher) {
4162
4163
  return tab;
4163
4164
  }
4164
4165
  }
4166
+ await this.switchToWindow(currentWindow);
4165
4167
  throw new Error(`No window found with title, url or name matching "${matcher}"`);
4166
4168
  }
4167
4169
 
4168
4170
  // src/commands/browser/switchFrame.ts
4171
+ import logger13 from "@wdio/logger";
4172
+ import { ELEMENT_KEY as ELEMENT_KEY8 } from "webdriver";
4173
+ var log13 = logger13("webdriverio:switchFrame");
4169
4174
  async function switchFrame(context) {
4175
+ if (!this.isBidi) {
4176
+ if (typeof context === "function") {
4177
+ throw new Error("Cannot use a function to fetch a context in WebDriver Classic");
4178
+ }
4179
+ if (typeof context === "string") {
4180
+ throw new Error("Cannot use a string to fetch a context in WebDriver Classic");
4181
+ }
4182
+ return switchToFrame(this, context);
4183
+ }
4170
4184
  if (context === null) {
4171
4185
  const handle = await this.getWindowHandle();
4172
- await switchToFrameHelper(this, handle);
4186
+ switchToFrameHelper(this, handle);
4187
+ await switchToFrame(this, context);
4173
4188
  return handle;
4174
4189
  }
4175
4190
  if (typeof context === "string") {
4176
4191
  const tree = await this.browsingContextGetTree({});
4177
- const urlContext = findContext(context, tree.contexts, byUrl)?.context;
4192
+ let newContextId;
4193
+ const urlContext = findContext(context, tree.contexts, byUrl) || /**
4194
+ * In case the user provides an url without `/` at the end, e.g. `https://example.com`,
4195
+ * the `browsingContextGetTree` command may return a context with the url `https://example.com/`.
4196
+ */
4197
+ findContext(`${context}/`, tree.contexts, byUrl);
4198
+ const urlContextContaining = findContext(context, tree.contexts, byUrlContaining);
4199
+ const contextIdContext = findContext(context, tree.contexts, byContextId);
4178
4200
  if (urlContext) {
4179
- await switchToFrameHelper(this, urlContext);
4180
- return urlContext;
4201
+ log13.info(`Found context by url "${urlContext.url}" with context id "${urlContext.context}"`);
4202
+ newContextId = urlContext.context;
4203
+ } else if (urlContextContaining) {
4204
+ log13.info(`Found context by url containing "${urlContextContaining.url}" with context id "${urlContextContaining.context}"`);
4205
+ newContextId = urlContextContaining.context;
4206
+ } else if (contextIdContext) {
4207
+ log13.info(`Found context by id "${contextIdContext}" with url "${contextIdContext.url}"`);
4208
+ newContextId = contextIdContext.context;
4209
+ }
4210
+ if (!newContextId) {
4211
+ throw new Error(`No frame with url or id "${context}" found!`);
4181
4212
  }
4182
- const urlContextContaining = findContext(context, tree.contexts, byUrlContaining)?.context;
4183
- if (urlContextContaining) {
4184
- await switchToFrameHelper(this, urlContextContaining);
4185
- return urlContextContaining;
4213
+ const sessionContext = getContextManager(this);
4214
+ const currentContext = await sessionContext.getCurrentContext();
4215
+ const allContexts = await getFlatContextTree(this);
4216
+ const allFrames = (await Promise.all(Object.keys(allContexts).map(async (id) => {
4217
+ const { nodes } = await this.browsingContextLocateNodes({
4218
+ locator: { type: "css", value: "iframe, frame" },
4219
+ context: id
4220
+ }).catch(() => ({ nodes: [] }));
4221
+ return Promise.all(nodes.map(async (node) => {
4222
+ const html = `<iframe${Object.entries(node.value?.attributes || {}).reduce((acc, [key, value]) => `${acc} ${key}="${value}"`, " ")}></iframe>`;
4223
+ const args = [{ [ELEMENT_KEY8]: node.sharedId }];
4224
+ const userScript = (iframe) => iframe.contentWindow;
4225
+ const functionDeclaration = new Function(`
4226
+ return (${SCRIPT_PREFIX}${userScript.toString()}${SCRIPT_SUFFIX}).apply(this, arguments);
4227
+ `).toString();
4228
+ const params = {
4229
+ functionDeclaration,
4230
+ awaitPromise: false,
4231
+ arguments: args.map((arg) => LocalValue.getArgument(arg)),
4232
+ target: { context: id }
4233
+ };
4234
+ const result = await this.scriptCallFunction(params).catch((err) => log13.warn(`Failed to identify frame context id: ${err.message}`));
4235
+ if (!result) {
4236
+ return [];
4237
+ }
4238
+ const { context: context2 } = parseScriptResult(params, result);
4239
+ return {
4240
+ /**
4241
+ * the actual frame context we need to switch WebDriver Bidi commands to
4242
+ */
4243
+ context: context2,
4244
+ /**
4245
+ * the element reference of the iframe so we can call `switchToFrame` to
4246
+ * switch context for WebDriver Classic commands
4247
+ */
4248
+ frameElement: { [ELEMENT_KEY8]: node.sharedId },
4249
+ /**
4250
+ * the context id in which the iframe was found
4251
+ */
4252
+ parentContext: id,
4253
+ /**
4254
+ * an HTML representation of the iframe for a good error message in case
4255
+ * we can't find the desired frame from this list
4256
+ */
4257
+ html
4258
+ };
4259
+ }));
4260
+ }))).flat(Infinity);
4261
+ let desiredFrame;
4262
+ let desiredContext = newContextId;
4263
+ const contextQueue = [];
4264
+ log13.info(`Available frames to switch to: ${allFrames.length}, desired context to switch: ${desiredContext}`);
4265
+ while (desiredContext !== currentContext) {
4266
+ desiredFrame = allFrames.find(({ context: context2 }) => context2 === desiredContext);
4267
+ if (!desiredFrame) {
4268
+ break;
4269
+ }
4270
+ log13.info(
4271
+ 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`
4272
+ );
4273
+ contextQueue.unshift(desiredFrame);
4274
+ desiredContext = desiredFrame.parentContext;
4275
+ }
4276
+ if (contextQueue.length === 0) {
4277
+ throw new Error(`Frame with url or context id "${context}" not found, available frames to switch to:
4278
+ - ${allFrames.map(({ html }) => html).join("\n - ")}`);
4186
4279
  }
4187
- const contextIdContext = findContext(context, tree.contexts, byContextId)?.context;
4188
- if (contextIdContext) {
4189
- await switchToFrameHelper(this, contextIdContext);
4190
- return contextIdContext;
4280
+ for (const contextToSwitch of contextQueue) {
4281
+ switchToFrameHelper(this, contextToSwitch.context);
4282
+ await switchToFrame(this, contextToSwitch.frameElement);
4191
4283
  }
4192
- throw new Error(`Frame with url or context id "${context}" not found`);
4284
+ return newContextId;
4193
4285
  }
4194
4286
  if (typeof context === "object" && typeof context.getElement === "function") {
4195
4287
  const element = await context.getElement();
4196
4288
  return switchToFrameUsingElement(this, element);
4197
4289
  }
4198
4290
  if (typeof context === "function") {
4199
- const tree = await this.browsingContextGetTree({});
4200
- const mapContext = (context2) => [
4201
- context2.context,
4202
- ...(context2.children || []).map(mapContext)
4203
- ];
4204
- const sessionContext = getContextManager(this);
4205
- const currentContext = await sessionContext.getCurrentContext();
4206
- const allContexts = tree.contexts.map(mapContext).flat(Infinity).filter((ctx) => ctx !== currentContext).reduce((acc, ctx) => {
4207
- const context2 = findContext(ctx, tree.contexts, byContextId);
4208
- acc[ctx] = context2;
4209
- return acc;
4210
- }, {});
4211
- for (const [contextId, ctx] of Object.entries(allContexts)) {
4212
- sessionContext.setCurrentContext(contextId);
4213
- const isDesiredFrame = await context(ctx);
4214
- if (isDesiredFrame) {
4215
- return context;
4291
+ const allContexts = await getFlatContextTree(this);
4292
+ const allContextIds = Object.keys(allContexts);
4293
+ for (const contextId of allContextIds) {
4294
+ const functionDeclaration = new Function(`
4295
+ return (${SCRIPT_PREFIX}${context.toString()}${SCRIPT_SUFFIX}).apply(this, arguments);
4296
+ `).toString();
4297
+ const params = {
4298
+ functionDeclaration,
4299
+ awaitPromise: false,
4300
+ arguments: [],
4301
+ target: { context: contextId }
4302
+ };
4303
+ const result = await this.scriptCallFunction(params).catch((err) => log13.warn(`switchFrame context callback threw error: ${err.message}`));
4304
+ if (!result || result.type !== "success" || result.result.type !== "boolean" || !result.result.value) {
4305
+ continue;
4216
4306
  }
4307
+ await this.switchFrame(contextId);
4308
+ return contextId;
4217
4309
  }
4218
- sessionContext.setCurrentContext(currentContext);
4219
4310
  throw new Error("Could not find the desired frame");
4220
4311
  }
4221
4312
  throw new Error(
@@ -4227,25 +4318,14 @@ function switchToFrameHelper(browser, context) {
4227
4318
  sessionContext.setCurrentContext(context);
4228
4319
  }
4229
4320
  async function switchToFrameUsingElement(browser, element) {
4230
- let frameSrc = await element.getAttribute("src");
4231
- if (!frameSrc) {
4232
- const source = await element.getHTML({ includeSelectorTag: true });
4233
- throw new Error(
4234
- `The provided frame element ("${source}") does not have a src attribute needed to detect the context, please use a different method to select the frame. For more information checkout our docs: https://webdriver.io/docs/api/browser/switchFrame.html`
4235
- );
4236
- }
4237
- if (!frameSrc.startsWith("http")) {
4238
- frameSrc = await browser.execute((urlPath) => URL.parse(urlPath, window.location.href)?.href, frameSrc) || frameSrc;
4239
- }
4240
- const tree = await browser.browsingContextGetTree({});
4241
- const urlContext = findContext(frameSrc, tree.contexts, byUrl)?.context;
4242
- if (!urlContext) {
4243
- throw new Error(
4244
- `Frame with url "${frameSrc}" not found! Please try a different method to select the frame. For more information checkout our docs: https://webdriver.io/docs/api/browser/switchFrame.html`
4245
- );
4246
- }
4247
- await switchToFrameHelper(browser, urlContext);
4248
- return urlContext;
4321
+ const frame = await browser.execute(
4322
+ (iframe) => iframe.contentWindow,
4323
+ element
4324
+ );
4325
+ switchToFrameHelper(browser, frame.context);
4326
+ const elementId = element[ELEMENT_KEY8];
4327
+ await switchToFrame(browser, { [ELEMENT_KEY8]: elementId });
4328
+ return frame.context;
4249
4329
  }
4250
4330
  function byUrl(context, url6) {
4251
4331
  return context.url === url6;
@@ -4256,13 +4336,13 @@ function byUrlContaining(context, url6) {
4256
4336
  function byContextId(context, contextId) {
4257
4337
  return context.context === contextId;
4258
4338
  }
4259
- function findContext(url6, contexts, matcher) {
4339
+ function findContext(urlOrId, contexts, matcher) {
4260
4340
  for (const context of contexts || []) {
4261
- if (matcher(context, url6)) {
4341
+ if (matcher(context, urlOrId)) {
4262
4342
  return context;
4263
4343
  }
4264
4344
  if (Array.isArray(context.children) && context.children.length > 0) {
4265
- const result = findContext(url6, context.children, matcher);
4345
+ const result = findContext(urlOrId, context.children, matcher);
4266
4346
  if (result) {
4267
4347
  return result;
4268
4348
  }
@@ -4270,13 +4350,32 @@ function findContext(url6, contexts, matcher) {
4270
4350
  }
4271
4351
  return void 0;
4272
4352
  }
4353
+ async function getFlatContextTree(browser) {
4354
+ const tree = await browser.browsingContextGetTree({});
4355
+ const mapContext = (context) => [
4356
+ context.context,
4357
+ ...(context.children || []).map(mapContext)
4358
+ ];
4359
+ const allContexts = tree.contexts.map(mapContext).flat(Infinity).reduce((acc, ctx) => {
4360
+ const context = findContext(ctx, tree.contexts, byContextId);
4361
+ acc[ctx] = context;
4362
+ return acc;
4363
+ }, {});
4364
+ return allContexts;
4365
+ }
4366
+ function switchToFrame(browser, frame) {
4367
+ process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS = "true";
4368
+ return browser.switchToFrame(frame).finally(() => {
4369
+ delete process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS;
4370
+ });
4371
+ }
4273
4372
 
4274
4373
  // src/commands/browser/throttle.ts
4275
- import logger13 from "@wdio/logger";
4374
+ import logger14 from "@wdio/logger";
4276
4375
  import { getBrowserObject as getBrowserObject8 } from "@wdio/utils";
4277
- var log13 = logger13("webdriverio:throttle");
4376
+ var log14 = logger14("webdriverio:throttle");
4278
4377
  async function throttle(params) {
4279
- log13.warn('Command "throttle" is deprecated and will be removed with the next major version release! Use `throttleNetwork` instead.');
4378
+ log14.warn('Command "throttle" is deprecated and will be removed with the next major version release! Use `throttleNetwork` instead.');
4280
4379
  const browser = getBrowserObject8(this);
4281
4380
  await browser.throttleNetwork(params);
4282
4381
  }
@@ -4432,13 +4531,15 @@ function getNetworkManager(browser) {
4432
4531
  networkManager.set(browser, newContext);
4433
4532
  return newContext;
4434
4533
  }
4534
+ var UNKNOWN_NAVIGATION_ID = "UNKNOWN_NAVIGATION_ID";
4435
4535
  var NetworkManager = class {
4436
4536
  #browser;
4437
4537
  #initialize;
4438
4538
  #requests = /* @__PURE__ */ new Map();
4539
+ #lastNetworkId;
4439
4540
  constructor(browser) {
4440
4541
  this.#browser = browser;
4441
- if (!browser.isBidi || process.env.VITEST_WORKER_ID || browser.options?.automationProtocol !== "webdriver") {
4542
+ if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
4442
4543
  this.#initialize = Promise.resolve(true);
4443
4544
  return;
4444
4545
  }
@@ -4458,15 +4559,15 @@ var NetworkManager = class {
4458
4559
  async initialize() {
4459
4560
  return this.#initialize;
4460
4561
  }
4461
- #beforeRequestSent(log21) {
4462
- if (log21.navigation) {
4562
+ #beforeRequestSent(log22) {
4563
+ if (log22.navigation) {
4463
4564
  return;
4464
4565
  }
4465
- const request = log21.context ? this.#requests.get(log21.context) : void 0;
4566
+ const request = this.#findRootRequest(log22.navigation);
4466
4567
  if (!request) {
4467
4568
  return;
4468
4569
  }
4469
- const { request: id, headers, cookies, url: url6 } = log21.request;
4570
+ const { request: id, headers, cookies, url: url6 } = log22.request;
4470
4571
  request.children?.push({
4471
4572
  id,
4472
4573
  url: url6,
@@ -4482,66 +4583,76 @@ var NetworkManager = class {
4482
4583
  sameSite: cookie.sameSite,
4483
4584
  expiry: cookie.expiry
4484
4585
  })),
4485
- timestamp: log21.timestamp
4586
+ timestamp: log22.timestamp
4486
4587
  });
4487
4588
  }
4488
- #navigationStarted(log21) {
4589
+ #navigationStarted(log22) {
4489
4590
  if (
4490
4591
  /**
4491
4592
  * we need a navigation id to identify the request
4492
4593
  */
4493
- !log21.navigation || /**
4594
+ !log22.navigation || /**
4494
4595
  * ignore urls that do not start with http
4495
4596
  */
4496
- !log21.url.startsWith("http")
4597
+ !log22.url.startsWith("http")
4497
4598
  ) {
4498
- if (log21.navigation === null && log21.url === "") {
4499
- return this.#requests.set(log21.context, {
4599
+ if (log22.navigation === null && log22.url === "") {
4600
+ this.#lastNetworkId = UNKNOWN_NAVIGATION_ID;
4601
+ return this.#requests.set(UNKNOWN_NAVIGATION_ID, {
4500
4602
  url: "",
4501
4603
  headers: {},
4502
- timestamp: log21.timestamp,
4604
+ timestamp: log22.timestamp,
4503
4605
  redirectChain: [],
4504
4606
  children: []
4505
4607
  });
4506
4608
  }
4507
4609
  return;
4508
4610
  }
4509
- this.#requests.set(log21.context, {
4510
- url: log21.url,
4611
+ this.#lastNetworkId = log22.navigation;
4612
+ this.#requests.set(log22.navigation, {
4613
+ url: log22.url,
4511
4614
  headers: {},
4512
- timestamp: log21.timestamp,
4513
- navigation: log21.navigation,
4615
+ timestamp: log22.timestamp,
4616
+ navigation: log22.navigation,
4514
4617
  redirectChain: [],
4515
4618
  children: []
4516
4619
  });
4517
4620
  }
4518
- #fetchError(log21) {
4519
- const response = log21.context ? this.#requests.get(log21.context) : void 0;
4621
+ #fetchError(log22) {
4622
+ const response = this.#findRootRequest(log22.navigation);
4520
4623
  if (!response) {
4521
4624
  return;
4522
4625
  }
4523
- const request = response.children?.find((child) => child.id === log21.request.request);
4626
+ const request = response.children?.find((child) => child.id === log22.request.request);
4524
4627
  if (!request) {
4525
4628
  return;
4526
4629
  }
4527
- request.error = log21.errorText;
4630
+ request.error = log22.errorText;
4631
+ }
4632
+ #findRootRequest(navigationId) {
4633
+ const response = this.#requests.get(navigationId || UNKNOWN_NAVIGATION_ID);
4634
+ if (response) {
4635
+ return response;
4636
+ }
4637
+ const firstRequest = this.#requests.values().next().value;
4638
+ return this.#lastNetworkId ? this.#requests.get(this.#lastNetworkId) || firstRequest : firstRequest;
4528
4639
  }
4529
- #responseCompleted(log21) {
4530
- const response = log21.context ? this.#requests.get(log21.context) : void 0;
4640
+ #responseCompleted(log22) {
4641
+ const response = this.#findRootRequest(log22.navigation);
4531
4642
  if (!response) {
4532
4643
  return;
4533
4644
  }
4534
4645
  if (!response.navigation && response.url === "") {
4535
- response.url = log21.request.url;
4536
- response.navigation = log21.navigation;
4646
+ response.url = log22.request.url;
4647
+ response.navigation = log22.navigation;
4537
4648
  }
4538
- if (log21.navigation === response.navigation) {
4539
- if (response.url !== log21.response.url) {
4649
+ if (log22.navigation === response.navigation) {
4650
+ if (response.url !== log22.response.url) {
4540
4651
  response.redirectChain?.push(response.url);
4541
4652
  }
4542
- response.url = log21.response.url;
4543
- const { headers: requestHeaders } = log21.request;
4544
- const { fromCache, headers: responseHeaders, mimeType, status } = log21.response;
4653
+ response.url = log22.response.url;
4654
+ const { headers: requestHeaders } = log22.request;
4655
+ const { fromCache, headers: responseHeaders, mimeType, status } = log22.response;
4545
4656
  response.headers = headerListToObject(requestHeaders), response.response = {
4546
4657
  fromCache,
4547
4658
  headers: headerListToObject(responseHeaders),
@@ -4550,30 +4661,30 @@ var NetworkManager = class {
4550
4661
  };
4551
4662
  return;
4552
4663
  }
4553
- const request = response.children?.find((child) => child.id === log21.request.request);
4664
+ const request = response.children?.find((child) => child.id === log22.request.request);
4554
4665
  if (!request) {
4555
4666
  return;
4556
4667
  }
4557
4668
  request.response = {
4558
- fromCache: log21.response.fromCache,
4559
- headers: headerListToObject(log21.response.headers),
4560
- mimeType: log21.response.mimeType,
4561
- status: log21.response.status
4669
+ fromCache: log22.response.fromCache,
4670
+ headers: headerListToObject(log22.response.headers),
4671
+ mimeType: log22.response.mimeType,
4672
+ status: log22.response.status
4562
4673
  };
4563
4674
  response.children?.push(request);
4564
4675
  }
4565
- getRequestResponseData(context) {
4566
- return this.#requests.get(context);
4676
+ getRequestResponseData(navigationId) {
4677
+ return this.#requests.get(navigationId);
4567
4678
  }
4568
4679
  /**
4569
4680
  * Returns the number of requests that are currently pending.
4570
4681
  * @param context browsing context id
4571
4682
  * @returns the number of requests that are currently pending
4572
4683
  */
4573
- getPendingRequests(context) {
4574
- const request = this.#requests.get(context);
4684
+ getPendingRequests(navigationId) {
4685
+ const request = this.#requests.get(navigationId);
4575
4686
  if (!request) {
4576
- throw new Error(`Couldn't find request for context ${context}`);
4687
+ throw new Error(`Couldn't find request for navigation with id ${navigationId}`);
4577
4688
  }
4578
4689
  const subRequests = request.children || [];
4579
4690
  return subRequests.filter((child) => (
@@ -4627,17 +4738,16 @@ async function url3(path4, options = {}) {
4627
4738
  }
4628
4739
  const classicPageLoadStrategy = this.capabilities.pageLoadStrategy === "none" ? "none" : this.capabilities.pageLoadStrategy === "normal" ? "complete" : this.capabilities.pageLoadStrategy === "eager" ? "interactive" : void 0;
4629
4740
  const wait = options.wait === "networkIdle" ? "complete" : options.wait || classicPageLoadStrategy || DEFAULT_WAIT_STATE;
4630
- await this.browsingContextNavigate({
4741
+ const navigation = await this.browsingContextNavigate({
4631
4742
  context,
4632
4743
  url: path4,
4633
4744
  wait
4634
4745
  });
4635
- const network = networkManager.get(this);
4636
- const request = network?.getRequestResponseData(context);
4637
4746
  if (mock2) {
4638
4747
  await mock2.restore();
4639
4748
  }
4640
- if (network && options.wait === "networkIdle") {
4749
+ const network = getNetworkManager(this);
4750
+ if (options.wait === "networkIdle") {
4641
4751
  const timeout = options.timeout || DEFAULT_NETWORK_IDLE_TIMEOUT;
4642
4752
  await this.waitUntil(async () => {
4643
4753
  return network.getPendingRequests(context).length === 0;
@@ -4649,6 +4759,15 @@ async function url3(path4, options = {}) {
4649
4759
  if (resetPreloadScript) {
4650
4760
  await resetPreloadScript.remove();
4651
4761
  }
4762
+ const request = await this.waitUntil(
4763
+ () => network.getRequestResponseData(navigation.navigation),
4764
+ /**
4765
+ * set a short interval to immediately return once the first request payload comes in
4766
+ */
4767
+ {
4768
+ interval: 1
4769
+ }
4770
+ );
4652
4771
  return request;
4653
4772
  }
4654
4773
  if (Object.keys(options).length > 0) {
@@ -4765,9 +4884,9 @@ function clearValue() {
4765
4884
  }
4766
4885
 
4767
4886
  // src/commands/element/click.ts
4768
- import logger14 from "@wdio/logger";
4887
+ import logger15 from "@wdio/logger";
4769
4888
  import { getBrowserObject as getBrowserObject10 } from "@wdio/utils";
4770
- var log14 = logger14("webdriver");
4889
+ var log15 = logger15("webdriver");
4771
4890
  function click(options) {
4772
4891
  if (typeof options !== "undefined") {
4773
4892
  if (typeof options !== "object" || Array.isArray(options)) {
@@ -4813,10 +4932,10 @@ async function actionClick(element, options) {
4813
4932
  if (x || y) {
4814
4933
  const { width, height } = await browser.getElementRect(element.elementId);
4815
4934
  if (x && x < -Math.floor(width / 2) || x && x > Math.floor(width / 2)) {
4816
- log14.warn("x would cause a out of bounds error as it goes outside of element");
4935
+ log15.warn("x would cause a out of bounds error as it goes outside of element");
4817
4936
  }
4818
4937
  if (y && y < -Math.floor(height / 2) || y && y > Math.floor(height / 2)) {
4819
- log14.warn("y would cause a out of bounds error as it goes outside of element");
4938
+ log15.warn("y would cause a out of bounds error as it goes outside of element");
4820
4939
  }
4821
4940
  }
4822
4941
  const clickNested = async () => {
@@ -4833,7 +4952,7 @@ async function actionClick(element, options) {
4833
4952
  }
4834
4953
 
4835
4954
  // src/commands/element/custom$$.ts
4836
- import { ELEMENT_KEY as ELEMENT_KEY8 } from "webdriver";
4955
+ import { ELEMENT_KEY as ELEMENT_KEY9 } from "webdriver";
4837
4956
  import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
4838
4957
  async function custom$$2(strategyName, ...strategyArguments) {
4839
4958
  const browserObject = getBrowserObject11(this);
@@ -4849,13 +4968,13 @@ async function custom$$2(strategyName, ...strategyArguments) {
4849
4968
  if (!Array.isArray(res)) {
4850
4969
  res = [res];
4851
4970
  }
4852
- res = res.filter((el) => !!el && typeof el[ELEMENT_KEY8] === "string");
4971
+ res = res.filter((el) => !!el && typeof el[ELEMENT_KEY9] === "string");
4853
4972
  const elements = res.length ? await getElements.call(this, strategyRef, res) : [];
4854
4973
  return enhanceElementsArray(elements, this, strategyName, "custom$$", strategyArguments);
4855
4974
  }
4856
4975
 
4857
4976
  // src/commands/element/custom$.ts
4858
- import { ELEMENT_KEY as ELEMENT_KEY9 } from "webdriver";
4977
+ import { ELEMENT_KEY as ELEMENT_KEY10 } from "webdriver";
4859
4978
  import { getBrowserObject as getBrowserObject12 } from "@wdio/utils";
4860
4979
  async function custom$2(strategyName, ...strategyArguments) {
4861
4980
  const browserObject = getBrowserObject12(this);
@@ -4871,7 +4990,7 @@ async function custom$2(strategyName, ...strategyArguments) {
4871
4990
  if (Array.isArray(res)) {
4872
4991
  res = res[0];
4873
4992
  }
4874
- if (res && typeof res[ELEMENT_KEY9] === "string") {
4993
+ if (res && typeof res[ELEMENT_KEY10] === "string") {
4875
4994
  return await getElement.call(this, strategyRef, res);
4876
4995
  }
4877
4996
  return await getElement.call(this, strategyRef, new Error("no such element"));
@@ -4885,7 +5004,7 @@ async function doubleClick() {
4885
5004
  }
4886
5005
 
4887
5006
  // src/commands/element/dragAndDrop.ts
4888
- import { ELEMENT_KEY as ELEMENT_KEY10 } from "webdriver";
5007
+ import { ELEMENT_KEY as ELEMENT_KEY11 } from "webdriver";
4889
5008
  import { getBrowserObject as getBrowserObject14 } from "@wdio/utils";
4890
5009
  var ACTION_BUTTON = 0;
4891
5010
  async function dragAndDrop(target, { duration = 10 } = {}) {
@@ -4906,8 +5025,8 @@ async function dragAndDrop(target, { duration = 10 } = {}) {
4906
5025
  throw new Error('command dragAndDrop requires an WebdriverIO Element or and object with "x" and "y" variables as first parameter');
4907
5026
  }
4908
5027
  const isMovingToElement = target.constructor.name === "Element";
4909
- const sourceRef = { [ELEMENT_KEY10]: this[ELEMENT_KEY10] };
4910
- const targetRef = { [ELEMENT_KEY10]: moveToElement[ELEMENT_KEY10] };
5028
+ const sourceRef = { [ELEMENT_KEY11]: this[ELEMENT_KEY11] };
5029
+ const targetRef = { [ELEMENT_KEY11]: moveToElement[ELEMENT_KEY11] };
4911
5030
  const origin = sourceRef;
4912
5031
  const targetOrigin = isMovingToElement ? targetRef : "pointer";
4913
5032
  const targetX = isMovingToElement ? 0 : moveToCoordinates.x;
@@ -5028,15 +5147,15 @@ async function getElement2() {
5028
5147
  }
5029
5148
 
5030
5149
  // src/commands/element/getHTML.ts
5031
- import { ELEMENT_KEY as ELEMENT_KEY11 } from "webdriver";
5150
+ import { ELEMENT_KEY as ELEMENT_KEY12 } from "webdriver";
5032
5151
  import { prettify as prettifyFn } from "htmlfy";
5033
5152
  import { getBrowserObject as getBrowserObject18 } from "@wdio/utils";
5034
5153
 
5035
5154
  // src/shadowRoot.ts
5036
- import logger15 from "@wdio/logger";
5155
+ import logger16 from "@wdio/logger";
5037
5156
  import customElementWrapper from "./scripts/customElement.js";
5038
5157
  var shadowRootManager = /* @__PURE__ */ new Map();
5039
- var log15 = logger15("webdriverio:ShadowRootManager");
5158
+ var log16 = logger16("webdriverio:ShadowRootManager");
5040
5159
  function getShadowRootManager(browser) {
5041
5160
  const existingShadowRootManager = shadowRootManager.get(browser);
5042
5161
  if (existingShadowRootManager) {
@@ -5053,7 +5172,7 @@ var ShadowRootManager = class {
5053
5172
  #frameDepth = 0;
5054
5173
  constructor(browser) {
5055
5174
  this.#browser = browser;
5056
- if (!browser.isBidi || process.env.VITEST_WORKER_ID || browser.options?.automationProtocol !== "webdriver") {
5175
+ if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
5057
5176
  this.#initialize = Promise.resolve(true);
5058
5177
  return;
5059
5178
  }
@@ -5144,9 +5263,9 @@ var ShadowRootManager = class {
5144
5263
  !shadowElem.value?.shadowRoot?.sharedId || // we expect the shadow root to have a proper type
5145
5264
  shadowElem.value.shadowRoot.value?.nodeType !== 11
5146
5265
  ) {
5147
- return log15.warn(`Expected element with shadow root but found <${shadowElem.value?.localName} />`);
5266
+ return log16.warn(`Expected element with shadow root but found <${shadowElem.value?.localName} />`);
5148
5267
  }
5149
- log15.info(`Registered new shadow root for element <${shadowElem.value.localName} /> with id ${shadowElem.value.shadowRoot.sharedId}`);
5268
+ log16.info(`Registered new shadow root for element <${shadowElem.value.localName} /> with id ${shadowElem.value.shadowRoot.sharedId}`);
5150
5269
  const newTree = new ShadowRootTree(
5151
5270
  shadowElem.sharedId,
5152
5271
  shadowElem.value.shadowRoot.sharedId,
@@ -5273,11 +5392,12 @@ var ShadowRootTree = class _ShadowRootTree {
5273
5392
  return [this, ...Array.from(this.children).map((tree) => tree.flat())].flat();
5274
5393
  }
5275
5394
  remove(element) {
5276
- for (const child of this.children) {
5277
- if (child.element === element) {
5278
- return this.children.delete(child);
5395
+ const childArray = Array.from(this.children);
5396
+ for (let i = childArray.length - 1; i >= 0; i--) {
5397
+ if (childArray[i].element === element) {
5398
+ return this.children.delete(childArray[i]);
5279
5399
  }
5280
- const wasFound = child.remove(element);
5400
+ const wasFound = childArray[i].remove(element);
5281
5401
  if (wasFound) {
5282
5402
  return true;
5283
5403
  }
@@ -5307,7 +5427,7 @@ async function getHTML(options = {}) {
5307
5427
  }, options);
5308
5428
  const basicGetHTML = (elementId, includeSelectorTag2) => {
5309
5429
  return browser.execute(getHTMLScript, {
5310
- [ELEMENT_KEY11]: elementId,
5430
+ [ELEMENT_KEY12]: elementId,
5311
5431
  // w3c compatible
5312
5432
  ELEMENT: elementId
5313
5433
  // jsonwp compatible
@@ -5327,18 +5447,18 @@ async function getHTML(options = {}) {
5327
5447
  const context = await contextManager2.getCurrentContext();
5328
5448
  const shadowRootElementPairs = shadowRootManager2.getShadowElementPairsByContextId(context, this.elementId);
5329
5449
  const elementsWithShadowRootAndIdVerified = (await Promise.all(
5330
- shadowRootElementPairs.map(([elemId, elem]) => browser.execute((elem2) => elem2.tagName, { [ELEMENT_KEY11]: elemId }).then(
5450
+ shadowRootElementPairs.map(([elemId, elem]) => browser.execute((elem2) => elem2.tagName, { [ELEMENT_KEY12]: elemId }).then(
5331
5451
  () => [elemId, elem],
5332
5452
  () => void 0
5333
5453
  ))
5334
5454
  )).filter(Boolean).map(([elemId, shadowId]) => [
5335
5455
  elemId,
5336
- { [ELEMENT_KEY11]: elemId },
5337
- shadowId ? { [ELEMENT_KEY11]: shadowId } : void 0
5456
+ { [ELEMENT_KEY12]: elemId },
5457
+ shadowId ? { [ELEMENT_KEY12]: shadowId } : void 0
5338
5458
  ]);
5339
5459
  const { html, shadowElementHTML } = await browser.execute(
5340
5460
  getHTMLShadowScript,
5341
- { [ELEMENT_KEY11]: this.elementId },
5461
+ { [ELEMENT_KEY12]: this.elementId },
5342
5462
  includeSelectorTag,
5343
5463
  elementsWithShadowRootAndIdVerified
5344
5464
  );
@@ -5436,7 +5556,7 @@ function getValue() {
5436
5556
  }
5437
5557
 
5438
5558
  // src/commands/element/isClickable.ts
5439
- import { ELEMENT_KEY as ELEMENT_KEY12 } from "webdriver";
5559
+ import { ELEMENT_KEY as ELEMENT_KEY13 } from "webdriver";
5440
5560
  import { getBrowserObject as getBrowserObject19 } from "@wdio/utils";
5441
5561
  import isElementClickableScript from "./scripts/isElementClickable.js";
5442
5562
  async function isClickable() {
@@ -5448,7 +5568,7 @@ async function isClickable() {
5448
5568
  }
5449
5569
  const browser = getBrowserObject19(this);
5450
5570
  return browser.execute(isElementClickableScript, {
5451
- [ELEMENT_KEY12]: this.elementId,
5571
+ [ELEMENT_KEY13]: this.elementId,
5452
5572
  // w3c compatible
5453
5573
  ELEMENT: this.elementId
5454
5574
  // jsonwp compatible
@@ -5486,10 +5606,10 @@ function isEnabled() {
5486
5606
  }
5487
5607
 
5488
5608
  // src/commands/element/isEqual.ts
5489
- import { ELEMENT_KEY as ELEMENT_KEY13 } from "webdriver";
5609
+ import { ELEMENT_KEY as ELEMENT_KEY14 } from "webdriver";
5490
5610
  import { getBrowserObject as getBrowserObject21 } from "@wdio/utils";
5491
5611
  var getWebElement = (el) => ({
5492
- [ELEMENT_KEY13]: el.elementId,
5612
+ [ELEMENT_KEY14]: el.elementId,
5493
5613
  // w3c compatible
5494
5614
  ELEMENT: el.elementId
5495
5615
  // jsonwp compatible
@@ -5532,13 +5652,13 @@ async function isExisting() {
5532
5652
  }
5533
5653
 
5534
5654
  // src/commands/element/isFocused.ts
5535
- import { ELEMENT_KEY as ELEMENT_KEY14 } from "webdriver";
5655
+ import { ELEMENT_KEY as ELEMENT_KEY15 } from "webdriver";
5536
5656
  import { getBrowserObject as getBrowserObject22 } from "@wdio/utils";
5537
5657
  import isFocusedScript from "./scripts/isFocused.js";
5538
5658
  async function isFocused() {
5539
5659
  const browser = await getBrowserObject22(this);
5540
5660
  return browser.execute(isFocusedScript, {
5541
- [ELEMENT_KEY14]: this.elementId,
5661
+ [ELEMENT_KEY15]: this.elementId,
5542
5662
  // w3c compatible
5543
5663
  ELEMENT: this.elementId
5544
5664
  // jsonwp compatible
@@ -5551,13 +5671,13 @@ function isSelected() {
5551
5671
  }
5552
5672
 
5553
5673
  // src/commands/element/isStable.ts
5554
- import { ELEMENT_KEY as ELEMENT_KEY15 } from "webdriver";
5674
+ import { ELEMENT_KEY as ELEMENT_KEY16 } from "webdriver";
5555
5675
  import { getBrowserObject as getBrowserObject23 } from "@wdio/utils";
5556
5676
  import isElementStable from "./scripts/isElementStable.js";
5557
5677
  async function isStable() {
5558
5678
  const browser = getBrowserObject23(this);
5559
5679
  return await browser.executeAsync(isElementStable, {
5560
- [ELEMENT_KEY15]: this.elementId,
5680
+ [ELEMENT_KEY16]: this.elementId,
5561
5681
  // w3c compatible
5562
5682
  ELEMENT: this.elementId
5563
5683
  // jsonwp compatible
@@ -5565,18 +5685,18 @@ async function isStable() {
5565
5685
  }
5566
5686
 
5567
5687
  // src/commands/element/moveTo.ts
5568
- import logger16 from "@wdio/logger";
5688
+ import logger17 from "@wdio/logger";
5569
5689
  import { getBrowserObject as getBrowserObject24 } from "@wdio/utils";
5570
- var log16 = logger16("webdriver");
5690
+ var log17 = logger17("webdriver");
5571
5691
  async function moveTo({ xOffset, yOffset } = {}) {
5572
5692
  const browser = getBrowserObject24(this);
5573
5693
  if (xOffset || yOffset) {
5574
5694
  const { width, height } = await browser.getElementRect(this.elementId);
5575
5695
  if (xOffset && xOffset < -Math.floor(width / 2) || xOffset && xOffset > Math.floor(width / 2)) {
5576
- log16.warn("xOffset would cause a out of bounds error as it goes outside of element");
5696
+ log17.warn("xOffset would cause a out of bounds error as it goes outside of element");
5577
5697
  }
5578
5698
  if (yOffset && yOffset < -Math.floor(height / 2) || yOffset && yOffset > Math.floor(height / 2)) {
5579
- log16.warn("yOffset would cause a out of bounds error as it goes outside of element");
5699
+ log17.warn("yOffset would cause a out of bounds error as it goes outside of element");
5580
5700
  }
5581
5701
  }
5582
5702
  const moveToNested = async () => {
@@ -5686,16 +5806,16 @@ async function saveScreenshot2(filepath) {
5686
5806
  }
5687
5807
 
5688
5808
  // src/commands/element/scrollIntoView.ts
5689
- import logger17 from "@wdio/logger";
5690
- import { ELEMENT_KEY as ELEMENT_KEY16 } from "webdriver";
5809
+ import logger18 from "@wdio/logger";
5810
+ import { ELEMENT_KEY as ELEMENT_KEY17 } from "webdriver";
5691
5811
  import { getBrowserObject as getBrowserObject27 } from "@wdio/utils";
5692
- var log17 = logger17("webdriverio");
5812
+ var log18 = logger18("webdriverio");
5693
5813
  function scrollIntoViewWeb(options = { block: "start", inline: "nearest" }) {
5694
5814
  const browser = getBrowserObject27(this);
5695
5815
  return browser.execute(
5696
5816
  (elem, options2) => elem.scrollIntoView(options2),
5697
5817
  {
5698
- [ELEMENT_KEY16]: this.elementId,
5818
+ [ELEMENT_KEY17]: this.elementId,
5699
5819
  // w3c compatible
5700
5820
  ELEMENT: this.elementId
5701
5821
  // jsonwp compatible
@@ -5748,7 +5868,7 @@ async function scrollIntoView(options = { block: "start", inline: "nearest" }) {
5748
5868
  deltaY = Math.round(deltaY - scrollY);
5749
5869
  await browser.action("wheel").scroll({ duration: 0, x: deltaX, y: deltaY, origin: this }).perform();
5750
5870
  } catch (err) {
5751
- log17.warn(
5871
+ log18.warn(
5752
5872
  `Failed to execute "scrollIntoView" using WebDriver Actions API: ${err.message}!
5753
5873
  Re-attempting using \`Element.scrollIntoView\` via Web API.`
5754
5874
  );
@@ -5813,7 +5933,7 @@ async function setValue(value) {
5813
5933
  }
5814
5934
 
5815
5935
  // src/commands/element/shadow$$.ts
5816
- import logger18 from "@wdio/logger";
5936
+ import logger19 from "@wdio/logger";
5817
5937
  import { getBrowserObject as getBrowserObject28 } from "@wdio/utils";
5818
5938
  import { SHADOW_ELEMENT_KEY } from "webdriver";
5819
5939
  import { shadowFnFactory } from "./scripts/shadowFnFactory.js";
@@ -6141,7 +6261,7 @@ var createRoleBaseXpathSelector = (role) => {
6141
6261
  };
6142
6262
 
6143
6263
  // src/commands/element/shadow$$.ts
6144
- var log18 = logger18("webdriverio");
6264
+ var log19 = logger19("webdriverio");
6145
6265
  async function shadow$$(selector) {
6146
6266
  const browser = getBrowserObject28(this);
6147
6267
  try {
@@ -6151,7 +6271,7 @@ async function shadow$$(selector) {
6151
6271
  const elements = await getElements.call(this, selector, res, { isShadowElement: true });
6152
6272
  return enhanceElementsArray(elements, this, selector);
6153
6273
  } catch (err) {
6154
- log18.warn(
6274
+ log19.warn(
6155
6275
  `Failed to fetch element within shadow DOM using WebDriver command: ${err.message}!
6156
6276
  Falling back to JavaScript shim.`
6157
6277
  );
@@ -6160,11 +6280,11 @@ Falling back to JavaScript shim.`
6160
6280
  }
6161
6281
 
6162
6282
  // src/commands/element/shadow$.ts
6163
- import logger19 from "@wdio/logger";
6283
+ import logger20 from "@wdio/logger";
6164
6284
  import { SHADOW_ELEMENT_KEY as SHADOW_ELEMENT_KEY2 } from "webdriver";
6165
6285
  import { shadowFnFactory as shadowFnFactory2 } from "./scripts/shadowFnFactory.js";
6166
6286
  import { getBrowserObject as getBrowserObject29 } from "@wdio/utils";
6167
- var log19 = logger19("webdriverio");
6287
+ var log20 = logger20("webdriverio");
6168
6288
  async function shadow$(selector) {
6169
6289
  const browser = getBrowserObject29(this);
6170
6290
  try {
@@ -6173,7 +6293,7 @@ async function shadow$(selector) {
6173
6293
  const res = await browser.findElementFromShadowRoot(shadowRoot[SHADOW_ELEMENT_KEY2], using, value);
6174
6294
  return getElement.call(this, selector, res, { isShadowElement: true });
6175
6295
  } catch (err) {
6176
- log19.warn(
6296
+ log20.warn(
6177
6297
  `Failed to fetch element within shadow DOM using WebDriver command: ${err.message}!
6178
6298
  Falling back to JavaScript shim.`
6179
6299
  );
@@ -6472,7 +6592,7 @@ function querySelectorAllDeep(findMany, s, r) {
6472
6592
  }
6473
6593
 
6474
6594
  // src/utils/index.ts
6475
- var log20 = logger20("webdriverio");
6595
+ var log21 = logger21("webdriverio");
6476
6596
  var INVALID_SELECTOR_ERROR = "selector needs to be typeof `string` or `function`";
6477
6597
  var IGNORED_COMMAND_FILE_EXPORTS = ["SESSION_MOCKS", "CDP_SESSIONS"];
6478
6598
  var scopes = {
@@ -6502,8 +6622,8 @@ var getElementFromResponse = (res) => {
6502
6622
  if (res.ELEMENT) {
6503
6623
  return res.ELEMENT;
6504
6624
  }
6505
- if (res[ELEMENT_KEY17]) {
6506
- return res[ELEMENT_KEY17];
6625
+ if (res[ELEMENT_KEY18]) {
6626
+ return res[ELEMENT_KEY18];
6507
6627
  }
6508
6628
  return null;
6509
6629
  };
@@ -6610,7 +6730,7 @@ async function findDeepElement(selector) {
6610
6730
  const startNodes = shadowRoots.length > 0 ? shadowRoots.map((shadowRootNodeId) => ({ sharedId: shadowRootNodeId })) : this.elementId ? [{ sharedId: this.elementId }] : void 0;
6611
6731
  const deepElementResult = await browser.browsingContextLocateNodes({ locator, context, startNodes }).then(async (result) => {
6612
6732
  const nodes = result.nodes.filter((node) => Boolean(node.sharedId)).map((node) => ({
6613
- [ELEMENT_KEY17]: node.sharedId,
6733
+ [ELEMENT_KEY18]: node.sharedId,
6614
6734
  locator
6615
6735
  }));
6616
6736
  if (!this.elementId) {
@@ -6619,15 +6739,15 @@ async function findDeepElement(selector) {
6619
6739
  const scopedNodes = await Promise.all(nodes.map(async (node) => {
6620
6740
  const isIn = await browser.execute(
6621
6741
  elementContains,
6622
- { [ELEMENT_KEY17]: this.elementId },
6742
+ { [ELEMENT_KEY18]: this.elementId },
6623
6743
  node
6624
6744
  );
6625
6745
  return [isIn, node];
6626
6746
  })).then((elems) => elems.filter(([isIn]) => isIn).map(([, elem]) => elem));
6627
6747
  return scopedNodes[0];
6628
6748
  }, (err) => {
6629
- log20.warn(`Failed to execute browser.browsingContextLocateNodes({ ... }) due to ${err}, falling back to regular WebDriver Classic command`);
6630
- return browser.findElement(using, value);
6749
+ log21.warn(`Failed to execute browser.browsingContextLocateNodes({ ... }) due to ${err}, falling back to regular WebDriver Classic command`);
6750
+ return this && "elementId" in this && this.elementId ? this.findElementFromElement(this.elementId, using, value) : browser.findElement(using, value);
6631
6751
  });
6632
6752
  if (!deepElementResult) {
6633
6753
  return new Error(`Couldn't find element with selector "${selector}"`);
@@ -6648,7 +6768,7 @@ async function findDeepElements(selector) {
6648
6768
  const startNodes = shadowRoots.length > 0 ? shadowRoots.map((shadowRootNodeId) => ({ sharedId: shadowRootNodeId })) : this.elementId ? [{ sharedId: this.elementId }] : void 0;
6649
6769
  const deepElementResult = await browser.browsingContextLocateNodes({ locator, context, startNodes }).then(async (result) => {
6650
6770
  const nodes = result.nodes.filter((node) => Boolean(node.sharedId)).map((node) => ({
6651
- [ELEMENT_KEY17]: node.sharedId,
6771
+ [ELEMENT_KEY18]: node.sharedId,
6652
6772
  locator
6653
6773
  }));
6654
6774
  if (!this.elementId) {
@@ -6657,15 +6777,15 @@ async function findDeepElements(selector) {
6657
6777
  const scopedNodes = await Promise.all(nodes.map(async (node) => {
6658
6778
  const isIn = await browser.execute(
6659
6779
  elementContains,
6660
- { [ELEMENT_KEY17]: this.elementId },
6780
+ { [ELEMENT_KEY18]: this.elementId },
6661
6781
  node
6662
6782
  );
6663
6783
  return [isIn, node];
6664
6784
  })).then((elems) => elems.filter(([isIn]) => isIn).map(([, elem]) => elem));
6665
6785
  return scopedNodes;
6666
6786
  }, (err) => {
6667
- log20.warn(`Failed to execute browser.browsingContextLocateNodes({ ... }) due to ${err}, falling back to regular WebDriver Classic command`);
6668
- return browser.findElements(using, value);
6787
+ log21.warn(`Failed to execute browser.browsingContextLocateNodes({ ... }) due to ${err}, falling back to regular WebDriver Classic command`);
6788
+ return this && "elementId" in this && this.elementId ? this.findElementsFromElement(this.elementId, using, value) : browser.findElements(using, value);
6669
6789
  });
6670
6790
  return deepElementResult;
6671
6791
  }
@@ -6761,7 +6881,7 @@ function verifyArgsAndStripIfElement(args) {
6761
6881
  throw new Error(`The element with selector "${elem.selector}" you are trying to pass into the execute method wasn't found`);
6762
6882
  }
6763
6883
  return {
6764
- [ELEMENT_KEY17]: elem.elementId,
6884
+ [ELEMENT_KEY18]: elem.elementId,
6765
6885
  ELEMENT: elem.elementId
6766
6886
  };
6767
6887
  }
@@ -6792,7 +6912,7 @@ async function getElementRect(scope) {
6792
6912
  if (rectJs && typeof rectJs[key] === "number") {
6793
6913
  rect[key] = Math.floor(rectJs[key]);
6794
6914
  } else {
6795
- log20.error("getElementRect", { rect, rectJs, key });
6915
+ log21.error("getElementRect", { rect, rectJs, key });
6796
6916
  throw new Error("Failed to receive element rects via execute command");
6797
6917
  }
6798
6918
  });
@@ -6867,7 +6987,7 @@ var elementErrorHandler = (fn) => (commandName, commandFn) => {
6867
6987
  }
6868
6988
  const element = await implicitWait(this, commandName);
6869
6989
  this.elementId = element.elementId;
6870
- this[ELEMENT_KEY18] = element.elementId;
6990
+ this[ELEMENT_KEY19] = element.elementId;
6871
6991
  try {
6872
6992
  const result = await fn(commandName, commandFn).apply(this, args);
6873
6993
  const caps = getBrowserObject31(this).capabilities;
@@ -7160,6 +7280,8 @@ function detectBackend(options = {}) {
7160
7280
 
7161
7281
  // src/protocol-stub.ts
7162
7282
  import { capabilitiesEnvironmentDetector } from "@wdio/utils";
7283
+ var NOOP2 = () => {
7284
+ };
7163
7285
  var ProtocolStub = class {
7164
7286
  static async newSession(options) {
7165
7287
  const capabilities = emulateSessionCapabilities(options.capabilities);
@@ -7172,6 +7294,9 @@ var ProtocolStub = class {
7172
7294
  overwrittenCommands: [],
7173
7295
  // internally used to transfer overwritten commands to the actual protocol instance
7174
7296
  commandList: [],
7297
+ getWindowHandle: NOOP2,
7298
+ on: NOOP2,
7299
+ off: NOOP2,
7175
7300
  ...capabilitiesEnvironmentDetector(capabilities)
7176
7301
  };
7177
7302
  browser.addCommand = (...args) => browser.customCommands.push(args);
@@ -7239,7 +7364,7 @@ var DialogManager = class {
7239
7364
  #autoHandleDialog = true;
7240
7365
  constructor(browser) {
7241
7366
  this.#browser = browser;
7242
- if (!browser.isBidi || process.env.VITEST_WORKER_ID || browser.options?.automationProtocol !== "webdriver") {
7367
+ if (!browser.isBidi || process.env.WDIO_UNIT_TESTS || browser.options?.automationProtocol !== "webdriver") {
7243
7368
  this.#initialize = Promise.resolve(true);
7244
7369
  return;
7245
7370
  }
@@ -7256,14 +7381,14 @@ var DialogManager = class {
7256
7381
  /**
7257
7382
  * capture shadow root elements propagated through console.debug
7258
7383
  */
7259
- async #handleUserPrompt(log21) {
7384
+ async #handleUserPrompt(log22) {
7260
7385
  if (this.#autoHandleDialog) {
7261
7386
  return this.#browser.browsingContextHandleUserPrompt({
7262
7387
  accept: false,
7263
- context: log21.context
7388
+ context: log22.context
7264
7389
  });
7265
7390
  }
7266
- const dialog = new Dialog(log21, this.#browser);
7391
+ const dialog = new Dialog(log22, this.#browser);
7267
7392
  this.#browser.emit("dialog", dialog);
7268
7393
  }
7269
7394
  /**
@@ -7326,7 +7451,7 @@ var remote = async function(params, remoteModifier) {
7326
7451
  const keysToKeep = Object.keys(process.env.WDIO_WORKER_ID ? params : DEFAULTS);
7327
7452
  const config = validateConfig(WDIO_DEFAULTS, params, keysToKeep);
7328
7453
  await enableFileLogging(config.outputDir);
7329
- logger21.setLogLevelsConfig(config.logLevels, config.logLevel);
7454
+ logger22.setLogLevelsConfig(config.logLevels, config.logLevel);
7330
7455
  const modifier = (client, options2) => {
7331
7456
  Object.assign(options2, Object.entries(config).reduce((a, [k, v]) => typeof v === "undefined" ? a : { ...a, [k]: v }, {}));
7332
7457
  if (typeof remoteModifier === "function") {