selenium-webdriver 4.19.0 → 4.21.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/CHANGES.md +21 -0
- package/README.md +1 -1
- package/bidi/addInterceptParameters.js +28 -2
- package/bidi/browser.js +17 -0
- package/bidi/browsingContext.js +206 -39
- package/bidi/browsingContextInspector.js +51 -0
- package/bidi/browsingContextTypes.js +31 -0
- package/bidi/captureScreenshotParameters.js +96 -0
- package/bidi/clipRectangle.js +127 -0
- package/bidi/continueRequestParameters.js +39 -0
- package/bidi/continueResponseParameters.js +40 -0
- package/bidi/cookieFilter.js +60 -0
- package/bidi/createContextParameters.js +73 -0
- package/bidi/evaluateResult.js +17 -0
- package/bidi/index.js +0 -1
- package/bidi/input.js +27 -3
- package/bidi/interceptPhase.js +4 -0
- package/bidi/logEntries.js +66 -0
- package/bidi/network.js +102 -0
- package/bidi/networkTypes.js +346 -1
- package/bidi/partialCookie.js +48 -0
- package/bidi/partitionDescriptor.js +33 -0
- package/bidi/partitionKey.js +17 -0
- package/bidi/protocolType.js +20 -0
- package/bidi/protocolValue.js +129 -2
- package/bidi/provideResponseParameters.js +40 -0
- package/bidi/realmInfo.js +27 -0
- package/bidi/resultOwnership.js +4 -0
- package/bidi/scriptManager.js +119 -1
- package/bidi/scriptTypes.js +36 -0
- package/bidi/storage.js +30 -0
- package/bidi/urlPattern.js +35 -0
- package/bin/linux/selenium-manager +0 -0
- package/bin/macos/selenium-manager +0 -0
- package/bin/windows/selenium-manager.exe +0 -0
- package/chromium.js +12 -9
- package/common/driverFinder.js +36 -4
- package/common/seleniumManager.js +7 -39
- package/eslint.config.js +107 -0
- package/firefox.js +20 -6
- package/http/index.js +6 -6
- package/ie.js +8 -4
- package/index.js +11 -0
- package/io/exec.js +1 -1
- package/io/index.js +3 -2
- package/io/zip.js +1 -1
- package/lib/atoms/find-elements.js +2 -2
- package/lib/http.js +5 -4
- package/lib/input.js +0 -1
- package/lib/pinnedScript.js +1 -1
- package/lib/select.js +102 -60
- package/lib/until.js +3 -3
- package/lib/util.js +1 -0
- package/lib/webdriver.js +2 -2
- package/net/index.js +1 -1
- package/net/portprober.js +1 -1
- package/package.json +13 -7
- package/remote/index.js +1 -1
- package/remote/util.js +2 -2
- package/safari.js +3 -3
- package/testing/index.js +12 -8
package/eslint.config.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Licensed to the Software Freedom Conservancy (SFC) under one
|
|
2
|
+
// or more contributor license agreements. See the NOTICE file
|
|
3
|
+
// distributed with this work for additional information
|
|
4
|
+
// regarding copyright ownership. The SFC licenses this file
|
|
5
|
+
// to you under the Apache License, Version 2.0 (the
|
|
6
|
+
// "License"); you may not use this file except in compliance
|
|
7
|
+
// with the License. You may obtain a copy of the License at
|
|
8
|
+
//
|
|
9
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
//
|
|
11
|
+
// Unless required by applicable law or agreed to in writing,
|
|
12
|
+
// software distributed under the License is distributed on an
|
|
13
|
+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
14
|
+
// KIND, either express or implied. See the License for the
|
|
15
|
+
// specific language governing permissions and limitations
|
|
16
|
+
// under the License.
|
|
17
|
+
|
|
18
|
+
const globals = require('globals')
|
|
19
|
+
const noOnlyTests = require('eslint-plugin-no-only-tests')
|
|
20
|
+
const js = require('@eslint/js')
|
|
21
|
+
const eslintPluginPrettierRecommended = require('eslint-plugin-prettier/recommended')
|
|
22
|
+
const mochaPlugin = require('eslint-plugin-mocha')
|
|
23
|
+
const nodePlugin = require('eslint-plugin-n')
|
|
24
|
+
|
|
25
|
+
module.exports = [
|
|
26
|
+
js.configs.recommended,
|
|
27
|
+
eslintPluginPrettierRecommended,
|
|
28
|
+
mochaPlugin.configs.flat.recommended,
|
|
29
|
+
nodePlugin.configs['flat/recommended-script'],
|
|
30
|
+
{
|
|
31
|
+
languageOptions: {
|
|
32
|
+
globals: {
|
|
33
|
+
mocha: true,
|
|
34
|
+
es6: true,
|
|
35
|
+
...globals.node,
|
|
36
|
+
},
|
|
37
|
+
parserOptions: {
|
|
38
|
+
ecmaVersion: 2022,
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
files: ['**/*.js', 'lib/http.js'],
|
|
42
|
+
ignores: ['node_modules/*', 'generator/*', 'devtools/generator/'],
|
|
43
|
+
plugins: {
|
|
44
|
+
'no-only-tests': noOnlyTests,
|
|
45
|
+
},
|
|
46
|
+
rules: {
|
|
47
|
+
'no-const-assign': 'error',
|
|
48
|
+
'no-this-before-super': 'error',
|
|
49
|
+
'no-undef': 'error',
|
|
50
|
+
'no-unreachable': 'error',
|
|
51
|
+
'no-unused-vars': [
|
|
52
|
+
'error',
|
|
53
|
+
{
|
|
54
|
+
varsIgnorePattern: '^_',
|
|
55
|
+
args: 'all',
|
|
56
|
+
argsIgnorePattern: '^_',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
'constructor-super': 'error',
|
|
60
|
+
'valid-typeof': 'error',
|
|
61
|
+
'no-only-tests/no-only-tests': 'error',
|
|
62
|
+
'n/no-deprecated-api': ['error'],
|
|
63
|
+
'n/no-missing-import': ['error'],
|
|
64
|
+
'n/no-missing-require': ['error'],
|
|
65
|
+
'n/no-mixed-requires': ['error'],
|
|
66
|
+
'n/no-new-require': ['error'],
|
|
67
|
+
'n/no-unpublished-import': ['error'],
|
|
68
|
+
'n/no-unpublished-require': [
|
|
69
|
+
'error',
|
|
70
|
+
{
|
|
71
|
+
allowModules: [
|
|
72
|
+
'globals',
|
|
73
|
+
'@eslint/js',
|
|
74
|
+
'eslint-plugin-mocha',
|
|
75
|
+
'eslint-plugin-prettier',
|
|
76
|
+
'eslint-plugin-n',
|
|
77
|
+
'eslint-plugin-no-only-tests',
|
|
78
|
+
],
|
|
79
|
+
tryExtensions: ['.js'],
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
'n/prefer-node-protocol': ['error'],
|
|
83
|
+
'mocha/no-skipped-tests': ['off'],
|
|
84
|
+
'mocha/no-mocha-arrows': ['off'],
|
|
85
|
+
'mocha/no-setup-in-describe': ['off'],
|
|
86
|
+
'mocha/no-top-level-hooks': ['off'],
|
|
87
|
+
'mocha/no-sibling-hooks': ['off'],
|
|
88
|
+
'mocha/no-exports': ['off'],
|
|
89
|
+
'mocha/no-empty-description': ['off'],
|
|
90
|
+
'mocha/max-top-level-suites': ['off'],
|
|
91
|
+
'mocha/consistent-spacing-between-blocks': ['off'],
|
|
92
|
+
'mocha/no-nested-tests': ['off'],
|
|
93
|
+
'mocha/no-pending-tests': ['off'],
|
|
94
|
+
'mocha/no-identical-title': ['off'],
|
|
95
|
+
'prettier/prettier': [
|
|
96
|
+
'error',
|
|
97
|
+
{
|
|
98
|
+
endOfLine: 'lf',
|
|
99
|
+
printWidth: 120,
|
|
100
|
+
semi: false,
|
|
101
|
+
singleQuote: true,
|
|
102
|
+
trailingComma: 'all',
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
]
|
package/firefox.js
CHANGED
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
|
|
108
108
|
'use strict'
|
|
109
109
|
|
|
110
|
-
const fs = require('fs')
|
|
111
|
-
const path = require('path')
|
|
110
|
+
const fs = require('node:fs')
|
|
111
|
+
const path = require('node:path')
|
|
112
112
|
const Symbols = require('./lib/symbols')
|
|
113
113
|
const command = require('./lib/command')
|
|
114
114
|
const http = require('./http')
|
|
@@ -116,9 +116,9 @@ const io = require('./io')
|
|
|
116
116
|
const remote = require('./remote')
|
|
117
117
|
const webdriver = require('./lib/webdriver')
|
|
118
118
|
const zip = require('./io/zip')
|
|
119
|
-
const { Browser, Capabilities } = require('./lib/capabilities')
|
|
119
|
+
const { Browser, Capabilities, Capability } = require('./lib/capabilities')
|
|
120
120
|
const { Zip } = require('./io/zip')
|
|
121
|
-
const {
|
|
121
|
+
const { getBinaryPaths } = require('./common/driverFinder')
|
|
122
122
|
const FIREFOX_CAPABILITY_KEY = 'moz:firefoxOptions'
|
|
123
123
|
|
|
124
124
|
/**
|
|
@@ -444,6 +444,7 @@ const ExtensionCommand = {
|
|
|
444
444
|
SET_CONTEXT: 'setContext',
|
|
445
445
|
INSTALL_ADDON: 'install addon',
|
|
446
446
|
UNINSTALL_ADDON: 'uninstall addon',
|
|
447
|
+
FULL_PAGE_SCREENSHOT: 'fullPage screenshot',
|
|
447
448
|
}
|
|
448
449
|
|
|
449
450
|
/**
|
|
@@ -470,6 +471,8 @@ function configureExecutor(executor) {
|
|
|
470
471
|
executor.defineCommand(ExtensionCommand.INSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/install')
|
|
471
472
|
|
|
472
473
|
executor.defineCommand(ExtensionCommand.UNINSTALL_ADDON, 'POST', '/session/:sessionId/moz/addon/uninstall')
|
|
474
|
+
|
|
475
|
+
executor.defineCommand(ExtensionCommand.FULL_PAGE_SCREENSHOT, 'GET', '/session/:sessionId/moz/screenshot/full')
|
|
473
476
|
}
|
|
474
477
|
|
|
475
478
|
/**
|
|
@@ -538,7 +541,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
538
541
|
configureExecutor(executor)
|
|
539
542
|
} else if (opt_executor instanceof remote.DriverService) {
|
|
540
543
|
if (!opt_executor.getExecutable()) {
|
|
541
|
-
const { driverPath, browserPath } =
|
|
544
|
+
const { driverPath, browserPath } = getBinaryPaths(caps)
|
|
542
545
|
opt_executor.setExecutable(driverPath)
|
|
543
546
|
firefoxBrowserPath = browserPath
|
|
544
547
|
}
|
|
@@ -547,7 +550,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
547
550
|
} else {
|
|
548
551
|
let service = new ServiceBuilder().build()
|
|
549
552
|
if (!service.getExecutable()) {
|
|
550
|
-
const { driverPath, browserPath } =
|
|
553
|
+
const { driverPath, browserPath } = getBinaryPaths(caps)
|
|
551
554
|
service.setExecutable(driverPath)
|
|
552
555
|
firefoxBrowserPath = browserPath
|
|
553
556
|
}
|
|
@@ -563,6 +566,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
563
566
|
} else {
|
|
564
567
|
caps.set(FIREFOX_CAPABILITY_KEY, { binary: firefoxBrowserPath })
|
|
565
568
|
}
|
|
569
|
+
caps.delete(Capability.BROWSER_VERSION)
|
|
566
570
|
}
|
|
567
571
|
|
|
568
572
|
return /** @type {!Driver} */ (super.createSession(executor, caps, onQuit))
|
|
@@ -645,6 +649,16 @@ class Driver extends webdriver.WebDriver {
|
|
|
645
649
|
id = await Promise.resolve(id)
|
|
646
650
|
return this.execute(new command.Command(ExtensionCommand.UNINSTALL_ADDON).setParameter('id', id))
|
|
647
651
|
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Take full page screenshot of the visible region
|
|
655
|
+
*
|
|
656
|
+
* @return {!Promise<string>} A promise that will be
|
|
657
|
+
* resolved to the screenshot as a base-64 encoded PNG.
|
|
658
|
+
*/
|
|
659
|
+
takeFullPageScreenshot() {
|
|
660
|
+
return this.execute(new command.Command(ExtensionCommand.FULL_PAGE_SCREENSHOT))
|
|
661
|
+
}
|
|
648
662
|
}
|
|
649
663
|
|
|
650
664
|
/**
|
package/http/index.js
CHANGED
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
|
|
23
23
|
'use strict'
|
|
24
24
|
|
|
25
|
-
const http = require('http')
|
|
26
|
-
const https = require('https')
|
|
27
|
-
const url = require('url')
|
|
25
|
+
const http = require('node:http')
|
|
26
|
+
const https = require('node:https')
|
|
27
|
+
const url = require('node:url')
|
|
28
28
|
|
|
29
29
|
const httpLib = require('../lib/http')
|
|
30
30
|
|
|
@@ -45,7 +45,7 @@ let RequestOptions // eslint-disable-line
|
|
|
45
45
|
* @throws {Error} if the URL does not include a hostname.
|
|
46
46
|
*/
|
|
47
47
|
function getRequestOptions(aUrl) {
|
|
48
|
-
//eslint-disable-next-line
|
|
48
|
+
// eslint-disable-next-line n/no-deprecated-api
|
|
49
49
|
let options = url.parse(aUrl)
|
|
50
50
|
if (!options.hostname) {
|
|
51
51
|
throw new Error('Invalid URL: ' + aUrl)
|
|
@@ -140,7 +140,7 @@ class HttpClient {
|
|
|
140
140
|
} else {
|
|
141
141
|
path += httpRequest.path
|
|
142
142
|
}
|
|
143
|
-
//eslint-disable-next-line
|
|
143
|
+
// eslint-disable-next-line n/no-deprecated-api
|
|
144
144
|
let parsedPath = url.parse(path)
|
|
145
145
|
|
|
146
146
|
let options = {
|
|
@@ -218,7 +218,7 @@ function sendRequest(options, onOk, onError, opt_data, opt_proxy, opt_retries) {
|
|
|
218
218
|
if (response.statusCode == 302 || response.statusCode == 303) {
|
|
219
219
|
let location
|
|
220
220
|
try {
|
|
221
|
-
// eslint-disable-next-line
|
|
221
|
+
// eslint-disable-next-line n/no-deprecated-api
|
|
222
222
|
location = url.parse(response.headers['location'])
|
|
223
223
|
} catch (ex) {
|
|
224
224
|
onError(
|
package/ie.js
CHANGED
|
@@ -33,7 +33,7 @@ const remote = require('./remote')
|
|
|
33
33
|
const webdriver = require('./lib/webdriver')
|
|
34
34
|
const { Browser, Capabilities } = require('./lib/capabilities')
|
|
35
35
|
const error = require('./lib/error')
|
|
36
|
-
const {
|
|
36
|
+
const { getBinaryPaths } = require('./common/driverFinder')
|
|
37
37
|
|
|
38
38
|
const OPTIONS_CAPABILITY_KEY = 'se:ieOptions'
|
|
39
39
|
const SCROLL_BEHAVIOUR = {
|
|
@@ -223,7 +223,9 @@ class Options extends Capabilities {
|
|
|
223
223
|
|
|
224
224
|
addBrowserCommandSwitches(...args) {
|
|
225
225
|
let current = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || []
|
|
226
|
-
if (typeof current == 'string')
|
|
226
|
+
if (typeof current == 'string') {
|
|
227
|
+
current = current.split(' ')
|
|
228
|
+
}
|
|
227
229
|
this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = current.concat(args).join(' ')
|
|
228
230
|
return this
|
|
229
231
|
}
|
|
@@ -239,7 +241,9 @@ class Options extends Capabilities {
|
|
|
239
241
|
|
|
240
242
|
addArguments(...args) {
|
|
241
243
|
let current = this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] || []
|
|
242
|
-
if (typeof current == 'string')
|
|
244
|
+
if (typeof current == 'string') {
|
|
245
|
+
current = current.split(' ')
|
|
246
|
+
}
|
|
243
247
|
this.options_[Key.BROWSER_COMMAND_LINE_SWITCHES] = current.concat(args).join(' ')
|
|
244
248
|
return this
|
|
245
249
|
}
|
|
@@ -447,7 +451,7 @@ class Driver extends webdriver.WebDriver {
|
|
|
447
451
|
service = createServiceFromCapabilities(options)
|
|
448
452
|
}
|
|
449
453
|
if (!service.getExecutable()) {
|
|
450
|
-
service.setExecutable(
|
|
454
|
+
service.setExecutable(getBinaryPaths(options).driverPath)
|
|
451
455
|
}
|
|
452
456
|
|
|
453
457
|
let client = service.start().then((url) => new http.HttpClient(url))
|
package/index.js
CHANGED
|
@@ -596,6 +596,17 @@ class Builder {
|
|
|
596
596
|
|
|
597
597
|
browser = capabilities.get(Capability.BROWSER_NAME)
|
|
598
598
|
|
|
599
|
+
/**
|
|
600
|
+
* If browser is not defined in forBrowser, check if browserOptions are defined to pick the browserName
|
|
601
|
+
*/
|
|
602
|
+
if (!browser) {
|
|
603
|
+
const options =
|
|
604
|
+
this.chromeOptions_ || this.firefoxOptions_ || this.ieOptions_ || this.safariOptions_ || this.edgeOptions_
|
|
605
|
+
if (options) {
|
|
606
|
+
browser = options['map_'].get(Capability.BROWSER_NAME)
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
599
610
|
if (typeof browser !== 'string') {
|
|
600
611
|
throw TypeError(
|
|
601
612
|
`Target browser must be a string, but is <${typeof browser}>;` + ' did you forget to call forBrowser()?',
|
package/io/exec.js
CHANGED
package/io/index.js
CHANGED
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
'use strict'
|
|
19
19
|
|
|
20
|
-
const fs = require('fs')
|
|
21
|
-
const path = require('path')
|
|
20
|
+
const fs = require('node:fs')
|
|
21
|
+
const path = require('node:path')
|
|
22
22
|
const tmp = require('tmp')
|
|
23
23
|
|
|
24
24
|
/**
|
|
@@ -219,6 +219,7 @@ function findInPath(file, opt_checkCwd) {
|
|
|
219
219
|
try {
|
|
220
220
|
let stats = fs.statSync(tmp)
|
|
221
221
|
return stats.isFile() && !stats.isDirectory()
|
|
222
|
+
/*eslint no-unused-vars: "off"*/
|
|
222
223
|
} catch (ex) {
|
|
223
224
|
return false
|
|
224
225
|
}
|
package/io/zip.js
CHANGED
|
@@ -106,8 +106,8 @@ function gd(a){var b=a.shape.toLowerCase();a=a.coords.split(",");if("rect"==b&&4
|
|
|
106
106
|
function hd(a){return a.replace(/^[^\S\xa0]+|[^\S\xa0]+$/g,"")}function id(a){var b=[];Yc?jd(a,b):kd(a,b);a=qa(b,hd);return hd(a.join("\n")).replace(/\xa0/g," ")}
|
|
107
107
|
function ld(a,b,c){if(S(a,"BR"))b.push("");else{var d=S(a,"TD"),e=U(a,"display"),f=!d&&!(0<=oa(md,e)),g=void 0!==a.previousElementSibling?a.previousElementSibling:gb(a.previousSibling);g=g?U(g,"display"):"";var h=U(a,"float")||U(a,"cssFloat")||U(a,"styleFloat");!f||"run-in"==g&&"none"==h||/^[\s\xa0]*$/.test(b[b.length-1]||"")||b.push("");var n=ed(a),u=null,p=null;n&&(u=U(a,"white-space"),p=U(a,"text-transform"));l(a.childNodes,function(G){c(G,b,n,u,p)});a=b[b.length-1]||"";!d&&"table-cell"!=e||!a||
|
|
108
108
|
za(a)||(b[b.length-1]+=" ");f&&"run-in"!=e&&!/^[\s\xa0]*$/.test(a)&&b.push("")}}function kd(a,b){ld(a,b,function(c,d,e,f,g){3==c.nodeType&&e?nd(c,d,f,g):S(c)&&kd(c,d)})}var md="inline inline-block inline-table none table-cell table-column table-column-group".split(" ");
|
|
109
|
-
function nd(a,b,c,d){a=a.nodeValue.replace(/[\u200b\u200e\u200f]/g,"");a=a.replace(/(\r\n|\r|\n)/g,"\n");if("normal"==c||"nowrap"==c)a=a.replace(/\n/g," ");a="pre"==c||"pre-wrap"==c?a.replace(/[ \f\t\v\u2028\u2029]/g,"\u00a0"):a.replace(/[ \f\t\v\u2028\u2029]+/g," ");"capitalize"==d?a=a.replace(t?/(^|\s|\b)(\S)/g:/(
|
|
110
|
-
|
|
109
|
+
function nd(a,b,c,d){a=a.nodeValue.replace(/[\u200b\u200e\u200f]/g,"");a=a.replace(/(\r\n|\r|\n)/g,"\n");if("normal"==c||"nowrap"==c)a=a.replace(/\n/g," ");a="pre"==c||"pre-wrap"==c?a.replace(/[ \f\t\v\u2028\u2029]/g,"\u00a0"):a.replace(/[ \f\t\v\u2028\u2029]+/g," ");"capitalize"==d?a=a.replace(t?/(^|\s|\b)(\S)/g:/(^|\s|\b)(\S)/gu,function(e,f,g){return f+g.toUpperCase()}):"uppercase"==d?a=a.toUpperCase():"lowercase"==d&&(a=a.toLowerCase());c=b.pop()||"";za(c)&&0==a.lastIndexOf(" ",0)&&(a=a.substr(1));
|
|
110
|
+
b.push(c+a)}function dd(a){if(Mc){if("relative"==U(a,"position"))return 1;a=U(a,"filter");return(a=a.match(/^alpha\(opacity=(\d*)\)/)||a.match(/^progid:DXImageTransform.Microsoft.Alpha\(Opacity=(\d*)\)/))?Number(a[1])/100:1}return od(a)}function od(a){var b=1,c=U(a,"opacity");c&&(b=Number(c));(a=Zc(a))&&(b*=od(a));return b}
|
|
111
111
|
function pd(a,b,c,d,e){if(3==a.nodeType&&c)nd(a,b,d,e);else if(S(a))if(S(a,"CONTENT")||S(a,"SLOT")){for(var f=a;f.parentNode;)f=f.parentNode;f instanceof ShadowRoot?(f=S(a,"CONTENT")?a.getDistributedNodes():a.assignedNodes(),l(0<f.length?f:a.childNodes,function(g){pd(g,b,c,d,e)})):jd(a,b)}else if(S(a,"SHADOW")){for(f=a;f.parentNode;)f=f.parentNode;if(f instanceof ShadowRoot&&(a=f))for(a=a.olderShadowRoot;a;)l(a.childNodes,function(g){pd(g,b,c,d,e)}),a=a.olderShadowRoot}else jd(a,b)}
|
|
112
112
|
function jd(a,b){a.shadowRoot&&l(a.shadowRoot.childNodes,function(c){pd(c,b,!0,null,null)});ld(a,b,function(c,d,e,f,g){var h=null;1==c.nodeType?h=c:3==c.nodeType&&(h=c);null!=h&&(null!=h.assignedSlot||h.getDestinationInsertionPoints&&0<h.getDestinationInsertionPoints().length)||pd(c,d,e,f,g)})};var qd={C:function(a,b){return!(!a.querySelectorAll||!a.querySelector)&&!/^\d.*/.test(b)},o:function(a,b){var c=eb(b),d="string"===typeof a?c.a.getElementById(a):a;return d?Uc(d,"id")==a&&b!=d&&hb(b,d)?d:ua(mb(c,"*"),function(e){return Uc(e,"id")==a&&b!=e&&hb(b,e)}):null},j:function(a,b){if(!a)return[];if(qd.C(b,a))try{return b.querySelectorAll("#"+qd.T(a))}catch(c){return[]}b=mb(eb(b),"*",null,b);return pa(b,function(c){return Uc(c,"id")==a})},T:function(a){return a.replace(/([\s'"\\#.:;,!?+<>=~*^$|%&@`{}\-\/\[\]\(\)])/g,
|
|
113
113
|
"\\$1")}};var X={},rd={};X.N=function(a,b,c){try{var d=Nc.j("a",b)}catch(e){d=mb(eb(b),"A",null,b)}return ua(d,function(e){e=id(e);e=e.replace(/^[\s]+|[\s]+$/g,"");return c&&-1!=e.indexOf(a)||e==a})};X.K=function(a,b,c){try{var d=Nc.j("a",b)}catch(e){d=mb(eb(b),"A",null,b)}return pa(d,function(e){e=id(e);e=e.replace(/^[\s]+|[\s]+$/g,"");return c&&-1!=e.indexOf(a)||e==a})};X.o=function(a,b){return X.N(a,b,!1)};X.j=function(a,b){return X.K(a,b,!1)};rd.o=function(a,b){return X.N(a,b,!0)};
|
package/lib/http.js
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
|
|
26
26
|
'use strict'
|
|
27
27
|
|
|
28
|
-
const path = require('path')
|
|
28
|
+
const path = require('node:path')
|
|
29
29
|
const cmd = require('./command')
|
|
30
30
|
const error = require('./error')
|
|
31
31
|
const logging = require('./logging')
|
|
@@ -161,10 +161,10 @@ function resource(method, path) {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
/** @typedef {{method: string, path: string}} */
|
|
164
|
-
var CommandSpec
|
|
164
|
+
var CommandSpec
|
|
165
165
|
|
|
166
166
|
/** @typedef {function(!cmd.Command): !cmd.Command} */
|
|
167
|
-
var CommandTransformer
|
|
167
|
+
var CommandTransformer
|
|
168
168
|
|
|
169
169
|
class InternalTypeError extends TypeError {}
|
|
170
170
|
|
|
@@ -337,7 +337,7 @@ class Client {
|
|
|
337
337
|
* @return {!Promise<Response>} A promise that will be fulfilled with the
|
|
338
338
|
* server's response.
|
|
339
339
|
*/
|
|
340
|
-
send(httpRequest) {}
|
|
340
|
+
send(httpRequest) {}
|
|
341
341
|
}
|
|
342
342
|
|
|
343
343
|
/**
|
|
@@ -472,6 +472,7 @@ class Executor {
|
|
|
472
472
|
function tryParse(str) {
|
|
473
473
|
try {
|
|
474
474
|
return JSON.parse(str)
|
|
475
|
+
/*eslint no-unused-vars: "off"*/
|
|
475
476
|
} catch (ignored) {
|
|
476
477
|
// Do nothing.
|
|
477
478
|
}
|
package/lib/input.js
CHANGED
package/lib/pinnedScript.js
CHANGED
package/lib/select.js
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
|
|
37
37
|
'use strict'
|
|
38
38
|
|
|
39
|
-
const { By
|
|
39
|
+
const { By } = require('./by')
|
|
40
40
|
const error = require('./error')
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -51,23 +51,23 @@ class ISelect {
|
|
|
51
51
|
* @return {!Promise<boolean>} Whether this select element supports selecting multiple options at the same time? This
|
|
52
52
|
* is done by checking the value of the "multiple" attribute.
|
|
53
53
|
*/
|
|
54
|
-
isMultiple() {}
|
|
54
|
+
isMultiple() {}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* @return {!Promise<!Array<!WebElement>>} All options belonging to this select tag
|
|
58
58
|
*/
|
|
59
|
-
getOptions() {}
|
|
59
|
+
getOptions() {}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
62
|
* @return {!Promise<!Array<!WebElement>>} All selected options belonging to this select tag
|
|
63
63
|
*/
|
|
64
|
-
getAllSelectedOptions() {}
|
|
64
|
+
getAllSelectedOptions() {}
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* @return {!Promise<!WebElement>} The first selected option in this select tag (or the currently selected option in a
|
|
68
68
|
* normal select)
|
|
69
69
|
*/
|
|
70
|
-
getFirstSelectedOption() {}
|
|
70
|
+
getFirstSelectedOption() {}
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Select all options that display text matching the argument. That is, when given "Bar" this
|
|
@@ -105,7 +105,7 @@ class ISelect {
|
|
|
105
105
|
*
|
|
106
106
|
* @return {Promise<void>}
|
|
107
107
|
*/
|
|
108
|
-
deselectAll() {}
|
|
108
|
+
deselectAll() {}
|
|
109
109
|
|
|
110
110
|
/**
|
|
111
111
|
* Deselect all options that display text matching the argument. That is, when given "Bar" this
|
|
@@ -146,6 +146,10 @@ class Select {
|
|
|
146
146
|
* @param {WebElement} element Select WebElement.
|
|
147
147
|
*/
|
|
148
148
|
constructor(element) {
|
|
149
|
+
if (element === null) {
|
|
150
|
+
throw new Error(`Element must not be null. Please provide a valid <select> element.`)
|
|
151
|
+
}
|
|
152
|
+
|
|
149
153
|
this.element = element
|
|
150
154
|
|
|
151
155
|
this.element.getAttribute('tagName').then(function (tagName) {
|
|
@@ -153,6 +157,10 @@ class Select {
|
|
|
153
157
|
throw new Error(`Select only works on <select> elements`)
|
|
154
158
|
}
|
|
155
159
|
})
|
|
160
|
+
|
|
161
|
+
this.element.getAttribute('multiple').then((multiple) => {
|
|
162
|
+
this.multiple = multiple !== null && multiple !== 'false'
|
|
163
|
+
})
|
|
156
164
|
}
|
|
157
165
|
|
|
158
166
|
/**
|
|
@@ -216,9 +224,7 @@ class Select {
|
|
|
216
224
|
let matched = false
|
|
217
225
|
let isMulti = await this.isMultiple()
|
|
218
226
|
|
|
219
|
-
let options = await this.element.findElements(
|
|
220
|
-
css: 'option[value =' + escapeCss(value) + ']',
|
|
221
|
-
})
|
|
227
|
+
let options = await this.element.findElements(By.xpath('.//option[@value = ' + escapeQuotes(value) + ']'))
|
|
222
228
|
|
|
223
229
|
for (let option of options) {
|
|
224
230
|
await this.setSelected(option)
|
|
@@ -254,30 +260,46 @@ class Select {
|
|
|
254
260
|
async selectByVisibleText(text) {
|
|
255
261
|
text = typeof text === 'number' ? text.toString() : text
|
|
256
262
|
|
|
257
|
-
const
|
|
258
|
-
.trim() // strip leading and trailing white-space characters
|
|
259
|
-
.replace(/\s+/, ' ') // replace sequences of whitespace characters by a single space
|
|
263
|
+
const xpath = './/option[normalize-space(.) = ' + escapeQuotes(text) + ']'
|
|
260
264
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
265
|
+
const options = await this.element.findElements(By.xpath(xpath))
|
|
266
|
+
|
|
267
|
+
for (let option of options) {
|
|
268
|
+
await this.setSelected(option)
|
|
269
|
+
if (!(await this.isMultiple())) {
|
|
270
|
+
return
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let matched = Array.isArray(options) && options.length > 0
|
|
275
|
+
|
|
276
|
+
if (!matched && text.includes(' ')) {
|
|
277
|
+
const subStringWithoutSpace = getLongestSubstringWithoutSpace(text)
|
|
278
|
+
let candidates
|
|
279
|
+
if ('' === subStringWithoutSpace) {
|
|
280
|
+
candidates = await this.element.findElements(By.tagName('option'))
|
|
281
|
+
} else {
|
|
282
|
+
const xpath = './/option[contains(., ' + escapeQuotes(subStringWithoutSpace) + ')]'
|
|
283
|
+
candidates = await this.element.findElements(By.xpath(xpath))
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const trimmed = text.trim()
|
|
287
|
+
|
|
288
|
+
for (let option of candidates) {
|
|
289
|
+
const optionText = await option.getText()
|
|
290
|
+
if (trimmed === optionText.trim()) {
|
|
291
|
+
await this.setSelected(option)
|
|
292
|
+
if (!(await this.isMultiple())) {
|
|
293
|
+
return
|
|
294
|
+
}
|
|
295
|
+
matched = true
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (!matched) {
|
|
301
|
+
throw new Error(`Cannot locate option with text: ${text}`)
|
|
302
|
+
}
|
|
281
303
|
}
|
|
282
304
|
|
|
283
305
|
/**
|
|
@@ -293,7 +315,7 @@ class Select {
|
|
|
293
315
|
* @returns {Promise<boolean>}
|
|
294
316
|
*/
|
|
295
317
|
async isMultiple() {
|
|
296
|
-
return
|
|
318
|
+
return this.multiple
|
|
297
319
|
}
|
|
298
320
|
|
|
299
321
|
/**
|
|
@@ -353,29 +375,9 @@ class Select {
|
|
|
353
375
|
*/
|
|
354
376
|
text = typeof text === 'number' ? text.toString() : text
|
|
355
377
|
|
|
356
|
-
const
|
|
357
|
-
.
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
/**
|
|
361
|
-
* find option element using xpath
|
|
362
|
-
*/
|
|
363
|
-
const formatted = /"/.test(normalized)
|
|
364
|
-
? 'concat("' + normalized.split('"').join('", \'"\', "') + '")'
|
|
365
|
-
: `"${normalized}"`
|
|
366
|
-
const dotFormat = `[. = ${formatted}]`
|
|
367
|
-
const spaceFormat = `[normalize-space(text()) = ${formatted}]`
|
|
368
|
-
|
|
369
|
-
const selections = [
|
|
370
|
-
`./option${dotFormat}`,
|
|
371
|
-
`./option${spaceFormat}`,
|
|
372
|
-
`./optgroup/option${dotFormat}`,
|
|
373
|
-
`./optgroup/option${spaceFormat}`,
|
|
374
|
-
]
|
|
375
|
-
|
|
376
|
-
const optionElement = await this.element.findElement({
|
|
377
|
-
xpath: selections.join('|'),
|
|
378
|
-
})
|
|
378
|
+
const optionElement = await this.element.findElement(
|
|
379
|
+
By.xpath('.//option[normalize-space(.) = ' + escapeQuotes(text) + ']'),
|
|
380
|
+
)
|
|
379
381
|
if (await optionElement.isSelected()) {
|
|
380
382
|
await optionElement.click()
|
|
381
383
|
}
|
|
@@ -431,9 +433,11 @@ class Select {
|
|
|
431
433
|
|
|
432
434
|
let matched = false
|
|
433
435
|
|
|
434
|
-
let options = await this.element.findElements(
|
|
435
|
-
|
|
436
|
-
|
|
436
|
+
let options = await this.element.findElements(By.xpath('.//option[@value = ' + escapeQuotes(value) + ']'))
|
|
437
|
+
|
|
438
|
+
if (options.length === 0) {
|
|
439
|
+
throw new Error(`Cannot locate option with value: ${value}`)
|
|
440
|
+
}
|
|
437
441
|
|
|
438
442
|
for (let option of options) {
|
|
439
443
|
if (await option.isSelected()) {
|
|
@@ -457,4 +461,42 @@ class Select {
|
|
|
457
461
|
}
|
|
458
462
|
}
|
|
459
463
|
|
|
460
|
-
|
|
464
|
+
function escapeQuotes(toEscape) {
|
|
465
|
+
if (toEscape.includes(`"`) && toEscape.includes(`'`)) {
|
|
466
|
+
const quoteIsLast = toEscape.lastIndexOf(`"`) === toEscape.length - 1
|
|
467
|
+
const substrings = toEscape.split(`"`)
|
|
468
|
+
|
|
469
|
+
// Remove the last element if it's an empty string
|
|
470
|
+
if (substrings[substrings.length - 1] === '') {
|
|
471
|
+
substrings.pop()
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
let result = 'concat('
|
|
475
|
+
|
|
476
|
+
for (let i = 0; i < substrings.length; i++) {
|
|
477
|
+
result += `"${substrings[i]}"`
|
|
478
|
+
result += i === substrings.length - 1 ? (quoteIsLast ? `, '"')` : `)`) : `, '"', `
|
|
479
|
+
}
|
|
480
|
+
return result
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (toEscape.includes('"')) {
|
|
484
|
+
return `'${toEscape}'`
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Otherwise return the quoted string
|
|
488
|
+
return `"${toEscape}"`
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function getLongestSubstringWithoutSpace(text) {
|
|
492
|
+
let words = text.split(' ')
|
|
493
|
+
let longestString = ''
|
|
494
|
+
for (let word of words) {
|
|
495
|
+
if (word.length > longestString.length) {
|
|
496
|
+
longestString = word
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return longestString
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
module.exports = { Select, escapeQuotes }
|
package/lib/until.js
CHANGED
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
|
|
44
44
|
const by = require('./by')
|
|
45
45
|
const error = require('./error')
|
|
46
|
-
const webdriver = require('./webdriver')
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
const webdriver = require('./webdriver')
|
|
47
|
+
const Condition = webdriver.Condition
|
|
48
|
+
const WebElementCondition = webdriver.WebElementCondition
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Creates a condition that will wait until the input driver is able to switch
|
package/lib/util.js
CHANGED
|
@@ -38,6 +38,7 @@ function isPromise(value) {
|
|
|
38
38
|
// Use array notation so the Closure compiler does not obfuscate away our
|
|
39
39
|
// contract.
|
|
40
40
|
return (typeof value === 'object' || typeof value === 'function') && typeof value['then'] === 'function'
|
|
41
|
+
/*eslint no-unused-vars: "off"*/
|
|
41
42
|
} catch (ex) {
|
|
42
43
|
return false
|
|
43
44
|
}
|