selenium-webdriver 4.8.2 → 4.9.1
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 +25 -1
- package/bidi/argumentValue.js +34 -0
- package/bidi/browsingContext.js +1 -25
- package/bidi/browsingContextInspector.js +95 -0
- package/bidi/browsingContextTypes.js +52 -0
- package/bidi/evaluateResult.js +60 -0
- package/bidi/logEntries.js +16 -13
- package/bidi/protocolType.js +97 -0
- package/bidi/protocolValue.js +204 -0
- package/bidi/realmInfo.js +99 -0
- package/bidi/resultOwnership.js +23 -0
- package/bidi/scriptManager.js +325 -0
- package/bin/linux/selenium-manager +0 -0
- package/bin/macos/selenium-manager +0 -0
- package/bin/windows/selenium-manager.exe +0 -0
- package/chrome.js +0 -18
- package/chromium.js +4 -0
- package/common/driverFinder.js +59 -0
- package/common/seleniumManager.js +47 -20
- package/edge.js +0 -17
- package/firefox.js +8 -20
- package/http/index.js +2 -1
- package/ie.js +4 -17
- package/index.js +4 -0
- package/lib/http.js +17 -3
- package/lib/webdriver.js +14 -7
- package/package.json +9 -8
- package/remote/index.js +8 -4
- package/remote/util.js +3 -1
|
@@ -0,0 +1,99 @@
|
|
|
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 RealmType = {
|
|
19
|
+
AUDIO_WORKLET: 'audio-worklet',
|
|
20
|
+
DEDICATED_WORKER: 'dedicated-worker',
|
|
21
|
+
PAINT_WORKLET: 'paint-worklet',
|
|
22
|
+
SERVICE_WORKED: 'service-worker',
|
|
23
|
+
SHARED_WORKED: 'shared-worker',
|
|
24
|
+
WINDOW: 'window',
|
|
25
|
+
WORKER: 'worker',
|
|
26
|
+
WORKLET: 'worklet',
|
|
27
|
+
|
|
28
|
+
findByName(name) {
|
|
29
|
+
return (
|
|
30
|
+
Object.values(this).find((type) => {
|
|
31
|
+
return (
|
|
32
|
+
typeof type === 'string' && name.toLowerCase() === type.toLowerCase()
|
|
33
|
+
)
|
|
34
|
+
}) || null
|
|
35
|
+
)
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
class RealmInfo {
|
|
40
|
+
constructor(realmId, origin, realmType) {
|
|
41
|
+
this.realmId = realmId
|
|
42
|
+
this.origin = origin
|
|
43
|
+
this.realmType = realmType
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static fromJson(input) {
|
|
47
|
+
let realmId = null
|
|
48
|
+
let origin = null
|
|
49
|
+
let realmType = null
|
|
50
|
+
let browsingContext = null
|
|
51
|
+
let sandbox = null
|
|
52
|
+
|
|
53
|
+
if ('type' in input) {
|
|
54
|
+
let typeString = input['type']
|
|
55
|
+
realmType = RealmType.findByName(typeString)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if ('realm' in input) {
|
|
59
|
+
realmId = input['realm']
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if ('origin' in input) {
|
|
63
|
+
origin = input['origin']
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if ('context' in input) {
|
|
67
|
+
browsingContext = input['context']
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if ('sandbox' in input) {
|
|
71
|
+
sandbox = input['sandbox']
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (realmType === RealmType.WINDOW) {
|
|
75
|
+
return new WindowRealmInfo(
|
|
76
|
+
realmId,
|
|
77
|
+
origin,
|
|
78
|
+
realmType,
|
|
79
|
+
browsingContext,
|
|
80
|
+
sandbox
|
|
81
|
+
)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return new RealmInfo(realmId, origin, realmType)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
class WindowRealmInfo extends RealmInfo {
|
|
89
|
+
constructor(realmId, origin, realmType, browsingContext, sandbox = null) {
|
|
90
|
+
super(realmId, origin, realmType)
|
|
91
|
+
this.browsingContext = browsingContext
|
|
92
|
+
this.sandbox = sandbox
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = {
|
|
97
|
+
RealmInfo,
|
|
98
|
+
RealmType,
|
|
99
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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 ResultOwnership = {
|
|
19
|
+
ROOT: 'root',
|
|
20
|
+
NONE: 'none',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
module.exports = { ResultOwnership }
|
|
@@ -0,0 +1,325 @@
|
|
|
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 {
|
|
19
|
+
EvaluateResultType,
|
|
20
|
+
EvaluateResultSuccess,
|
|
21
|
+
EvaluateResultException,
|
|
22
|
+
ExceptionDetails,
|
|
23
|
+
} = require('./evaluateResult')
|
|
24
|
+
const { RealmInfo } = require('./realmInfo')
|
|
25
|
+
const { RemoteValue } = require('./protocolValue')
|
|
26
|
+
|
|
27
|
+
class ScriptManager {
|
|
28
|
+
constructor(driver) {
|
|
29
|
+
this._driver = driver
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async init(browsingContextId) {
|
|
33
|
+
if (!(await this._driver.getCapabilities()).get('webSocketUrl')) {
|
|
34
|
+
throw Error('WebDriver instance must support BiDi protocol')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.bidi = await this._driver.getBidi()
|
|
38
|
+
this._browsingContextId = browsingContextId
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async disownRealmScript(realmId, handles) {
|
|
42
|
+
const params = {
|
|
43
|
+
method: 'script.disown',
|
|
44
|
+
params: {
|
|
45
|
+
handles: handles,
|
|
46
|
+
target: {
|
|
47
|
+
realm: realmId,
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
await this.bidi.send(params)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async disownBrowsingContextScript(
|
|
56
|
+
browsingContextId,
|
|
57
|
+
handles,
|
|
58
|
+
sandbox = null
|
|
59
|
+
) {
|
|
60
|
+
const params = {
|
|
61
|
+
method: 'script.disown',
|
|
62
|
+
params: {
|
|
63
|
+
handles: handles,
|
|
64
|
+
target: {
|
|
65
|
+
context: browsingContextId,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (sandbox != null) {
|
|
71
|
+
params.params.target['sandbox'] = sandbox
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
await this.bidi.send(params)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async callFunctionInRealm(
|
|
78
|
+
realmId,
|
|
79
|
+
functionDeclaration,
|
|
80
|
+
awaitPromise,
|
|
81
|
+
argumentValueList = null,
|
|
82
|
+
thisParameter = null,
|
|
83
|
+
resultOwnership = null
|
|
84
|
+
) {
|
|
85
|
+
const params = this.getCallFunctionParams(
|
|
86
|
+
'realm',
|
|
87
|
+
realmId,
|
|
88
|
+
null,
|
|
89
|
+
functionDeclaration,
|
|
90
|
+
awaitPromise,
|
|
91
|
+
argumentValueList,
|
|
92
|
+
thisParameter,
|
|
93
|
+
resultOwnership
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
const command = {
|
|
97
|
+
method: 'script.callFunction',
|
|
98
|
+
params,
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let response = await this.bidi.send(command)
|
|
102
|
+
return this.createEvaluateResult(response)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async callFunctionInBrowsingContext(
|
|
106
|
+
browsingContextId,
|
|
107
|
+
functionDeclaration,
|
|
108
|
+
awaitPromise,
|
|
109
|
+
argumentValueList = null,
|
|
110
|
+
thisParameter = null,
|
|
111
|
+
resultOwnership = null,
|
|
112
|
+
sandbox = null
|
|
113
|
+
) {
|
|
114
|
+
const params = this.getCallFunctionParams(
|
|
115
|
+
'contextTarget',
|
|
116
|
+
browsingContextId,
|
|
117
|
+
sandbox,
|
|
118
|
+
functionDeclaration,
|
|
119
|
+
awaitPromise,
|
|
120
|
+
argumentValueList,
|
|
121
|
+
thisParameter,
|
|
122
|
+
resultOwnership
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
const command = {
|
|
126
|
+
method: 'script.callFunction',
|
|
127
|
+
params,
|
|
128
|
+
}
|
|
129
|
+
const response = await this.bidi.send(command)
|
|
130
|
+
return this.createEvaluateResult(response)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async evaluateFunctionInRealm(
|
|
134
|
+
realmId,
|
|
135
|
+
expression,
|
|
136
|
+
awaitPromise,
|
|
137
|
+
resultOwnership = null
|
|
138
|
+
) {
|
|
139
|
+
const params = this.getEvaluateParams(
|
|
140
|
+
'realm',
|
|
141
|
+
realmId,
|
|
142
|
+
null,
|
|
143
|
+
expression,
|
|
144
|
+
awaitPromise,
|
|
145
|
+
resultOwnership
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
const command = {
|
|
149
|
+
method: 'script.evaluate',
|
|
150
|
+
params,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let response = await this.bidi.send(command)
|
|
154
|
+
return this.createEvaluateResult(response)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async evaluateFunctionInBrowsingContext(
|
|
158
|
+
browsingContextId,
|
|
159
|
+
expression,
|
|
160
|
+
awaitPromise,
|
|
161
|
+
resultOwnership = null,
|
|
162
|
+
sandbox = null
|
|
163
|
+
) {
|
|
164
|
+
const params = this.getEvaluateParams(
|
|
165
|
+
'contextTarget',
|
|
166
|
+
browsingContextId,
|
|
167
|
+
sandbox,
|
|
168
|
+
expression,
|
|
169
|
+
awaitPromise,
|
|
170
|
+
resultOwnership
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
const command = {
|
|
174
|
+
method: 'script.evaluate',
|
|
175
|
+
params,
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
let response = await this.bidi.send(command)
|
|
179
|
+
return this.createEvaluateResult(response)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getCallFunctionParams(
|
|
183
|
+
targetType,
|
|
184
|
+
id,
|
|
185
|
+
sandbox,
|
|
186
|
+
functionDeclaration,
|
|
187
|
+
awaitPromise,
|
|
188
|
+
argumentValueList = null,
|
|
189
|
+
thisParameter = null,
|
|
190
|
+
resultOwnership = null
|
|
191
|
+
) {
|
|
192
|
+
const params = {
|
|
193
|
+
functionDeclaration: functionDeclaration,
|
|
194
|
+
awaitPromise: awaitPromise,
|
|
195
|
+
}
|
|
196
|
+
if (targetType === 'contextTarget') {
|
|
197
|
+
if (sandbox != null) {
|
|
198
|
+
params['target'] = { context: id, sandbox: sandbox }
|
|
199
|
+
} else {
|
|
200
|
+
params['target'] = { context: id }
|
|
201
|
+
}
|
|
202
|
+
} else {
|
|
203
|
+
params['target'] = { realm: id }
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (argumentValueList != null) {
|
|
207
|
+
let argumentParams = []
|
|
208
|
+
argumentValueList.forEach((argumentValue) => {
|
|
209
|
+
argumentParams.push(argumentValue.asMap())
|
|
210
|
+
})
|
|
211
|
+
params['arguments'] = argumentParams
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (thisParameter != null) {
|
|
215
|
+
params['this'] = thisParameter
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (resultOwnership != null) {
|
|
219
|
+
params['resultOwnership'] = resultOwnership
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return params
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
getEvaluateParams(
|
|
226
|
+
targetType,
|
|
227
|
+
id,
|
|
228
|
+
sandbox,
|
|
229
|
+
expression,
|
|
230
|
+
awaitPromise,
|
|
231
|
+
resultOwnership = null
|
|
232
|
+
) {
|
|
233
|
+
const params = {
|
|
234
|
+
expression: expression,
|
|
235
|
+
awaitPromise: awaitPromise,
|
|
236
|
+
}
|
|
237
|
+
if (targetType === 'contextTarget') {
|
|
238
|
+
if (sandbox != null) {
|
|
239
|
+
params['target'] = { context: id, sandbox: sandbox }
|
|
240
|
+
} else {
|
|
241
|
+
params['target'] = { context: id }
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
params['target'] = { realm: id }
|
|
245
|
+
}
|
|
246
|
+
if (resultOwnership != null) {
|
|
247
|
+
params['resultOwnership'] = resultOwnership
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return params
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
createEvaluateResult(response) {
|
|
254
|
+
var type = response.result.type
|
|
255
|
+
var realmId = response.result.realm
|
|
256
|
+
var evaluateResult
|
|
257
|
+
|
|
258
|
+
if (type === EvaluateResultType.SUCCESS) {
|
|
259
|
+
var result = response.result.result
|
|
260
|
+
evaluateResult = new EvaluateResultSuccess(
|
|
261
|
+
realmId,
|
|
262
|
+
new RemoteValue(result)
|
|
263
|
+
)
|
|
264
|
+
} else {
|
|
265
|
+
var exceptionDetails = response.result.exceptionDetails
|
|
266
|
+
evaluateResult = new EvaluateResultException(
|
|
267
|
+
realmId,
|
|
268
|
+
new ExceptionDetails(exceptionDetails)
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
return evaluateResult
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
realmInfoMapper(realms) {
|
|
275
|
+
var realmsList = []
|
|
276
|
+
realms.forEach((realm) => {
|
|
277
|
+
realmsList.push(RealmInfo.fromJson(realm))
|
|
278
|
+
})
|
|
279
|
+
return realmsList
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
async getAllRealms() {
|
|
283
|
+
const command = {
|
|
284
|
+
method: 'script.getRealms',
|
|
285
|
+
params: {},
|
|
286
|
+
}
|
|
287
|
+
let response = await this.bidi.send(command)
|
|
288
|
+
return this.realmInfoMapper(response.result.realms)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async getRealmsByType(type) {
|
|
292
|
+
const command = {
|
|
293
|
+
method: 'script.getRealms',
|
|
294
|
+
params: { type: type },
|
|
295
|
+
}
|
|
296
|
+
let response = await this.bidi.send(command)
|
|
297
|
+
return this.realmInfoMapper(response.result.realms)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
async getRealmsInBrowsingContext(browsingContext) {
|
|
301
|
+
const command = {
|
|
302
|
+
method: 'script.getRealms',
|
|
303
|
+
params: { context: browsingContext },
|
|
304
|
+
}
|
|
305
|
+
let response = await this.bidi.send(command)
|
|
306
|
+
return this.realmInfoMapper(response.result.realms)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async getRealmsInBrowsingContextByType(browsingContext, type) {
|
|
310
|
+
const command = {
|
|
311
|
+
method: 'script.getRealms',
|
|
312
|
+
params: { context: browsingContext, type: type },
|
|
313
|
+
}
|
|
314
|
+
let response = await this.bidi.send(command)
|
|
315
|
+
return this.realmInfoMapper(response.result.realms)
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function getScriptManagerInstance(browsingContextId, driver) {
|
|
320
|
+
let instance = new ScriptManager(driver)
|
|
321
|
+
await instance.init(browsingContextId)
|
|
322
|
+
return instance
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
module.exports = getScriptManagerInstance
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/chrome.js
CHANGED
|
@@ -130,7 +130,6 @@
|
|
|
130
130
|
const io = require('./io')
|
|
131
131
|
const { Browser } = require('./lib/capabilities')
|
|
132
132
|
const chromium = require('./chromium')
|
|
133
|
-
const { driverLocation } = require('./common/seleniumManager')
|
|
134
133
|
|
|
135
134
|
/**
|
|
136
135
|
* Name of the ChromeDriver executable.
|
|
@@ -158,23 +157,6 @@ class ServiceBuilder extends chromium.ServiceBuilder {
|
|
|
158
157
|
*/
|
|
159
158
|
constructor(opt_exe) {
|
|
160
159
|
let exe = opt_exe || locateSynchronously()
|
|
161
|
-
|
|
162
|
-
if (!exe) {
|
|
163
|
-
try {
|
|
164
|
-
exe = driverLocation(Browser.CHROME)
|
|
165
|
-
} catch (err) {
|
|
166
|
-
console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (!exe) {
|
|
171
|
-
throw Error(
|
|
172
|
-
`ChromeDriver could not be found on the PATH.
|
|
173
|
-
For more information on how to install drivers see
|
|
174
|
-
https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/.`
|
|
175
|
-
)
|
|
176
|
-
}
|
|
177
|
-
|
|
178
160
|
super(exe)
|
|
179
161
|
}
|
|
180
162
|
}
|
package/chromium.js
CHANGED
|
@@ -83,6 +83,7 @@ const error = require('./lib/error')
|
|
|
83
83
|
const Symbols = require('./lib/symbols')
|
|
84
84
|
const webdriver = require('./lib/webdriver')
|
|
85
85
|
const remote = require('./remote')
|
|
86
|
+
const { getPath } = require('./common/driverFinder')
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* Custom command names supported by Chromium WebDriver.
|
|
@@ -684,6 +685,9 @@ class Driver extends webdriver.WebDriver {
|
|
|
684
685
|
configureExecutor(executor, this.VENDOR_COMMAND_PREFIX)
|
|
685
686
|
} else {
|
|
686
687
|
let service = opt_serviceExecutor || this.getDefaultService()
|
|
688
|
+
if (!service.getExecutable()) {
|
|
689
|
+
service.setExecutable(getPath(service, caps))
|
|
690
|
+
}
|
|
687
691
|
onQuit = () => service.kill()
|
|
688
692
|
executor = createExecutor(service.start(), this.VENDOR_COMMAND_PREFIX)
|
|
689
693
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
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
|
+
/**
|
|
19
|
+
* This implementation is still in beta, and may change.
|
|
20
|
+
*
|
|
21
|
+
* Utility to find if a given file is present and executable.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const { driverLocation } = require('./seleniumManager')
|
|
25
|
+
const fs = require('fs')
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Determines the path of the correct Selenium Manager binary
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
function getPath(service, capabilities) {
|
|
32
|
+
try {
|
|
33
|
+
return pathExists(service.getExecutable()) || driverLocation(capabilities)
|
|
34
|
+
} catch (e) {
|
|
35
|
+
throw Error(
|
|
36
|
+
`Unable to obtain browser driver.
|
|
37
|
+
For more information on how to install drivers see
|
|
38
|
+
https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/. ${e}`
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* _Synchronously_ attempts to locate the driver executable on the current
|
|
45
|
+
* system.
|
|
46
|
+
*
|
|
47
|
+
* @param {!string} driverPath
|
|
48
|
+
*
|
|
49
|
+
* @return {?string} the located executable, or `null`.
|
|
50
|
+
*/
|
|
51
|
+
function pathExists(driverPath) {
|
|
52
|
+
if (!driverPath || !fs.existsSync(driverPath)) {
|
|
53
|
+
return null
|
|
54
|
+
}
|
|
55
|
+
return driverPath
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// PUBLIC API
|
|
59
|
+
module.exports = { getPath }
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
const { platform } = require('process')
|
|
25
25
|
const path = require('path')
|
|
26
26
|
const fs = require('fs')
|
|
27
|
-
const
|
|
27
|
+
const spawnSync = require('child_process').spawnSync
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* currently supported browsers for selenium-manager
|
|
31
31
|
* @type {string[]}
|
|
32
32
|
*/
|
|
33
|
-
const Browser = ['chrome', 'firefox', 'edge', 'iexplorer']
|
|
33
|
+
const Browser = ['chrome', 'firefox', 'edge', 'MicrosoftEdge', 'iexplorer']
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Determines the path of the correct Selenium Manager binary
|
|
@@ -47,7 +47,14 @@ function getBinary() {
|
|
|
47
47
|
const file =
|
|
48
48
|
directory === 'windows' ? 'selenium-manager.exe' : 'selenium-manager'
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
let seleniumManagerBasePath
|
|
51
|
+
if (process.env.SELENIUM_MANAGER_BASE_PATH) {
|
|
52
|
+
seleniumManagerBasePath = process.env.SELENIUM_MANAGER_BASE_PATH
|
|
53
|
+
} else {
|
|
54
|
+
seleniumManagerBasePath = path.join(__dirname, '..', '/bin')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const filePath = path.join(seleniumManagerBasePath, directory, file)
|
|
51
58
|
|
|
52
59
|
if (!fs.existsSync(filePath)) {
|
|
53
60
|
throw new Error(`Unable to obtain Selenium Manager`)
|
|
@@ -58,35 +65,55 @@ function getBinary() {
|
|
|
58
65
|
|
|
59
66
|
/**
|
|
60
67
|
* Determines the path of the correct driver
|
|
61
|
-
* @param {
|
|
68
|
+
* @param {Capabilities} options browser options to fetch the driver
|
|
62
69
|
* @returns {string} path of the driver location
|
|
63
70
|
*/
|
|
64
71
|
|
|
65
|
-
function driverLocation(
|
|
66
|
-
if (!Browser.includes(
|
|
72
|
+
function driverLocation(options) {
|
|
73
|
+
if (!Browser.includes(options.getBrowserName().toLocaleString())) {
|
|
67
74
|
throw new Error(
|
|
68
|
-
`Unable to locate driver associated with browser name: ${
|
|
75
|
+
`Unable to locate driver associated with browser name: ${options.getBrowserName()}`
|
|
69
76
|
)
|
|
70
77
|
}
|
|
71
78
|
|
|
72
|
-
let args = [
|
|
73
|
-
|
|
79
|
+
let args = ['--browser', options.getBrowserName(), '--output', 'json']
|
|
80
|
+
|
|
81
|
+
if (options.getBrowserVersion() && options.getBrowserVersion() !== "") {
|
|
82
|
+
args.push("--browser-version", options.getBrowserVersion())
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const vendorOptions = options.get('goog:chromeOptions') || options.get('ms:edgeOptions')
|
|
86
|
+
|| options.get('moz:firefoxOptions')
|
|
87
|
+
if (vendorOptions && vendorOptions.binary && vendorOptions.binary !== "") {
|
|
88
|
+
args.push("--browser-path", '"' + vendorOptions.binary + '"')
|
|
89
|
+
}
|
|
74
90
|
|
|
91
|
+
const smBinary = getBinary()
|
|
92
|
+
const spawnResult = spawnSync(smBinary, args)
|
|
93
|
+
let output
|
|
94
|
+
if (spawnResult.status) {
|
|
95
|
+
let errorMessage
|
|
96
|
+
if (spawnResult.stderr.toString()) {
|
|
97
|
+
errorMessage = spawnResult.stderr.toString()
|
|
98
|
+
}
|
|
99
|
+
if (spawnResult.stdout.toString()) {
|
|
100
|
+
try {
|
|
101
|
+
output = JSON.parse(spawnResult.stdout.toString())
|
|
102
|
+
errorMessage = output.result.message
|
|
103
|
+
} catch (e) {
|
|
104
|
+
errorMessage = e.toString()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
throw new Error(`Error executing command for ${smBinary} with ${args}: ${errorMessage}`)
|
|
108
|
+
}
|
|
75
109
|
try {
|
|
76
|
-
output = JSON.parse(
|
|
110
|
+
output = JSON.parse(spawnResult.stdout.toString())
|
|
77
111
|
} catch (e) {
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
error = JSON.parse(e.stdout.toString()).result.message
|
|
81
|
-
} catch (e) {
|
|
82
|
-
error = e.toString()
|
|
83
|
-
}
|
|
84
|
-
throw new Error(
|
|
85
|
-
`Error executing command with ${args}: ${error}`
|
|
86
|
-
)
|
|
112
|
+
throw new Error(`Error executing command for ${smBinary} with ${args}: ${e.toString()}`)
|
|
87
113
|
}
|
|
88
114
|
|
|
89
|
-
|
|
115
|
+
|
|
116
|
+
for (const key in output.logs) {
|
|
90
117
|
if (output.logs[key].level === 'WARN') {
|
|
91
118
|
console.warn(`${output.logs[key].message}`)
|
|
92
119
|
}
|
package/edge.js
CHANGED
|
@@ -80,7 +80,6 @@
|
|
|
80
80
|
const { Browser } = require('./lib/capabilities')
|
|
81
81
|
const io = require('./io')
|
|
82
82
|
const chromium = require('./chromium')
|
|
83
|
-
const { driverLocation } = require('./common/seleniumManager')
|
|
84
83
|
|
|
85
84
|
/**
|
|
86
85
|
* Name of the EdgeDriver executable.
|
|
@@ -107,22 +106,6 @@ class ServiceBuilder extends chromium.ServiceBuilder {
|
|
|
107
106
|
*/
|
|
108
107
|
constructor(opt_exe) {
|
|
109
108
|
let exe = opt_exe || locateSynchronously()
|
|
110
|
-
|
|
111
|
-
if (!exe) {
|
|
112
|
-
try {
|
|
113
|
-
exe = driverLocation('edge')
|
|
114
|
-
} catch (err) {
|
|
115
|
-
console.log(`Unable to obtain driver using Selenium Manager: ${err}`)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!exe) {
|
|
120
|
-
throw Error(
|
|
121
|
-
`MSEdgeDriver could not be found on the PATH.
|
|
122
|
-
For more information on how to install drivers see
|
|
123
|
-
https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/.`
|
|
124
|
-
)
|
|
125
|
-
}
|
|
126
109
|
super(exe)
|
|
127
110
|
this.setLoopback(true)
|
|
128
111
|
}
|