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/commands/browser/reloadSession.d.ts.map +1 -1
- package/build/commands/browser/url.d.ts.map +1 -1
- package/build/commands/element/dragAndDrop.d.ts +2 -2
- package/build/commands/mobile/dragAndDrop.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1458 -1402
- package/build/{context.d.ts → session/context.d.ts} +3 -1
- package/build/session/context.d.ts.map +1 -0
- package/build/{dialog.d.ts → session/dialog.d.ts} +3 -1
- package/build/session/dialog.d.ts.map +1 -0
- package/build/session/index.d.ts +5 -0
- package/build/session/index.d.ts.map +1 -0
- package/build/{networkManager.d.ts → session/networkManager.d.ts} +3 -2
- package/build/session/networkManager.d.ts.map +1 -0
- package/build/{polyfill.d.ts → session/polyfill.d.ts} +3 -2
- package/build/session/polyfill.d.ts.map +1 -0
- package/build/session/session.d.ts +19 -0
- package/build/session/session.d.ts.map +1 -0
- package/build/{shadowRoot.d.ts → session/shadowRoot.d.ts} +3 -1
- package/build/session/shadowRoot.d.ts.map +1 -0
- package/build/types.d.ts +1 -1
- package/build/types.d.ts.map +1 -1
- package/build/utils/index.d.ts +1 -1
- package/build/utils/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/build/context.d.ts.map +0 -1
- package/build/dialog.d.ts.map +0 -1
- package/build/networkManager.d.ts.map +0 -1
- package/build/polyfill.d.ts.map +0 -1
- package/build/shadowRoot.d.ts.map +0 -1
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
|
-
|
|
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", (
|
|
3079
|
-
|
|
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", (
|
|
3088
|
-
|
|
3089
|
-
|
|
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
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
3205
|
-
const context = await
|
|
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
|
|
3239
|
-
const context = await
|
|
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
|
|
3940
|
-
const context = await
|
|
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
|
|
4048
|
+
const contextManager = getContextManager(this);
|
|
3995
4049
|
const { context } = await this.browsingContextCreate({ type });
|
|
3996
|
-
|
|
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
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4073
|
-
|
|
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
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
if (!
|
|
4104
|
-
|
|
4105
|
-
|
|
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
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
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
|
-
|
|
4182
|
-
|
|
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
|
-
|
|
4185
|
-
|
|
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
|
-
|
|
4198
|
-
|
|
4199
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
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
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
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
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
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
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
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
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
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
|
-
|
|
4270
|
-
|
|
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
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
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
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
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
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
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
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
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
|
-
|
|
4306
|
-
|
|
4307
|
-
return
|
|
4332
|
+
if (scope instanceof _ShadowRootTree) {
|
|
4333
|
+
this.children.add(scope);
|
|
4334
|
+
return;
|
|
4308
4335
|
}
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
()
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
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
|
-
|
|
4318
|
-
|
|
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
|
-
|
|
4334
|
-
|
|
4350
|
+
for (const child of this.children) {
|
|
4351
|
+
const elem = child.find(element);
|
|
4352
|
+
if (elem) {
|
|
4353
|
+
return elem;
|
|
4354
|
+
}
|
|
4335
4355
|
}
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4370
|
+
getAllLookupScopes() {
|
|
4371
|
+
return [
|
|
4372
|
+
this.shadowRoot ?? this.element,
|
|
4373
|
+
...Array.from(this.children).map((tree) => tree.getAllLookupScopes())
|
|
4374
|
+
].flat();
|
|
4350
4375
|
}
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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) =>
|
|
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/
|
|
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
|
|
5159
|
+
import logger16 from "@wdio/logger";
|
|
4539
5160
|
import { getBrowserObject as getBrowserObject9 } from "@wdio/utils";
|
|
4540
|
-
var
|
|
5161
|
+
var log16 = logger16("webdriverio:throttle");
|
|
4541
5162
|
async function throttle(params) {
|
|
4542
|
-
|
|
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
|
-
|
|
4867
|
-
return
|
|
4868
|
-
|
|
4869
|
-
|
|
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
|
|
4886
|
-
const context = await
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
5468
|
+
log17.warn("The percentage to swipe should be a number.");
|
|
5033
5469
|
} else if (percentage < 0 || percentage > 1) {
|
|
5034
|
-
|
|
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
|
|
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
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
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/
|
|
5537
|
+
// src/commands/mobile/tap.ts
|
|
5314
5538
|
import logger18 from "@wdio/logger";
|
|
5315
|
-
import { getBrowserObject as
|
|
5539
|
+
import { getBrowserObject as getBrowserObject11 } from "@wdio/utils";
|
|
5316
5540
|
var log18 = logger18("webdriver");
|
|
5317
|
-
function
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
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
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
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
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
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
|
-
|
|
5338
|
-
|
|
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
|
-
|
|
5341
|
-
|
|
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
|
-
|
|
5366
|
-
|
|
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
|
-
|
|
5405
|
-
|
|
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 (
|
|
5422
|
-
|
|
5585
|
+
if (options) {
|
|
5586
|
+
log18.warn("The options object is not supported in Web environments and will be ignored.");
|
|
5423
5587
|
}
|
|
5424
|
-
return await
|
|
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
|
-
|
|
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
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
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
|
-
|
|
5478
|
-
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
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
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
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
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
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
|
|
5524
|
-
const {
|
|
5525
|
-
if (
|
|
5526
|
-
return await
|
|
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
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
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
|
-
|
|
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
|
|
5567
|
-
|
|
5568
|
-
|
|
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
|
|
5572
|
-
|
|
5573
|
-
return this.getElementComputedLabel(this.elementId);
|
|
5574
|
-
}
|
|
5727
|
+
// src/commands/element/$$.ts
|
|
5728
|
+
var $$2 = $$;
|
|
5575
5729
|
|
|
5576
|
-
// src/commands/element
|
|
5577
|
-
|
|
5578
|
-
|
|
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/
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5744
|
+
// src/commands/element/clearValue.ts
|
|
5745
|
+
function clearValue() {
|
|
5746
|
+
return this.elementClear(this.elementId);
|
|
5747
|
+
}
|
|
5585
5748
|
|
|
5586
|
-
// src/
|
|
5749
|
+
// src/commands/element/click.ts
|
|
5587
5750
|
import logger19 from "@wdio/logger";
|
|
5588
|
-
import
|
|
5589
|
-
var
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
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
|
-
|
|
5633
|
-
this.#shadowRoots.delete(params.context);
|
|
5758
|
+
return actionClick(this, options);
|
|
5634
5759
|
}
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
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 (
|
|
5644
|
-
|
|
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
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
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
|
-
|
|
5732
|
-
|
|
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
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
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
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
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
|
-
|
|
5771
|
-
|
|
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
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
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
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
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
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
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
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
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
|
-
|
|
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
|
|
5892
|
-
const
|
|
5893
|
-
const context = await
|
|
5894
|
-
const shadowRootElementPairs =
|
|
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:
|
|
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
|
|
7319
|
-
const
|
|
7320
|
-
const context = await
|
|
7321
|
-
const shadowRoots =
|
|
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
|
|
7357
|
-
const
|
|
7358
|
-
const context = await
|
|
7359
|
-
const shadowRoots =
|
|
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
|
|
7392
|
-
if (this.isBidi && typeof selector === "string" && !selector.startsWith(DEEP_SELECTOR) && !
|
|
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
|
|
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
|
|
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 } = {}) {
|