webdriver 9.3.1 → 9.4.0
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/bidi/socket.d.ts +1 -6
- package/build/bidi/socket.d.ts.map +1 -1
- package/build/browser.d.ts +4 -0
- package/build/browser.d.ts.map +1 -0
- package/build/constants.d.ts.map +1 -1
- package/build/environment.d.ts +23 -0
- package/build/environment.d.ts.map +1 -0
- package/build/index.cjs +7 -264
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +778 -763
- package/build/node.d.ts +4 -0
- package/build/node.d.ts.map +1 -0
- package/build/node.js +1730 -0
- package/build/request/node.d.ts +13 -0
- package/build/request/node.d.ts.map +1 -0
- package/build/request/request.d.ts +15 -4
- package/build/request/request.d.ts.map +1 -1
- package/build/request/types.d.ts +3 -0
- package/build/request/types.d.ts.map +1 -1
- package/build/request/web.d.ts +8 -0
- package/build/request/web.d.ts.map +1 -0
- package/build/utils.d.ts.map +1 -1
- package/package.json +8 -4
- package/build/request/index.d.ts +0 -26
- package/build/request/index.d.ts.map +0 -1
package/build/index.js
CHANGED
|
@@ -1,131 +1,301 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __typeError = (msg) => {
|
|
3
|
+
throw TypeError(msg);
|
|
4
|
+
};
|
|
5
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
8
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
9
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
10
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
11
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
12
|
+
var __privateWrapper = (obj, member, setter, getter) => ({
|
|
13
|
+
set _(value) {
|
|
14
|
+
__privateSet(obj, member, value, setter);
|
|
15
|
+
},
|
|
16
|
+
get _() {
|
|
17
|
+
return __privateGet(obj, member, getter);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
1
21
|
// src/index.ts
|
|
2
|
-
import
|
|
22
|
+
import logger4 from "@wdio/logger";
|
|
3
23
|
import { webdriverMonad, sessionEnvironmentDetector, startWebDriver, isBidi } from "@wdio/utils";
|
|
4
24
|
import { validateConfig } from "@wdio/config";
|
|
5
25
|
|
|
6
26
|
// src/command.ts
|
|
7
|
-
import
|
|
27
|
+
import logger from "@wdio/logger";
|
|
8
28
|
import { commandCallStructure, isValidParameter, getArgumentType } from "@wdio/utils";
|
|
9
|
-
import { WebDriverBidiProtocol
|
|
10
|
-
|
|
11
|
-
// src/request/request.ts
|
|
12
|
-
import { performance } from "node:perf_hooks";
|
|
13
|
-
import dns from "node:dns";
|
|
14
|
-
|
|
15
|
-
// src/request/index.ts
|
|
16
|
-
import path from "node:path";
|
|
17
|
-
import { EventEmitter } from "node:events";
|
|
18
|
-
import { WebDriverProtocol as WebDriverProtocol2 } from "@wdio/protocols";
|
|
19
|
-
import { URL } from "node:url";
|
|
20
|
-
import logger3 from "@wdio/logger";
|
|
21
|
-
import { transformCommandLogResult as transformCommandLogResult2, sleep } from "@wdio/utils";
|
|
22
|
-
|
|
23
|
-
// src/request/error.ts
|
|
24
|
-
import { transformCommandLogResult } from "@wdio/utils";
|
|
25
|
-
|
|
26
|
-
// src/request/constants.ts
|
|
27
|
-
var RETRYABLE_STATUS_CODES = [408, 413, 429, 500, 502, 503, 504];
|
|
28
|
-
var RETRYABLE_ERROR_CODES = [
|
|
29
|
-
"ETIMEDOUT",
|
|
30
|
-
"ECONNRESET",
|
|
31
|
-
"EADDRINUSE",
|
|
32
|
-
"ECONNREFUSED",
|
|
33
|
-
"EPIPE",
|
|
34
|
-
"ENOTFOUND",
|
|
35
|
-
"ENETUNREACH",
|
|
36
|
-
"EAI_AGAIN",
|
|
37
|
-
// additional error codes we like to retry
|
|
38
|
-
"UND_ERR_CONNECT_TIMEOUT",
|
|
39
|
-
"UND_ERR_SOCKET"
|
|
40
|
-
];
|
|
41
|
-
var REG_EXPS = {
|
|
42
|
-
commandName: /.*\/session\/[0-9a-f-]+\/(.*)/,
|
|
43
|
-
execFn: /return \(([\s\S]*)\)\.apply\(null, arguments\)/
|
|
44
|
-
};
|
|
29
|
+
import { WebDriverBidiProtocol } from "@wdio/protocols";
|
|
45
30
|
|
|
46
|
-
// src/
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
#getExecCmdName() {
|
|
59
|
-
const { href } = this.url;
|
|
60
|
-
const res = href.match(REG_EXPS.commandName) || [];
|
|
61
|
-
return res[1] || href;
|
|
62
|
-
}
|
|
63
|
-
#getExecCmdArgs(requestOptions) {
|
|
64
|
-
const { body: cmdJson } = requestOptions;
|
|
65
|
-
if (typeof cmdJson !== "object") {
|
|
66
|
-
return "";
|
|
67
|
-
}
|
|
68
|
-
const transformedRes = transformCommandLogResult(cmdJson);
|
|
69
|
-
if (typeof transformedRes === "string") {
|
|
70
|
-
return transformedRes;
|
|
71
|
-
}
|
|
72
|
-
if (typeof cmdJson.script === "string") {
|
|
73
|
-
const scriptRes = cmdJson.script.match(REG_EXPS.execFn) || [];
|
|
74
|
-
return `"${scriptRes[1] || cmdJson.script}"`;
|
|
31
|
+
// src/environment.ts
|
|
32
|
+
var isNode = !!(typeof process !== "undefined" && process.version);
|
|
33
|
+
var environment = {
|
|
34
|
+
value: {
|
|
35
|
+
get Request() {
|
|
36
|
+
throw new Error("Request is not available in this environment");
|
|
37
|
+
},
|
|
38
|
+
get Socket() {
|
|
39
|
+
throw new Error("Socket is not available in this environment");
|
|
40
|
+
},
|
|
41
|
+
get variables() {
|
|
42
|
+
return {};
|
|
75
43
|
}
|
|
76
|
-
return Object.keys(cmdJson).length ? `"${JSON.stringify(cmdJson)}"` : "";
|
|
77
44
|
}
|
|
78
45
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
46
|
+
|
|
47
|
+
// src/command.ts
|
|
48
|
+
var log = logger("webdriver");
|
|
49
|
+
var BIDI_COMMANDS = Object.values(WebDriverBidiProtocol).map((def) => def.socket.command);
|
|
50
|
+
function command_default(method, endpointUri, commandInfo, doubleEncodeVariables = false) {
|
|
51
|
+
const { command, deprecated, ref, parameters, variables = [], isHubCommand = false } = commandInfo;
|
|
52
|
+
return async function protocolCommand(...args) {
|
|
53
|
+
const isBidiCommand = BIDI_COMMANDS.includes(command);
|
|
54
|
+
let endpoint = endpointUri;
|
|
55
|
+
const commandParams = [...variables.map((v) => Object.assign(v, {
|
|
56
|
+
/**
|
|
57
|
+
* url variables are:
|
|
58
|
+
*/
|
|
59
|
+
required: true,
|
|
60
|
+
// always required as they are part of the endpoint
|
|
61
|
+
type: "string"
|
|
62
|
+
// have to be always type of string
|
|
63
|
+
})), ...parameters];
|
|
64
|
+
const commandUsage = "".concat(command, "(").concat(commandParams.map((p) => p.name).join(", "), ")");
|
|
65
|
+
const moreInfo = "\n\nFor more info see ".concat(ref, "\n");
|
|
66
|
+
const body = {};
|
|
67
|
+
if (typeof deprecated === "string" && !process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS) {
|
|
68
|
+
const warning = deprecated.replace("This command", 'The "'.concat(command, '" command'));
|
|
69
|
+
log.warn(warning);
|
|
70
|
+
console.warn("\u26A0\uFE0F [WEBDRIVERIO DEPRECATION NOTICE] ".concat(warning));
|
|
89
71
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (errorCode) {
|
|
95
|
-
this.code = errorCode;
|
|
96
|
-
this.message = errorCode === "UND_ERR_CONNECT_TIMEOUT" ? 'Request timed out! Consider increasing the "connectionRetryTimeout" option.' : "Request failed with error code " + errorCode;
|
|
72
|
+
if (isBidiCommand) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
'Failed to execute WebDriver Bidi command "'.concat(command, '" as no Bidi session ') + 'was established. Make sure you enable it by setting "webSocketUrl: true" in your capabilities and verify that your environment and browser supports it.'
|
|
75
|
+
);
|
|
97
76
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
constructor(response, url, opts) {
|
|
105
|
-
const errorObj = !response.body ? new Error("Response has empty body") : typeof response.body === "string" && response.body.length ? new Error(response.body) : typeof response.body !== "object" ? new Error("Unknown error") : response.body.value || response.body;
|
|
106
|
-
let errorMessage = errorObj.message || errorObj.error || errorObj.class || "unknown error";
|
|
107
|
-
if (typeof errorMessage === "string" && errorMessage.includes("invalid locator")) {
|
|
108
|
-
const requestOptions = opts.body;
|
|
109
|
-
errorMessage = `The selector "${requestOptions.value}" used with strategy "${requestOptions.using}" is invalid!`;
|
|
77
|
+
const minAllowedParams = commandParams.filter((param) => param.required).length;
|
|
78
|
+
if (args.length < minAllowedParams || args.length > commandParams.length) {
|
|
79
|
+
const parameterDescription = commandParams.length ? "\n\nProperty Description:\n".concat(commandParams.map((p) => ' "'.concat(p.name, '" (').concat(p.type, "): ").concat(p.description)).join("\n")) : "";
|
|
80
|
+
throw new Error(
|
|
81
|
+
"Wrong parameters applied for ".concat(command, "\n") + "Usage: ".concat(commandUsage) + parameterDescription + moreInfo
|
|
82
|
+
);
|
|
110
83
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
84
|
+
for (const [it, arg] of Object.entries(args)) {
|
|
85
|
+
if (isBidiCommand) {
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
const i = parseInt(it, 10);
|
|
89
|
+
const commandParam = commandParams[i];
|
|
90
|
+
if (!isValidParameter(arg, commandParam.type)) {
|
|
91
|
+
if (typeof arg === "undefined" && !commandParam.required) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const actual = commandParam.type.endsWith("[]") ? "(".concat((Array.isArray(arg) ? arg : [arg]).map((a) => getArgumentType(a)), ")[]") : getArgumentType(arg);
|
|
95
|
+
throw new Error(
|
|
96
|
+
'Malformed type for "'.concat(commandParam.name, '" parameter of command ').concat(command, "\n") + "Expected: ".concat(commandParam.type, "\n") + "Actual: ".concat(actual) + moreInfo
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
if (i < variables.length) {
|
|
100
|
+
const encodedArg = doubleEncodeVariables ? encodeURIComponent(encodeURIComponent(arg)) : encodeURIComponent(arg);
|
|
101
|
+
endpoint = endpoint.replace(":".concat(commandParams[i].name), encodedArg);
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
body[commandParams[i].name] = arg;
|
|
118
105
|
}
|
|
119
|
-
|
|
120
|
-
this.
|
|
121
|
-
this.
|
|
122
|
-
|
|
106
|
+
const request = new environment.value.Request(method, endpoint, body, isHubCommand);
|
|
107
|
+
request.on("performance", (...args2) => this.emit("request.performance", ...args2));
|
|
108
|
+
this.emit("command", { command, method, endpoint, body });
|
|
109
|
+
log.info("COMMAND", commandCallStructure(command, args));
|
|
110
|
+
return request.makeRequest(this.options, this.sessionId).then((result) => {
|
|
111
|
+
var _a, _b;
|
|
112
|
+
if (typeof result.value !== "undefined") {
|
|
113
|
+
let resultLog = result.value;
|
|
114
|
+
if (/screenshot|recording/i.test(command) && typeof result.value === "string" && result.value.length > 64) {
|
|
115
|
+
resultLog = "".concat(result.value.slice(0, 61), "...");
|
|
116
|
+
} else if (command === "executeScript" && body.script && body.script.includes("(() => window.__wdioEvents__)")) {
|
|
117
|
+
resultLog = "[".concat(result.value.length, " framework events captured]");
|
|
118
|
+
}
|
|
119
|
+
log.info("RESULT", resultLog);
|
|
120
|
+
}
|
|
121
|
+
this.emit("result", { command, method, endpoint, body, result });
|
|
122
|
+
if (command === "deleteSession") {
|
|
123
|
+
const browser = this;
|
|
124
|
+
(_a = browser._bidiHandler) == null ? void 0 : _a.close();
|
|
125
|
+
const shutdownDriver = ((_b = body.deleteSessionOpts) == null ? void 0 : _b.shutdownDriver) !== false;
|
|
126
|
+
if (shutdownDriver && "wdio:driverPID" in this.capabilities && this.capabilities["wdio:driverPID"]) {
|
|
127
|
+
log.info("Kill driver process with PID ".concat(this.capabilities["wdio:driverPID"]));
|
|
128
|
+
try {
|
|
129
|
+
const killedSuccessfully = process.kill(this.capabilities["wdio:driverPID"], "SIGKILL");
|
|
130
|
+
if (!killedSuccessfully) {
|
|
131
|
+
log.warn("Failed to kill driver process, manually clean-up might be required");
|
|
132
|
+
}
|
|
133
|
+
} catch (err) {
|
|
134
|
+
log.warn("Failed to kill driver process", err);
|
|
135
|
+
}
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
for (const handle of process._getActiveHandles()) {
|
|
138
|
+
if (handle.servername && handle.servername.includes("edgedl.me")) {
|
|
139
|
+
handle.destroy();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}, 10);
|
|
143
|
+
}
|
|
144
|
+
if (!process.env.WDIO_WORKER_ID) {
|
|
145
|
+
logger.clearLogger();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return result.value;
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/constants.ts
|
|
154
|
+
var DEFAULTS = {
|
|
155
|
+
/**
|
|
156
|
+
* protocol of automation driver
|
|
157
|
+
*/
|
|
158
|
+
protocol: {
|
|
159
|
+
type: "string",
|
|
160
|
+
default: "http",
|
|
161
|
+
match: /(http|https)/
|
|
162
|
+
},
|
|
163
|
+
/**
|
|
164
|
+
* hostname of automation driver
|
|
165
|
+
*/
|
|
166
|
+
hostname: {
|
|
167
|
+
type: "string",
|
|
168
|
+
default: "localhost"
|
|
169
|
+
},
|
|
170
|
+
/**
|
|
171
|
+
* port of automation driver
|
|
172
|
+
*/
|
|
173
|
+
port: {
|
|
174
|
+
type: "number"
|
|
175
|
+
},
|
|
176
|
+
/**
|
|
177
|
+
* path to WebDriver endpoints
|
|
178
|
+
*/
|
|
179
|
+
path: {
|
|
180
|
+
type: "string",
|
|
181
|
+
validate: (path2) => {
|
|
182
|
+
if (!path2.startsWith("/")) {
|
|
183
|
+
throw new TypeError('The option "path" needs to start with a "/"');
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
},
|
|
187
|
+
default: "/"
|
|
188
|
+
},
|
|
189
|
+
/**
|
|
190
|
+
* A key-value store of query parameters to be added to every selenium request
|
|
191
|
+
*/
|
|
192
|
+
queryParams: {
|
|
193
|
+
type: "object"
|
|
194
|
+
},
|
|
195
|
+
/**
|
|
196
|
+
* cloud user if applicable
|
|
197
|
+
*/
|
|
198
|
+
user: {
|
|
199
|
+
type: "string"
|
|
200
|
+
},
|
|
201
|
+
/**
|
|
202
|
+
* access key to user
|
|
203
|
+
*/
|
|
204
|
+
key: {
|
|
205
|
+
type: "string"
|
|
206
|
+
},
|
|
207
|
+
/**
|
|
208
|
+
* capability of WebDriver session
|
|
209
|
+
*/
|
|
210
|
+
capabilities: {
|
|
211
|
+
type: "object",
|
|
212
|
+
required: true
|
|
213
|
+
},
|
|
214
|
+
/**
|
|
215
|
+
* Level of logging verbosity
|
|
216
|
+
*/
|
|
217
|
+
logLevel: {
|
|
218
|
+
type: "string",
|
|
219
|
+
default: "info",
|
|
220
|
+
match: /(trace|debug|info|warn|error|silent)/
|
|
221
|
+
},
|
|
222
|
+
/**
|
|
223
|
+
* directory for log files
|
|
224
|
+
*/
|
|
225
|
+
outputDir: {
|
|
226
|
+
type: "string"
|
|
227
|
+
},
|
|
228
|
+
/**
|
|
229
|
+
* Timeout for any WebDriver request to a driver or grid
|
|
230
|
+
*/
|
|
231
|
+
connectionRetryTimeout: {
|
|
232
|
+
type: "number",
|
|
233
|
+
default: 12e4
|
|
234
|
+
},
|
|
235
|
+
/**
|
|
236
|
+
* Count of request retries to the Selenium server
|
|
237
|
+
*/
|
|
238
|
+
connectionRetryCount: {
|
|
239
|
+
type: "number",
|
|
240
|
+
default: 3
|
|
241
|
+
},
|
|
242
|
+
/**
|
|
243
|
+
* Override default agent
|
|
244
|
+
*/
|
|
245
|
+
logLevels: {
|
|
246
|
+
type: "object"
|
|
247
|
+
},
|
|
248
|
+
/**
|
|
249
|
+
* Pass custom headers
|
|
250
|
+
*/
|
|
251
|
+
headers: {
|
|
252
|
+
type: "object"
|
|
253
|
+
},
|
|
254
|
+
/**
|
|
255
|
+
* Function transforming the request options before the request is made
|
|
256
|
+
*/
|
|
257
|
+
transformRequest: {
|
|
258
|
+
type: "function",
|
|
259
|
+
default: (requestOptions) => requestOptions
|
|
260
|
+
},
|
|
261
|
+
/**
|
|
262
|
+
* Function transforming the response object after it is received
|
|
263
|
+
*/
|
|
264
|
+
transformResponse: {
|
|
265
|
+
type: "function",
|
|
266
|
+
default: (response) => response
|
|
267
|
+
},
|
|
268
|
+
/**
|
|
269
|
+
* Appium direct connect options server (https://appiumpro.com/editions/86-connecting-directly-to-appium-hosts-in-distributed-environments)
|
|
270
|
+
* Whether to allow direct connect caps to adjust endpoint details (Appium only)
|
|
271
|
+
*/
|
|
272
|
+
enableDirectConnect: {
|
|
273
|
+
type: "boolean",
|
|
274
|
+
default: true
|
|
275
|
+
},
|
|
276
|
+
/**
|
|
277
|
+
* Whether it requires SSL certificates to be valid in HTTP/s requests
|
|
278
|
+
* for an environment which cannot get process environment well.
|
|
279
|
+
*/
|
|
280
|
+
strictSSL: {
|
|
281
|
+
type: "boolean",
|
|
282
|
+
default: true
|
|
283
|
+
},
|
|
284
|
+
/**
|
|
285
|
+
* The path to the root of the cache directory. This directory is used to store all drivers that are downloaded
|
|
286
|
+
* when attempting to start a session.
|
|
287
|
+
*/
|
|
288
|
+
cacheDir: {
|
|
289
|
+
type: "string",
|
|
290
|
+
default: environment.value.variables.WEBDRIVER_CACHE_DIR
|
|
123
291
|
}
|
|
124
292
|
};
|
|
293
|
+
var ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf";
|
|
294
|
+
var SHADOW_ELEMENT_KEY = "shadow-6066-11e4-a52e-4f735466cecf";
|
|
125
295
|
|
|
126
296
|
// src/utils.ts
|
|
127
297
|
import { deepmergeCustom } from "deepmerge-ts";
|
|
128
|
-
import
|
|
298
|
+
import logger3 from "@wdio/logger";
|
|
129
299
|
import {
|
|
130
300
|
WebDriverProtocol,
|
|
131
301
|
MJsonWProtocol,
|
|
@@ -134,71 +304,32 @@ import {
|
|
|
134
304
|
SauceLabsProtocol,
|
|
135
305
|
SeleniumProtocol,
|
|
136
306
|
GeckoProtocol,
|
|
137
|
-
WebDriverBidiProtocol
|
|
307
|
+
WebDriverBidiProtocol as WebDriverBidiProtocol2
|
|
138
308
|
} from "@wdio/protocols";
|
|
139
309
|
import { CAPABILITY_KEYS } from "@wdio/protocols";
|
|
140
310
|
|
|
141
311
|
// src/bidi/core.ts
|
|
142
|
-
import
|
|
143
|
-
|
|
144
|
-
// src/bidi/socket.ts
|
|
145
|
-
var BrowserSocket = class {
|
|
146
|
-
#callbacks = /* @__PURE__ */ new Set();
|
|
147
|
-
#ws;
|
|
148
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
149
|
-
constructor(wsUrl, opts) {
|
|
150
|
-
this.#ws = new globalThis.WebSocket(wsUrl);
|
|
151
|
-
this.#ws.onmessage = this.handleMessage.bind(this);
|
|
152
|
-
}
|
|
153
|
-
handleMessage(event) {
|
|
154
|
-
for (const callback of this.#callbacks) {
|
|
155
|
-
callback(event.data);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
send(data) {
|
|
159
|
-
this.#ws.send(data);
|
|
160
|
-
}
|
|
161
|
-
on(event, callback) {
|
|
162
|
-
if (event === "open") {
|
|
163
|
-
this.#ws.onopen = callback;
|
|
164
|
-
} else if (event === "close") {
|
|
165
|
-
this.#ws.onclose = callback;
|
|
166
|
-
} else if (event === "error") {
|
|
167
|
-
this.#ws.onerror = callback;
|
|
168
|
-
} else {
|
|
169
|
-
this.#callbacks.add(callback);
|
|
170
|
-
}
|
|
171
|
-
return this;
|
|
172
|
-
}
|
|
173
|
-
off(event, callback) {
|
|
174
|
-
this.#callbacks.delete(callback);
|
|
175
|
-
return this;
|
|
176
|
-
}
|
|
177
|
-
close() {
|
|
178
|
-
this.#ws.close();
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
var socket_default = globalThis.window ? BrowserSocket : (await import("ws")).default;
|
|
182
|
-
|
|
183
|
-
// src/bidi/core.ts
|
|
184
|
-
var log = logger("webdriver");
|
|
312
|
+
import logger2 from "@wdio/logger";
|
|
313
|
+
var log2 = logger2("webdriver");
|
|
185
314
|
var RESPONSE_TIMEOUT = 1e3 * 60;
|
|
315
|
+
var _id, _ws, _waitForConnected, _webSocketUrl, _pendingCommands, _BidiCore_instances, handleResponse_fn;
|
|
186
316
|
var BidiCore = class {
|
|
187
|
-
#id = 0;
|
|
188
|
-
#ws;
|
|
189
|
-
#waitForConnected = Promise.resolve(false);
|
|
190
|
-
#webSocketUrl;
|
|
191
|
-
#pendingCommands = /* @__PURE__ */ new Map();
|
|
192
|
-
client;
|
|
193
|
-
/**
|
|
194
|
-
* @private
|
|
195
|
-
*/
|
|
196
|
-
_isConnected = false;
|
|
197
317
|
constructor(webSocketUrl, opts) {
|
|
198
|
-
this
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
this
|
|
318
|
+
__privateAdd(this, _BidiCore_instances);
|
|
319
|
+
__privateAdd(this, _id, 0);
|
|
320
|
+
__privateAdd(this, _ws);
|
|
321
|
+
__privateAdd(this, _waitForConnected, Promise.resolve(false));
|
|
322
|
+
__privateAdd(this, _webSocketUrl);
|
|
323
|
+
__privateAdd(this, _pendingCommands, /* @__PURE__ */ new Map());
|
|
324
|
+
__publicField(this, "client");
|
|
325
|
+
/**
|
|
326
|
+
* @private
|
|
327
|
+
*/
|
|
328
|
+
__publicField(this, "_isConnected", false);
|
|
329
|
+
__privateSet(this, _webSocketUrl, webSocketUrl);
|
|
330
|
+
log2.info("Connect to webSocketUrl ".concat(__privateGet(this, _webSocketUrl)));
|
|
331
|
+
__privateSet(this, _ws, new environment.value.Socket(__privateGet(this, _webSocketUrl), opts));
|
|
332
|
+
__privateGet(this, _ws).on("message", __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this));
|
|
202
333
|
}
|
|
203
334
|
/**
|
|
204
335
|
* We initiate the Bidi instance before a WebdriverIO instance is created.
|
|
@@ -213,36 +344,36 @@ var BidiCore = class {
|
|
|
213
344
|
this._isConnected = true;
|
|
214
345
|
return;
|
|
215
346
|
}
|
|
216
|
-
this
|
|
217
|
-
this
|
|
218
|
-
|
|
347
|
+
__privateSet(this, _waitForConnected, new Promise((resolve) => {
|
|
348
|
+
__privateGet(this, _ws).on("open", () => {
|
|
349
|
+
log2.info("Connected session to Bidi protocol");
|
|
219
350
|
this._isConnected = true;
|
|
220
351
|
resolve(this._isConnected);
|
|
221
352
|
});
|
|
222
|
-
this
|
|
223
|
-
|
|
353
|
+
__privateGet(this, _ws).on("error", (err) => {
|
|
354
|
+
log2.warn("Couldn't connect to Bidi protocol: ".concat(err.message));
|
|
224
355
|
this._isConnected = false;
|
|
225
356
|
resolve(this._isConnected);
|
|
226
357
|
});
|
|
227
|
-
});
|
|
228
|
-
return this
|
|
358
|
+
}));
|
|
359
|
+
return __privateGet(this, _waitForConnected);
|
|
229
360
|
}
|
|
230
361
|
close() {
|
|
231
362
|
if (!this._isConnected) {
|
|
232
363
|
return;
|
|
233
364
|
}
|
|
234
|
-
|
|
365
|
+
log2.info("Close Bidi connection to ".concat(__privateGet(this, _webSocketUrl)));
|
|
235
366
|
this._isConnected = false;
|
|
236
|
-
this
|
|
237
|
-
this
|
|
238
|
-
this
|
|
367
|
+
__privateGet(this, _ws).off("message", __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this));
|
|
368
|
+
__privateGet(this, _ws).close();
|
|
369
|
+
__privateGet(this, _ws).terminate();
|
|
239
370
|
}
|
|
240
371
|
reconnect(webSocketUrl, opts) {
|
|
241
|
-
|
|
372
|
+
log2.info("Reconnect to new Bidi session at ".concat(webSocketUrl));
|
|
242
373
|
this.close();
|
|
243
|
-
this
|
|
244
|
-
this
|
|
245
|
-
this
|
|
374
|
+
__privateSet(this, _webSocketUrl, webSocketUrl);
|
|
375
|
+
__privateSet(this, _ws, new environment.value.Socket(__privateGet(this, _webSocketUrl), opts));
|
|
376
|
+
__privateGet(this, _ws).on("message", __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this));
|
|
246
377
|
return this.connect();
|
|
247
378
|
}
|
|
248
379
|
/**
|
|
@@ -250,10 +381,10 @@ var BidiCore = class {
|
|
|
250
381
|
* @returns a promise that resolves once the connection to WebDriver Bidi protocol was established
|
|
251
382
|
*/
|
|
252
383
|
waitForConnected() {
|
|
253
|
-
return this
|
|
384
|
+
return __privateGet(this, _waitForConnected);
|
|
254
385
|
}
|
|
255
386
|
get socket() {
|
|
256
|
-
return this
|
|
387
|
+
return __privateGet(this, _ws);
|
|
257
388
|
}
|
|
258
389
|
get isConnected() {
|
|
259
390
|
return this._isConnected;
|
|
@@ -263,76 +394,81 @@ var BidiCore = class {
|
|
|
263
394
|
* @internal
|
|
264
395
|
*/
|
|
265
396
|
get __handleResponse() {
|
|
266
|
-
return this
|
|
267
|
-
}
|
|
268
|
-
#handleResponse(data) {
|
|
269
|
-
try {
|
|
270
|
-
const payload = JSON.parse(data.toString());
|
|
271
|
-
if (!payload.id) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
log.info("BIDI RESULT", data.toString());
|
|
275
|
-
this.client?.emit("bidiResult", payload);
|
|
276
|
-
const resolve = this.#pendingCommands.get(payload.id);
|
|
277
|
-
if (!resolve) {
|
|
278
|
-
log.error(`Couldn't resolve command with id ${payload.id}`);
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
this.#pendingCommands.delete(payload.id);
|
|
282
|
-
resolve(payload);
|
|
283
|
-
} catch (err) {
|
|
284
|
-
const error = err instanceof Error ? err : new Error(`Failed parse message: ${String(err)}`);
|
|
285
|
-
log.error(`Failed parse message: ${error.message}`);
|
|
286
|
-
}
|
|
397
|
+
return __privateMethod(this, _BidiCore_instances, handleResponse_fn).bind(this);
|
|
287
398
|
}
|
|
288
399
|
async send(params) {
|
|
289
400
|
const id = this.sendAsync(params);
|
|
290
|
-
const failError = new Error(
|
|
401
|
+
const failError = new Error('WebDriver Bidi command "'.concat(params.method, '" failed'));
|
|
291
402
|
const payload = await new Promise((resolve, reject) => {
|
|
292
403
|
const t = setTimeout(() => {
|
|
293
|
-
reject(new Error(
|
|
294
|
-
this
|
|
404
|
+
reject(new Error("Command ".concat(params.method, " with id ").concat(id, " (with the following parameter: ").concat(JSON.stringify(params.params), ") timed out")));
|
|
405
|
+
__privateGet(this, _pendingCommands).delete(id);
|
|
295
406
|
}, RESPONSE_TIMEOUT);
|
|
296
|
-
this
|
|
407
|
+
__privateGet(this, _pendingCommands).set(id, (payload2) => {
|
|
297
408
|
clearTimeout(t);
|
|
298
409
|
resolve(payload2);
|
|
299
410
|
});
|
|
300
411
|
});
|
|
301
412
|
if (payload.error) {
|
|
302
|
-
failError.message +=
|
|
413
|
+
failError.message += " with error: ".concat(payload.error, " - ").concat(payload.message);
|
|
303
414
|
if (payload.stacktrace) {
|
|
304
|
-
const driverStack = payload.stacktrace.split("\n").filter(Boolean).map((line) =>
|
|
305
|
-
failError.stack +=
|
|
306
|
-
|
|
307
|
-
Driver Stack:
|
|
308
|
-
${driverStack}`;
|
|
415
|
+
const driverStack = payload.stacktrace.split("\n").filter(Boolean).map((line) => " at ".concat(line)).join("\n");
|
|
416
|
+
failError.stack += "\n\nDriver Stack:\n".concat(driverStack);
|
|
309
417
|
}
|
|
310
418
|
throw failError;
|
|
311
419
|
}
|
|
312
420
|
return payload;
|
|
313
421
|
}
|
|
314
422
|
sendAsync(params) {
|
|
423
|
+
var _a;
|
|
315
424
|
if (!this._isConnected) {
|
|
316
425
|
throw new Error("No connection to WebDriver Bidi was established");
|
|
317
426
|
}
|
|
318
|
-
|
|
319
|
-
const id = ++this
|
|
320
|
-
this.client
|
|
321
|
-
this
|
|
427
|
+
log2.info("BIDI COMMAND", ...parseBidiCommand(params));
|
|
428
|
+
const id = ++__privateWrapper(this, _id)._;
|
|
429
|
+
(_a = this.client) == null ? void 0 : _a.emit("bidiCommand", params);
|
|
430
|
+
__privateGet(this, _ws).send(JSON.stringify({ id, ...params }));
|
|
322
431
|
return id;
|
|
323
432
|
}
|
|
324
433
|
};
|
|
434
|
+
_id = new WeakMap();
|
|
435
|
+
_ws = new WeakMap();
|
|
436
|
+
_waitForConnected = new WeakMap();
|
|
437
|
+
_webSocketUrl = new WeakMap();
|
|
438
|
+
_pendingCommands = new WeakMap();
|
|
439
|
+
_BidiCore_instances = new WeakSet();
|
|
440
|
+
handleResponse_fn = function(data) {
|
|
441
|
+
var _a;
|
|
442
|
+
try {
|
|
443
|
+
const payload = JSON.parse(data.toString());
|
|
444
|
+
if (!payload.id) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
log2.info("BIDI RESULT", data.toString());
|
|
448
|
+
(_a = this.client) == null ? void 0 : _a.emit("bidiResult", payload);
|
|
449
|
+
const resolve = __privateGet(this, _pendingCommands).get(payload.id);
|
|
450
|
+
if (!resolve) {
|
|
451
|
+
log2.error("Couldn't resolve command with id ".concat(payload.id));
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
__privateGet(this, _pendingCommands).delete(payload.id);
|
|
455
|
+
resolve(payload);
|
|
456
|
+
} catch (err) {
|
|
457
|
+
const error = err instanceof Error ? err : new Error("Failed parse message: ".concat(String(err)));
|
|
458
|
+
log2.error("Failed parse message: ".concat(error.message));
|
|
459
|
+
}
|
|
460
|
+
};
|
|
325
461
|
function parseBidiCommand(params) {
|
|
326
462
|
const commandName = params.method;
|
|
327
463
|
if (commandName === "script.addPreloadScript") {
|
|
328
464
|
const param = params.params;
|
|
329
|
-
const logString =
|
|
465
|
+
const logString = "{ functionDeclaration: <PreloadScript[".concat(new TextEncoder().encode(param.functionDeclaration).length, " bytes]>, contexts: ").concat(JSON.stringify(param.contexts), " }");
|
|
330
466
|
return [commandName, logString];
|
|
331
467
|
} else if (commandName === "script.callFunction") {
|
|
332
468
|
const param = params.params;
|
|
333
469
|
const logString = JSON.stringify({
|
|
334
470
|
...param,
|
|
335
|
-
functionDeclaration:
|
|
471
|
+
functionDeclaration: "<Function[".concat(new TextEncoder().encode(param.functionDeclaration).length, " bytes]>")
|
|
336
472
|
});
|
|
337
473
|
return [commandName, logString];
|
|
338
474
|
}
|
|
@@ -864,7 +1000,7 @@ var BidiHandler = class extends BidiCore {
|
|
|
864
1000
|
};
|
|
865
1001
|
|
|
866
1002
|
// src/utils.ts
|
|
867
|
-
var
|
|
1003
|
+
var log3 = logger3("webdriver");
|
|
868
1004
|
var deepmerge = deepmergeCustom({ mergeArrays: false });
|
|
869
1005
|
var BROWSER_DRIVER_ERRORS = [
|
|
870
1006
|
"unknown command: wd/hub/session",
|
|
@@ -891,7 +1027,7 @@ async function startWebDriverSession(params) {
|
|
|
891
1027
|
capabilities.alwaysMatch.unhandledPromptBehavior = "ignore";
|
|
892
1028
|
}
|
|
893
1029
|
validateCapabilities(capabilities.alwaysMatch);
|
|
894
|
-
const sessionRequest = new
|
|
1030
|
+
const sessionRequest = new environment.value.Request(
|
|
895
1031
|
"POST",
|
|
896
1032
|
"/session",
|
|
897
1033
|
{ capabilities }
|
|
@@ -900,7 +1036,7 @@ async function startWebDriverSession(params) {
|
|
|
900
1036
|
try {
|
|
901
1037
|
response = await sessionRequest.makeRequest(params);
|
|
902
1038
|
} catch (err) {
|
|
903
|
-
|
|
1039
|
+
log3.error(err);
|
|
904
1040
|
const message = getSessionError(err, params);
|
|
905
1041
|
throw new Error("Failed to create session.\n" + message);
|
|
906
1042
|
}
|
|
@@ -909,7 +1045,8 @@ async function startWebDriverSession(params) {
|
|
|
909
1045
|
return { sessionId, capabilities: params.capabilities };
|
|
910
1046
|
}
|
|
911
1047
|
function validateCapabilities(capabilities) {
|
|
912
|
-
|
|
1048
|
+
var _a;
|
|
1049
|
+
const chromeArgs = ((_a = capabilities["goog:chromeOptions"]) == null ? void 0 : _a.args) || [];
|
|
913
1050
|
if (chromeArgs.includes("incognito") || chromeArgs.includes("--incognito")) {
|
|
914
1051
|
throw new Error(
|
|
915
1052
|
'Please remove "incognito" from `"goog:chromeOptions".args` as it is not supported running Chrome with WebDriver. WebDriver sessions are always incognito mode and do not persist across browser sessions.'
|
|
@@ -920,14 +1057,14 @@ function validateCapabilities(capabilities) {
|
|
|
920
1057
|
const invalidWebDriverCaps = Object.keys(capabilities).filter((cap) => !CAPABILITY_KEYS.includes(cap) && !cap.includes(":"));
|
|
921
1058
|
if (extensionCaps.length && invalidWebDriverCaps.length) {
|
|
922
1059
|
throw new Error(
|
|
923
|
-
|
|
1060
|
+
'Invalid or unsupported WebDriver capabilities found ("'.concat(invalidWebDriverCaps.join('", "'), '"). ') + 'Ensure to only use valid W3C WebDriver capabilities (see https://w3c.github.io/webdriver/#capabilities).If you run your tests on a remote vendor, like Sauce Labs or BrowserStack, make sure that you put them into vendor specific capabilities, e.g. "sauce:options" or "bstack:options". Please reach out to your vendor support team if you have further questions.'
|
|
924
1061
|
);
|
|
925
1062
|
}
|
|
926
1063
|
}
|
|
927
1064
|
}
|
|
928
1065
|
function isSuccessfulResponse(statusCode, body) {
|
|
929
1066
|
if (!body || typeof body.value === "undefined") {
|
|
930
|
-
|
|
1067
|
+
log3.debug("request failed due to missing body");
|
|
931
1068
|
return false;
|
|
932
1069
|
}
|
|
933
1070
|
if (body.status === 7 && body.value && body.value.message && (body.value.message.toLowerCase().startsWith("no such element") || // Appium
|
|
@@ -936,7 +1073,7 @@ function isSuccessfulResponse(statusCode, body) {
|
|
|
936
1073
|
return true;
|
|
937
1074
|
}
|
|
938
1075
|
if (body.status && body.status !== 0) {
|
|
939
|
-
|
|
1076
|
+
log3.debug("request failed due to status ".concat(body.status));
|
|
940
1077
|
return false;
|
|
941
1078
|
}
|
|
942
1079
|
const hasErrorResponse = body.value && (body.value.error || body.value.stackTrace || body.value.stacktrace);
|
|
@@ -947,7 +1084,7 @@ function isSuccessfulResponse(statusCode, body) {
|
|
|
947
1084
|
return true;
|
|
948
1085
|
}
|
|
949
1086
|
if (hasErrorResponse) {
|
|
950
|
-
|
|
1087
|
+
log3.debug("request failed due to response error:", body.value.error);
|
|
951
1088
|
return false;
|
|
952
1089
|
}
|
|
953
1090
|
return true;
|
|
@@ -964,7 +1101,7 @@ function getPrototype({ isW3C, isChromium, isFirefox, isMobile, isSauce, isSelen
|
|
|
964
1101
|
/**
|
|
965
1102
|
* enable Bidi protocol for W3C sessions
|
|
966
1103
|
*/
|
|
967
|
-
isW3C ?
|
|
1104
|
+
isW3C ? WebDriverBidiProtocol2 : {},
|
|
968
1105
|
/**
|
|
969
1106
|
* only apply mobile protocol if session is actually for mobile
|
|
970
1107
|
*/
|
|
@@ -1011,7 +1148,8 @@ function getEnvironmentVars({ isW3C, isMobile, isIOS, isAndroid, isFirefox, isSa
|
|
|
1011
1148
|
* by the session response.
|
|
1012
1149
|
*/
|
|
1013
1150
|
get: function() {
|
|
1014
|
-
|
|
1151
|
+
var _a;
|
|
1152
|
+
return Boolean((_a = this._bidiHandler) == null ? void 0 : _a.isConnected);
|
|
1015
1153
|
}
|
|
1016
1154
|
},
|
|
1017
1155
|
isChromium: { value: isChromium }
|
|
@@ -1024,7 +1162,7 @@ function setupDirectConnect(client) {
|
|
|
1024
1162
|
const directConnectPath = capabilities["appium:directConnectPath"];
|
|
1025
1163
|
const directConnectPort = capabilities["appium:directConnectPort"];
|
|
1026
1164
|
if (directConnectProtocol && directConnectHost && directConnectPort && (directConnectPath || directConnectPath === "")) {
|
|
1027
|
-
|
|
1165
|
+
log3.info("Found direct connect information in new session response. " + "Will connect to server at ".concat(directConnectProtocol, "://") + "".concat(directConnectHost, ":").concat(directConnectPort).concat(directConnectPath));
|
|
1028
1166
|
client.options.protocol = directConnectProtocol;
|
|
1029
1167
|
client.options.hostname = directConnectHost;
|
|
1030
1168
|
client.options.port = directConnectPort;
|
|
@@ -1032,228 +1170,380 @@ function setupDirectConnect(client) {
|
|
|
1032
1170
|
}
|
|
1033
1171
|
}
|
|
1034
1172
|
var getSessionError = (err, params = {}) => {
|
|
1173
|
+
var _a;
|
|
1035
1174
|
if (err.code === "ECONNREFUSED") {
|
|
1036
|
-
return
|
|
1037
|
-
It seems like the service failed to start or is rejecting any connections.`;
|
|
1175
|
+
return 'Unable to connect to "'.concat(params.protocol, "://").concat(params.hostname, ":").concat(params.port).concat(params.path, '", make sure browser driver is running on that address.') + "\nIt seems like the service failed to start or is rejecting any connections.";
|
|
1038
1176
|
}
|
|
1039
1177
|
if (err.message === "unhandled request") {
|
|
1040
|
-
return
|
|
1178
|
+
return 'The browser driver couldn\'t start the session. Make sure you have set the "path" correctly!';
|
|
1041
1179
|
}
|
|
1042
1180
|
if (!err.message) {
|
|
1043
1181
|
return "See wdio.* logs for more information.";
|
|
1044
1182
|
}
|
|
1045
|
-
if (err.message.includes("Whoops! The URL specified routes to this help page.")) {
|
|
1046
|
-
return "It seems you are running a Selenium Standalone server and point to a wrong path. Please set `path: '/wd/hub'` in your wdio.conf.js!";
|
|
1183
|
+
if (err.message.includes("Whoops! The URL specified routes to this help page.")) {
|
|
1184
|
+
return "It seems you are running a Selenium Standalone server and point to a wrong path. Please set `path: '/wd/hub'` in your wdio.conf.js!";
|
|
1185
|
+
}
|
|
1186
|
+
if (BROWSER_DRIVER_ERRORS.some((m) => err && err.message && err.message.includes(m))) {
|
|
1187
|
+
return "Make sure to set `path: '/'` in your wdio.conf.js!";
|
|
1188
|
+
}
|
|
1189
|
+
if (err.message.includes("Bad Request - Invalid Hostname") && err.message.includes("HTTP Error 400")) {
|
|
1190
|
+
return "Run edge driver on 127.0.0.1 instead of localhost, ex: --host=127.0.0.1, or set `hostname: 'localhost'` in your wdio.conf.js";
|
|
1191
|
+
}
|
|
1192
|
+
const w3cCapMessage = '\nMake sure to add vendor prefix like "goog:", "appium:", "moz:", etc to non W3C capabilities.\nSee more https://www.w3.org/TR/webdriver/#capabilities';
|
|
1193
|
+
if (err.message.includes("Illegal key values seen in w3c capabilities")) {
|
|
1194
|
+
return err.message + w3cCapMessage;
|
|
1195
|
+
}
|
|
1196
|
+
if (err.message === "Response has empty body") {
|
|
1197
|
+
return "Make sure to connect to valid hostname:port or the port is not in use.\nIf you use a grid server " + w3cCapMessage;
|
|
1198
|
+
}
|
|
1199
|
+
if (err.message.includes("failed serving request POST /wd/hub/session: Unauthorized") && ((_a = params.hostname) == null ? void 0 : _a.endsWith("saucelabs.com"))) {
|
|
1200
|
+
return "Session request was not authorized because you either did provide a wrong access key or tried to run in a region that has not been enabled for your user. If have registered a free trial account it is connected to a specific region. Ensure this region is set in your configuration (https://webdriver.io/docs/options.html#region).";
|
|
1201
|
+
}
|
|
1202
|
+
return err.message;
|
|
1203
|
+
};
|
|
1204
|
+
function initiateBidi(socketUrl, strictSSL = true) {
|
|
1205
|
+
socketUrl = socketUrl.replace("localhost", "127.0.0.1");
|
|
1206
|
+
const bidiReqOpts = strictSSL ? {} : { rejectUnauthorized: false };
|
|
1207
|
+
const handler = new BidiHandler(socketUrl, bidiReqOpts);
|
|
1208
|
+
handler.connect().then((isConnected) => isConnected && log3.info("Connected to WebDriver Bidi interface at ".concat(socketUrl)));
|
|
1209
|
+
return {
|
|
1210
|
+
_bidiHandler: { value: handler },
|
|
1211
|
+
...Object.values(WebDriverBidiProtocol2).map((def) => def.socket).reduce((acc, cur) => {
|
|
1212
|
+
acc[cur.command] = {
|
|
1213
|
+
value: function(...args) {
|
|
1214
|
+
const bidiFn = handler[cur.command];
|
|
1215
|
+
handler.attachClient(this);
|
|
1216
|
+
this.emit(cur.command, args);
|
|
1217
|
+
return bidiFn == null ? void 0 : bidiFn.apply(handler, args);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
return acc;
|
|
1221
|
+
}, {})
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
function parseBidiMessage(data) {
|
|
1225
|
+
try {
|
|
1226
|
+
const payload = JSON.parse(data.toString());
|
|
1227
|
+
if (payload.type !== "event") {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
this.emit(payload.method, payload.params);
|
|
1231
|
+
} catch (err) {
|
|
1232
|
+
log3.error("Failed parse WebDriver Bidi message: ".concat(err.message));
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// src/bidi/localTypes.ts
|
|
1237
|
+
var localTypes_exports = {};
|
|
1238
|
+
|
|
1239
|
+
// src/bidi/remoteTypes.ts
|
|
1240
|
+
var remoteTypes_exports = {};
|
|
1241
|
+
|
|
1242
|
+
// src/index.ts
|
|
1243
|
+
var log4 = logger4("webdriver");
|
|
1244
|
+
var WebDriver = class _WebDriver {
|
|
1245
|
+
static async newSession(options, modifier, userPrototype = {}, customCommandWrapper) {
|
|
1246
|
+
const envLogLevel = process.env.WDIO_LOG_LEVEL;
|
|
1247
|
+
options.logLevel = envLogLevel != null ? envLogLevel : options.logLevel;
|
|
1248
|
+
const params = validateConfig(DEFAULTS, options);
|
|
1249
|
+
if (params.logLevel && (!options.logLevels || !options.logLevels.webdriver)) {
|
|
1250
|
+
logger4.setLevel("webdriver", params.logLevel);
|
|
1251
|
+
}
|
|
1252
|
+
log4.info("Initiate new session using the WebDriver protocol");
|
|
1253
|
+
const driverProcess = await startWebDriver(params);
|
|
1254
|
+
const requestedCapabilities = { ...params.capabilities };
|
|
1255
|
+
const { sessionId, capabilities } = await startWebDriverSession(params);
|
|
1256
|
+
const environment2 = sessionEnvironmentDetector({ capabilities, requestedCapabilities });
|
|
1257
|
+
const environmentPrototype = getEnvironmentVars(environment2);
|
|
1258
|
+
const protocolCommands = getPrototype(environment2);
|
|
1259
|
+
if (driverProcess == null ? void 0 : driverProcess.pid) {
|
|
1260
|
+
capabilities["wdio:driverPID"] = driverProcess.pid;
|
|
1261
|
+
}
|
|
1262
|
+
const bidiPrototype = {};
|
|
1263
|
+
if (isBidi(requestedCapabilities, capabilities)) {
|
|
1264
|
+
log4.info("Register BiDi handler for session with id ".concat(sessionId));
|
|
1265
|
+
Object.assign(bidiPrototype, initiateBidi(capabilities.webSocketUrl, options.strictSSL));
|
|
1266
|
+
}
|
|
1267
|
+
const monad = webdriverMonad(
|
|
1268
|
+
{ ...params, requestedCapabilities },
|
|
1269
|
+
modifier,
|
|
1270
|
+
{
|
|
1271
|
+
...protocolCommands,
|
|
1272
|
+
...environmentPrototype,
|
|
1273
|
+
...userPrototype,
|
|
1274
|
+
...bidiPrototype
|
|
1275
|
+
}
|
|
1276
|
+
);
|
|
1277
|
+
const client = monad(sessionId, customCommandWrapper);
|
|
1278
|
+
if (isBidi(requestedCapabilities, capabilities)) {
|
|
1279
|
+
await client._bidiHandler.waitForConnected();
|
|
1280
|
+
client._bidiHandler.socket.on("message", parseBidiMessage.bind(client));
|
|
1281
|
+
}
|
|
1282
|
+
if (params.enableDirectConnect) {
|
|
1283
|
+
setupDirectConnect(client);
|
|
1284
|
+
}
|
|
1285
|
+
return client;
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* allows user to attach to existing sessions
|
|
1289
|
+
*/
|
|
1290
|
+
static attachToSession(options, modifier, userPrototype = {}, commandWrapper) {
|
|
1291
|
+
var _a, _b;
|
|
1292
|
+
if (!options || typeof options.sessionId !== "string") {
|
|
1293
|
+
throw new Error("sessionId is required to attach to existing session");
|
|
1294
|
+
}
|
|
1295
|
+
if (options.logLevel) {
|
|
1296
|
+
logger4.setLevel("webdriver", options.logLevel);
|
|
1297
|
+
}
|
|
1298
|
+
options.capabilities = options.capabilities || {};
|
|
1299
|
+
options.isW3C = options.isW3C === false ? false : true;
|
|
1300
|
+
options.protocol = options.protocol || DEFAULTS.protocol.default;
|
|
1301
|
+
options.hostname = options.hostname || DEFAULTS.hostname.default;
|
|
1302
|
+
options.port = options.port || DEFAULTS.port.default;
|
|
1303
|
+
options.path = options.path || DEFAULTS.path.default;
|
|
1304
|
+
const environment2 = sessionEnvironmentDetector({ capabilities: options.capabilities, requestedCapabilities: options.capabilities });
|
|
1305
|
+
options = Object.assign(environment2, options);
|
|
1306
|
+
const environmentPrototype = getEnvironmentVars(options);
|
|
1307
|
+
const protocolCommands = getPrototype(options);
|
|
1308
|
+
const bidiPrototype = {};
|
|
1309
|
+
if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
|
|
1310
|
+
const webSocketUrl = (_a = options.capabilities) == null ? void 0 : _a.webSocketUrl;
|
|
1311
|
+
log4.info("Register BiDi handler for session with id ".concat(options.sessionId));
|
|
1312
|
+
Object.assign(bidiPrototype, initiateBidi(webSocketUrl, options.strictSSL));
|
|
1313
|
+
}
|
|
1314
|
+
const prototype = { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...bidiPrototype };
|
|
1315
|
+
const monad = webdriverMonad(options, modifier, prototype);
|
|
1316
|
+
const client = monad(options.sessionId, commandWrapper);
|
|
1317
|
+
if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
|
|
1318
|
+
(_b = client._bidiHandler) == null ? void 0 : _b.socket.on("message", parseBidiMessage.bind(client));
|
|
1319
|
+
}
|
|
1320
|
+
return client;
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Changes The instance session id and browser capabilities for the new session
|
|
1324
|
+
* directly into the passed in browser object
|
|
1325
|
+
*
|
|
1326
|
+
* @param {object} instance the object we get from a new browser session.
|
|
1327
|
+
* @returns {string} the new session id of the browser
|
|
1328
|
+
*/
|
|
1329
|
+
static async reloadSession(instance, newCapabilities) {
|
|
1330
|
+
var _a;
|
|
1331
|
+
const capabilities = newCapabilities ? newCapabilities : Object.assign({}, instance.requestedCapabilities);
|
|
1332
|
+
let params = { ...instance.options, capabilities };
|
|
1333
|
+
for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
|
|
1334
|
+
if (prop in capabilities) {
|
|
1335
|
+
params = { ...params, [prop]: capabilities[prop] };
|
|
1336
|
+
delete capabilities[prop];
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
let driverProcess;
|
|
1340
|
+
if (params.hostname === "localhost" && (newCapabilities == null ? void 0 : newCapabilities.browserName)) {
|
|
1341
|
+
delete params.port;
|
|
1342
|
+
delete params.hostname;
|
|
1343
|
+
driverProcess = await startWebDriver(params);
|
|
1344
|
+
}
|
|
1345
|
+
const { sessionId, capabilities: newSessionCapabilities } = await startWebDriverSession(params);
|
|
1346
|
+
if (driverProcess == null ? void 0 : driverProcess.pid) {
|
|
1347
|
+
newSessionCapabilities["wdio:driverPID"] = driverProcess.pid;
|
|
1348
|
+
}
|
|
1349
|
+
for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
|
|
1350
|
+
if (prop in params) {
|
|
1351
|
+
instance.options[prop] = params[prop];
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
for (const prop in instance.requestedCapabilities) {
|
|
1355
|
+
delete instance.requestedCapabilities[prop];
|
|
1356
|
+
}
|
|
1357
|
+
const driverPid = instance.capabilities["wdio:driverPID"];
|
|
1358
|
+
instance.sessionId = sessionId;
|
|
1359
|
+
instance.capabilities = newSessionCapabilities;
|
|
1360
|
+
instance.capabilities["wdio:driverPID"] = driverPid;
|
|
1361
|
+
Object.assign(instance.requestedCapabilities, capabilities);
|
|
1362
|
+
if (isBidi(instance.requestedCapabilities || {}, instance.capabilities || {})) {
|
|
1363
|
+
const bidiReqOpts = instance.options.strictSSL ? {} : { rejectUnauthorized: false };
|
|
1364
|
+
await ((_a = instance._bidiHandler) == null ? void 0 : _a.reconnect(newSessionCapabilities.webSocketUrl, bidiReqOpts));
|
|
1365
|
+
}
|
|
1366
|
+
return sessionId;
|
|
1367
|
+
}
|
|
1368
|
+
static get WebDriver() {
|
|
1369
|
+
return _WebDriver;
|
|
1370
|
+
}
|
|
1371
|
+
};
|
|
1372
|
+
|
|
1373
|
+
// src/bidi/socket.ts
|
|
1374
|
+
var _callbacks, _ws2;
|
|
1375
|
+
var BrowserSocket = class {
|
|
1376
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1377
|
+
constructor(wsUrl, opts) {
|
|
1378
|
+
__privateAdd(this, _callbacks, /* @__PURE__ */ new Set());
|
|
1379
|
+
__privateAdd(this, _ws2);
|
|
1380
|
+
__privateSet(this, _ws2, new WebSocket(wsUrl));
|
|
1381
|
+
__privateGet(this, _ws2).onmessage = this.handleMessage.bind(this);
|
|
1382
|
+
}
|
|
1383
|
+
handleMessage(event) {
|
|
1384
|
+
for (const callback of __privateGet(this, _callbacks)) {
|
|
1385
|
+
callback(event.data);
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
send(data) {
|
|
1389
|
+
__privateGet(this, _ws2).send(data);
|
|
1390
|
+
}
|
|
1391
|
+
on(event, callback) {
|
|
1392
|
+
if (event === "open") {
|
|
1393
|
+
__privateGet(this, _ws2).onopen = callback;
|
|
1394
|
+
} else if (event === "close") {
|
|
1395
|
+
__privateGet(this, _ws2).onclose = callback;
|
|
1396
|
+
} else if (event === "error") {
|
|
1397
|
+
__privateGet(this, _ws2).onerror = callback;
|
|
1398
|
+
} else {
|
|
1399
|
+
__privateGet(this, _callbacks).add(callback);
|
|
1400
|
+
}
|
|
1401
|
+
return this;
|
|
1402
|
+
}
|
|
1403
|
+
off(event, callback) {
|
|
1404
|
+
__privateGet(this, _callbacks).delete(callback);
|
|
1405
|
+
return this;
|
|
1406
|
+
}
|
|
1407
|
+
close() {
|
|
1408
|
+
__privateGet(this, _ws2).close();
|
|
1047
1409
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1410
|
+
};
|
|
1411
|
+
_callbacks = new WeakMap();
|
|
1412
|
+
_ws2 = new WeakMap();
|
|
1413
|
+
|
|
1414
|
+
// src/request/request.ts
|
|
1415
|
+
import path from "node:path";
|
|
1416
|
+
import { EventEmitter } from "node:events";
|
|
1417
|
+
import logger5 from "@wdio/logger";
|
|
1418
|
+
import { transformCommandLogResult as transformCommandLogResult2, sleep } from "@wdio/utils";
|
|
1419
|
+
|
|
1420
|
+
// src/request/error.ts
|
|
1421
|
+
import { transformCommandLogResult } from "@wdio/utils";
|
|
1422
|
+
|
|
1423
|
+
// src/request/constants.ts
|
|
1424
|
+
var RETRYABLE_STATUS_CODES = [408, 413, 429, 500, 502, 503, 504];
|
|
1425
|
+
var RETRYABLE_ERROR_CODES = [
|
|
1426
|
+
"ETIMEDOUT",
|
|
1427
|
+
"ECONNRESET",
|
|
1428
|
+
"EADDRINUSE",
|
|
1429
|
+
"ECONNREFUSED",
|
|
1430
|
+
"EPIPE",
|
|
1431
|
+
"ENOTFOUND",
|
|
1432
|
+
"ENETUNREACH",
|
|
1433
|
+
"EAI_AGAIN",
|
|
1434
|
+
// additional error codes we like to retry
|
|
1435
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
1436
|
+
"UND_ERR_SOCKET"
|
|
1437
|
+
];
|
|
1438
|
+
var REG_EXPS = {
|
|
1439
|
+
commandName: /.*\/session\/[0-9a-f-]+\/(.*)/,
|
|
1440
|
+
execFn: /return \(([\s\S]*)\)\.apply\(null, arguments\)/
|
|
1441
|
+
};
|
|
1442
|
+
|
|
1443
|
+
// src/request/error.ts
|
|
1444
|
+
var _WebDriverError_instances, getExecCmdName_fn, getExecCmdArgs_fn;
|
|
1445
|
+
var WebDriverError = class extends Error {
|
|
1446
|
+
constructor() {
|
|
1447
|
+
super(...arguments);
|
|
1448
|
+
__privateAdd(this, _WebDriverError_instances);
|
|
1050
1449
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1450
|
+
/**
|
|
1451
|
+
* return timeout error with information about the executing command on which the test hangs
|
|
1452
|
+
*/
|
|
1453
|
+
computeErrorMessage() {
|
|
1454
|
+
const cmdName = __privateMethod(this, _WebDriverError_instances, getExecCmdName_fn).call(this);
|
|
1455
|
+
const cmdArgs = __privateMethod(this, _WebDriverError_instances, getExecCmdArgs_fn).call(this, this.opts);
|
|
1456
|
+
const cmdInfoMsg = 'when running "'.concat(cmdName, '" with method "').concat(this.opts.method, '"');
|
|
1457
|
+
const cmdArgsMsg = cmdArgs ? " and args ".concat(cmdArgs) : "";
|
|
1458
|
+
return "WebDriverError: ".concat(this.message, " ").concat(cmdInfoMsg).concat(cmdArgsMsg);
|
|
1053
1459
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1460
|
+
};
|
|
1461
|
+
_WebDriverError_instances = new WeakSet();
|
|
1462
|
+
getExecCmdName_fn = function() {
|
|
1463
|
+
const { href } = this.url;
|
|
1464
|
+
const res = href.match(REG_EXPS.commandName) || [];
|
|
1465
|
+
return res[1] || href;
|
|
1466
|
+
};
|
|
1467
|
+
getExecCmdArgs_fn = function(requestOptions) {
|
|
1468
|
+
const { body: cmdJson } = requestOptions;
|
|
1469
|
+
if (typeof cmdJson !== "object") {
|
|
1470
|
+
return "";
|
|
1057
1471
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1472
|
+
const transformedRes = transformCommandLogResult(cmdJson);
|
|
1473
|
+
if (typeof transformedRes === "string") {
|
|
1474
|
+
return transformedRes;
|
|
1060
1475
|
}
|
|
1061
|
-
if (
|
|
1062
|
-
|
|
1476
|
+
if (typeof cmdJson.script === "string") {
|
|
1477
|
+
const scriptRes = cmdJson.script.match(REG_EXPS.execFn) || [];
|
|
1478
|
+
return '"'.concat(scriptRes[1] || cmdJson.script, '"');
|
|
1063
1479
|
}
|
|
1064
|
-
return
|
|
1480
|
+
return Object.keys(cmdJson).length ? '"'.concat(JSON.stringify(cmdJson), '"') : "";
|
|
1065
1481
|
};
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
return {
|
|
1072
|
-
_bidiHandler: { value: handler },
|
|
1073
|
-
...Object.values(WebDriverBidiProtocol).map((def) => def.socket).reduce((acc, cur) => {
|
|
1074
|
-
acc[cur.command] = {
|
|
1075
|
-
value: function(...args) {
|
|
1076
|
-
const bidiFn = handler[cur.command];
|
|
1077
|
-
handler.attachClient(this);
|
|
1078
|
-
this.emit(cur.command, args);
|
|
1079
|
-
return bidiFn?.apply(handler, args);
|
|
1080
|
-
}
|
|
1081
|
-
};
|
|
1082
|
-
return acc;
|
|
1083
|
-
}, {})
|
|
1084
|
-
};
|
|
1085
|
-
}
|
|
1086
|
-
function parseBidiMessage(data) {
|
|
1087
|
-
try {
|
|
1088
|
-
const payload = JSON.parse(data.toString());
|
|
1089
|
-
if (payload.type !== "event") {
|
|
1090
|
-
return;
|
|
1482
|
+
var WebDriverRequestError = class extends WebDriverError {
|
|
1483
|
+
constructor(err, url, opts) {
|
|
1484
|
+
let message = err.message;
|
|
1485
|
+
if (err.message === "fetch failed") {
|
|
1486
|
+
message = "Failed to fetch [".concat(opts.method, "] ").concat(url.href, ": please make sure you have a WebDriver compatible server running on ").concat(url.origin);
|
|
1091
1487
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1488
|
+
super(message);
|
|
1489
|
+
__publicField(this, "url");
|
|
1490
|
+
__publicField(this, "opts");
|
|
1491
|
+
__publicField(this, "statusCode");
|
|
1492
|
+
__publicField(this, "body");
|
|
1493
|
+
__publicField(this, "code");
|
|
1494
|
+
this.url = url;
|
|
1495
|
+
this.opts = opts;
|
|
1496
|
+
const errorCode = typeof err.cause === "object" && err.cause && "code" in err.cause && typeof err.cause.code === "string" ? err.cause.code : "code" in err && typeof err.code === "string" ? err.code : void 0;
|
|
1497
|
+
if (errorCode) {
|
|
1498
|
+
this.code = errorCode;
|
|
1499
|
+
this.message = errorCode === "UND_ERR_CONNECT_TIMEOUT" ? 'Request timed out! Consider increasing the "connectionRetryTimeout" option.' : "Request failed with error code " + errorCode;
|
|
1500
|
+
}
|
|
1501
|
+
this.message = this.computeErrorMessage();
|
|
1095
1502
|
}
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
port: {
|
|
1120
|
-
type: "number"
|
|
1121
|
-
},
|
|
1122
|
-
/**
|
|
1123
|
-
* path to WebDriver endpoints
|
|
1124
|
-
*/
|
|
1125
|
-
path: {
|
|
1126
|
-
type: "string",
|
|
1127
|
-
validate: (path2) => {
|
|
1128
|
-
if (!path2.startsWith("/")) {
|
|
1129
|
-
throw new TypeError('The option "path" needs to start with a "/"');
|
|
1130
|
-
}
|
|
1131
|
-
return true;
|
|
1132
|
-
},
|
|
1133
|
-
default: "/"
|
|
1134
|
-
},
|
|
1135
|
-
/**
|
|
1136
|
-
* A key-value store of query parameters to be added to every selenium request
|
|
1137
|
-
*/
|
|
1138
|
-
queryParams: {
|
|
1139
|
-
type: "object"
|
|
1140
|
-
},
|
|
1141
|
-
/**
|
|
1142
|
-
* cloud user if applicable
|
|
1143
|
-
*/
|
|
1144
|
-
user: {
|
|
1145
|
-
type: "string"
|
|
1146
|
-
},
|
|
1147
|
-
/**
|
|
1148
|
-
* access key to user
|
|
1149
|
-
*/
|
|
1150
|
-
key: {
|
|
1151
|
-
type: "string"
|
|
1152
|
-
},
|
|
1153
|
-
/**
|
|
1154
|
-
* capability of WebDriver session
|
|
1155
|
-
*/
|
|
1156
|
-
capabilities: {
|
|
1157
|
-
type: "object",
|
|
1158
|
-
required: true
|
|
1159
|
-
},
|
|
1160
|
-
/**
|
|
1161
|
-
* Level of logging verbosity
|
|
1162
|
-
*/
|
|
1163
|
-
logLevel: {
|
|
1164
|
-
type: "string",
|
|
1165
|
-
default: "info",
|
|
1166
|
-
match: /(trace|debug|info|warn|error|silent)/
|
|
1167
|
-
},
|
|
1168
|
-
/**
|
|
1169
|
-
* directory for log files
|
|
1170
|
-
*/
|
|
1171
|
-
outputDir: {
|
|
1172
|
-
type: "string"
|
|
1173
|
-
},
|
|
1174
|
-
/**
|
|
1175
|
-
* Timeout for any WebDriver request to a driver or grid
|
|
1176
|
-
*/
|
|
1177
|
-
connectionRetryTimeout: {
|
|
1178
|
-
type: "number",
|
|
1179
|
-
default: 12e4
|
|
1180
|
-
},
|
|
1181
|
-
/**
|
|
1182
|
-
* Count of request retries to the Selenium server
|
|
1183
|
-
*/
|
|
1184
|
-
connectionRetryCount: {
|
|
1185
|
-
type: "number",
|
|
1186
|
-
default: 3
|
|
1187
|
-
},
|
|
1188
|
-
/**
|
|
1189
|
-
* Override default agent
|
|
1190
|
-
*/
|
|
1191
|
-
logLevels: {
|
|
1192
|
-
type: "object"
|
|
1193
|
-
},
|
|
1194
|
-
/**
|
|
1195
|
-
* Pass custom headers
|
|
1196
|
-
*/
|
|
1197
|
-
headers: {
|
|
1198
|
-
type: "object"
|
|
1199
|
-
},
|
|
1200
|
-
/**
|
|
1201
|
-
* Function transforming the request options before the request is made
|
|
1202
|
-
*/
|
|
1203
|
-
transformRequest: {
|
|
1204
|
-
type: "function",
|
|
1205
|
-
default: (requestOptions) => requestOptions
|
|
1206
|
-
},
|
|
1207
|
-
/**
|
|
1208
|
-
* Function transforming the response object after it is received
|
|
1209
|
-
*/
|
|
1210
|
-
transformResponse: {
|
|
1211
|
-
type: "function",
|
|
1212
|
-
default: (response) => response
|
|
1213
|
-
},
|
|
1214
|
-
/**
|
|
1215
|
-
* Appium direct connect options server (https://appiumpro.com/editions/86-connecting-directly-to-appium-hosts-in-distributed-environments)
|
|
1216
|
-
* Whether to allow direct connect caps to adjust endpoint details (Appium only)
|
|
1217
|
-
*/
|
|
1218
|
-
enableDirectConnect: {
|
|
1219
|
-
type: "boolean",
|
|
1220
|
-
default: true
|
|
1221
|
-
},
|
|
1222
|
-
/**
|
|
1223
|
-
* Whether it requires SSL certificates to be valid in HTTP/s requests
|
|
1224
|
-
* for an environment which cannot get process environment well.
|
|
1225
|
-
*/
|
|
1226
|
-
strictSSL: {
|
|
1227
|
-
type: "boolean",
|
|
1228
|
-
default: true
|
|
1229
|
-
},
|
|
1230
|
-
/**
|
|
1231
|
-
* The path to the root of the cache directory. This directory is used to store all drivers that are downloaded
|
|
1232
|
-
* when attempting to start a session.
|
|
1233
|
-
*/
|
|
1234
|
-
cacheDir: {
|
|
1235
|
-
type: "string",
|
|
1236
|
-
default: process.env.WEBDRIVER_CACHE_DIR || os.tmpdir()
|
|
1503
|
+
};
|
|
1504
|
+
var WebDriverResponseError = class _WebDriverResponseError extends WebDriverError {
|
|
1505
|
+
constructor(response, url, opts) {
|
|
1506
|
+
const errorObj = !response.body ? new Error("Response has empty body") : typeof response.body === "string" && response.body.length ? new Error(response.body) : typeof response.body !== "object" ? new Error("Unknown error") : response.body.value || response.body;
|
|
1507
|
+
let errorMessage = errorObj.message || errorObj.error || errorObj.class || "unknown error";
|
|
1508
|
+
if (typeof errorMessage === "string" && errorMessage.includes("invalid locator")) {
|
|
1509
|
+
const requestOptions = opts.body;
|
|
1510
|
+
errorMessage = 'The selector "'.concat(requestOptions.value, '" used with strategy "').concat(requestOptions.using, '" is invalid!');
|
|
1511
|
+
}
|
|
1512
|
+
super(errorMessage);
|
|
1513
|
+
__publicField(this, "url");
|
|
1514
|
+
__publicField(this, "opts");
|
|
1515
|
+
if (errorObj.error) {
|
|
1516
|
+
this.name = errorObj.error;
|
|
1517
|
+
} else if (errorMessage && errorMessage.includes("stale element reference")) {
|
|
1518
|
+
this.name = "stale element reference";
|
|
1519
|
+
} else {
|
|
1520
|
+
this.name = errorObj.name || "WebDriver Error";
|
|
1521
|
+
}
|
|
1522
|
+
Error.captureStackTrace(this, _WebDriverResponseError);
|
|
1523
|
+
this.url = url;
|
|
1524
|
+
this.opts = opts;
|
|
1525
|
+
this.message = this.computeErrorMessage();
|
|
1237
1526
|
}
|
|
1238
1527
|
};
|
|
1239
|
-
var ELEMENT_KEY = "element-6066-11e4-a52e-4f735466cecf";
|
|
1240
|
-
var SHADOW_ELEMENT_KEY = "shadow-6066-11e4-a52e-4f735466cecf";
|
|
1241
1528
|
|
|
1242
1529
|
// package.json
|
|
1243
1530
|
var package_default = {
|
|
1244
1531
|
name: "webdriver",
|
|
1245
|
-
version: "9.3.
|
|
1532
|
+
version: "9.3.1",
|
|
1246
1533
|
description: "A Node.js bindings implementation for the W3C WebDriver and Mobile JSONWire Protocol",
|
|
1247
1534
|
author: "Christian Bromann <mail@bromann.dev>",
|
|
1248
1535
|
homepage: "https://github.com/webdriverio/webdriverio/tree/main/packages/webdriver",
|
|
1249
1536
|
license: "MIT",
|
|
1250
|
-
main: "./build/index.cjs",
|
|
1251
1537
|
type: "module",
|
|
1538
|
+
main: "./build/index.cjs",
|
|
1252
1539
|
module: "./build/index.js",
|
|
1253
1540
|
exports: {
|
|
1254
1541
|
".": {
|
|
1255
1542
|
types: "./build/index.d.ts",
|
|
1256
|
-
|
|
1543
|
+
browserSource: "./src/browser.js",
|
|
1544
|
+
browser: "./build/index.js",
|
|
1545
|
+
importSource: "./src/node.ts",
|
|
1546
|
+
import: "./build/node.js",
|
|
1257
1547
|
requireSource: "./src/index.cts",
|
|
1258
1548
|
require: "./build/index.cjs"
|
|
1259
1549
|
}
|
|
@@ -1283,34 +1573,31 @@ var package_default = {
|
|
|
1283
1573
|
"@wdio/types": "workspace:*",
|
|
1284
1574
|
"@wdio/utils": "workspace:*",
|
|
1285
1575
|
"deepmerge-ts": "^7.0.3",
|
|
1576
|
+
undici: "^6.20.1",
|
|
1286
1577
|
ws: "^8.8.0"
|
|
1287
1578
|
}
|
|
1288
1579
|
};
|
|
1289
1580
|
|
|
1290
|
-
// src/request/
|
|
1581
|
+
// src/request/request.ts
|
|
1291
1582
|
var ERRORS_TO_EXCLUDE_FROM_RETRY = [
|
|
1292
1583
|
"detached shadow root",
|
|
1293
1584
|
"move target out of bounds"
|
|
1294
1585
|
];
|
|
1295
|
-
var COMMANDS_WITHOUT_RETRY = [
|
|
1296
|
-
findCommandPathByName("performActions")
|
|
1297
|
-
];
|
|
1298
1586
|
var DEFAULT_HEADERS = {
|
|
1299
1587
|
"Content-Type": "application/json; charset=utf-8",
|
|
1300
1588
|
"Connection": "keep-alive",
|
|
1301
1589
|
"Accept": "application/json",
|
|
1302
1590
|
"User-Agent": "webdriver/" + package_default.version
|
|
1303
1591
|
};
|
|
1304
|
-
var
|
|
1592
|
+
var log5 = logger5("webdriver");
|
|
1305
1593
|
var WebDriverRequest = class extends EventEmitter {
|
|
1306
|
-
#requestTimeout;
|
|
1307
|
-
body;
|
|
1308
|
-
method;
|
|
1309
|
-
endpoint;
|
|
1310
|
-
isHubCommand;
|
|
1311
|
-
requiresSessionId;
|
|
1312
1594
|
constructor(method, endpoint, body, isHubCommand = false) {
|
|
1313
1595
|
super();
|
|
1596
|
+
__publicField(this, "body");
|
|
1597
|
+
__publicField(this, "method");
|
|
1598
|
+
__publicField(this, "endpoint");
|
|
1599
|
+
__publicField(this, "isHubCommand");
|
|
1600
|
+
__publicField(this, "requiresSessionId");
|
|
1314
1601
|
this.body = body;
|
|
1315
1602
|
this.method = method;
|
|
1316
1603
|
this.endpoint = endpoint;
|
|
@@ -1318,25 +1605,15 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1318
1605
|
this.requiresSessionId = Boolean(this.endpoint.match(/:sessionId/));
|
|
1319
1606
|
}
|
|
1320
1607
|
async makeRequest(options, sessionId) {
|
|
1321
|
-
const { url, requestOptions } = await this.
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
fullRequestOptions = options.transformRequest(fullRequestOptions);
|
|
1328
|
-
}
|
|
1329
|
-
this.emit("request", fullRequestOptions);
|
|
1330
|
-
return this._request(url, fullRequestOptions, options.transformResponse, options.connectionRetryCount, 0);
|
|
1331
|
-
}
|
|
1332
|
-
async _createOptions(options, sessionId, isBrowser = false) {
|
|
1333
|
-
const controller = new AbortController();
|
|
1334
|
-
this.#requestTimeout = setTimeout(
|
|
1335
|
-
() => controller.abort(),
|
|
1336
|
-
options.connectionRetryTimeout || DEFAULTS.connectionRetryTimeout.default
|
|
1337
|
-
);
|
|
1608
|
+
const { url, requestOptions } = await this.createOptions(options, sessionId);
|
|
1609
|
+
this.emit("request", requestOptions);
|
|
1610
|
+
return this._request(url, requestOptions, options.transformResponse, options.connectionRetryCount, 0);
|
|
1611
|
+
}
|
|
1612
|
+
async createOptions(options, sessionId, isBrowser = false) {
|
|
1613
|
+
const timeout = options.connectionRetryTimeout || DEFAULTS.connectionRetryTimeout.default;
|
|
1338
1614
|
const requestOptions = {
|
|
1339
|
-
|
|
1615
|
+
method: this.method,
|
|
1616
|
+
signal: AbortSignal.timeout(timeout)
|
|
1340
1617
|
};
|
|
1341
1618
|
const requestHeaders = new Headers({
|
|
1342
1619
|
...DEFAULT_HEADERS,
|
|
@@ -1346,7 +1623,7 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1346
1623
|
if (this.body && (Object.keys(this.body).length || this.method === "POST")) {
|
|
1347
1624
|
const contentLength = Buffer.byteLength(JSON.stringify(this.body), "utf8");
|
|
1348
1625
|
requestOptions.body = this.body;
|
|
1349
|
-
requestHeaders.set("Content-Length",
|
|
1626
|
+
requestHeaders.set("Content-Length", "".concat(contentLength));
|
|
1350
1627
|
}
|
|
1351
1628
|
let endpoint = this.endpoint;
|
|
1352
1629
|
if (this.requiresSessionId) {
|
|
@@ -1355,7 +1632,7 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1355
1632
|
}
|
|
1356
1633
|
endpoint = endpoint.replace(":sessionId", sessionId);
|
|
1357
1634
|
}
|
|
1358
|
-
const url = new URL(
|
|
1635
|
+
const url = new URL("".concat(options.protocol, "://").concat(options.hostname, ":").concat(options.port).concat(this.isHubCommand ? this.endpoint : path.join(options.path || "", endpoint)));
|
|
1359
1636
|
if (searchParams) {
|
|
1360
1637
|
url.search = new URLSearchParams(searchParams).toString();
|
|
1361
1638
|
}
|
|
@@ -1363,29 +1640,48 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1363
1640
|
requestHeaders.set("Authorization", "Basic " + btoa(options.user + ":" + options.key));
|
|
1364
1641
|
}
|
|
1365
1642
|
requestOptions.headers = requestHeaders;
|
|
1366
|
-
return {
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1643
|
+
return {
|
|
1644
|
+
url,
|
|
1645
|
+
requestOptions: typeof options.transformRequest === "function" ? options.transformRequest(requestOptions) : requestOptions
|
|
1646
|
+
};
|
|
1370
1647
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1648
|
+
async _libRequest(url, opts) {
|
|
1649
|
+
var _a;
|
|
1650
|
+
try {
|
|
1651
|
+
const response = await this.fetch(url, {
|
|
1652
|
+
method: opts.method,
|
|
1653
|
+
body: JSON.stringify(opts.body),
|
|
1654
|
+
headers: opts.headers,
|
|
1655
|
+
signal: opts.signal
|
|
1656
|
+
});
|
|
1657
|
+
const resp = response.clone();
|
|
1658
|
+
return {
|
|
1659
|
+
statusCode: resp.status,
|
|
1660
|
+
body: (_a = await resp.json()) != null ? _a : {}
|
|
1661
|
+
};
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
if (!(err instanceof Error)) {
|
|
1664
|
+
throw new WebDriverRequestError(
|
|
1665
|
+
new Error("Failed to fetch ".concat(url.href, ": ").concat(err.message || err || "Unknown error")),
|
|
1666
|
+
url,
|
|
1667
|
+
opts
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
throw new WebDriverRequestError(err, url, opts);
|
|
1671
|
+
}
|
|
1373
1672
|
}
|
|
1374
1673
|
async _request(url, fullRequestOptions, transformResponse, totalRetryCount = 0, retryCount = 0) {
|
|
1375
|
-
|
|
1674
|
+
log5.info("[".concat(fullRequestOptions.method, "] ").concat(url.href));
|
|
1376
1675
|
if (fullRequestOptions.body && Object.keys(fullRequestOptions.body).length) {
|
|
1377
|
-
|
|
1676
|
+
log5.info("DATA", transformCommandLogResult2(fullRequestOptions.body));
|
|
1378
1677
|
}
|
|
1379
1678
|
const { ...requestLibOptions } = fullRequestOptions;
|
|
1380
|
-
const startTime =
|
|
1679
|
+
const startTime = performance.now();
|
|
1381
1680
|
let response = await this._libRequest(url, requestLibOptions).catch((err) => err);
|
|
1382
|
-
const durationMillisecond =
|
|
1383
|
-
if (this.#requestTimeout) {
|
|
1384
|
-
clearTimeout(this.#requestTimeout);
|
|
1385
|
-
}
|
|
1681
|
+
const durationMillisecond = performance.now() - startTime;
|
|
1386
1682
|
const retry = async (error2) => {
|
|
1387
1683
|
if (retryCount >= totalRetryCount || error2.message.includes("invalid session id")) {
|
|
1388
|
-
|
|
1684
|
+
log5.error(error2.message);
|
|
1389
1685
|
this.emit("response", { error: error2 });
|
|
1390
1686
|
this.emit("performance", { request: fullRequestOptions, durationMillisecond, success: false, error: error2, retryCount });
|
|
1391
1687
|
throw error2;
|
|
@@ -1396,8 +1692,8 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1396
1692
|
++retryCount;
|
|
1397
1693
|
this.emit("retry", { error: error2, retryCount });
|
|
1398
1694
|
this.emit("performance", { request: fullRequestOptions, durationMillisecond, success: false, error: error2, retryCount });
|
|
1399
|
-
|
|
1400
|
-
|
|
1695
|
+
log5.warn(error2.message);
|
|
1696
|
+
log5.info("Retrying ".concat(retryCount, "/").concat(totalRetryCount));
|
|
1401
1697
|
return this._request(url, fullRequestOptions, transformResponse, totalRetryCount, retryCount);
|
|
1402
1698
|
};
|
|
1403
1699
|
if (response instanceof Error) {
|
|
@@ -1425,7 +1721,7 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1425
1721
|
return { value: response.body || null };
|
|
1426
1722
|
}
|
|
1427
1723
|
if (error.name === "stale element reference") {
|
|
1428
|
-
|
|
1724
|
+
log5.warn("Request encountered a stale element - terminating request");
|
|
1429
1725
|
this.emit("response", { error });
|
|
1430
1726
|
this.emit("performance", { request: fullRequestOptions, durationMillisecond, success: false, error, retryCount });
|
|
1431
1727
|
throw error;
|
|
@@ -1436,310 +1732,29 @@ var WebDriverRequest = class extends EventEmitter {
|
|
|
1436
1732
|
return retry(error);
|
|
1437
1733
|
}
|
|
1438
1734
|
};
|
|
1439
|
-
function findCommandPathByName(commandName) {
|
|
1440
|
-
const command = Object.entries(WebDriverProtocol2).find(
|
|
1441
|
-
([, command2]) => Object.values(command2).find(
|
|
1442
|
-
(cmd) => cmd.command === commandName
|
|
1443
|
-
)
|
|
1444
|
-
);
|
|
1445
|
-
if (!command) {
|
|
1446
|
-
throw new Error(`Couldn't find command "${commandName}"`);
|
|
1447
|
-
}
|
|
1448
|
-
return command[0];
|
|
1449
|
-
}
|
|
1450
1735
|
|
|
1451
|
-
// src/request/
|
|
1452
|
-
if ("process" in globalThis && globalThis.process.versions?.node) {
|
|
1453
|
-
dns.setDefaultResultOrder("ipv4first");
|
|
1454
|
-
}
|
|
1736
|
+
// src/request/web.ts
|
|
1455
1737
|
var FetchRequest = class extends WebDriverRequest {
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
}
|
|
1459
|
-
async _libRequest(url, opts) {
|
|
1460
|
-
try {
|
|
1461
|
-
const response = await fetch(url, {
|
|
1462
|
-
method: opts.method,
|
|
1463
|
-
body: JSON.stringify(opts.body),
|
|
1464
|
-
headers: opts.headers,
|
|
1465
|
-
signal: opts.signal
|
|
1466
|
-
});
|
|
1467
|
-
const resp = response.clone();
|
|
1468
|
-
return {
|
|
1469
|
-
statusCode: resp.status,
|
|
1470
|
-
body: await resp.json() ?? {}
|
|
1471
|
-
};
|
|
1472
|
-
} catch (err) {
|
|
1473
|
-
if (!(err instanceof Error)) {
|
|
1474
|
-
throw new WebDriverRequestError(
|
|
1475
|
-
new Error(`Failed to fetch ${url.href}: ${err.message || err || "Unknown error"}`),
|
|
1476
|
-
url,
|
|
1477
|
-
opts
|
|
1478
|
-
);
|
|
1479
|
-
}
|
|
1480
|
-
throw new WebDriverRequestError(err, url, opts);
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
_libPerformanceNow() {
|
|
1484
|
-
return performance.now();
|
|
1738
|
+
fetch(url, opts) {
|
|
1739
|
+
return fetch(url, opts);
|
|
1485
1740
|
}
|
|
1486
1741
|
};
|
|
1487
1742
|
|
|
1488
|
-
// src/
|
|
1489
|
-
var
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
const isBidiCommand = BIDI_COMMANDS.includes(command);
|
|
1495
|
-
let endpoint = endpointUri;
|
|
1496
|
-
const commandParams = [...variables.map((v) => Object.assign(v, {
|
|
1497
|
-
/**
|
|
1498
|
-
* url variables are:
|
|
1499
|
-
*/
|
|
1500
|
-
required: true,
|
|
1501
|
-
// always required as they are part of the endpoint
|
|
1502
|
-
type: "string"
|
|
1503
|
-
// have to be always type of string
|
|
1504
|
-
})), ...parameters];
|
|
1505
|
-
const commandUsage = `${command}(${commandParams.map((p) => p.name).join(", ")})`;
|
|
1506
|
-
const moreInfo = `
|
|
1507
|
-
|
|
1508
|
-
For more info see ${ref}
|
|
1509
|
-
`;
|
|
1510
|
-
const body = {};
|
|
1511
|
-
if (typeof deprecated === "string" && !process.env.DISABLE_WEBDRIVERIO_DEPRECATION_WARNINGS) {
|
|
1512
|
-
const warning = deprecated.replace("This command", `The "${command}" command`);
|
|
1513
|
-
log4.warn(warning);
|
|
1514
|
-
console.warn(`\u26A0\uFE0F [WEBDRIVERIO DEPRECATION NOTICE] ${warning}`);
|
|
1515
|
-
}
|
|
1516
|
-
if (isBidiCommand) {
|
|
1517
|
-
throw new Error(
|
|
1518
|
-
`Failed to execute WebDriver Bidi command "${command}" as no Bidi session was established. Make sure you enable it by setting "webSocketUrl: true" in your capabilities and verify that your environment and browser supports it.`
|
|
1519
|
-
);
|
|
1520
|
-
}
|
|
1521
|
-
const minAllowedParams = commandParams.filter((param) => param.required).length;
|
|
1522
|
-
if (args.length < minAllowedParams || args.length > commandParams.length) {
|
|
1523
|
-
const parameterDescription = commandParams.length ? `
|
|
1524
|
-
|
|
1525
|
-
Property Description:
|
|
1526
|
-
${commandParams.map((p) => ` "${p.name}" (${p.type}): ${p.description}`).join("\n")}` : "";
|
|
1527
|
-
throw new Error(
|
|
1528
|
-
`Wrong parameters applied for ${command}
|
|
1529
|
-
Usage: ${commandUsage}` + parameterDescription + moreInfo
|
|
1530
|
-
);
|
|
1531
|
-
}
|
|
1532
|
-
for (const [it, arg] of Object.entries(args)) {
|
|
1533
|
-
if (isBidiCommand) {
|
|
1534
|
-
break;
|
|
1535
|
-
}
|
|
1536
|
-
const i = parseInt(it, 10);
|
|
1537
|
-
const commandParam = commandParams[i];
|
|
1538
|
-
if (!isValidParameter(arg, commandParam.type)) {
|
|
1539
|
-
if (typeof arg === "undefined" && !commandParam.required) {
|
|
1540
|
-
continue;
|
|
1541
|
-
}
|
|
1542
|
-
const actual = commandParam.type.endsWith("[]") ? `(${(Array.isArray(arg) ? arg : [arg]).map((a) => getArgumentType(a))})[]` : getArgumentType(arg);
|
|
1543
|
-
throw new Error(
|
|
1544
|
-
`Malformed type for "${commandParam.name}" parameter of command ${command}
|
|
1545
|
-
Expected: ${commandParam.type}
|
|
1546
|
-
Actual: ${actual}` + moreInfo
|
|
1547
|
-
);
|
|
1548
|
-
}
|
|
1549
|
-
if (i < variables.length) {
|
|
1550
|
-
const encodedArg = doubleEncodeVariables ? encodeURIComponent(encodeURIComponent(arg)) : encodeURIComponent(arg);
|
|
1551
|
-
endpoint = endpoint.replace(`:${commandParams[i].name}`, encodedArg);
|
|
1552
|
-
continue;
|
|
1553
|
-
}
|
|
1554
|
-
body[commandParams[i].name] = arg;
|
|
1555
|
-
}
|
|
1556
|
-
const request = new FetchRequest(method, endpoint, body, isHubCommand);
|
|
1557
|
-
request.on("performance", (...args2) => this.emit("request.performance", ...args2));
|
|
1558
|
-
this.emit("command", { command, method, endpoint, body });
|
|
1559
|
-
log4.info("COMMAND", commandCallStructure(command, args));
|
|
1560
|
-
return request.makeRequest(this.options, this.sessionId).then((result) => {
|
|
1561
|
-
if (typeof result.value !== "undefined") {
|
|
1562
|
-
let resultLog = result.value;
|
|
1563
|
-
if (/screenshot|recording/i.test(command) && typeof result.value === "string" && result.value.length > 64) {
|
|
1564
|
-
resultLog = `${result.value.slice(0, 61)}...`;
|
|
1565
|
-
} else if (command === "executeScript" && body.script && body.script.includes("(() => window.__wdioEvents__)")) {
|
|
1566
|
-
resultLog = `[${result.value.length} framework events captured]`;
|
|
1567
|
-
}
|
|
1568
|
-
log4.info("RESULT", resultLog);
|
|
1569
|
-
}
|
|
1570
|
-
this.emit("result", { command, method, endpoint, body, result });
|
|
1571
|
-
if (command === "deleteSession") {
|
|
1572
|
-
const browser = this;
|
|
1573
|
-
browser._bidiHandler?.close();
|
|
1574
|
-
const shutdownDriver = body.deleteSessionOpts?.shutdownDriver !== false;
|
|
1575
|
-
if (shutdownDriver && "wdio:driverPID" in this.capabilities && this.capabilities["wdio:driverPID"]) {
|
|
1576
|
-
log4.info(`Kill driver process with PID ${this.capabilities["wdio:driverPID"]}`);
|
|
1577
|
-
try {
|
|
1578
|
-
const killedSuccessfully = process.kill(this.capabilities["wdio:driverPID"], "SIGKILL");
|
|
1579
|
-
if (!killedSuccessfully) {
|
|
1580
|
-
log4.warn("Failed to kill driver process, manually clean-up might be required");
|
|
1581
|
-
}
|
|
1582
|
-
} catch (err) {
|
|
1583
|
-
log4.warn("Failed to kill driver process", err);
|
|
1584
|
-
}
|
|
1585
|
-
setTimeout(() => {
|
|
1586
|
-
for (const handle of process._getActiveHandles()) {
|
|
1587
|
-
if (handle.servername && handle.servername.includes("edgedl.me")) {
|
|
1588
|
-
handle.destroy();
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
}, 10);
|
|
1592
|
-
}
|
|
1593
|
-
if (!process.env.WDIO_WORKER_ID) {
|
|
1594
|
-
logger4.clearLogger();
|
|
1595
|
-
}
|
|
1596
|
-
}
|
|
1597
|
-
return result.value;
|
|
1598
|
-
});
|
|
1599
|
-
};
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
// src/bidi/localTypes.ts
|
|
1603
|
-
var localTypes_exports = {};
|
|
1604
|
-
|
|
1605
|
-
// src/bidi/remoteTypes.ts
|
|
1606
|
-
var remoteTypes_exports = {};
|
|
1607
|
-
|
|
1608
|
-
// src/index.ts
|
|
1609
|
-
var log5 = logger5("webdriver");
|
|
1610
|
-
var WebDriver = class _WebDriver {
|
|
1611
|
-
static async newSession(options, modifier, userPrototype = {}, customCommandWrapper) {
|
|
1612
|
-
const envLogLevel = process.env.WDIO_LOG_LEVEL;
|
|
1613
|
-
options.logLevel = envLogLevel ?? options.logLevel;
|
|
1614
|
-
const params = validateConfig(DEFAULTS, options);
|
|
1615
|
-
if (params.logLevel && (!options.logLevels || !options.logLevels.webdriver)) {
|
|
1616
|
-
logger5.setLevel("webdriver", params.logLevel);
|
|
1617
|
-
}
|
|
1618
|
-
log5.info("Initiate new session using the WebDriver protocol");
|
|
1619
|
-
const driverProcess = await startWebDriver(params);
|
|
1620
|
-
const requestedCapabilities = { ...params.capabilities };
|
|
1621
|
-
const { sessionId, capabilities } = await startWebDriverSession(params);
|
|
1622
|
-
const environment = sessionEnvironmentDetector({ capabilities, requestedCapabilities });
|
|
1623
|
-
const environmentPrototype = getEnvironmentVars(environment);
|
|
1624
|
-
const protocolCommands = getPrototype(environment);
|
|
1625
|
-
if (driverProcess?.pid) {
|
|
1626
|
-
capabilities["wdio:driverPID"] = driverProcess.pid;
|
|
1627
|
-
}
|
|
1628
|
-
const bidiPrototype = {};
|
|
1629
|
-
if (isBidi(requestedCapabilities, capabilities)) {
|
|
1630
|
-
log5.info(`Register BiDi handler for session with id ${sessionId}`);
|
|
1631
|
-
Object.assign(bidiPrototype, initiateBidi(capabilities.webSocketUrl, options.strictSSL));
|
|
1632
|
-
}
|
|
1633
|
-
const monad = webdriverMonad(
|
|
1634
|
-
{ ...params, requestedCapabilities },
|
|
1635
|
-
modifier,
|
|
1636
|
-
{
|
|
1637
|
-
...protocolCommands,
|
|
1638
|
-
...environmentPrototype,
|
|
1639
|
-
...userPrototype,
|
|
1640
|
-
...bidiPrototype
|
|
1641
|
-
}
|
|
1642
|
-
);
|
|
1643
|
-
const client = monad(sessionId, customCommandWrapper);
|
|
1644
|
-
if (isBidi(requestedCapabilities, capabilities)) {
|
|
1645
|
-
await client._bidiHandler.waitForConnected();
|
|
1646
|
-
client._bidiHandler.socket.on("message", parseBidiMessage.bind(client));
|
|
1647
|
-
}
|
|
1648
|
-
if (params.enableDirectConnect) {
|
|
1649
|
-
setupDirectConnect(client);
|
|
1650
|
-
}
|
|
1651
|
-
return client;
|
|
1652
|
-
}
|
|
1653
|
-
/**
|
|
1654
|
-
* allows user to attach to existing sessions
|
|
1655
|
-
*/
|
|
1656
|
-
static attachToSession(options, modifier, userPrototype = {}, commandWrapper) {
|
|
1657
|
-
if (!options || typeof options.sessionId !== "string") {
|
|
1658
|
-
throw new Error("sessionId is required to attach to existing session");
|
|
1659
|
-
}
|
|
1660
|
-
if (options.logLevel) {
|
|
1661
|
-
logger5.setLevel("webdriver", options.logLevel);
|
|
1662
|
-
}
|
|
1663
|
-
options.capabilities = options.capabilities || {};
|
|
1664
|
-
options.isW3C = options.isW3C === false ? false : true;
|
|
1665
|
-
options.protocol = options.protocol || DEFAULTS.protocol.default;
|
|
1666
|
-
options.hostname = options.hostname || DEFAULTS.hostname.default;
|
|
1667
|
-
options.port = options.port || DEFAULTS.port.default;
|
|
1668
|
-
options.path = options.path || DEFAULTS.path.default;
|
|
1669
|
-
const environment = sessionEnvironmentDetector({ capabilities: options.capabilities, requestedCapabilities: options.capabilities });
|
|
1670
|
-
options = Object.assign(environment, options);
|
|
1671
|
-
const environmentPrototype = getEnvironmentVars(options);
|
|
1672
|
-
const protocolCommands = getPrototype(options);
|
|
1673
|
-
const bidiPrototype = {};
|
|
1674
|
-
if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
|
|
1675
|
-
const webSocketUrl = options.capabilities?.webSocketUrl;
|
|
1676
|
-
log5.info(`Register BiDi handler for session with id ${options.sessionId}`);
|
|
1677
|
-
Object.assign(bidiPrototype, initiateBidi(webSocketUrl, options.strictSSL));
|
|
1678
|
-
}
|
|
1679
|
-
const prototype = { ...protocolCommands, ...environmentPrototype, ...userPrototype, ...bidiPrototype };
|
|
1680
|
-
const monad = webdriverMonad(options, modifier, prototype);
|
|
1681
|
-
const client = monad(options.sessionId, commandWrapper);
|
|
1682
|
-
if (isBidi(options.requestedCapabilities || {}, options.capabilities || {})) {
|
|
1683
|
-
client._bidiHandler?.socket.on("message", parseBidiMessage.bind(client));
|
|
1684
|
-
}
|
|
1685
|
-
return client;
|
|
1686
|
-
}
|
|
1687
|
-
/**
|
|
1688
|
-
* Changes The instance session id and browser capabilities for the new session
|
|
1689
|
-
* directly into the passed in browser object
|
|
1690
|
-
*
|
|
1691
|
-
* @param {object} instance the object we get from a new browser session.
|
|
1692
|
-
* @returns {string} the new session id of the browser
|
|
1693
|
-
*/
|
|
1694
|
-
static async reloadSession(instance, newCapabilities) {
|
|
1695
|
-
const capabilities = newCapabilities ? newCapabilities : Object.assign({}, instance.requestedCapabilities);
|
|
1696
|
-
let params = { ...instance.options, capabilities };
|
|
1697
|
-
for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
|
|
1698
|
-
if (prop in capabilities) {
|
|
1699
|
-
params = { ...params, [prop]: capabilities[prop] };
|
|
1700
|
-
delete capabilities[prop];
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
let driverProcess;
|
|
1704
|
-
if (params.hostname === "localhost" && newCapabilities?.browserName) {
|
|
1705
|
-
delete params.port;
|
|
1706
|
-
delete params.hostname;
|
|
1707
|
-
driverProcess = await startWebDriver(params);
|
|
1708
|
-
}
|
|
1709
|
-
const { sessionId, capabilities: newSessionCapabilities } = await startWebDriverSession(params);
|
|
1710
|
-
if (driverProcess?.pid) {
|
|
1711
|
-
newSessionCapabilities["wdio:driverPID"] = driverProcess.pid;
|
|
1712
|
-
}
|
|
1713
|
-
for (const prop of ["protocol", "hostname", "port", "path", "queryParams", "user", "key"]) {
|
|
1714
|
-
if (prop in params) {
|
|
1715
|
-
instance.options[prop] = params[prop];
|
|
1716
|
-
}
|
|
1717
|
-
}
|
|
1718
|
-
for (const prop in instance.requestedCapabilities) {
|
|
1719
|
-
delete instance.requestedCapabilities[prop];
|
|
1720
|
-
}
|
|
1721
|
-
const driverPid = instance.capabilities["wdio:driverPID"];
|
|
1722
|
-
instance.sessionId = sessionId;
|
|
1723
|
-
instance.capabilities = newSessionCapabilities;
|
|
1724
|
-
instance.capabilities["wdio:driverPID"] = driverPid;
|
|
1725
|
-
Object.assign(instance.requestedCapabilities, capabilities);
|
|
1726
|
-
if (isBidi(instance.requestedCapabilities || {}, instance.capabilities || {})) {
|
|
1727
|
-
const bidiReqOpts = instance.options.strictSSL ? {} : { rejectUnauthorized: false };
|
|
1728
|
-
await instance._bidiHandler?.reconnect(newSessionCapabilities.webSocketUrl, bidiReqOpts);
|
|
1729
|
-
}
|
|
1730
|
-
return sessionId;
|
|
1731
|
-
}
|
|
1732
|
-
static get WebDriver() {
|
|
1733
|
-
return _WebDriver;
|
|
1734
|
-
}
|
|
1743
|
+
// src/browser.ts
|
|
1744
|
+
var browser_default = WebDriver;
|
|
1745
|
+
environment.value = {
|
|
1746
|
+
Request: FetchRequest,
|
|
1747
|
+
Socket: BrowserSocket,
|
|
1748
|
+
variables: {}
|
|
1735
1749
|
};
|
|
1736
1750
|
export {
|
|
1737
1751
|
BidiHandler,
|
|
1738
1752
|
DEFAULTS,
|
|
1739
1753
|
ELEMENT_KEY,
|
|
1740
1754
|
SHADOW_ELEMENT_KEY,
|
|
1755
|
+
WebDriver,
|
|
1741
1756
|
command_default as command,
|
|
1742
|
-
|
|
1757
|
+
browser_default as default,
|
|
1743
1758
|
getEnvironmentVars,
|
|
1744
1759
|
getPrototype,
|
|
1745
1760
|
initiateBidi,
|