selenium-webdriver 4.0.0-alpha.7 → 4.0.0-alpha.8
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/.eslintrc.js +27 -0
- package/.prettierrc +4 -0
- package/CHANGES.md +21 -0
- package/README.md +2 -0
- package/chrome.js +69 -79
- package/chromium.js +423 -161
- package/devtools/CDPConnection.js +37 -0
- package/devtools/generator/protocol-dts-generator.js +429 -0
- package/edge.js +57 -58
- package/firefox.js +200 -202
- package/http/index.js +128 -116
- package/http/util.js +56 -64
- package/ie.js +180 -104
- package/index.js +246 -199
- package/io/exec.js +34 -39
- package/io/index.js +141 -151
- package/io/zip.js +75 -65
- package/lib/actions.js +137 -120
- package/lib/atoms/find-elements.js +123 -0
- package/lib/atoms/get-attribute.js +79 -59
- package/lib/atoms/is-displayed.js +72 -72
- package/lib/atoms/make-atoms-module.js +13 -7
- package/lib/by.js +192 -65
- package/lib/capabilities.js +75 -84
- package/lib/command.js +17 -20
- package/lib/error.js +134 -168
- package/lib/http.js +321 -232
- package/lib/input.js +319 -296
- package/lib/logging.js +126 -137
- package/lib/promise.js +60 -66
- package/lib/proxy.js +31 -41
- package/lib/session.js +12 -15
- package/lib/symbols.js +3 -4
- package/lib/until.js +162 -162
- package/lib/webdriver.js +711 -530
- package/net/index.js +30 -36
- package/net/portprober.js +30 -143
- package/opera.js +406 -0
- package/package.json +14 -6
- package/proxy.js +2 -2
- package/remote/index.js +190 -187
- package/safari.js +40 -49
package/chromium.js
CHANGED
|
@@ -73,18 +73,20 @@
|
|
|
73
73
|
* let driver = chrome.Driver.createSession(options, service);
|
|
74
74
|
*/
|
|
75
75
|
|
|
76
|
-
'use strict'
|
|
77
|
-
|
|
78
|
-
const http = require('./http');
|
|
79
|
-
const io = require('./io');
|
|
80
|
-
const {Capabilities, Capability} = require('./lib/capabilities');
|
|
81
|
-
const command = require('./lib/command');
|
|
82
|
-
const error = require('./lib/error');
|
|
83
|
-
const promise = require('./lib/promise');
|
|
84
|
-
const Symbols = require('./lib/symbols');
|
|
85
|
-
const webdriver = require('./lib/webdriver');
|
|
86
|
-
const remote = require('./remote');
|
|
76
|
+
'use strict'
|
|
87
77
|
|
|
78
|
+
const http = require('./http')
|
|
79
|
+
const io = require('./io')
|
|
80
|
+
const { Capabilities, Capability } = require('./lib/capabilities')
|
|
81
|
+
const command = require('./lib/command')
|
|
82
|
+
const error = require('./lib/error')
|
|
83
|
+
const Symbols = require('./lib/symbols')
|
|
84
|
+
const webdriver = require('./lib/webdriver')
|
|
85
|
+
const WebSocket = require('ws')
|
|
86
|
+
const cdp = require('./devtools/CDPConnection')
|
|
87
|
+
const remote = require('./remote')
|
|
88
|
+
const cdpTargets = ['page', 'browser']
|
|
89
|
+
const fs = require('fs')
|
|
88
90
|
|
|
89
91
|
/**
|
|
90
92
|
* Custom command names supported by Chromium WebDriver.
|
|
@@ -95,13 +97,14 @@ const Command = {
|
|
|
95
97
|
GET_NETWORK_CONDITIONS: 'getNetworkConditions',
|
|
96
98
|
SET_NETWORK_CONDITIONS: 'setNetworkConditions',
|
|
97
99
|
SEND_DEVTOOLS_COMMAND: 'sendDevToolsCommand',
|
|
100
|
+
SEND_AND_GET_DEVTOOLS_COMMAND: 'sendAndGetDevToolsCommand',
|
|
101
|
+
SET_PERMISSION: 'setPermission',
|
|
98
102
|
GET_CAST_SINKS: 'getCastSinks',
|
|
99
103
|
SET_CAST_SINK_TO_USE: 'setCastSinkToUse',
|
|
100
104
|
START_CAST_TAB_MIRRORING: 'setCastTabMirroring',
|
|
101
105
|
GET_CAST_ISSUE_MESSAGE: 'getCastIssueMessage',
|
|
102
106
|
STOP_CASTING: 'stopCasting',
|
|
103
|
-
}
|
|
104
|
-
|
|
107
|
+
}
|
|
105
108
|
|
|
106
109
|
/**
|
|
107
110
|
* Creates a command executor with support for Chromium's custom commands.
|
|
@@ -109,58 +112,75 @@ const Command = {
|
|
|
109
112
|
* @return {!command.Executor} The new command executor.
|
|
110
113
|
*/
|
|
111
114
|
function createExecutor(url, vendorPrefix) {
|
|
112
|
-
let agent = new http.Agent({ keepAlive: true })
|
|
113
|
-
let client = url.then(url => new http.HttpClient(url, agent))
|
|
114
|
-
let executor = new http.Executor(client)
|
|
115
|
-
configureExecutor(executor, vendorPrefix)
|
|
116
|
-
return executor
|
|
115
|
+
let agent = new http.Agent({ keepAlive: true })
|
|
116
|
+
let client = url.then((url) => new http.HttpClient(url, agent))
|
|
117
|
+
let executor = new http.Executor(client)
|
|
118
|
+
configureExecutor(executor, vendorPrefix)
|
|
119
|
+
return executor
|
|
117
120
|
}
|
|
118
121
|
|
|
119
|
-
|
|
120
122
|
/**
|
|
121
123
|
* Configures the given executor with Chromium-specific commands.
|
|
122
124
|
* @param {!http.Executor} executor the executor to configure.
|
|
123
125
|
*/
|
|
124
126
|
function configureExecutor(executor, vendorPrefix) {
|
|
125
127
|
executor.defineCommand(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
Command.LAUNCH_APP,
|
|
129
|
+
'POST',
|
|
130
|
+
'/session/:sessionId/chromium/launch_app'
|
|
131
|
+
)
|
|
132
|
+
executor.defineCommand(
|
|
133
|
+
Command.GET_NETWORK_CONDITIONS,
|
|
134
|
+
'GET',
|
|
135
|
+
'/session/:sessionId/chromium/network_conditions'
|
|
136
|
+
)
|
|
137
|
+
executor.defineCommand(
|
|
138
|
+
Command.SET_NETWORK_CONDITIONS,
|
|
139
|
+
'POST',
|
|
140
|
+
'/session/:sessionId/chromium/network_conditions'
|
|
141
|
+
)
|
|
129
142
|
executor.defineCommand(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
143
|
+
Command.SEND_DEVTOOLS_COMMAND,
|
|
144
|
+
'POST',
|
|
145
|
+
'/session/:sessionId/chromium/send_command'
|
|
146
|
+
)
|
|
133
147
|
executor.defineCommand(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
148
|
+
Command.SEND_AND_GET_DEVTOOLS_COMMAND,
|
|
149
|
+
'POST',
|
|
150
|
+
'/session/:sessionId/chromium/send_command_and_get_result'
|
|
151
|
+
)
|
|
137
152
|
executor.defineCommand(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
153
|
+
Command.SET_PERMISSION,
|
|
154
|
+
'POST',
|
|
155
|
+
'/session/:sessionId/permissions'
|
|
156
|
+
)
|
|
141
157
|
executor.defineCommand(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
158
|
+
Command.GET_CAST_SINKS,
|
|
159
|
+
'GET',
|
|
160
|
+
`/session/:sessionId/${vendorPrefix}/cast/get_sinks`
|
|
161
|
+
)
|
|
145
162
|
executor.defineCommand(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
163
|
+
Command.SET_CAST_SINK_TO_USE,
|
|
164
|
+
'POST',
|
|
165
|
+
`/session/:sessionId/${vendorPrefix}/cast/set_sink_to_use`
|
|
166
|
+
)
|
|
149
167
|
executor.defineCommand(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
168
|
+
Command.START_CAST_TAB_MIRRORING,
|
|
169
|
+
'POST',
|
|
170
|
+
`/session/:sessionId/${vendorPrefix}/cast/start_tab_mirroring`
|
|
171
|
+
)
|
|
153
172
|
executor.defineCommand(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
173
|
+
Command.GET_CAST_ISSUE_MESSAGE,
|
|
174
|
+
'GET',
|
|
175
|
+
`/session/:sessionId/${vendorPrefix}/cast/get_issue_message`
|
|
176
|
+
)
|
|
157
177
|
executor.defineCommand(
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
178
|
+
Command.STOP_CASTING,
|
|
179
|
+
'POST',
|
|
180
|
+
`/session/:sessionId/${vendorPrefix}/cast/stop_casting`
|
|
181
|
+
)
|
|
161
182
|
}
|
|
162
183
|
|
|
163
|
-
|
|
164
184
|
/**
|
|
165
185
|
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
|
166
186
|
* a WebDriver server in a child process.
|
|
@@ -171,8 +191,8 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
171
191
|
* should ensure a valid path to the appropriate exe is provided.
|
|
172
192
|
*/
|
|
173
193
|
constructor(exe) {
|
|
174
|
-
super(exe)
|
|
175
|
-
this.setLoopback(true)
|
|
194
|
+
super(exe)
|
|
195
|
+
this.setLoopback(true) // Required
|
|
176
196
|
}
|
|
177
197
|
|
|
178
198
|
/**
|
|
@@ -184,7 +204,7 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
184
204
|
* @return {!ServiceBuilder} A self reference.
|
|
185
205
|
*/
|
|
186
206
|
setAdbPort(port) {
|
|
187
|
-
return this.addArguments('--adb-port=' + port)
|
|
207
|
+
return this.addArguments('--adb-port=' + port)
|
|
188
208
|
}
|
|
189
209
|
|
|
190
210
|
/**
|
|
@@ -194,7 +214,7 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
194
214
|
* @return {!ServiceBuilder} A self reference.
|
|
195
215
|
*/
|
|
196
216
|
loggingTo(path) {
|
|
197
|
-
return this.addArguments('--log-path=' + path)
|
|
217
|
+
return this.addArguments('--log-path=' + path)
|
|
198
218
|
}
|
|
199
219
|
|
|
200
220
|
/**
|
|
@@ -202,7 +222,7 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
202
222
|
* @return {!ServiceBuilder} A self reference.
|
|
203
223
|
*/
|
|
204
224
|
enableVerboseLogging() {
|
|
205
|
-
return this.addArguments('--verbose')
|
|
225
|
+
return this.addArguments('--verbose')
|
|
206
226
|
}
|
|
207
227
|
|
|
208
228
|
/**
|
|
@@ -212,19 +232,18 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
212
232
|
* @return {!ServiceBuilder} A self reference.
|
|
213
233
|
*/
|
|
214
234
|
setNumHttpThreads(n) {
|
|
215
|
-
return this.addArguments('--http-threads=' + n)
|
|
235
|
+
return this.addArguments('--http-threads=' + n)
|
|
216
236
|
}
|
|
217
237
|
|
|
218
238
|
/**
|
|
219
239
|
* @override
|
|
220
240
|
*/
|
|
221
241
|
setPath(path) {
|
|
222
|
-
super.setPath(path)
|
|
223
|
-
return this.addArguments('--url-base=' + path)
|
|
242
|
+
super.setPath(path)
|
|
243
|
+
return this.addArguments('--url-base=' + path)
|
|
224
244
|
}
|
|
225
245
|
}
|
|
226
246
|
|
|
227
|
-
|
|
228
247
|
/**
|
|
229
248
|
* Class for managing WebDriver options specific to a Chromium-based browser.
|
|
230
249
|
*/
|
|
@@ -234,13 +253,13 @@ class Options extends Capabilities {
|
|
|
234
253
|
* capabilities to initialize this instance from.
|
|
235
254
|
*/
|
|
236
255
|
constructor(other = undefined) {
|
|
237
|
-
super(other)
|
|
256
|
+
super(other)
|
|
238
257
|
|
|
239
258
|
/** @private {!Object} */
|
|
240
|
-
this.options_ = this.get(this.CAPABILITY_KEY) || {}
|
|
259
|
+
this.options_ = this.get(this.CAPABILITY_KEY) || {}
|
|
241
260
|
|
|
242
|
-
this.setBrowserName(this.BROWSER_NAME_VALUE)
|
|
243
|
-
this.set(this.CAPABILITY_KEY, this.options_)
|
|
261
|
+
this.setBrowserName(this.BROWSER_NAME_VALUE)
|
|
262
|
+
this.set(this.CAPABILITY_KEY, this.options_)
|
|
244
263
|
}
|
|
245
264
|
|
|
246
265
|
/**
|
|
@@ -253,11 +272,11 @@ class Options extends Capabilities {
|
|
|
253
272
|
* @return {!Options} A self reference.
|
|
254
273
|
*/
|
|
255
274
|
addArguments(...args) {
|
|
256
|
-
let newArgs = (this.options_.args || []).concat(...args)
|
|
275
|
+
let newArgs = (this.options_.args || []).concat(...args)
|
|
257
276
|
if (newArgs.length) {
|
|
258
|
-
this.options_.args = newArgs
|
|
277
|
+
this.options_.args = newArgs
|
|
259
278
|
}
|
|
260
|
-
return this
|
|
279
|
+
return this
|
|
261
280
|
}
|
|
262
281
|
|
|
263
282
|
/**
|
|
@@ -276,7 +295,7 @@ class Options extends Capabilities {
|
|
|
276
295
|
* @return {!Options} A self reference.
|
|
277
296
|
*/
|
|
278
297
|
headless() {
|
|
279
|
-
return this.addArguments('headless')
|
|
298
|
+
return this.addArguments('headless')
|
|
280
299
|
}
|
|
281
300
|
|
|
282
301
|
/**
|
|
@@ -287,15 +306,15 @@ class Options extends Capabilities {
|
|
|
287
306
|
* @throws {TypeError} if width or height is unspecified, not a number, or
|
|
288
307
|
* less than or equal to 0.
|
|
289
308
|
*/
|
|
290
|
-
windowSize({width, height}) {
|
|
309
|
+
windowSize({ width, height }) {
|
|
291
310
|
function checkArg(arg) {
|
|
292
311
|
if (typeof arg !== 'number' || arg <= 0) {
|
|
293
|
-
throw TypeError('Arguments must be {width, height} with numbers > 0')
|
|
312
|
+
throw TypeError('Arguments must be {width, height} with numbers > 0')
|
|
294
313
|
}
|
|
295
314
|
}
|
|
296
|
-
checkArg(width)
|
|
297
|
-
checkArg(height)
|
|
298
|
-
return this.addArguments(`window-size=${width},${height}`)
|
|
315
|
+
checkArg(width)
|
|
316
|
+
checkArg(height)
|
|
317
|
+
return this.addArguments(`window-size=${width},${height}`)
|
|
299
318
|
}
|
|
300
319
|
|
|
301
320
|
/**
|
|
@@ -306,11 +325,11 @@ class Options extends Capabilities {
|
|
|
306
325
|
* @return {!Options} A self reference.
|
|
307
326
|
*/
|
|
308
327
|
excludeSwitches(...args) {
|
|
309
|
-
let switches = (this.options_.excludeSwitches || []).concat(...args)
|
|
328
|
+
let switches = (this.options_.excludeSwitches || []).concat(...args)
|
|
310
329
|
if (switches.length) {
|
|
311
|
-
this.options_.excludeSwitches = switches
|
|
330
|
+
this.options_.excludeSwitches = switches
|
|
312
331
|
}
|
|
313
|
-
return this
|
|
332
|
+
return this
|
|
314
333
|
}
|
|
315
334
|
|
|
316
335
|
/**
|
|
@@ -322,9 +341,9 @@ class Options extends Capabilities {
|
|
|
322
341
|
* @return {!Options} A self reference.
|
|
323
342
|
*/
|
|
324
343
|
addExtensions(...args) {
|
|
325
|
-
let current = this.options_.extensions || []
|
|
326
|
-
this.options_.extensions = current.concat(...args)
|
|
327
|
-
return this
|
|
344
|
+
let current = this.options_.extensions || []
|
|
345
|
+
this.options_.extensions = current.concat(...args)
|
|
346
|
+
return this
|
|
328
347
|
}
|
|
329
348
|
|
|
330
349
|
/**
|
|
@@ -339,8 +358,8 @@ class Options extends Capabilities {
|
|
|
339
358
|
* @return {!Options} A self reference.
|
|
340
359
|
*/
|
|
341
360
|
setBinaryPath(path) {
|
|
342
|
-
this.options_.binary = path
|
|
343
|
-
return this
|
|
361
|
+
this.options_.binary = path
|
|
362
|
+
return this
|
|
344
363
|
}
|
|
345
364
|
|
|
346
365
|
/**
|
|
@@ -352,8 +371,8 @@ class Options extends Capabilities {
|
|
|
352
371
|
* @return {!Options} A self reference.
|
|
353
372
|
*/
|
|
354
373
|
detachDriver(detach) {
|
|
355
|
-
this.options_.detach = detach
|
|
356
|
-
return this
|
|
374
|
+
this.options_.detach = detach
|
|
375
|
+
return this
|
|
357
376
|
}
|
|
358
377
|
|
|
359
378
|
/**
|
|
@@ -363,8 +382,8 @@ class Options extends Capabilities {
|
|
|
363
382
|
* @return {!Options} A self reference.
|
|
364
383
|
*/
|
|
365
384
|
setUserPreferences(prefs) {
|
|
366
|
-
this.options_.prefs = prefs
|
|
367
|
-
return this
|
|
385
|
+
this.options_.prefs = prefs
|
|
386
|
+
return this
|
|
368
387
|
}
|
|
369
388
|
|
|
370
389
|
/**
|
|
@@ -392,8 +411,8 @@ class Options extends Capabilities {
|
|
|
392
411
|
* @return {!Options} A self reference.
|
|
393
412
|
*/
|
|
394
413
|
setPerfLoggingPrefs(prefs) {
|
|
395
|
-
this.options_.perfLoggingPrefs = prefs
|
|
396
|
-
return this
|
|
414
|
+
this.options_.perfLoggingPrefs = prefs
|
|
415
|
+
return this
|
|
397
416
|
}
|
|
398
417
|
|
|
399
418
|
/**
|
|
@@ -403,8 +422,8 @@ class Options extends Capabilities {
|
|
|
403
422
|
* @return {!Options} A self reference.
|
|
404
423
|
*/
|
|
405
424
|
setLocalState(state) {
|
|
406
|
-
this.options_.localState = state
|
|
407
|
-
return this
|
|
425
|
+
this.options_.localState = state
|
|
426
|
+
return this
|
|
408
427
|
}
|
|
409
428
|
|
|
410
429
|
/**
|
|
@@ -416,8 +435,8 @@ class Options extends Capabilities {
|
|
|
416
435
|
* @return {!Options} A self reference.
|
|
417
436
|
*/
|
|
418
437
|
androidActivity(name) {
|
|
419
|
-
this.options_.androidActivity = name
|
|
420
|
-
return this
|
|
438
|
+
this.options_.androidActivity = name
|
|
439
|
+
return this
|
|
421
440
|
}
|
|
422
441
|
|
|
423
442
|
/**
|
|
@@ -429,8 +448,8 @@ class Options extends Capabilities {
|
|
|
429
448
|
* @return {!Options} A self reference.
|
|
430
449
|
*/
|
|
431
450
|
androidDeviceSerial(serial) {
|
|
432
|
-
this.options_.androidDeviceSerial = serial
|
|
433
|
-
return this
|
|
451
|
+
this.options_.androidDeviceSerial = serial
|
|
452
|
+
return this
|
|
434
453
|
}
|
|
435
454
|
|
|
436
455
|
/**
|
|
@@ -441,8 +460,8 @@ class Options extends Capabilities {
|
|
|
441
460
|
* @return {!Options} A self reference.
|
|
442
461
|
*/
|
|
443
462
|
androidPackage(pkg) {
|
|
444
|
-
this.options_.androidPackage = pkg
|
|
445
|
-
return this
|
|
463
|
+
this.options_.androidPackage = pkg
|
|
464
|
+
return this
|
|
446
465
|
}
|
|
447
466
|
|
|
448
467
|
/**
|
|
@@ -454,8 +473,8 @@ class Options extends Capabilities {
|
|
|
454
473
|
* @return {!Options} A self reference.
|
|
455
474
|
*/
|
|
456
475
|
androidProcess(processName) {
|
|
457
|
-
this.options_.androidProcess = processName
|
|
458
|
-
return this
|
|
476
|
+
this.options_.androidProcess = processName
|
|
477
|
+
return this
|
|
459
478
|
}
|
|
460
479
|
|
|
461
480
|
/**
|
|
@@ -467,8 +486,8 @@ class Options extends Capabilities {
|
|
|
467
486
|
* @return {!Options} A self reference.
|
|
468
487
|
*/
|
|
469
488
|
androidUseRunningApp(useRunning) {
|
|
470
|
-
this.options_.androidUseRunningApp = useRunning
|
|
471
|
-
return this
|
|
489
|
+
this.options_.androidUseRunningApp = useRunning
|
|
490
|
+
return this
|
|
472
491
|
}
|
|
473
492
|
|
|
474
493
|
/**
|
|
@@ -478,8 +497,8 @@ class Options extends Capabilities {
|
|
|
478
497
|
* @return {!Options} A self reference.
|
|
479
498
|
*/
|
|
480
499
|
setBrowserLogFile(path) {
|
|
481
|
-
this.options_.logPath = path
|
|
482
|
-
return this
|
|
500
|
+
this.options_.logPath = path
|
|
501
|
+
return this
|
|
483
502
|
}
|
|
484
503
|
|
|
485
504
|
/**
|
|
@@ -489,8 +508,8 @@ class Options extends Capabilities {
|
|
|
489
508
|
* @return {!Options} A self reference.
|
|
490
509
|
*/
|
|
491
510
|
setBrowserMinidumpPath(path) {
|
|
492
|
-
this.options_.minidumpPath = path
|
|
493
|
-
return this
|
|
511
|
+
this.options_.minidumpPath = path
|
|
512
|
+
return this
|
|
494
513
|
}
|
|
495
514
|
|
|
496
515
|
/**
|
|
@@ -512,11 +531,11 @@ class Options extends Capabilities {
|
|
|
512
531
|
*
|
|
513
532
|
* __Example 2: Using Custom Screen Configuration__
|
|
514
533
|
*
|
|
515
|
-
* let options = new chrome.Options().setMobileEmulation({
|
|
534
|
+
* let options = new chrome.Options().setMobileEmulation({deviceMetrics: {
|
|
516
535
|
* width: 360,
|
|
517
536
|
* height: 640,
|
|
518
537
|
* pixelRatio: 3.0
|
|
519
|
-
* });
|
|
538
|
+
* }});
|
|
520
539
|
*
|
|
521
540
|
* let driver = chrome.Driver.createSession(options);
|
|
522
541
|
*
|
|
@@ -530,8 +549,8 @@ class Options extends Capabilities {
|
|
|
530
549
|
* @return {!Options} A self reference.
|
|
531
550
|
*/
|
|
532
551
|
setMobileEmulation(config) {
|
|
533
|
-
this.options_.mobileEmulation = config
|
|
534
|
-
return this
|
|
552
|
+
this.options_.mobileEmulation = config
|
|
553
|
+
return this
|
|
535
554
|
}
|
|
536
555
|
|
|
537
556
|
/**
|
|
@@ -542,21 +561,22 @@ class Options extends Capabilities {
|
|
|
542
561
|
* @suppress {checkTypes} Suppress [] access on a struct.
|
|
543
562
|
*/
|
|
544
563
|
[Symbols.serialize]() {
|
|
545
|
-
if (this.options_.extensions &&
|
|
546
|
-
this.options_.extensions =
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
})
|
|
564
|
+
if (this.options_.extensions && this.options_.extensions.length) {
|
|
565
|
+
this.options_.extensions = this.options_.extensions.map(function (
|
|
566
|
+
extension
|
|
567
|
+
) {
|
|
568
|
+
if (Buffer.isBuffer(extension)) {
|
|
569
|
+
return extension.toString('base64')
|
|
570
|
+
}
|
|
571
|
+
return io
|
|
572
|
+
.read(/** @type {string} */ (extension))
|
|
573
|
+
.then((buffer) => buffer.toString('base64'))
|
|
574
|
+
})
|
|
554
575
|
}
|
|
555
|
-
return super[Symbols.serialize]()
|
|
576
|
+
return super[Symbols.serialize]()
|
|
556
577
|
}
|
|
557
578
|
}
|
|
558
579
|
|
|
559
|
-
|
|
560
580
|
/**
|
|
561
581
|
* Creates a new WebDriver client for Chromium-based browsers.
|
|
562
582
|
*/
|
|
@@ -573,28 +593,28 @@ class Driver extends webdriver.WebDriver {
|
|
|
573
593
|
* @return {!Driver} A new driver instance.
|
|
574
594
|
*/
|
|
575
595
|
static createSession(caps, opt_serviceExecutor) {
|
|
576
|
-
let executor
|
|
577
|
-
let onQuit
|
|
596
|
+
let executor
|
|
597
|
+
let onQuit
|
|
578
598
|
if (opt_serviceExecutor instanceof http.Executor) {
|
|
579
|
-
executor = opt_serviceExecutor
|
|
580
|
-
configureExecutor(executor, this.VENDOR_COMMAND_PREFIX)
|
|
599
|
+
executor = opt_serviceExecutor
|
|
600
|
+
configureExecutor(executor, this.VENDOR_COMMAND_PREFIX)
|
|
581
601
|
} else {
|
|
582
|
-
let service = opt_serviceExecutor || this.getDefaultService()
|
|
583
|
-
executor = createExecutor(service.start(), this.VENDOR_COMMAND_PREFIX)
|
|
584
|
-
onQuit = () => service.kill()
|
|
602
|
+
let service = opt_serviceExecutor || this.getDefaultService()
|
|
603
|
+
executor = createExecutor(service.start(), this.VENDOR_COMMAND_PREFIX)
|
|
604
|
+
onQuit = () => service.kill()
|
|
585
605
|
}
|
|
586
606
|
|
|
587
607
|
// W3C spec requires noProxy value to be an array of strings, but Chromium
|
|
588
608
|
// expects a single host as a string.
|
|
589
|
-
let proxy = caps.get(Capability.PROXY)
|
|
609
|
+
let proxy = caps.get(Capability.PROXY)
|
|
590
610
|
if (proxy && Array.isArray(proxy.noProxy)) {
|
|
591
|
-
proxy.noProxy = proxy.noProxy[0]
|
|
611
|
+
proxy.noProxy = proxy.noProxy[0]
|
|
592
612
|
if (!proxy.noProxy) {
|
|
593
|
-
proxy.noProxy = undefined
|
|
613
|
+
proxy.noProxy = undefined
|
|
594
614
|
}
|
|
595
615
|
}
|
|
596
616
|
|
|
597
|
-
return /** @type {!Driver} */(super.createSession(executor, caps, onQuit))
|
|
617
|
+
return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
|
|
598
618
|
}
|
|
599
619
|
|
|
600
620
|
/**
|
|
@@ -612,7 +632,8 @@ class Driver extends webdriver.WebDriver {
|
|
|
612
632
|
*/
|
|
613
633
|
launchApp(id) {
|
|
614
634
|
return this.execute(
|
|
615
|
-
|
|
635
|
+
new command.Command(Command.LAUNCH_APP).setParameter('id', id)
|
|
636
|
+
)
|
|
616
637
|
}
|
|
617
638
|
|
|
618
639
|
/**
|
|
@@ -621,7 +642,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
621
642
|
* emulation settings are retrievied.
|
|
622
643
|
*/
|
|
623
644
|
getNetworkConditions() {
|
|
624
|
-
return this.execute(new command.Command(Command.GET_NETWORK_CONDITIONS))
|
|
645
|
+
return this.execute(new command.Command(Command.GET_NETWORK_CONDITIONS))
|
|
625
646
|
}
|
|
626
647
|
|
|
627
648
|
/**
|
|
@@ -642,11 +663,16 @@ class Driver extends webdriver.WebDriver {
|
|
|
642
663
|
*/
|
|
643
664
|
setNetworkConditions(spec) {
|
|
644
665
|
if (!spec || typeof spec !== 'object') {
|
|
645
|
-
throw TypeError(
|
|
666
|
+
throw TypeError(
|
|
667
|
+
'setNetworkConditions called with non-network-conditions parameter'
|
|
668
|
+
)
|
|
646
669
|
}
|
|
647
670
|
return this.execute(
|
|
648
|
-
|
|
649
|
-
|
|
671
|
+
new command.Command(Command.SET_NETWORK_CONDITIONS).setParameter(
|
|
672
|
+
'network_conditions',
|
|
673
|
+
spec
|
|
674
|
+
)
|
|
675
|
+
)
|
|
650
676
|
}
|
|
651
677
|
|
|
652
678
|
/**
|
|
@@ -660,9 +686,232 @@ class Driver extends webdriver.WebDriver {
|
|
|
660
686
|
*/
|
|
661
687
|
sendDevToolsCommand(cmd, params = {}) {
|
|
662
688
|
return this.execute(
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
689
|
+
new command.Command(Command.SEND_DEVTOOLS_COMMAND)
|
|
690
|
+
.setParameter('cmd', cmd)
|
|
691
|
+
.setParameter('params', params)
|
|
692
|
+
)
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* ends an arbitrary devtools command to the browser and get the result.
|
|
697
|
+
*
|
|
698
|
+
* @param {string} cmd The name of the command to send.
|
|
699
|
+
* @param {Object=} params The command parameters.
|
|
700
|
+
* @return {!Promise<void>} A promise that will be resolved when the command
|
|
701
|
+
* has finished.
|
|
702
|
+
* @see <https://chromedevtools.github.io/devtools-protocol/>
|
|
703
|
+
*/
|
|
704
|
+
sendAndGetDevToolsCommand(cmd, params = {}) {
|
|
705
|
+
return this.execute(
|
|
706
|
+
new command.Command(Command.SEND_AND_GET_DEVTOOLS_COMMAND)
|
|
707
|
+
.setParameter('cmd', cmd)
|
|
708
|
+
.setParameter('params', params)
|
|
709
|
+
)
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Creates a new WebSocket connection.
|
|
714
|
+
* @return {!Promise<resolved>} A new CDP instance.
|
|
715
|
+
*/
|
|
716
|
+
async createCDPConnection(target) {
|
|
717
|
+
const caps = await this.getCapabilities()
|
|
718
|
+
const seOptions = caps['map_'].get('se:options') || new Map()
|
|
719
|
+
const vendorInfo =
|
|
720
|
+
caps['map_'].get(this.VENDOR_COMMAND_PREFIX + ':chromeOptions') ||
|
|
721
|
+
new Map()
|
|
722
|
+
const debuggerUrl = seOptions['cdp'] || vendorInfo['debuggerAddress']
|
|
723
|
+
this._wsUrl = await this.getWsUrl(debuggerUrl, target)
|
|
724
|
+
|
|
725
|
+
return new Promise((resolve, reject) => {
|
|
726
|
+
try {
|
|
727
|
+
this._wsConnection = new WebSocket(this._wsUrl)
|
|
728
|
+
} catch (err) {
|
|
729
|
+
reject(err)
|
|
730
|
+
return
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
this._wsConnection.on('open', () => {
|
|
734
|
+
this._cdpConnection = new cdp.CdpConnection(this._wsConnection)
|
|
735
|
+
resolve(this._cdpConnection)
|
|
736
|
+
})
|
|
737
|
+
|
|
738
|
+
this._wsConnection.on('error', (error) => {
|
|
739
|
+
reject(error)
|
|
740
|
+
})
|
|
741
|
+
})
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Retrieves 'webSocketDebuggerUrl' by sending a http request using debugger address
|
|
746
|
+
* @param {string} debuggerAddress
|
|
747
|
+
* @param {string} target
|
|
748
|
+
* @return {string} Returns parsed webSocketDebuggerUrl obtained from the http request
|
|
749
|
+
*/
|
|
750
|
+
async getWsUrl(debuggerAddress, target) {
|
|
751
|
+
if (target && cdpTargets.indexOf(target.toLowerCase()) === -1) {
|
|
752
|
+
throw new error.InvalidArgumentError('invalid target value')
|
|
753
|
+
}
|
|
754
|
+
let path = '/json/version'
|
|
755
|
+
|
|
756
|
+
if (target === 'page') {
|
|
757
|
+
path = '/json'
|
|
758
|
+
}
|
|
759
|
+
let request = new http.Request('GET', path)
|
|
760
|
+
let client = new http.HttpClient('http://' + debuggerAddress)
|
|
761
|
+
let response = await client.send(request)
|
|
762
|
+
let url = JSON.parse(response.body)['webSocketDebuggerUrl']
|
|
763
|
+
if (target.toLowerCase() === 'page') {
|
|
764
|
+
url = JSON.parse(response.body)[0]['webSocketDebuggerUrl']
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return url
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Sets a listener for Fetch.authRequired event from CDP
|
|
772
|
+
* If event is triggered, it enter username and password
|
|
773
|
+
* and allows the test to move forward
|
|
774
|
+
* @param {string} username
|
|
775
|
+
* @param {string} password
|
|
776
|
+
* @param connection CDP Connection
|
|
777
|
+
*/
|
|
778
|
+
async register(username, password, connection) {
|
|
779
|
+
await connection.execute("Network.setCacheDisabled", this.getRandomNumber(1, 10), {
|
|
780
|
+
cacheDisabled: true,
|
|
781
|
+
}, null);
|
|
782
|
+
|
|
783
|
+
this._wsConnection.on('message', (message) => {
|
|
784
|
+
const params = JSON.parse(message)
|
|
785
|
+
|
|
786
|
+
if (params.method === 'Fetch.authRequired') {
|
|
787
|
+
const requestParams = params['params']
|
|
788
|
+
connection.execute('Fetch.continueWithAuth', this.getRandomNumber(1, 10), {
|
|
789
|
+
requestId: requestParams['requestId'],
|
|
790
|
+
authChallengeResponse: {
|
|
791
|
+
response: 'ProvideCredentials',
|
|
792
|
+
username: username,
|
|
793
|
+
password: password,
|
|
794
|
+
}})
|
|
795
|
+
} else if (params.method === 'Fetch.requestPaused') {
|
|
796
|
+
const requestPausedParams = params['params']
|
|
797
|
+
connection.execute('Fetch.continueRequest', this.getRandomNumber(1, 10), {
|
|
798
|
+
requestId: requestPausedParams['requestId'],
|
|
799
|
+
})
|
|
800
|
+
}
|
|
801
|
+
})
|
|
802
|
+
|
|
803
|
+
await connection.execute('Fetch.enable', 1, {
|
|
804
|
+
handleAuthRequests: true,
|
|
805
|
+
}, null)
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/**
|
|
809
|
+
*
|
|
810
|
+
* @param connection
|
|
811
|
+
* @param callback
|
|
812
|
+
* @returns {Promise<void>}
|
|
813
|
+
*/
|
|
814
|
+
async onLogEvent(connection, callback) {
|
|
815
|
+
await connection.execute('Runtime.enable', this.getRandomNumber(1, 10), {}, null)
|
|
816
|
+
|
|
817
|
+
this._wsConnection.on('message', (message) => {
|
|
818
|
+
const params = JSON.parse(message)
|
|
819
|
+
|
|
820
|
+
if (params.method === 'Runtime.consoleAPICalled') {
|
|
821
|
+
const consoleEventParams = params['params']
|
|
822
|
+
let event = {
|
|
823
|
+
type: consoleEventParams['type'],
|
|
824
|
+
timestamp: new Date(consoleEventParams['timestamp']),
|
|
825
|
+
args: consoleEventParams['args']
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
callback(event)
|
|
829
|
+
}
|
|
830
|
+
})
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
/**
|
|
834
|
+
* Set a permission state to the given value.
|
|
835
|
+
*
|
|
836
|
+
* @param {string} name A name of the permission to update.
|
|
837
|
+
* @param {('granted'|'denied'|'prompt')} state State to set permission to.
|
|
838
|
+
* @returns {!Promise<Object>} A promise that will be resolved when the
|
|
839
|
+
* command has finished.
|
|
840
|
+
* @see <https://w3c.github.io/permissions/#permission-registry> for valid
|
|
841
|
+
* names
|
|
842
|
+
*/
|
|
843
|
+
setPermission(name, state) {
|
|
844
|
+
return this.execute(
|
|
845
|
+
new command.Command(Command.SET_PERMISSION)
|
|
846
|
+
.setParameter('descriptor', { name })
|
|
847
|
+
.setParameter('state', state)
|
|
848
|
+
)
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
*
|
|
853
|
+
* @param connection
|
|
854
|
+
* @param callback
|
|
855
|
+
* @returns {Promise<void>}
|
|
856
|
+
*/
|
|
857
|
+
async onLogException(connection, callback) {
|
|
858
|
+
await connection.execute('Runtime.enable', this.getRandomNumber(1, 10), {}, null)
|
|
859
|
+
|
|
860
|
+
this._wsConnection.on('message', (message) => {
|
|
861
|
+
const params = JSON.parse(message)
|
|
862
|
+
|
|
863
|
+
if (params.method === 'Runtime.exceptionThrown') {
|
|
864
|
+
const exceptionEventParams = params['params']
|
|
865
|
+
let event = {
|
|
866
|
+
exceptionDetails: exceptionEventParams['exceptionDetails'],
|
|
867
|
+
timestamp: new Date(exceptionEventParams['timestamp']),
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
callback(event)
|
|
871
|
+
}
|
|
872
|
+
})
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* @param connection
|
|
877
|
+
* @param callback
|
|
878
|
+
* @returns {Promise<void>}
|
|
879
|
+
*/
|
|
880
|
+
async logMutationEvents(connection, callback) {
|
|
881
|
+
await connection.execute('Runtime.enable', this.getRandomNumber(1, 10), {}, null)
|
|
882
|
+
await connection.execute('Page.enable', this.getRandomNumber(1, 10), {}, null)
|
|
883
|
+
|
|
884
|
+
await connection.execute('Runtime.addBinding', this.getRandomNumber(1, 10), {
|
|
885
|
+
name: '__webdriver_attribute',
|
|
886
|
+
}, null)
|
|
887
|
+
|
|
888
|
+
const mutationListener = fs.readFileSync('../../cdp-support/mutation-listener.js', 'utf-8').toString()
|
|
889
|
+
|
|
890
|
+
this.executeScript(mutationListener)
|
|
891
|
+
|
|
892
|
+
await connection.execute('Page.addScriptToEvaluateOnNewDocument', this.getRandomNumber(1, 10), {
|
|
893
|
+
source: mutationListener,
|
|
894
|
+
}, null)
|
|
895
|
+
|
|
896
|
+
this._wsConnection.on('message', async (message) => {
|
|
897
|
+
const params = JSON.parse(message)
|
|
898
|
+
if (params.method === 'Runtime.bindingCalled') {
|
|
899
|
+
let payload = JSON.parse(params['params']['payload'])
|
|
900
|
+
let elements = await this.findElements({css: "*[data-__webdriver_id=" + payload['target']})
|
|
901
|
+
|
|
902
|
+
if (elements.length === 0) {
|
|
903
|
+
return
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
let event = {
|
|
907
|
+
element: elements[0],
|
|
908
|
+
attribute_name: payload['name'],
|
|
909
|
+
current_value: payload['value'],
|
|
910
|
+
old_value: payload['oldValue']
|
|
911
|
+
}
|
|
912
|
+
callback(event)
|
|
913
|
+
}
|
|
914
|
+
})
|
|
666
915
|
}
|
|
667
916
|
|
|
668
917
|
/**
|
|
@@ -675,19 +924,18 @@ class Driver extends webdriver.WebDriver {
|
|
|
675
924
|
*/
|
|
676
925
|
async setDownloadPath(path) {
|
|
677
926
|
if (!path || typeof path !== 'string') {
|
|
678
|
-
throw new error.InvalidArgumentError('invalid download path')
|
|
927
|
+
throw new error.InvalidArgumentError('invalid download path')
|
|
679
928
|
}
|
|
680
|
-
const stat = await io.stat(path)
|
|
929
|
+
const stat = await io.stat(path)
|
|
681
930
|
if (!stat.isDirectory()) {
|
|
682
|
-
throw new error.InvalidArgumentError('not a directory: ' + path)
|
|
931
|
+
throw new error.InvalidArgumentError('not a directory: ' + path)
|
|
683
932
|
}
|
|
684
933
|
return this.sendDevToolsCommand('Page.setDownloadBehavior', {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
})
|
|
934
|
+
behavior: 'allow',
|
|
935
|
+
downloadPath: path,
|
|
936
|
+
})
|
|
688
937
|
}
|
|
689
938
|
|
|
690
|
-
|
|
691
939
|
/**
|
|
692
940
|
* Returns the list of cast sinks (Cast devices) available to the Chrome media router.
|
|
693
941
|
*
|
|
@@ -696,67 +944,81 @@ class Driver extends webdriver.WebDriver {
|
|
|
696
944
|
*/
|
|
697
945
|
getCastSinks() {
|
|
698
946
|
return this.schedule(
|
|
699
|
-
|
|
700
|
-
|
|
947
|
+
new command.Command(Command.GET_CAST_SINKS),
|
|
948
|
+
'Driver.getCastSinks()'
|
|
949
|
+
)
|
|
701
950
|
}
|
|
702
951
|
|
|
703
952
|
/**
|
|
704
953
|
* Selects a cast sink (Cast device) as the recipient of media router intents (connect or play).
|
|
705
954
|
*
|
|
706
|
-
* @param {String}
|
|
955
|
+
* @param {String} deviceName name of the target device.
|
|
707
956
|
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
708
957
|
* when the target device has been selected to respond further webdriver commands.
|
|
709
958
|
*/
|
|
710
959
|
setCastSinkToUse(deviceName) {
|
|
711
960
|
return this.schedule(
|
|
712
|
-
|
|
713
|
-
'
|
|
961
|
+
new command.Command(Command.SET_CAST_SINK_TO_USE).setParameter(
|
|
962
|
+
'sinkName',
|
|
963
|
+
deviceName
|
|
964
|
+
),
|
|
965
|
+
'Driver.setCastSinkToUse(' + deviceName + ')'
|
|
966
|
+
)
|
|
714
967
|
}
|
|
715
968
|
|
|
716
969
|
/**
|
|
717
970
|
* Initiates tab mirroring for the current browser tab on the specified device.
|
|
718
971
|
*
|
|
719
|
-
* @param {String}
|
|
972
|
+
* @param {String} deviceName name of the target device.
|
|
720
973
|
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
721
974
|
* when the mirror command has been issued to the device.
|
|
722
975
|
*/
|
|
723
976
|
startCastTabMirroring(deviceName) {
|
|
724
977
|
return this.schedule(
|
|
725
|
-
|
|
726
|
-
'
|
|
978
|
+
new command.Command(Command.START_CAST_TAB_MIRRORING).setParameter(
|
|
979
|
+
'sinkName',
|
|
980
|
+
deviceName
|
|
981
|
+
),
|
|
982
|
+
'Driver.startCastTabMirroring(' + deviceName + ')'
|
|
983
|
+
)
|
|
727
984
|
}
|
|
728
985
|
|
|
729
986
|
/**
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
* @param {String} Friendly name of the target device.
|
|
987
|
+
* Returns an error message when there is any issue in a Cast session.
|
|
733
988
|
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
734
989
|
* when the mirror command has been issued to the device.
|
|
735
990
|
*/
|
|
736
991
|
getCastIssueMessage() {
|
|
737
992
|
return this.schedule(
|
|
738
|
-
|
|
739
|
-
|
|
993
|
+
new command.Command(Command.GET_CAST_ISSUE_MESSAGE),
|
|
994
|
+
'Driver.getCastIssueMessage()'
|
|
995
|
+
)
|
|
740
996
|
}
|
|
741
997
|
|
|
742
998
|
/**
|
|
743
999
|
* Stops casting from media router to the specified device, if connected.
|
|
744
1000
|
*
|
|
745
|
-
* @param {String}
|
|
1001
|
+
* @param {String} deviceName name of the target device.
|
|
746
1002
|
* @return {!promise.Thenable<void>} A promise that will be resolved
|
|
747
1003
|
* when the stop command has been issued to the device.
|
|
748
1004
|
*/
|
|
749
1005
|
stopCasting(deviceName) {
|
|
750
1006
|
return this.schedule(
|
|
751
|
-
|
|
752
|
-
'
|
|
1007
|
+
new command.Command(Command.STOP_CASTING).setParameter(
|
|
1008
|
+
'sinkName',
|
|
1009
|
+
deviceName
|
|
1010
|
+
),
|
|
1011
|
+
'Driver.stopCasting(' + deviceName + ')'
|
|
1012
|
+
)
|
|
753
1013
|
}
|
|
754
|
-
}
|
|
755
1014
|
|
|
1015
|
+
getRandomNumber(min, max) {
|
|
1016
|
+
return Math.floor(Math.random() * (max - min + 1) + min)
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
756
1019
|
|
|
757
1020
|
// PUBLIC API
|
|
758
1021
|
|
|
759
|
-
|
|
760
|
-
exports.
|
|
761
|
-
exports.
|
|
762
|
-
exports.ServiceBuilder = ServiceBuilder;
|
|
1022
|
+
exports.Driver = Driver
|
|
1023
|
+
exports.Options = Options
|
|
1024
|
+
exports.ServiceBuilder = ServiceBuilder
|