mirror of
https://github.com/appium/appium.git
synced 2026-10-28 04:14:00 +08:00
Add W3C Error Response (#176)
* Fixed HTTPResponseBody for W3C error
* Refactored `getResponseForW3CError` into errors.js file
* Changed method so that it returns `{value: {stack: '...', message: '...', error: '...'}}`
* Message is the same as the JSONWP error message
* Error is a static message indicating the type of error
* 'stack' is the callstack provided in the error
* Added W3C 'error' messages to all of the errors
* Make 'getResponseForW3CError' handle BadParametersError
* Refactor 'getResponseForJsonwpError' to errors.js
This commit is contained in:
+213
-32
@@ -1,17 +1,20 @@
|
||||
import ES6Error from 'es6-error';
|
||||
import _ from 'lodash';
|
||||
import { util } from 'appium-support';
|
||||
import { util, logger } from 'appium-support';
|
||||
import HTTPStatusCodes from 'http-status-codes';
|
||||
|
||||
const log = logger.getLogger('MJSONWP');
|
||||
|
||||
// base error class for all of our errors
|
||||
class MJSONWPError extends ES6Error {
|
||||
constructor (msg, jsonwpCode, w3cStatus = 400) {
|
||||
constructor (msg, jsonwpCode, w3cStatus, error) {
|
||||
super(msg);
|
||||
this.jsonwpCode = jsonwpCode;
|
||||
this.error = error || 'An unknown error has occurred';
|
||||
if (this.jsonwpCode === null) {
|
||||
this.jsonwpCode = 13;
|
||||
}
|
||||
this.w3cStatus = w3cStatus;
|
||||
this.w3cStatus = w3cStatus || HTTPStatusCodes.BAD_REQUEST;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +26,15 @@ class NoSuchDriverError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.NOT_FOUND;
|
||||
}
|
||||
static error () {
|
||||
return 'The given session id is not in the list of active sessions';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A session is either terminated or not started', NoSuchDriverError.code(), NoSuchDriverError.w3cStatus());
|
||||
super(err || 'A session is either terminated or not started', NoSuchDriverError.code(), NoSuchDriverError.w3cStatus(), NoSuchDriverError.error());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class NoSuchElementError extends MJSONWPError {
|
||||
static code () {
|
||||
return 7;
|
||||
@@ -35,9 +42,12 @@ class NoSuchElementError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.NOT_FOUND;
|
||||
}
|
||||
static error () {
|
||||
return 'An element could not be located on the page using the given search parameters';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An element could not be located on the page using the given ' +
|
||||
'search parameters.', NoSuchElementError.code(), NoSuchElementError.w3cStatus());
|
||||
'search parameters.', NoSuchElementError.code(), NoSuchElementError.w3cStatus(), NoSuchElementError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,9 +55,12 @@ class NoSuchFrameError extends MJSONWPError {
|
||||
static code () {
|
||||
return 8;
|
||||
}
|
||||
static error () {
|
||||
return 'A command to switch to a frame could not be satisfied because the frame could not be found';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A request to switch to a frame could not be satisfied because ' +
|
||||
'the frame could not be found.', NoSuchFrameError.code());
|
||||
'the frame could not be found.', NoSuchFrameError.code(), null, NoSuchFrameError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +71,13 @@ class UnknownCommandError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.NOT_FOUND;
|
||||
}
|
||||
static error () {
|
||||
return 'A command could not be executed because the remote end is not aware of it';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'The requested resource could not be found, or a request was ' +
|
||||
'received using an HTTP method that is not supported by the mapped ' +
|
||||
'resource.', UnknownCommandError.code(), UnknownCommandError.w3cStatus());
|
||||
'resource.', UnknownCommandError.code(), UnknownCommandError.w3cStatus(), UnknownCommandError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,9 +85,12 @@ class StaleElementReferenceError extends MJSONWPError {
|
||||
static code () {
|
||||
return 10;
|
||||
}
|
||||
static error () {
|
||||
return 'A command failed because the referenced element is no longer attached to the DOM';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An element command failed because the referenced element is no ' +
|
||||
'longer attached to the DOM.', StaleElementReferenceError.code());
|
||||
'longer attached to the DOM.', StaleElementReferenceError.code(), null, StaleElementReferenceError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +108,13 @@ class InvalidElementStateError extends MJSONWPError {
|
||||
static code () {
|
||||
return 12;
|
||||
}
|
||||
static error () {
|
||||
return 'A command could not be completed because the element is in an invalid state';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An element command could not be completed because the element is ' +
|
||||
'in an invalid state (e.g. attempting to click a disabled element).',
|
||||
InvalidElementStateError.code());
|
||||
InvalidElementStateError.code(), null, InvalidElementStateError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +125,9 @@ class UnknownError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'An unknown error occurred in the remote end while processing the command';
|
||||
}
|
||||
constructor (originalError) {
|
||||
let origMessage = originalError;
|
||||
if (originalError instanceof Error) {
|
||||
@@ -114,7 +139,7 @@ class UnknownError extends MJSONWPError {
|
||||
message = `${message} Original error: ${origMessage}`;
|
||||
}
|
||||
|
||||
super(message, UnknownError.code(), UnknownError.w3cStatus());
|
||||
super(message, UnknownError.code(), UnknownError.w3cStatus(), UnknownError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +147,11 @@ class UnknownMethodError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.METHOD_NOT_ALLOWED;
|
||||
}
|
||||
static error () {
|
||||
return 'The requested command matched a known URL but did not match an method for that URL';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'Unknown method.', null, UnknownMethodError.w3cStatus());
|
||||
super(err || 'Unknown method.', null, UnknownMethodError.w3cStatus(), UnknownMethodError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,8 +159,12 @@ class UnsupportedOperationError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'A command that should have executed properly cannot be supported for some reason';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A server-side error occurred. Command cannot be supported.', null, UnsupportedOperationError.w3cStatus());
|
||||
super(err || 'A server-side error occurred. Command cannot be supported.',
|
||||
null, UnsupportedOperationError.w3cStatus(), UnsupportedOperationError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,9 +172,39 @@ class ElementIsNotSelectableError extends MJSONWPError {
|
||||
static code () {
|
||||
return 15;
|
||||
}
|
||||
static error () {
|
||||
return 'An attempt was made to select an element that cannot be selected';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An attempt was made to select an element that cannot be selected.',
|
||||
ElementIsNotSelectableError.code());
|
||||
ElementIsNotSelectableError.code(), null, ElementIsNotSelectableError.error());
|
||||
}
|
||||
}
|
||||
|
||||
class ElementClickInterceptedError extends MJSONWPError {
|
||||
static error () {
|
||||
return 'The Element Click command could not be completed because the element receiving the events is obscuring the element that was requested clicked';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || ElementClickInterceptedError.error(), ElementIsNotSelectableError.code(), null, ElementClickInterceptedError.error());
|
||||
}
|
||||
}
|
||||
|
||||
class ElementNotInteractableError extends MJSONWPError {
|
||||
static error () {
|
||||
return 'A command could not be completed because the element is not pointer- or keyboard interactable';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || ElementNotInteractableError.error(), ElementIsNotSelectableError.code(), null, ElementNotInteractableError.error());
|
||||
}
|
||||
}
|
||||
|
||||
class InsecureCertificateError extends MJSONWPError {
|
||||
static error () {
|
||||
return 'An action caused the user agent to hit a certificate warning, which is usually the result of an expired or invalid TLS certificate.';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || InsecureCertificateError.error(), ElementIsNotSelectableError.code(), null, InsecureCertificateError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,11 +213,14 @@ class JavaScriptError extends MJSONWPError {
|
||||
return 17;
|
||||
}
|
||||
static w3cStatus () {
|
||||
return 500;
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'An error occurred while executing JavaScript supplied by the user';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An error occurred while executing user supplied JavaScript.',
|
||||
JavaScriptError.code(), JavaScriptError.w3cStatus());
|
||||
JavaScriptError.code(), JavaScriptError.w3cStatus(), JavaScriptError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,9 +241,12 @@ class TimeoutError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.REQUEST_TIMEOUT;
|
||||
}
|
||||
static error () {
|
||||
return 'An operation did not complete before its timeout expired';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An operation did not complete before its timeout expired.',
|
||||
TimeoutError.code(), TimeoutError.w3cStatus());
|
||||
TimeoutError.code(), TimeoutError.w3cStatus(), TimeoutError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,15 +254,18 @@ class NoSuchWindowError extends MJSONWPError {
|
||||
static code () {
|
||||
return 23;
|
||||
}
|
||||
static error () {
|
||||
return 'A command to switch to a window could not be satisfied because the window could not be found';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A request to switch to a different window could not be satisfied ' +
|
||||
'because the window could not be found.', NoSuchWindowError.code());
|
||||
'because the window could not be found.', NoSuchWindowError.code(), null, NoSuchWindowError.error());
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidArgumentError extends MJSONWPError {
|
||||
constructor (err) {
|
||||
super(err || 'The arguments passed to the command are either invalid or malformed');
|
||||
super(err || 'The arguments passed to the command are either invalid or malformed', null, null, BadParametersError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,9 +273,21 @@ class InvalidCookieDomainError extends MJSONWPError {
|
||||
static code () {
|
||||
return 24;
|
||||
}
|
||||
static error () {
|
||||
return 'An illegal attempt was made to set a cookie under a different domain than the current page';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'An illegal attempt was made to set a cookie under a different ' +
|
||||
'domain than the current page.', InvalidCookieDomainError.code());
|
||||
'domain than the current page.', InvalidCookieDomainError.code(), null, InvalidCookieDomainError.error());
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidCoordinatesError extends MJSONWPError {
|
||||
static error () {
|
||||
return 'The coordinates provided to an interactions operation are invalid';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || InvalidCoordinatesError.error(), ElementIsNotSelectableError.code(), null, InvalidCoordinatesError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +295,11 @@ class NoSuchCookieError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.NOT_FOUND;
|
||||
}
|
||||
static error () {
|
||||
return 'No cookie matching the given path name was found amongst the associated cookies of the current browsing context’s active document';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'Could not find cookie', null, NoSuchCookieError.w3cStatus());
|
||||
super(err || 'Could not find cookie', null, NoSuchCookieError.w3cStatus(), NoSuchCookieError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,9 +310,12 @@ class UnableToSetCookieError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'A command to set a cookie’s value could not be satisfied';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A request to set a cookie\'s value could not be satisfied.',
|
||||
UnableToSetCookieError.code(), UnableToSetCookieError.w3cStatus());
|
||||
UnableToSetCookieError.code(), UnableToSetCookieError.w3cStatus(), UnableToSetCookieError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,9 +326,12 @@ class UnexpectedAlertOpenError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'A modal dialog was open, blocking this operation';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A modal dialog was open, blocking this operation',
|
||||
UnexpectedAlertOpenError.code(), UnexpectedAlertOpenError.w3cStatus());
|
||||
UnexpectedAlertOpenError.code(), UnexpectedAlertOpenError.w3cStatus(), UnexpectedAlertOpenError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,9 +352,12 @@ class ScriptTimeoutError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.REQUEST_TIMEOUT;
|
||||
}
|
||||
static error () {
|
||||
return 'A script did not complete before its timeout expired';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'A script did not complete before its timeout expired.',
|
||||
ScriptTimeoutError.code(), ScriptTimeoutError.w3cStatus());
|
||||
ScriptTimeoutError.code(), ScriptTimeoutError.w3cStatus(), ScriptTimeoutError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,9 +394,12 @@ class InvalidSelectorError extends MJSONWPError {
|
||||
static code () {
|
||||
return 32;
|
||||
}
|
||||
static error () {
|
||||
return 'Argument was an invalid selector';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'Argument was an invalid selector (e.g. XPath/CSS).',
|
||||
InvalidSelectorError.code());
|
||||
InvalidSelectorError.code(), null, InvalidSelectorError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,13 +410,16 @@ class SessionNotCreatedError extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'A new session could not be created';
|
||||
}
|
||||
constructor (details) {
|
||||
let message = 'A new session could not be created.';
|
||||
if (details) {
|
||||
message += ` Details: ${details}`;
|
||||
}
|
||||
|
||||
super(message, SessionNotCreatedError.code(), SessionNotCreatedError.w3cStatus());
|
||||
super(message, SessionNotCreatedError.code(), SessionNotCreatedError.w3cStatus(), SessionNotCreatedError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,11 +428,23 @@ class MoveTargetOutOfBoundsError extends MJSONWPError {
|
||||
return 34;
|
||||
}
|
||||
static w3cStatus () {
|
||||
return 500;
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'The target for mouse interaction is not in the browser’s viewport and cannot be brought into that viewport';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'Target provided for a move action is out of bounds.',
|
||||
MoveTargetOutOfBoundsError.code(), MoveTargetOutOfBoundsError.w3cStatus());
|
||||
MoveTargetOutOfBoundsError.code(), MoveTargetOutOfBoundsError.w3cStatus(), MoveTargetOutOfBoundsError.error());
|
||||
}
|
||||
}
|
||||
|
||||
class NoSuchAlertError extends MJSONWPError {
|
||||
static error () {
|
||||
return 'An attempt was made to operate on a modal dialog when one was not open';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || NoSuchAlertError.error(), null, null, NoSuchAlertError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,6 +467,7 @@ class InvalidContextError extends MJSONWPError {
|
||||
}
|
||||
}
|
||||
|
||||
// Treat this as an alias for UnknownCommandError
|
||||
class NotYetImplementedError extends MJSONWPError {
|
||||
static code () {
|
||||
return 13;
|
||||
@@ -362,7 +476,8 @@ class NotYetImplementedError extends MJSONWPError {
|
||||
return HTTPStatusCodes.NOT_FOUND; // W3C equivalent is called 'Unknown Command' (A command could not be executed because the remote end is not aware of it)
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'Method has not yet been implemented', NotYetImplementedError.code(), NotYetImplementedError.w3cStatus());
|
||||
super(err || 'Method has not yet been implemented',
|
||||
NotYetImplementedError.code(), NotYetImplementedError.w3cStatus(), UnknownCommandError.error());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,14 +497,20 @@ class UnableToCaptureScreen extends MJSONWPError {
|
||||
static w3cStatus () {
|
||||
return HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
static error () {
|
||||
return 'A screen capture was made impossible';
|
||||
}
|
||||
constructor (err) {
|
||||
super(err || 'Unable to capture screen', null, UnableToCaptureScreen.w3cStatus());
|
||||
super(err || 'Unable to capture screen', null, UnableToCaptureScreen.w3cStatus(), UnableToCaptureScreen.error());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Equivalent to W3C InvalidArgumentError
|
||||
class BadParametersError extends ES6Error {
|
||||
static error () {
|
||||
return 'The arguments passed to a command are either invalid or malformed';
|
||||
}
|
||||
constructor (requiredParams, actualParams, errMessage) {
|
||||
let message;
|
||||
if (!errMessage) {
|
||||
@@ -427,6 +548,7 @@ class ProxyRequestError extends ES6Error {
|
||||
//returns actual error cause for request failure based on `jsonwp.status`
|
||||
return errorFromCode(this.jsonwp.status, this.jsonwp.value);
|
||||
}
|
||||
// TODO: Handle W3C proxying
|
||||
return new UnknownError(this.message);
|
||||
}
|
||||
}
|
||||
@@ -434,6 +556,7 @@ class ProxyRequestError extends ES6Error {
|
||||
const errors = {NotYetImplementedError,
|
||||
NotImplementedError,
|
||||
BadParametersError,
|
||||
InvalidArgumentError,
|
||||
NoSuchDriverError,
|
||||
NoSuchElementError,
|
||||
UnknownCommandError,
|
||||
@@ -441,14 +564,17 @@ const errors = {NotYetImplementedError,
|
||||
ElementNotVisibleError,
|
||||
InvalidElementStateError,
|
||||
UnknownError,
|
||||
InvalidArgumentError,
|
||||
ElementIsNotSelectableError,
|
||||
ElementClickInterceptedError,
|
||||
ElementNotInteractableError,
|
||||
InsecureCertificateError,
|
||||
JavaScriptError,
|
||||
XPathLookupError,
|
||||
TimeoutError,
|
||||
NoSuchWindowError,
|
||||
NoSuchCookieError,
|
||||
InvalidCookieDomainError,
|
||||
InvalidCoordinatesError,
|
||||
UnableToSetCookieError,
|
||||
UnexpectedAlertOpenError,
|
||||
NoAlertOpenError,
|
||||
@@ -459,6 +585,7 @@ const errors = {NotYetImplementedError,
|
||||
InvalidSelectorError,
|
||||
SessionNotCreatedError,
|
||||
MoveTargetOutOfBoundsError,
|
||||
NoSuchAlertError,
|
||||
NoSuchContextError,
|
||||
InvalidContextError,
|
||||
NoSuchFrameError,
|
||||
@@ -500,4 +627,58 @@ function errorFromCode (code, message) {
|
||||
return new UnknownError(message);
|
||||
}
|
||||
|
||||
export { MJSONWPError, errors, isErrorType, errorFromCode };
|
||||
function getResponseForW3CError (err) {
|
||||
let httpStatus;
|
||||
let error;
|
||||
if (!err.w3cStatus) {
|
||||
log.error(`Encountered internal error running command: ${err.stack}`);
|
||||
err = new errors.UnknownError(err.message);
|
||||
}
|
||||
|
||||
if (isErrorType(err, errors.BadParametersError)) {
|
||||
// respond with a 400 if we have bad parameters
|
||||
log.debug(`Bad parameters: ${err}`);
|
||||
httpStatus = HTTPStatusCodes.BAD_REQUEST;
|
||||
error = 'The arguments passed to a command are either invalid or malformed';
|
||||
} else {
|
||||
error = err.error;
|
||||
}
|
||||
httpStatus = err.w3cStatus;
|
||||
let httpResBody = {
|
||||
value: {
|
||||
error,
|
||||
message: err.message,
|
||||
stacktrace: err.stack,
|
||||
}
|
||||
};
|
||||
return [httpStatus, httpResBody];
|
||||
}
|
||||
|
||||
function getResponseForJsonwpError (err) {
|
||||
let httpStatus = HTTPStatusCodes.INTERNAL_SERVER_ERROR;
|
||||
let httpResBody = {
|
||||
status: err.jsonwpCode,
|
||||
value: {
|
||||
message: err.message
|
||||
}
|
||||
};
|
||||
|
||||
if (isErrorType(err, errors.BadParametersError)) {
|
||||
// respond with a 400 if we have bad parameters
|
||||
log.debug(`Bad parameters: ${err}`);
|
||||
httpStatus = HTTPStatusCodes.BAD_REQUEST;
|
||||
httpResBody = err.message;
|
||||
} else if (isErrorType(err, errors.NotYetImplementedError) ||
|
||||
isErrorType(err, errors.NotImplementedError)) {
|
||||
// respond with a 501 if the method is not implemented
|
||||
httpStatus = HTTPStatusCodes.NOT_IMPLEMENTED;
|
||||
} else if (isErrorType(err, errors.NoSuchDriverError)) {
|
||||
// respond with a 404 if there is no driver for the session
|
||||
httpStatus = HTTPStatusCodes.NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
return [httpStatus, httpResBody];
|
||||
}
|
||||
|
||||
export { MJSONWPError, errors, isErrorType, errorFromCode, getResponseForW3CError, getResponseForJsonwpError };
|
||||
|
||||
+1
-41
@@ -1,7 +1,7 @@
|
||||
import _ from 'lodash';
|
||||
import { logger, util } from 'appium-support';
|
||||
import { validators } from './validators';
|
||||
import { errors, isErrorType, MJSONWPError, errorFromCode } from './errors';
|
||||
import { errors, isErrorType, MJSONWPError, errorFromCode, getResponseForW3CError, getResponseForJsonwpError } from './errors';
|
||||
import { METHOD_MAP, NO_SESSION_ID_COMMANDS } from './routes';
|
||||
import { renameKey } from '../basedriver/helpers';
|
||||
import B from 'bluebird';
|
||||
@@ -166,46 +166,6 @@ function makeArgs (requestParams, jsonObj, payloadParams, protocol) {
|
||||
return args;
|
||||
}
|
||||
|
||||
function getResponseForJsonwpError (err) {
|
||||
let httpStatus = 500;
|
||||
let httpResBody = {
|
||||
status: err.jsonwpCode,
|
||||
value: {
|
||||
message: err.message
|
||||
}
|
||||
};
|
||||
|
||||
if (isErrorType(err, errors.BadParametersError)) {
|
||||
// respond with a 400 if we have bad parameters
|
||||
log.debug(`Bad parameters: ${err}`);
|
||||
httpStatus = 400;
|
||||
httpResBody = err.message;
|
||||
} else if (isErrorType(err, errors.NotYetImplementedError) ||
|
||||
isErrorType(err, errors.NotImplementedError)) {
|
||||
// respond with a 501 if the method is not implemented
|
||||
httpStatus = 501;
|
||||
} else if (isErrorType(err, errors.NoSuchDriverError)) {
|
||||
// respond with a 404 if there is no driver for the session
|
||||
httpStatus = 404;
|
||||
}
|
||||
|
||||
|
||||
return [httpStatus, httpResBody];
|
||||
}
|
||||
|
||||
function getResponseForW3CError (err) {
|
||||
let httpStatus;
|
||||
if (!err.w3cStatus) {
|
||||
log.error(`Encountered internal error running command: ${err.stack}`);
|
||||
err = new errors.UnknownError(err.message);
|
||||
}
|
||||
httpStatus = err.w3cStatus;
|
||||
let httpResBody = err.message;
|
||||
return [httpStatus, httpResBody];
|
||||
}
|
||||
|
||||
|
||||
|
||||
function routeConfiguringFunction (driver) {
|
||||
if (!driver.sessionExists) {
|
||||
throw new Error('Drivers used with MJSONWP must implement `sessionExists`');
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { errors, errorFromCode } from '../..';
|
||||
import { getResponseForW3CError } from '../../lib/mjsonwp/errors';
|
||||
import chai from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised';
|
||||
import _ from 'lodash';
|
||||
@@ -177,3 +178,38 @@ describe('w3c Status Codes', function () {
|
||||
(new errors.NoSuchFrameError()).should.have.property('w3cStatus', 400);
|
||||
});
|
||||
});
|
||||
describe('.getResponseForW3CError', function () {
|
||||
it('should return an error, message and stacktrace for just a generic exception', function () {
|
||||
try {
|
||||
throw new Error('Some random error');
|
||||
} catch (e) {
|
||||
const [httpStatus, httpResponseBody] = getResponseForW3CError(e);
|
||||
httpStatus.should.equal(500);
|
||||
const {error, message, stacktrace} = httpResponseBody.value;
|
||||
message.should.match(/Some random error/);
|
||||
error.should.equal('An unknown error occurred in the remote end while processing the command');
|
||||
stacktrace.should.match(/at getResponseForW3CError/);
|
||||
stacktrace.should.match(/Some random error/);
|
||||
stacktrace.should.match(/errors-specs.js/);
|
||||
}
|
||||
});
|
||||
it('should return an error, message and stacktrace for a NoSuchElementError', function () {
|
||||
const noSuchElementError = new errors.NoSuchElementError('specific error message');
|
||||
const [httpStatus, httpResponseBody] = getResponseForW3CError(noSuchElementError);
|
||||
httpStatus.should.equal(404);
|
||||
const {error, message, stacktrace} = httpResponseBody.value;
|
||||
error.should.equal('An element could not be located on the page using the given search parameters');
|
||||
message.should.match(/specific error message/);
|
||||
stacktrace.should.match(/errors-specs.js/);
|
||||
});
|
||||
it('should handle BadParametersError', function () {
|
||||
const badParamsError = new errors.BadParametersError('__FOO__', '__BAR__', '__HELLO_WORLD__');
|
||||
const [httpStatus, httpResponseBody] = getResponseForW3CError(badParamsError);
|
||||
httpStatus.should.equal(400);
|
||||
const {error, message, stacktrace} = httpResponseBody.value;
|
||||
error.should.equal('The arguments passed to a command are either invalid or malformed');
|
||||
message.should.match(/__BAR__/);
|
||||
message.should.match(/__HELLO_WORLD__/);
|
||||
stacktrace.should.match(/errors-specs.js/);
|
||||
});
|
||||
});
|
||||
@@ -426,34 +426,51 @@ describe('MJSONWP', async function () {
|
||||
});
|
||||
|
||||
it(`should throw 400 Bad Parameters exception if the parameters are bad`, async function () {
|
||||
const {message, statusCode} = await request.post(`${sessionUrl}/actions`, {
|
||||
const {statusCode, error} = await request.post(`${sessionUrl}/actions`, {
|
||||
json: {
|
||||
bad: 'params',
|
||||
}
|
||||
}).should.eventually.be.rejected;
|
||||
message.should.match(/Parameters were incorrect/);
|
||||
statusCode.should.equal(400);
|
||||
|
||||
const {error:w3cError, message, stacktrace} = error.value;
|
||||
message.should.match(/Parameters were incorrect/);
|
||||
stacktrace.should.match(/mjsonwp.js/);
|
||||
w3cError.should.be.a.string;
|
||||
w3cError.should.equal(errors.BadParametersError.error());
|
||||
});
|
||||
|
||||
it(`should throw 404 Not Found exception if the command hasn't been implemented yet`, async function () {
|
||||
const {message, statusCode} = await request.post(`${sessionUrl}/actions`, {
|
||||
const {statusCode, error} = await request.post(`${sessionUrl}/actions`, {
|
||||
json: {
|
||||
actions: [],
|
||||
}
|
||||
}).should.eventually.be.rejected;
|
||||
message.should.match(/Method has not yet been implemented/);
|
||||
statusCode.should.equal(404);
|
||||
|
||||
const {error:w3cError, message, stacktrace} = error.value;
|
||||
message.should.match(/Method has not yet been implemented/);
|
||||
stacktrace.should.match(/mjsonwp.js/);
|
||||
w3cError.should.be.a.string;
|
||||
w3cError.should.equal(errors.UnknownCommandError.error());
|
||||
message.should.match(/Method has not yet been implemented/);
|
||||
});
|
||||
|
||||
it(`should throw 500 Unknown Error if the command throws an unexpected exception`, async function () {
|
||||
driver.performActions = () => { throw new Error(`Didn't work`); };
|
||||
const {message, statusCode} = await request.post(`${sessionUrl}/actions`, {
|
||||
const {statusCode, error} = await request.post(`${sessionUrl}/actions`, {
|
||||
json: {
|
||||
actions: [],
|
||||
}
|
||||
}).should.eventually.be.rejected;
|
||||
message.should.match(/500[\w\W]*Didn't work/);
|
||||
statusCode.should.equal(500);
|
||||
|
||||
const {error:w3cError, message, stacktrace} = error.value;
|
||||
stacktrace.should.match(/mjsonwp.js/);
|
||||
w3cError.should.be.a.string;
|
||||
w3cError.should.equal(errors.UnknownError.error());
|
||||
message.should.match(/Didn't work/);
|
||||
|
||||
delete driver.performActions;
|
||||
});
|
||||
|
||||
@@ -496,7 +513,7 @@ describe('MJSONWP', async function () {
|
||||
|
||||
it(`should fail with a 408 error if it throws a TimeoutError exception`, async () => {
|
||||
sinon.stub(driver, 'setUrl', () => { throw new errors.TimeoutError; });
|
||||
let {statusCode} = await request({
|
||||
let {statusCode, error} = await request({
|
||||
url: `${sessionUrl}/url`,
|
||||
method: 'POST',
|
||||
json: {
|
||||
@@ -504,6 +521,13 @@ describe('MJSONWP', async function () {
|
||||
}
|
||||
}).should.eventually.be.rejected;
|
||||
statusCode.should.equal(408);
|
||||
|
||||
const {error:w3cError, message, stacktrace} = error.value;
|
||||
stacktrace.should.match(/mjsonwp.js/);
|
||||
w3cError.should.be.a.string;
|
||||
w3cError.should.equal(errors.TimeoutError.error());
|
||||
message.should.match(/An operation did not complete before its timeout expired/);
|
||||
|
||||
sinon.restore(driver, 'setUrl');
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user