error.js 17 KB

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