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