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/firefox.js
CHANGED
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
* .setFirefoxOptions(options)
|
|
80
80
|
* .build();
|
|
81
81
|
*
|
|
82
|
-
* On all platforms, you may
|
|
82
|
+
* On all platforms, you may configure WebDriver to use a Firefox specific
|
|
83
83
|
* executable:
|
|
84
84
|
*
|
|
85
85
|
* let options = new firefox.Options()
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
*
|
|
90
90
|
* You may customize the Firefox binary and profile when running against a
|
|
91
91
|
* remote Selenium server. Your custom profile will be packaged as a zip and
|
|
92
|
-
*
|
|
92
|
+
* transferred to the remote host for use. The profile will be transferred
|
|
93
93
|
* _once for each new session_. The performance impact should be minimal if
|
|
94
94
|
* you've only configured a few extra browser preferences. If you have a large
|
|
95
95
|
* profile with several extensions, you should consider installing it on the
|
|
@@ -114,25 +114,18 @@
|
|
|
114
114
|
* [PATH]: http://en.wikipedia.org/wiki/PATH_%28variable%29
|
|
115
115
|
*/
|
|
116
116
|
|
|
117
|
-
'use strict'
|
|
118
|
-
|
|
119
|
-
const path = require('path');
|
|
120
|
-
const url = require('url');
|
|
121
|
-
|
|
122
|
-
const Symbols = require('./lib/symbols');
|
|
123
|
-
const command = require('./lib/command');
|
|
124
|
-
const exec = require('./io/exec');
|
|
125
|
-
const http = require('./http');
|
|
126
|
-
const httpUtil = require('./http/util');
|
|
127
|
-
const io = require('./io');
|
|
128
|
-
const net = require('./net');
|
|
129
|
-
const portprober = require('./net/portprober');
|
|
130
|
-
const remote = require('./remote');
|
|
131
|
-
const webdriver = require('./lib/webdriver');
|
|
132
|
-
const zip = require('./io/zip');
|
|
133
|
-
const {Browser, Capabilities} = require('./lib/capabilities');
|
|
134
|
-
const {Zip} = require('./io/zip');
|
|
117
|
+
'use strict'
|
|
135
118
|
|
|
119
|
+
const path = require('path')
|
|
120
|
+
const Symbols = require('./lib/symbols')
|
|
121
|
+
const command = require('./lib/command')
|
|
122
|
+
const http = require('./http')
|
|
123
|
+
const io = require('./io')
|
|
124
|
+
const remote = require('./remote')
|
|
125
|
+
const webdriver = require('./lib/webdriver')
|
|
126
|
+
const zip = require('./io/zip')
|
|
127
|
+
const { Browser, Capabilities } = require('./lib/capabilities')
|
|
128
|
+
const { Zip } = require('./io/zip')
|
|
136
129
|
|
|
137
130
|
/**
|
|
138
131
|
* Thrown when there an add-on is malformed.
|
|
@@ -141,13 +134,12 @@ const {Zip} = require('./io/zip');
|
|
|
141
134
|
class AddonFormatError extends Error {
|
|
142
135
|
/** @param {string} msg The error message. */
|
|
143
136
|
constructor(msg) {
|
|
144
|
-
super(msg)
|
|
137
|
+
super(msg)
|
|
145
138
|
/** @override */
|
|
146
|
-
this.name = this.constructor.name
|
|
139
|
+
this.name = this.constructor.name
|
|
147
140
|
}
|
|
148
141
|
}
|
|
149
142
|
|
|
150
|
-
|
|
151
143
|
/**
|
|
152
144
|
* Installs an extension to the given directory.
|
|
153
145
|
* @param {string} extension Path to the xpi extension file to install.
|
|
@@ -156,57 +148,53 @@ class AddonFormatError extends Error {
|
|
|
156
148
|
* installed.
|
|
157
149
|
*/
|
|
158
150
|
async function installExtension(extension, dir) {
|
|
159
|
-
const ext = extension.slice(-4)
|
|
151
|
+
const ext = extension.slice(-4)
|
|
160
152
|
if (ext !== '.xpi' && ext !== '.zip') {
|
|
161
|
-
throw Error('File name does not end in ".zip" or ".xpi": ' + ext)
|
|
153
|
+
throw Error('File name does not end in ".zip" or ".xpi": ' + ext)
|
|
162
154
|
}
|
|
163
155
|
|
|
164
|
-
let archive = await zip.load(extension)
|
|
156
|
+
let archive = await zip.load(extension)
|
|
165
157
|
if (!archive.has('manifest.json')) {
|
|
166
|
-
throw new AddonFormatError(`Couldn't find manifest.json in ${extension}`)
|
|
158
|
+
throw new AddonFormatError(`Couldn't find manifest.json in ${extension}`)
|
|
167
159
|
}
|
|
168
160
|
|
|
169
|
-
let buf = await archive.getFile('manifest.json')
|
|
170
|
-
let parsedJSON = JSON.parse(buf.toString('utf8'))
|
|
171
|
-
|
|
161
|
+
let buf = await archive.getFile('manifest.json')
|
|
162
|
+
let parsedJSON = JSON.parse(buf.toString('utf8'))
|
|
163
|
+
|
|
172
164
|
let { browser_specific_settings } =
|
|
173
165
|
/** @type {{browser_specific_settings:{gecko:{id:string}}}} */
|
|
174
|
-
parsedJSON
|
|
166
|
+
parsedJSON
|
|
175
167
|
|
|
176
|
-
if (
|
|
177
|
-
browser_specific_settings &&
|
|
178
|
-
browser_specific_settings.gecko
|
|
179
|
-
) {
|
|
168
|
+
if (browser_specific_settings && browser_specific_settings.gecko) {
|
|
180
169
|
/* browser_specific_settings is an alternative to applications
|
|
181
170
|
* It is meant to facilitate cross-browser plugins since Firefox48
|
|
182
171
|
* see https://bugzilla.mozilla.org/show_bug.cgi?id=1262005
|
|
183
172
|
*/
|
|
184
|
-
parsedJSON.applications = browser_specific_settings
|
|
173
|
+
parsedJSON.applications = browser_specific_settings
|
|
185
174
|
}
|
|
186
175
|
|
|
187
176
|
let { applications } =
|
|
188
177
|
/** @type {{applications:{gecko:{id:string}}}} */
|
|
189
|
-
parsedJSON
|
|
178
|
+
parsedJSON
|
|
190
179
|
if (!(applications && applications.gecko && applications.gecko.id)) {
|
|
191
|
-
throw new AddonFormatError(`Could not find add-on ID for ${extension}`)
|
|
180
|
+
throw new AddonFormatError(`Could not find add-on ID for ${extension}`)
|
|
192
181
|
}
|
|
193
182
|
|
|
194
|
-
await io.copy(extension, `${path.join(dir, applications.gecko.id)}.xpi`)
|
|
195
|
-
return applications.gecko.id
|
|
183
|
+
await io.copy(extension, `${path.join(dir, applications.gecko.id)}.xpi`)
|
|
184
|
+
return applications.gecko.id
|
|
196
185
|
}
|
|
197
186
|
|
|
198
|
-
|
|
199
187
|
class Profile {
|
|
200
188
|
constructor() {
|
|
201
189
|
/** @private {?string} */
|
|
202
|
-
this.template_ = null
|
|
190
|
+
this.template_ = null
|
|
203
191
|
|
|
204
192
|
/** @private {!Array<string>} */
|
|
205
|
-
this.extensions_ = []
|
|
193
|
+
this.extensions_ = []
|
|
206
194
|
}
|
|
207
195
|
|
|
208
|
-
addExtensions(/** !Array<string> */paths) {
|
|
209
|
-
this.extensions_ = this.extensions_.concat(...paths)
|
|
196
|
+
addExtensions(/** !Array<string> */ paths) {
|
|
197
|
+
this.extensions_ = this.extensions_.concat(...paths)
|
|
210
198
|
}
|
|
211
199
|
|
|
212
200
|
/**
|
|
@@ -215,13 +203,12 @@ class Profile {
|
|
|
215
203
|
*/
|
|
216
204
|
[Symbols.serialize]() {
|
|
217
205
|
if (this.template_ || this.extensions_.length) {
|
|
218
|
-
return buildProfile(this.template_, this.extensions_)
|
|
206
|
+
return buildProfile(this.template_, this.extensions_)
|
|
219
207
|
}
|
|
220
|
-
return undefined
|
|
208
|
+
return undefined
|
|
221
209
|
}
|
|
222
210
|
}
|
|
223
211
|
|
|
224
|
-
|
|
225
212
|
/**
|
|
226
213
|
* @param {?string} template path to an existing profile to use as a template.
|
|
227
214
|
* @param {!Array<string>} extensions paths to extensions to install in the new
|
|
@@ -229,31 +216,33 @@ class Profile {
|
|
|
229
216
|
* @return {!Promise<string>} a promise for the base64 encoded profile.
|
|
230
217
|
*/
|
|
231
218
|
async function buildProfile(template, extensions) {
|
|
232
|
-
let dir = template
|
|
219
|
+
let dir = template
|
|
233
220
|
|
|
234
221
|
if (extensions.length) {
|
|
235
|
-
dir = await io.tmpDir()
|
|
222
|
+
dir = await io.tmpDir()
|
|
236
223
|
if (template) {
|
|
237
224
|
await io.copyDir(
|
|
238
|
-
|
|
239
|
-
|
|
225
|
+
/** @type {string} */ (template),
|
|
226
|
+
dir,
|
|
227
|
+
/(parent\.lock|lock|\.parentlock)/
|
|
228
|
+
)
|
|
240
229
|
}
|
|
241
230
|
|
|
242
|
-
const extensionsDir = path.join(dir, 'extensions')
|
|
243
|
-
await io.mkdir(extensionsDir)
|
|
231
|
+
const extensionsDir = path.join(dir, 'extensions')
|
|
232
|
+
await io.mkdir(extensionsDir)
|
|
244
233
|
|
|
245
234
|
for (let i = 0; i < extensions.length; i++) {
|
|
246
|
-
await installExtension(extensions[i], extensionsDir)
|
|
235
|
+
await installExtension(extensions[i], extensionsDir)
|
|
247
236
|
}
|
|
248
237
|
}
|
|
249
238
|
|
|
250
|
-
let zip = new Zip
|
|
251
|
-
return zip
|
|
252
|
-
|
|
253
|
-
|
|
239
|
+
let zip = new Zip()
|
|
240
|
+
return zip
|
|
241
|
+
.addDir(dir)
|
|
242
|
+
.then(() => zip.toBuffer())
|
|
243
|
+
.then((buf) => buf.toString('base64'))
|
|
254
244
|
}
|
|
255
245
|
|
|
256
|
-
|
|
257
246
|
/**
|
|
258
247
|
* Configuration options for the FirefoxDriver.
|
|
259
248
|
*/
|
|
@@ -263,8 +252,8 @@ class Options extends Capabilities {
|
|
|
263
252
|
* capabilities to initialize this instance from.
|
|
264
253
|
*/
|
|
265
254
|
constructor(other) {
|
|
266
|
-
super(other)
|
|
267
|
-
this.setBrowserName(Browser.FIREFOX)
|
|
255
|
+
super(other)
|
|
256
|
+
this.setBrowserName(Browser.FIREFOX)
|
|
268
257
|
}
|
|
269
258
|
|
|
270
259
|
/**
|
|
@@ -272,12 +261,12 @@ class Options extends Capabilities {
|
|
|
272
261
|
* @private
|
|
273
262
|
*/
|
|
274
263
|
firefoxOptions_() {
|
|
275
|
-
let options = this.get('moz:firefoxOptions')
|
|
264
|
+
let options = this.get('moz:firefoxOptions')
|
|
276
265
|
if (!options) {
|
|
277
|
-
options = {}
|
|
278
|
-
this.set('moz:firefoxOptions', options)
|
|
266
|
+
options = {}
|
|
267
|
+
this.set('moz:firefoxOptions', options)
|
|
279
268
|
}
|
|
280
|
-
return options
|
|
269
|
+
return options
|
|
281
270
|
}
|
|
282
271
|
|
|
283
272
|
/**
|
|
@@ -285,11 +274,11 @@ class Options extends Capabilities {
|
|
|
285
274
|
* @private
|
|
286
275
|
*/
|
|
287
276
|
profile_() {
|
|
288
|
-
let options = this.firefoxOptions_()
|
|
277
|
+
let options = this.firefoxOptions_()
|
|
289
278
|
if (!options.profile) {
|
|
290
|
-
options.profile = new Profile()
|
|
279
|
+
options.profile = new Profile()
|
|
291
280
|
}
|
|
292
|
-
return options.profile
|
|
281
|
+
return options.profile
|
|
293
282
|
}
|
|
294
283
|
|
|
295
284
|
/**
|
|
@@ -301,10 +290,10 @@ class Options extends Capabilities {
|
|
|
301
290
|
*/
|
|
302
291
|
addArguments(...args) {
|
|
303
292
|
if (args.length) {
|
|
304
|
-
let options = this.firefoxOptions_()
|
|
305
|
-
options.args = options.args ? options.args.concat(...args) : args
|
|
293
|
+
let options = this.firefoxOptions_()
|
|
294
|
+
options.args = options.args ? options.args.concat(...args) : args
|
|
306
295
|
}
|
|
307
|
-
return this
|
|
296
|
+
return this
|
|
308
297
|
}
|
|
309
298
|
|
|
310
299
|
/**
|
|
@@ -313,7 +302,7 @@ class Options extends Capabilities {
|
|
|
313
302
|
* @return {!Options} A self reference.
|
|
314
303
|
*/
|
|
315
304
|
headless() {
|
|
316
|
-
return this.addArguments('-headless')
|
|
305
|
+
return this.addArguments('-headless')
|
|
317
306
|
}
|
|
318
307
|
|
|
319
308
|
/**
|
|
@@ -325,15 +314,15 @@ class Options extends Capabilities {
|
|
|
325
314
|
* @throws {TypeError} if width or height is unspecified, not a number, or
|
|
326
315
|
* less than or equal to 0.
|
|
327
316
|
*/
|
|
328
|
-
windowSize({width, height}) {
|
|
317
|
+
windowSize({ width, height }) {
|
|
329
318
|
function checkArg(arg) {
|
|
330
319
|
if (typeof arg !== 'number' || arg <= 0) {
|
|
331
|
-
throw TypeError('Arguments must be {width, height} with numbers > 0')
|
|
320
|
+
throw TypeError('Arguments must be {width, height} with numbers > 0')
|
|
332
321
|
}
|
|
333
322
|
}
|
|
334
|
-
checkArg(width)
|
|
335
|
-
checkArg(height)
|
|
336
|
-
return this.addArguments(`--width=${width}`, `--height=${height}`)
|
|
323
|
+
checkArg(width)
|
|
324
|
+
checkArg(height)
|
|
325
|
+
return this.addArguments(`--width=${width}`, `--height=${height}`)
|
|
337
326
|
}
|
|
338
327
|
|
|
339
328
|
/**
|
|
@@ -343,8 +332,8 @@ class Options extends Capabilities {
|
|
|
343
332
|
* @return {!Options} A self reference.
|
|
344
333
|
*/
|
|
345
334
|
addExtensions(...paths) {
|
|
346
|
-
this.profile_().addExtensions(paths)
|
|
347
|
-
return this
|
|
335
|
+
this.profile_().addExtensions(paths)
|
|
336
|
+
return this
|
|
348
337
|
}
|
|
349
338
|
|
|
350
339
|
/**
|
|
@@ -355,18 +344,21 @@ class Options extends Capabilities {
|
|
|
355
344
|
*/
|
|
356
345
|
setPreference(key, value) {
|
|
357
346
|
if (typeof key !== 'string') {
|
|
358
|
-
throw TypeError(`key must be a string, but got ${typeof key}`)
|
|
347
|
+
throw TypeError(`key must be a string, but got ${typeof key}`)
|
|
359
348
|
}
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
349
|
+
if (
|
|
350
|
+
typeof value !== 'string' &&
|
|
351
|
+
typeof value !== 'number' &&
|
|
352
|
+
typeof value !== 'boolean'
|
|
353
|
+
) {
|
|
363
354
|
throw TypeError(
|
|
364
|
-
|
|
355
|
+
`value must be a string, number, or boolean, but got ${typeof value}`
|
|
356
|
+
)
|
|
365
357
|
}
|
|
366
|
-
let options = this.firefoxOptions_()
|
|
367
|
-
options.prefs = options.prefs || {}
|
|
368
|
-
options.prefs[key] = value
|
|
369
|
-
return this
|
|
358
|
+
let options = this.firefoxOptions_()
|
|
359
|
+
options.prefs = options.prefs || {}
|
|
360
|
+
options.prefs[key] = value
|
|
361
|
+
return this
|
|
370
362
|
}
|
|
371
363
|
|
|
372
364
|
/**
|
|
@@ -380,10 +372,10 @@ class Options extends Capabilities {
|
|
|
380
372
|
*/
|
|
381
373
|
setProfile(profile) {
|
|
382
374
|
if (typeof profile !== 'string') {
|
|
383
|
-
throw TypeError(`profile must be a string, but got ${typeof profile}`)
|
|
375
|
+
throw TypeError(`profile must be a string, but got ${typeof profile}`)
|
|
384
376
|
}
|
|
385
|
-
this.profile_().template_ = profile
|
|
386
|
-
return this
|
|
377
|
+
this.profile_().template_ = profile
|
|
378
|
+
return this
|
|
387
379
|
}
|
|
388
380
|
|
|
389
381
|
/**
|
|
@@ -396,14 +388,13 @@ class Options extends Capabilities {
|
|
|
396
388
|
*/
|
|
397
389
|
setBinary(binary) {
|
|
398
390
|
if (binary instanceof Channel || typeof binary === 'string') {
|
|
399
|
-
this.firefoxOptions_().binary = binary
|
|
400
|
-
return this
|
|
391
|
+
this.firefoxOptions_().binary = binary
|
|
392
|
+
return this
|
|
401
393
|
}
|
|
402
|
-
throw TypeError('binary must be a string path or Channel object')
|
|
394
|
+
throw TypeError('binary must be a string path or Channel object')
|
|
403
395
|
}
|
|
404
396
|
}
|
|
405
397
|
|
|
406
|
-
|
|
407
398
|
/**
|
|
408
399
|
* Enum of available command contexts.
|
|
409
400
|
*
|
|
@@ -415,14 +406,12 @@ class Options extends Capabilities {
|
|
|
415
406
|
* @enum {string}
|
|
416
407
|
*/
|
|
417
408
|
const Context = {
|
|
418
|
-
CONTENT:
|
|
419
|
-
CHROME:
|
|
420
|
-
}
|
|
421
|
-
|
|
409
|
+
CONTENT: 'content',
|
|
410
|
+
CHROME: 'chrome',
|
|
411
|
+
}
|
|
422
412
|
|
|
423
413
|
const GECKO_DRIVER_EXE =
|
|
424
|
-
|
|
425
|
-
|
|
414
|
+
process.platform === 'win32' ? 'geckodriver.exe' : 'geckodriver'
|
|
426
415
|
|
|
427
416
|
/**
|
|
428
417
|
* _Synchronously_ attempts to locate the geckodriver executable on the current
|
|
@@ -431,27 +420,28 @@ const GECKO_DRIVER_EXE =
|
|
|
431
420
|
* @return {?string} the located executable, or `null`.
|
|
432
421
|
*/
|
|
433
422
|
function locateSynchronously() {
|
|
434
|
-
return io.findInPath(GECKO_DRIVER_EXE, true)
|
|
423
|
+
return io.findInPath(GECKO_DRIVER_EXE, true)
|
|
435
424
|
}
|
|
436
425
|
|
|
437
|
-
|
|
438
426
|
/**
|
|
439
427
|
* @return {string} .
|
|
440
428
|
* @throws {Error}
|
|
441
429
|
*/
|
|
442
430
|
function findGeckoDriver() {
|
|
443
|
-
let exe = locateSynchronously()
|
|
431
|
+
let exe = locateSynchronously()
|
|
444
432
|
if (!exe) {
|
|
445
433
|
throw Error(
|
|
446
|
-
'The ' +
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
434
|
+
'The ' +
|
|
435
|
+
GECKO_DRIVER_EXE +
|
|
436
|
+
' executable could not be found on the current ' +
|
|
437
|
+
'PATH. Please download the latest version from ' +
|
|
438
|
+
'https://github.com/mozilla/geckodriver/releases/ ' +
|
|
439
|
+
'and ensure it can be found on your PATH.'
|
|
440
|
+
)
|
|
450
441
|
}
|
|
451
|
-
return exe
|
|
442
|
+
return exe
|
|
452
443
|
}
|
|
453
444
|
|
|
454
|
-
|
|
455
445
|
/**
|
|
456
446
|
* @param {string} file Path to the file to find, relative to the program files
|
|
457
447
|
* root.
|
|
@@ -461,24 +451,24 @@ function findGeckoDriver() {
|
|
|
461
451
|
function findInProgramFiles(file) {
|
|
462
452
|
let files = [
|
|
463
453
|
process.env['PROGRAMFILES'] || 'C:\\Program Files',
|
|
464
|
-
process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)'
|
|
465
|
-
].map(prefix => path.join(prefix, file))
|
|
466
|
-
return io.exists(files[0]).then(function(exists) {
|
|
467
|
-
return exists
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
454
|
+
process.env['PROGRAMFILES(X86)'] || 'C:\\Program Files (x86)',
|
|
455
|
+
].map((prefix) => path.join(prefix, file))
|
|
456
|
+
return io.exists(files[0]).then(function (exists) {
|
|
457
|
+
return exists
|
|
458
|
+
? files[0]
|
|
459
|
+
: io.exists(files[1]).then(function (exists) {
|
|
460
|
+
return exists ? files[1] : null
|
|
461
|
+
})
|
|
462
|
+
})
|
|
471
463
|
}
|
|
472
464
|
|
|
473
|
-
|
|
474
465
|
/** @enum {string} */
|
|
475
466
|
const ExtensionCommand = {
|
|
476
467
|
GET_CONTEXT: 'getContext',
|
|
477
468
|
SET_CONTEXT: 'setContext',
|
|
478
469
|
INSTALL_ADDON: 'install addon',
|
|
479
470
|
UNINSTALL_ADDON: 'uninstall addon',
|
|
480
|
-
}
|
|
481
|
-
|
|
471
|
+
}
|
|
482
472
|
|
|
483
473
|
/**
|
|
484
474
|
* Creates a command executor with support for Marionette's custom commands.
|
|
@@ -486,40 +476,42 @@ const ExtensionCommand = {
|
|
|
486
476
|
* @return {!command.Executor} The new command executor.
|
|
487
477
|
*/
|
|
488
478
|
function createExecutor(serverUrl) {
|
|
489
|
-
let client = serverUrl.then(url => new http.HttpClient(url))
|
|
490
|
-
let executor = new http.Executor(client)
|
|
491
|
-
configureExecutor(executor)
|
|
492
|
-
return executor
|
|
479
|
+
let client = serverUrl.then((url) => new http.HttpClient(url))
|
|
480
|
+
let executor = new http.Executor(client)
|
|
481
|
+
configureExecutor(executor)
|
|
482
|
+
return executor
|
|
493
483
|
}
|
|
494
484
|
|
|
495
|
-
|
|
496
485
|
/**
|
|
497
486
|
* Configures the given executor with Firefox-specific commands.
|
|
498
487
|
* @param {!http.Executor} executor the executor to configure.
|
|
499
488
|
*/
|
|
500
489
|
function configureExecutor(executor) {
|
|
501
490
|
executor.defineCommand(
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
491
|
+
ExtensionCommand.GET_CONTEXT,
|
|
492
|
+
'GET',
|
|
493
|
+
'/session/:sessionId/moz/context'
|
|
494
|
+
)
|
|
505
495
|
|
|
506
496
|
executor.defineCommand(
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
497
|
+
ExtensionCommand.SET_CONTEXT,
|
|
498
|
+
'POST',
|
|
499
|
+
'/session/:sessionId/moz/context'
|
|
500
|
+
)
|
|
510
501
|
|
|
511
502
|
executor.defineCommand(
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
503
|
+
ExtensionCommand.INSTALL_ADDON,
|
|
504
|
+
'POST',
|
|
505
|
+
'/session/:sessionId/moz/addon/install'
|
|
506
|
+
)
|
|
515
507
|
|
|
516
508
|
executor.defineCommand(
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
509
|
+
ExtensionCommand.UNINSTALL_ADDON,
|
|
510
|
+
'POST',
|
|
511
|
+
'/session/:sessionId/moz/addon/uninstall'
|
|
512
|
+
)
|
|
520
513
|
}
|
|
521
514
|
|
|
522
|
-
|
|
523
515
|
/**
|
|
524
516
|
* Creates {@link selenium-webdriver/remote.DriverService} instances that manage
|
|
525
517
|
* a [geckodriver](https://github.com/mozilla/geckodriver) server in a child
|
|
@@ -531,8 +523,8 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
531
523
|
* the builder will attempt to locate the geckodriver on the system PATH.
|
|
532
524
|
*/
|
|
533
525
|
constructor(opt_exe) {
|
|
534
|
-
super(opt_exe || findGeckoDriver())
|
|
535
|
-
this.setLoopback(true)
|
|
526
|
+
super(opt_exe || findGeckoDriver())
|
|
527
|
+
this.setLoopback(true) // Required.
|
|
536
528
|
}
|
|
537
529
|
|
|
538
530
|
/**
|
|
@@ -543,11 +535,10 @@ class ServiceBuilder extends remote.DriverService.Builder {
|
|
|
543
535
|
* @return {!ServiceBuilder} A self reference.
|
|
544
536
|
*/
|
|
545
537
|
enableVerboseLogging(opt_trace) {
|
|
546
|
-
return this.addArguments(opt_trace ? '-vv' : '-v')
|
|
538
|
+
return this.addArguments(opt_trace ? '-vv' : '-v')
|
|
547
539
|
}
|
|
548
540
|
}
|
|
549
541
|
|
|
550
|
-
|
|
551
542
|
/**
|
|
552
543
|
* A WebDriver client for Firefox.
|
|
553
544
|
*/
|
|
@@ -576,25 +567,24 @@ class Driver extends webdriver.WebDriver {
|
|
|
576
567
|
*/
|
|
577
568
|
static createSession(opt_config, opt_executor) {
|
|
578
569
|
let caps =
|
|
579
|
-
|
|
580
|
-
? opt_config : new Options(opt_config);
|
|
570
|
+
opt_config instanceof Capabilities ? opt_config : new Options(opt_config)
|
|
581
571
|
|
|
582
|
-
let executor
|
|
583
|
-
let onQuit
|
|
572
|
+
let executor
|
|
573
|
+
let onQuit
|
|
584
574
|
|
|
585
575
|
if (opt_executor instanceof http.Executor) {
|
|
586
|
-
executor = opt_executor
|
|
587
|
-
configureExecutor(executor)
|
|
576
|
+
executor = opt_executor
|
|
577
|
+
configureExecutor(executor)
|
|
588
578
|
} else if (opt_executor instanceof remote.DriverService) {
|
|
589
|
-
executor = createExecutor(opt_executor.start())
|
|
590
|
-
onQuit = () => opt_executor.kill()
|
|
579
|
+
executor = createExecutor(opt_executor.start())
|
|
580
|
+
onQuit = () => opt_executor.kill()
|
|
591
581
|
} else {
|
|
592
|
-
let service = new ServiceBuilder().build()
|
|
593
|
-
executor = createExecutor(service.start())
|
|
594
|
-
onQuit = () => service.kill()
|
|
582
|
+
let service = new ServiceBuilder().build()
|
|
583
|
+
executor = createExecutor(service.start())
|
|
584
|
+
onQuit = () => service.kill()
|
|
595
585
|
}
|
|
596
586
|
|
|
597
|
-
return /** @type {!Driver} */(super.createSession(executor, caps, onQuit))
|
|
587
|
+
return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
|
|
598
588
|
}
|
|
599
589
|
|
|
600
590
|
/**
|
|
@@ -602,8 +592,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
602
592
|
* implementation.
|
|
603
593
|
* @override
|
|
604
594
|
*/
|
|
605
|
-
setFileDetector() {
|
|
606
|
-
}
|
|
595
|
+
setFileDetector() {}
|
|
607
596
|
|
|
608
597
|
/**
|
|
609
598
|
* Get the context that is currently in effect.
|
|
@@ -611,7 +600,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
611
600
|
* @return {!Promise<Context>} Current context.
|
|
612
601
|
*/
|
|
613
602
|
getContext() {
|
|
614
|
-
return this.execute(new command.Command(ExtensionCommand.GET_CONTEXT))
|
|
603
|
+
return this.execute(new command.Command(ExtensionCommand.GET_CONTEXT))
|
|
615
604
|
}
|
|
616
605
|
|
|
617
606
|
/**
|
|
@@ -630,8 +619,11 @@ class Driver extends webdriver.WebDriver {
|
|
|
630
619
|
*/
|
|
631
620
|
setContext(ctx) {
|
|
632
621
|
return this.execute(
|
|
633
|
-
|
|
634
|
-
|
|
622
|
+
new command.Command(ExtensionCommand.SET_CONTEXT).setParameter(
|
|
623
|
+
'context',
|
|
624
|
+
ctx
|
|
625
|
+
)
|
|
626
|
+
)
|
|
635
627
|
}
|
|
636
628
|
|
|
637
629
|
/**
|
|
@@ -642,18 +634,19 @@ class Driver extends webdriver.WebDriver {
|
|
|
642
634
|
*
|
|
643
635
|
* @param {string} path Path on the local filesystem to the web extension to
|
|
644
636
|
* install.
|
|
645
|
-
* @param {boolean} temporary Flag indicating whether the extension should be
|
|
637
|
+
* @param {boolean} temporary Flag indicating whether the extension should be
|
|
646
638
|
* installed temporarily - gets removed on restart
|
|
647
639
|
* @return {!Promise<string>} A promise that will resolve to an ID for the
|
|
648
640
|
* newly installed addon.
|
|
649
641
|
* @see #uninstallAddon
|
|
650
642
|
*/
|
|
651
|
-
async installAddon(path, temporary=false) {
|
|
652
|
-
let buf = await io.read(path)
|
|
643
|
+
async installAddon(path, temporary = false) {
|
|
644
|
+
let buf = await io.read(path)
|
|
653
645
|
return this.execute(
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
646
|
+
new command.Command(ExtensionCommand.INSTALL_ADDON)
|
|
647
|
+
.setParameter('addon', buf.toString('base64'))
|
|
648
|
+
.setParameter('temporary', temporary)
|
|
649
|
+
)
|
|
657
650
|
}
|
|
658
651
|
|
|
659
652
|
/**
|
|
@@ -665,14 +658,16 @@ class Driver extends webdriver.WebDriver {
|
|
|
665
658
|
* @see #installAddon
|
|
666
659
|
*/
|
|
667
660
|
async uninstallAddon(id) {
|
|
668
|
-
id = await Promise.resolve(id)
|
|
661
|
+
id = await Promise.resolve(id)
|
|
669
662
|
return this.execute(
|
|
670
|
-
|
|
671
|
-
|
|
663
|
+
new command.Command(ExtensionCommand.UNINSTALL_ADDON).setParameter(
|
|
664
|
+
'id',
|
|
665
|
+
id
|
|
666
|
+
)
|
|
667
|
+
)
|
|
672
668
|
}
|
|
673
669
|
}
|
|
674
670
|
|
|
675
|
-
|
|
676
671
|
/**
|
|
677
672
|
* Provides methods for locating the executable for a Firefox release channel
|
|
678
673
|
* on Windows and MacOS. For other systems (i.e. Linux), Firefox will always
|
|
@@ -686,10 +681,10 @@ class Channel {
|
|
|
686
681
|
* @param {string} win32 The path to check when running on Windows.
|
|
687
682
|
*/
|
|
688
683
|
constructor(darwin, win32) {
|
|
689
|
-
/** @private @const */ this.darwin_ = darwin
|
|
690
|
-
/** @private @const */ this.win32_ = win32
|
|
684
|
+
/** @private @const */ this.darwin_ = darwin
|
|
685
|
+
/** @private @const */ this.win32_ = win32
|
|
691
686
|
/** @private {Promise<string>} */
|
|
692
|
-
this.found_ = null
|
|
687
|
+
this.found_ = null
|
|
693
688
|
}
|
|
694
689
|
|
|
695
690
|
/**
|
|
@@ -703,43 +698,44 @@ class Channel {
|
|
|
703
698
|
*/
|
|
704
699
|
locate() {
|
|
705
700
|
if (this.found_) {
|
|
706
|
-
return this.found_
|
|
701
|
+
return this.found_
|
|
707
702
|
}
|
|
708
703
|
|
|
709
|
-
let found
|
|
704
|
+
let found
|
|
710
705
|
switch (process.platform) {
|
|
711
706
|
case 'darwin':
|
|
712
|
-
found = io
|
|
713
|
-
|
|
714
|
-
|
|
707
|
+
found = io
|
|
708
|
+
.exists(this.darwin_)
|
|
709
|
+
.then((exists) => (exists ? this.darwin_ : io.findInPath('firefox')))
|
|
710
|
+
break
|
|
715
711
|
|
|
716
712
|
case 'win32':
|
|
717
|
-
found = findInProgramFiles(this.win32_)
|
|
718
|
-
|
|
719
|
-
|
|
713
|
+
found = findInProgramFiles(this.win32_).then(
|
|
714
|
+
(found) => found || io.findInPath('firefox.exe')
|
|
715
|
+
)
|
|
716
|
+
break
|
|
720
717
|
|
|
721
718
|
default:
|
|
722
|
-
found = Promise.resolve(io.findInPath('firefox'))
|
|
723
|
-
break
|
|
719
|
+
found = Promise.resolve(io.findInPath('firefox'))
|
|
720
|
+
break
|
|
724
721
|
}
|
|
725
722
|
|
|
726
|
-
this.found_ = found.then(found => {
|
|
723
|
+
this.found_ = found.then((found) => {
|
|
727
724
|
if (found) {
|
|
728
725
|
// TODO: verify version info.
|
|
729
|
-
return found
|
|
726
|
+
return found
|
|
730
727
|
}
|
|
731
|
-
throw Error('Could not locate Firefox on the current system')
|
|
732
|
-
})
|
|
733
|
-
return this.found_
|
|
728
|
+
throw Error('Could not locate Firefox on the current system')
|
|
729
|
+
})
|
|
730
|
+
return this.found_
|
|
734
731
|
}
|
|
735
732
|
|
|
736
733
|
/** @return {!Promise<string>} */
|
|
737
734
|
[Symbols.serialize]() {
|
|
738
|
-
return this.locate()
|
|
735
|
+
return this.locate()
|
|
739
736
|
}
|
|
740
737
|
}
|
|
741
738
|
|
|
742
|
-
|
|
743
739
|
/**
|
|
744
740
|
* Firefox's developer channel.
|
|
745
741
|
* @const
|
|
@@ -747,7 +743,8 @@ class Channel {
|
|
|
747
743
|
*/
|
|
748
744
|
Channel.AURORA = new Channel(
|
|
749
745
|
'/Applications/FirefoxDeveloperEdition.app/Contents/MacOS/firefox-bin',
|
|
750
|
-
'Firefox Developer Edition\\firefox.exe'
|
|
746
|
+
'Firefox Developer Edition\\firefox.exe'
|
|
747
|
+
)
|
|
751
748
|
|
|
752
749
|
/**
|
|
753
750
|
* Firefox's beta channel. Note this is provided mainly for convenience as
|
|
@@ -758,7 +755,8 @@ Channel.AURORA = new Channel(
|
|
|
758
755
|
*/
|
|
759
756
|
Channel.BETA = new Channel(
|
|
760
757
|
'/Applications/Firefox.app/Contents/MacOS/firefox-bin',
|
|
761
|
-
'Mozilla Firefox\\firefox.exe'
|
|
758
|
+
'Mozilla Firefox\\firefox.exe'
|
|
759
|
+
)
|
|
762
760
|
|
|
763
761
|
/**
|
|
764
762
|
* Firefox's release channel.
|
|
@@ -767,7 +765,8 @@ Channel.BETA = new Channel(
|
|
|
767
765
|
*/
|
|
768
766
|
Channel.RELEASE = new Channel(
|
|
769
767
|
'/Applications/Firefox.app/Contents/MacOS/firefox-bin',
|
|
770
|
-
'Mozilla Firefox\\firefox.exe'
|
|
768
|
+
'Mozilla Firefox\\firefox.exe'
|
|
769
|
+
)
|
|
771
770
|
|
|
772
771
|
/**
|
|
773
772
|
* Firefox's nightly release channel.
|
|
@@ -776,15 +775,14 @@ Channel.RELEASE = new Channel(
|
|
|
776
775
|
*/
|
|
777
776
|
Channel.NIGHTLY = new Channel(
|
|
778
777
|
'/Applications/Firefox Nightly.app/Contents/MacOS/firefox-bin',
|
|
779
|
-
'Nightly\\firefox.exe'
|
|
780
|
-
|
|
778
|
+
'Nightly\\firefox.exe'
|
|
779
|
+
)
|
|
781
780
|
|
|
782
781
|
// PUBLIC API
|
|
783
782
|
|
|
784
|
-
|
|
785
|
-
exports.
|
|
786
|
-
exports.
|
|
787
|
-
exports.
|
|
788
|
-
exports.
|
|
789
|
-
exports.
|
|
790
|
-
exports.locateSynchronously = locateSynchronously;
|
|
783
|
+
exports.Channel = Channel
|
|
784
|
+
exports.Context = Context
|
|
785
|
+
exports.Driver = Driver
|
|
786
|
+
exports.Options = Options
|
|
787
|
+
exports.ServiceBuilder = ServiceBuilder
|
|
788
|
+
exports.locateSynchronously = locateSynchronously
|