Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/javascript/selenium-webdriver/lib/error.js
2884 views
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
'use strict'
19
20
const { isObject } = require('./util')
21
22
/**
23
* The base WebDriver error type. This error type is only used directly when a
24
* more appropriate category is not defined for the offending error.
25
*/
26
class WebDriverError extends Error {
27
/** @param {string=} opt_error the error message, if any. */
28
constructor(opt_error) {
29
super(opt_error)
30
31
/** @override */
32
this.name = this.constructor.name
33
34
/**
35
* A stacktrace reported by the remote webdriver endpoint that initially
36
* reported this error. This property will be an empty string if the remote
37
* end did not provide a stacktrace.
38
* @type {string}
39
*/
40
this.remoteStacktrace = ''
41
}
42
}
43
44
/**
45
* Indicates the shadow root is no longer attached to the DOM
46
*/
47
class DetachedShadowRootError extends WebDriverError {
48
/** @param {string=} opt_error the error message, if any. */
49
constructor(opt_error) {
50
super(opt_error)
51
}
52
}
53
54
/**
55
* Indicates a {@linkplain ./webdriver.WebElement#click click command} could not
56
* completed because the click target is obscured by other elements on the
57
* page.
58
*/
59
class ElementClickInterceptedError extends WebDriverError {
60
/** @param {string=} opt_error the error message, if any. */
61
constructor(opt_error) {
62
super(opt_error)
63
}
64
}
65
66
/**
67
* An attempt was made to select an element that cannot be selected.
68
*/
69
class ElementNotSelectableError extends WebDriverError {
70
/** @param {string=} opt_error the error message, if any. */
71
constructor(opt_error) {
72
super(opt_error)
73
}
74
}
75
76
/**
77
* Indicates a command could not be completed because the target element is
78
* not pointer or keyboard interactable. This will often occur if an element
79
* is present in the DOM, but not rendered (i.e. its CSS style has
80
* "display: none").
81
*/
82
class ElementNotInteractableError extends WebDriverError {
83
/** @param {string=} opt_error the error message, if any. */
84
constructor(opt_error) {
85
super(opt_error)
86
}
87
}
88
89
/**
90
* Indicates a navigation event caused the browser to generate a certificate
91
* warning. This is usually caused by an expired or invalid TLS certificate.
92
*/
93
class InsecureCertificateError extends WebDriverError {
94
/** @param {string=} opt_error the error message, if any. */
95
constructor(opt_error) {
96
super(opt_error)
97
}
98
}
99
100
/**
101
* The arguments passed to a command are either invalid or malformed.
102
*/
103
class InvalidArgumentError extends WebDriverError {
104
/** @param {string=} opt_error the error message, if any. */
105
constructor(opt_error) {
106
super(opt_error)
107
}
108
}
109
110
/**
111
* An illegal attempt was made to set a cookie under a different domain than
112
* the current page.
113
*/
114
class InvalidCookieDomainError extends WebDriverError {
115
/** @param {string=} opt_error the error message, if any. */
116
constructor(opt_error) {
117
super(opt_error)
118
}
119
}
120
121
/**
122
* The coordinates provided to an interactions operation are invalid.
123
*/
124
class InvalidCoordinatesError extends WebDriverError {
125
/** @param {string=} opt_error the error message, if any. */
126
constructor(opt_error) {
127
super(opt_error)
128
}
129
}
130
131
/**
132
* An element command could not be completed because the element is in an
133
* invalid state, e.g. attempting to click an element that is no longer attached
134
* to the document.
135
*/
136
class InvalidElementStateError extends WebDriverError {
137
/** @param {string=} opt_error the error message, if any. */
138
constructor(opt_error) {
139
super(opt_error)
140
}
141
}
142
143
/**
144
* Argument was an invalid selector.
145
*/
146
class InvalidSelectorError extends WebDriverError {
147
/** @param {string=} opt_error the error message, if any. */
148
constructor(opt_error) {
149
super(opt_error)
150
}
151
}
152
153
/**
154
* Occurs when a command is directed to a session that does not exist.
155
*/
156
class NoSuchSessionError extends WebDriverError {
157
/** @param {string=} opt_error the error message, if any. */
158
constructor(opt_error) {
159
super(opt_error)
160
}
161
}
162
163
/**
164
* An error occurred while executing JavaScript supplied by the user.
165
*/
166
class JavascriptError extends WebDriverError {
167
/** @param {string=} opt_error the error message, if any. */
168
constructor(opt_error) {
169
super(opt_error)
170
}
171
}
172
173
/**
174
* The target for mouse interaction is not in the browser’s viewport and cannot
175
* be brought into that viewport.
176
*/
177
class MoveTargetOutOfBoundsError extends WebDriverError {
178
/** @param {string=} opt_error the error message, if any. */
179
constructor(opt_error) {
180
super(opt_error)
181
}
182
}
183
184
/**
185
* An attempt was made to operate on a modal dialog when one was not open.
186
*/
187
class NoSuchAlertError extends WebDriverError {
188
/** @param {string=} opt_error the error message, if any. */
189
constructor(opt_error) {
190
super(opt_error)
191
}
192
}
193
194
/**
195
* Indicates a named cookie could not be found in the cookie jar for the
196
* currently selected document.
197
*/
198
class NoSuchCookieError extends WebDriverError {
199
/** @param {string=} opt_error the error message, if any. */
200
constructor(opt_error) {
201
super(opt_error)
202
}
203
}
204
205
/**
206
* An element could not be located on the page using the given search
207
* parameters.
208
*/
209
class NoSuchElementError extends WebDriverError {
210
/** @param {string=} opt_error the error message, if any. */
211
constructor(opt_error) {
212
super(opt_error)
213
}
214
}
215
216
/**
217
* A ShadowRoot could not be located on the element
218
*/
219
class NoSuchShadowRootError extends WebDriverError {
220
/** @param {string=} opt_error the error message, if any. */
221
constructor(opt_error) {
222
super(opt_error)
223
}
224
}
225
226
/**
227
* A request to switch to a frame could not be satisfied because the frame
228
* could not be found.
229
*/
230
class NoSuchFrameError extends WebDriverError {
231
/** @param {string=} opt_error the error message, if any. */
232
constructor(opt_error) {
233
super(opt_error)
234
}
235
}
236
237
/**
238
* A request to switch to a window could not be satisfied because the window
239
* could not be found.
240
*/
241
class NoSuchWindowError extends WebDriverError {
242
/** @param {string=} opt_error the error message, if any. */
243
constructor(opt_error) {
244
super(opt_error)
245
}
246
}
247
248
/**
249
* A script did not complete before its timeout expired.
250
*/
251
class ScriptTimeoutError extends WebDriverError {
252
/** @param {string=} opt_error the error message, if any. */
253
constructor(opt_error) {
254
super(opt_error)
255
}
256
}
257
258
/**
259
* A new session could not be created.
260
*/
261
class SessionNotCreatedError extends WebDriverError {
262
/** @param {string=} opt_error the error message, if any. */
263
constructor(opt_error) {
264
super(opt_error)
265
}
266
}
267
268
/**
269
* An element command failed because the referenced element is no longer
270
* attached to the DOM.
271
*/
272
class StaleElementReferenceError extends WebDriverError {
273
/** @param {string=} opt_error the error message, if any. */
274
constructor(opt_error) {
275
super(opt_error)
276
}
277
}
278
279
/**
280
* An operation did not complete before its timeout expired.
281
*/
282
class TimeoutError extends WebDriverError {
283
/** @param {string=} opt_error the error message, if any. */
284
constructor(opt_error) {
285
super(opt_error)
286
}
287
}
288
289
/**
290
* A request to set a cookie’s value could not be satisfied.
291
*/
292
class UnableToSetCookieError extends WebDriverError {
293
/** @param {string=} opt_error the error message, if any. */
294
constructor(opt_error) {
295
super(opt_error)
296
}
297
}
298
299
/**
300
* A screen capture operation was not possible.
301
*/
302
class UnableToCaptureScreenError extends WebDriverError {
303
/** @param {string=} opt_error the error message, if any. */
304
constructor(opt_error) {
305
super(opt_error)
306
}
307
}
308
309
/**
310
* A modal dialog was open, blocking this operation.
311
*/
312
class UnexpectedAlertOpenError extends WebDriverError {
313
/**
314
* @param {string=} opt_error the error message, if any.
315
* @param {string=} opt_text the text of the open dialog, if available.
316
*/
317
constructor(opt_error, opt_text) {
318
super(opt_error)
319
320
/** @private {(string|undefined)} */
321
this.text_ = opt_text
322
}
323
324
/**
325
* @return {(string|undefined)} The text displayed with the unhandled alert,
326
* if available.
327
*/
328
getAlertText() {
329
return this.text_
330
}
331
}
332
333
/**
334
* A command could not be executed because the remote end is not aware of it.
335
*/
336
class UnknownCommandError extends WebDriverError {
337
/** @param {string=} opt_error the error message, if any. */
338
constructor(opt_error) {
339
super(opt_error)
340
}
341
}
342
343
/**
344
* The requested command matched a known URL but did not match an method for
345
* that URL.
346
*/
347
class UnknownMethodError extends WebDriverError {
348
/** @param {string=} opt_error the error message, if any. */
349
constructor(opt_error) {
350
super(opt_error)
351
}
352
}
353
354
/**
355
* Reports an unsupported operation.
356
*/
357
class UnsupportedOperationError extends WebDriverError {
358
/** @param {string=} opt_error the error message, if any. */
359
constructor(opt_error) {
360
super(opt_error)
361
}
362
}
363
364
// TODO(jleyba): Define UnknownError as an alias of WebDriverError?
365
366
/**
367
* Enum of legacy error codes.
368
* TODO: remove this when all code paths have been switched to the new error
369
* types.
370
* @deprecated
371
* @enum {number}
372
*/
373
const ErrorCode = {
374
SUCCESS: 0,
375
NO_SUCH_SESSION: 6,
376
NO_SUCH_ELEMENT: 7,
377
NO_SUCH_FRAME: 8,
378
UNKNOWN_COMMAND: 9,
379
UNSUPPORTED_OPERATION: 9,
380
STALE_ELEMENT_REFERENCE: 10,
381
ELEMENT_NOT_VISIBLE: 11,
382
INVALID_ELEMENT_STATE: 12,
383
UNKNOWN_ERROR: 13,
384
ELEMENT_NOT_SELECTABLE: 15,
385
JAVASCRIPT_ERROR: 17,
386
XPATH_LOOKUP_ERROR: 19,
387
TIMEOUT: 21,
388
NO_SUCH_WINDOW: 23,
389
INVALID_COOKIE_DOMAIN: 24,
390
UNABLE_TO_SET_COOKIE: 25,
391
UNEXPECTED_ALERT_OPEN: 26,
392
NO_SUCH_ALERT: 27,
393
SCRIPT_TIMEOUT: 28,
394
INVALID_ELEMENT_COORDINATES: 29,
395
IME_NOT_AVAILABLE: 30,
396
IME_ENGINE_ACTIVATION_FAILED: 31,
397
INVALID_SELECTOR_ERROR: 32,
398
SESSION_NOT_CREATED: 33,
399
MOVE_TARGET_OUT_OF_BOUNDS: 34,
400
SQL_DATABASE_ERROR: 35,
401
INVALID_XPATH_SELECTOR: 51,
402
INVALID_XPATH_SELECTOR_RETURN_TYPE: 52,
403
ELEMENT_NOT_INTERACTABLE: 60,
404
INVALID_ARGUMENT: 61,
405
NO_SUCH_COOKIE: 62,
406
UNABLE_TO_CAPTURE_SCREEN: 63,
407
ELEMENT_CLICK_INTERCEPTED: 64,
408
DETACHED_SHADOW_ROOT: 65,
409
METHOD_NOT_ALLOWED: 405,
410
}
411
412
const LEGACY_ERROR_CODE_TO_TYPE = new Map([
413
[ErrorCode.NO_SUCH_SESSION, NoSuchSessionError],
414
[ErrorCode.NO_SUCH_ELEMENT, NoSuchElementError],
415
[ErrorCode.NO_SUCH_FRAME, NoSuchFrameError],
416
[ErrorCode.UNSUPPORTED_OPERATION, UnsupportedOperationError],
417
[ErrorCode.STALE_ELEMENT_REFERENCE, StaleElementReferenceError],
418
[ErrorCode.INVALID_ELEMENT_STATE, InvalidElementStateError],
419
[ErrorCode.UNKNOWN_ERROR, WebDriverError],
420
[ErrorCode.ELEMENT_NOT_SELECTABLE, ElementNotSelectableError],
421
[ErrorCode.JAVASCRIPT_ERROR, JavascriptError],
422
[ErrorCode.XPATH_LOOKUP_ERROR, InvalidSelectorError],
423
[ErrorCode.TIMEOUT, TimeoutError],
424
[ErrorCode.NO_SUCH_WINDOW, NoSuchWindowError],
425
[ErrorCode.INVALID_COOKIE_DOMAIN, InvalidCookieDomainError],
426
[ErrorCode.UNABLE_TO_SET_COOKIE, UnableToSetCookieError],
427
[ErrorCode.UNEXPECTED_ALERT_OPEN, UnexpectedAlertOpenError],
428
[ErrorCode.NO_SUCH_ALERT, NoSuchAlertError],
429
[ErrorCode.SCRIPT_TIMEOUT, ScriptTimeoutError],
430
[ErrorCode.INVALID_ELEMENT_COORDINATES, InvalidCoordinatesError],
431
[ErrorCode.INVALID_SELECTOR_ERROR, InvalidSelectorError],
432
[ErrorCode.SESSION_NOT_CREATED, SessionNotCreatedError],
433
[ErrorCode.MOVE_TARGET_OUT_OF_BOUNDS, MoveTargetOutOfBoundsError],
434
[ErrorCode.INVALID_XPATH_SELECTOR, InvalidSelectorError],
435
[ErrorCode.INVALID_XPATH_SELECTOR_RETURN_TYPE, InvalidSelectorError],
436
[ErrorCode.ELEMENT_NOT_INTERACTABLE, ElementNotInteractableError],
437
[ErrorCode.INVALID_ARGUMENT, InvalidArgumentError],
438
[ErrorCode.NO_SUCH_COOKIE, NoSuchCookieError],
439
[ErrorCode.UNABLE_TO_CAPTURE_SCREEN, UnableToCaptureScreenError],
440
[ErrorCode.ELEMENT_CLICK_INTERCEPTED, ElementClickInterceptedError],
441
[ErrorCode.DETACHED_SHADOW_ROOT, DetachedShadowRootError],
442
[ErrorCode.METHOD_NOT_ALLOWED, UnsupportedOperationError],
443
])
444
445
const ERROR_CODE_TO_TYPE = new Map([
446
['unknown error', WebDriverError],
447
['detached shadow root', DetachedShadowRootError],
448
['element click intercepted', ElementClickInterceptedError],
449
['element not interactable', ElementNotInteractableError],
450
['element not selectable', ElementNotSelectableError],
451
['insecure certificate', InsecureCertificateError],
452
['invalid argument', InvalidArgumentError],
453
['invalid cookie domain', InvalidCookieDomainError],
454
['invalid coordinates', InvalidCoordinatesError],
455
['invalid element state', InvalidElementStateError],
456
['invalid selector', InvalidSelectorError],
457
['invalid session id', NoSuchSessionError],
458
['javascript error', JavascriptError],
459
['move target out of bounds', MoveTargetOutOfBoundsError],
460
['no such alert', NoSuchAlertError],
461
['no such cookie', NoSuchCookieError],
462
['no such element', NoSuchElementError],
463
['no such frame', NoSuchFrameError],
464
['no such shadow root', NoSuchShadowRootError],
465
['no such window', NoSuchWindowError],
466
['script timeout', ScriptTimeoutError],
467
['session not created', SessionNotCreatedError],
468
['stale element reference', StaleElementReferenceError],
469
['timeout', TimeoutError],
470
['unable to set cookie', UnableToSetCookieError],
471
['unable to capture screen', UnableToCaptureScreenError],
472
['unexpected alert open', UnexpectedAlertOpenError],
473
['unknown command', UnknownCommandError],
474
['unknown method', UnknownMethodError],
475
['unsupported operation', UnsupportedOperationError],
476
])
477
478
const TYPE_TO_ERROR_CODE = new Map()
479
ERROR_CODE_TO_TYPE.forEach((value, key) => {
480
TYPE_TO_ERROR_CODE.set(value, key)
481
})
482
483
/**
484
* @param {*} err The error to encode.
485
* @return {{error: string, message: string}} the encoded error.
486
*/
487
function encodeError(err) {
488
let type = WebDriverError
489
if (err instanceof WebDriverError && TYPE_TO_ERROR_CODE.has(err.constructor)) {
490
type = err.constructor
491
}
492
493
let message = err instanceof Error ? err.message : err + ''
494
495
let code = /** @type {string} */ (TYPE_TO_ERROR_CODE.get(type))
496
return { error: code, message: message }
497
}
498
499
/**
500
* Tests if the given value is a valid error response object according to the
501
* W3C WebDriver spec.
502
*
503
* @param {?} data The value to test.
504
* @return {boolean} Whether the given value data object is a valid error
505
* response.
506
* @see https://w3c.github.io/webdriver/webdriver-spec.html#protocol
507
*/
508
function isErrorResponse(data) {
509
return isObject(data) && typeof data.error === 'string'
510
}
511
512
/**
513
* Throws an error coded from the W3C protocol. A generic error will be thrown
514
* if the provided `data` is not a valid encoded error.
515
*
516
* @param {{error: string, message: string}} data The error data to decode.
517
* @throws {WebDriverError} the decoded error.
518
* @see https://w3c.github.io/webdriver/webdriver-spec.html#protocol
519
*/
520
function throwDecodedError(data) {
521
if (isErrorResponse(data)) {
522
let ctor = ERROR_CODE_TO_TYPE.get(data.error) || WebDriverError
523
let err = new ctor(data.message)
524
// TODO(jleyba): remove whichever case is excluded from the final W3C spec.
525
if (typeof data.stacktrace === 'string') {
526
err.remoteStacktrace = data.stacktrace
527
} else if (typeof data.stackTrace === 'string') {
528
err.remoteStacktrace = data.stackTrace
529
}
530
throw err
531
}
532
throw new WebDriverError('Unknown error: ' + JSON.stringify(data))
533
}
534
535
/**
536
* Checks a legacy response from the Selenium 2.0 wire protocol for an error.
537
* @param {*} responseObj the response object to check.
538
* @return {*} responseObj the original response if it does not define an error.
539
* @throws {WebDriverError} if the response object defines an error.
540
*/
541
function checkLegacyResponse(responseObj) {
542
// Handle the legacy Selenium error response format.
543
if (isObject(responseObj) && typeof responseObj.status === 'number' && responseObj.status !== 0) {
544
const { status, value } = responseObj
545
546
let ctor = LEGACY_ERROR_CODE_TO_TYPE.get(status) || WebDriverError
547
548
if (!value || typeof value !== 'object') {
549
throw new ctor(value + '')
550
} else {
551
let message = value['message'] + ''
552
if (ctor !== UnexpectedAlertOpenError) {
553
throw new ctor(message)
554
}
555
556
let text = ''
557
if (value['alert'] && typeof value['alert']['text'] === 'string') {
558
text = value['alert']['text']
559
}
560
throw new UnexpectedAlertOpenError(message, text)
561
}
562
}
563
return responseObj
564
}
565
566
// PUBLIC API
567
568
module.exports = {
569
ErrorCode,
570
571
WebDriverError,
572
DetachedShadowRootError,
573
ElementClickInterceptedError,
574
ElementNotInteractableError,
575
ElementNotSelectableError,
576
InsecureCertificateError,
577
InvalidArgumentError,
578
InvalidCookieDomainError,
579
InvalidCoordinatesError,
580
InvalidElementStateError,
581
InvalidSelectorError,
582
JavascriptError,
583
MoveTargetOutOfBoundsError,
584
NoSuchAlertError,
585
NoSuchCookieError,
586
NoSuchElementError,
587
NoSuchFrameError,
588
NoSuchShadowRootError,
589
NoSuchSessionError,
590
NoSuchWindowError,
591
ScriptTimeoutError,
592
SessionNotCreatedError,
593
StaleElementReferenceError,
594
TimeoutError,
595
UnableToSetCookieError,
596
UnableToCaptureScreenError,
597
UnexpectedAlertOpenError,
598
UnknownCommandError,
599
UnknownMethodError,
600
UnsupportedOperationError,
601
checkLegacyResponse,
602
encodeError,
603
isErrorResponse,
604
throwDecodedError,
605
}
606
607