summaryrefslogtreecommitdiffstats
path: root/testing/webdriver/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--testing/webdriver/src/error.rs423
1 files changed, 423 insertions, 0 deletions
diff --git a/testing/webdriver/src/error.rs b/testing/webdriver/src/error.rs
new file mode 100644
index 0000000000..ed95fe2277
--- /dev/null
+++ b/testing/webdriver/src/error.rs
@@ -0,0 +1,423 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use base64::DecodeError;
+use http::StatusCode;
+use serde::de::{Deserialize, Deserializer};
+use serde::ser::{Serialize, Serializer};
+use std::borrow::Cow;
+use std::convert::From;
+use std::error::Error;
+use std::fmt;
+use std::io;
+
+#[derive(Debug, PartialEq)]
+pub enum ErrorStatus {
+ /// The [element]'s [ShadowRoot] is not attached to the active document,
+ /// or the reference is stale
+ /// [element]: ../common/struct.WebElement.html
+ /// [ShadowRoot]: ../common/struct.ShadowRoot.html
+ DetachedShadowRoot,
+
+ /// The [`ElementClick`] command could not be completed because the
+ /// [element] receiving the events is obscuring the element that was
+ /// requested clicked.
+ ///
+ /// [`ElementClick`]:
+ /// ../command/enum.WebDriverCommand.html#variant.ElementClick
+ /// [element]: ../common/struct.WebElement.html
+ ElementClickIntercepted,
+
+ /// A [command] could not be completed because the element is not pointer-
+ /// or keyboard interactable.
+ ///
+ /// [command]: ../command/index.html
+ ElementNotInteractable,
+
+ /// An attempt was made to select an [element] that cannot be selected.
+ ///
+ /// [element]: ../common/struct.WebElement.html
+ ElementNotSelectable,
+
+ /// Navigation caused the user agent to hit a certificate warning, which is
+ /// usually the result of an expired or invalid TLS certificate.
+ InsecureCertificate,
+
+ /// The arguments passed to a [command] are either invalid or malformed.
+ ///
+ /// [command]: ../command/index.html
+ InvalidArgument,
+
+ /// An illegal attempt was made to set a cookie under a different domain
+ /// than the current page.
+ InvalidCookieDomain,
+
+ /// The coordinates provided to an interactions operation are invalid.
+ InvalidCoordinates,
+
+ /// A [command] could not be completed because the element is an invalid
+ /// state, e.g. attempting to click an element that is no longer attached
+ /// to the document.
+ ///
+ /// [command]: ../command/index.html
+ InvalidElementState,
+
+ /// Argument was an invalid selector.
+ InvalidSelector,
+
+ /// Occurs if the given session ID is not in the list of active sessions,
+ /// meaning the session either does not exist or that it’s not active.
+ InvalidSessionId,
+
+ /// An error occurred while executing JavaScript supplied by the user.
+ JavascriptError,
+
+ /// The target for mouse interaction is not in the browser’s viewport and
+ /// cannot be brought into that viewport.
+ MoveTargetOutOfBounds,
+
+ /// An attempt was made to operate on a modal dialogue when one was not
+ /// open.
+ NoSuchAlert,
+
+ /// No cookie matching the given path name was found amongst the associated
+ /// cookies of the current browsing context’s active document.
+ NoSuchCookie,
+
+ /// An [element] could not be located on the page using the given search
+ /// parameters.
+ ///
+ /// [element]: ../common/struct.WebElement.html
+ NoSuchElement,
+
+ /// A [command] to switch to a frame could not be satisfied because the
+ /// frame could not be found.
+ ///
+ /// [command]: ../command/index.html
+ NoSuchFrame,
+
+ /// An [element]'s [ShadowRoot] was not found attached to the element.
+ ///
+ /// [element]: ../common/struct.WebElement.html
+ /// [ShadowRoot]: ../common/struct.ShadowRoot.html
+ NoSuchShadowRoot,
+
+ /// A [command] to switch to a window could not be satisfied because the
+ /// window could not be found.
+ ///
+ /// [command]: ../command/index.html
+ NoSuchWindow,
+
+ /// A script did not complete before its timeout expired.
+ ScriptTimeout,
+
+ /// A new session could not be created.
+ SessionNotCreated,
+
+ /// A [command] failed because the referenced [element] is no longer
+ /// attached to the DOM.
+ ///
+ /// [command]: ../command/index.html
+ /// [element]: ../common/struct.WebElement.html
+ StaleElementReference,
+
+ /// An operation did not complete before its timeout expired.
+ Timeout,
+
+ /// A screen capture was made impossible.
+ UnableToCaptureScreen,
+
+ /// Setting the cookie’s value could not be done.
+ UnableToSetCookie,
+
+ /// A modal dialogue was open, blocking this operation.
+ UnexpectedAlertOpen,
+
+ /// The requested command could not be executed because it does not exist.
+ UnknownCommand,
+
+ /// An unknown error occurred in the remote end whilst processing the
+ /// [command].
+ ///
+ /// [command]: ../command/index.html
+ UnknownError,
+
+ /// The requested [command] matched a known endpoint, but did not match a
+ /// method for that endpoint.
+ ///
+ /// [command]: ../command/index.html
+ UnknownMethod,
+
+ UnknownPath,
+
+ /// Indicates that a [command] that should have executed properly is not
+ /// currently supported.
+ UnsupportedOperation,
+}
+
+impl Serialize for ErrorStatus {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.error_code().serialize(serializer)
+ }
+}
+
+impl<'de> Deserialize<'de> for ErrorStatus {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ let error_string = String::deserialize(deserializer)?;
+ Ok(ErrorStatus::from(error_string))
+ }
+}
+
+impl ErrorStatus {
+ /// Returns the string serialisation of the error type.
+ pub fn error_code(&self) -> &'static str {
+ use self::ErrorStatus::*;
+ match *self {
+ DetachedShadowRoot => "detached shadow root",
+ ElementClickIntercepted => "element click intercepted",
+ ElementNotInteractable => "element not interactable",
+ ElementNotSelectable => "element not selectable",
+ InsecureCertificate => "insecure certificate",
+ InvalidArgument => "invalid argument",
+ InvalidCookieDomain => "invalid cookie domain",
+ InvalidCoordinates => "invalid coordinates",
+ InvalidElementState => "invalid element state",
+ InvalidSelector => "invalid selector",
+ InvalidSessionId => "invalid session id",
+ JavascriptError => "javascript error",
+ MoveTargetOutOfBounds => "move target out of bounds",
+ NoSuchAlert => "no such alert",
+ NoSuchCookie => "no such cookie",
+ NoSuchElement => "no such element",
+ NoSuchFrame => "no such frame",
+ NoSuchShadowRoot => "no such shadow root",
+ NoSuchWindow => "no such window",
+ ScriptTimeout => "script timeout",
+ SessionNotCreated => "session not created",
+ StaleElementReference => "stale element reference",
+ Timeout => "timeout",
+ UnableToCaptureScreen => "unable to capture screen",
+ UnableToSetCookie => "unable to set cookie",
+ UnexpectedAlertOpen => "unexpected alert open",
+ UnknownCommand | UnknownError => "unknown error",
+ UnknownMethod => "unknown method",
+ UnknownPath => "unknown command",
+ UnsupportedOperation => "unsupported operation",
+ }
+ }
+
+ /// Returns the correct HTTP status code associated with the error type.
+ pub fn http_status(&self) -> StatusCode {
+ use self::ErrorStatus::*;
+ match *self {
+ DetachedShadowRoot => StatusCode::NOT_FOUND,
+ ElementClickIntercepted => StatusCode::BAD_REQUEST,
+ ElementNotInteractable => StatusCode::BAD_REQUEST,
+ ElementNotSelectable => StatusCode::BAD_REQUEST,
+ InsecureCertificate => StatusCode::BAD_REQUEST,
+ InvalidArgument => StatusCode::BAD_REQUEST,
+ InvalidCookieDomain => StatusCode::BAD_REQUEST,
+ InvalidCoordinates => StatusCode::BAD_REQUEST,
+ InvalidElementState => StatusCode::BAD_REQUEST,
+ InvalidSelector => StatusCode::BAD_REQUEST,
+ InvalidSessionId => StatusCode::NOT_FOUND,
+ JavascriptError => StatusCode::INTERNAL_SERVER_ERROR,
+ MoveTargetOutOfBounds => StatusCode::INTERNAL_SERVER_ERROR,
+ NoSuchAlert => StatusCode::NOT_FOUND,
+ NoSuchCookie => StatusCode::NOT_FOUND,
+ NoSuchElement => StatusCode::NOT_FOUND,
+ NoSuchFrame => StatusCode::NOT_FOUND,
+ NoSuchShadowRoot => StatusCode::NOT_FOUND,
+ NoSuchWindow => StatusCode::NOT_FOUND,
+ ScriptTimeout => StatusCode::INTERNAL_SERVER_ERROR,
+ SessionNotCreated => StatusCode::INTERNAL_SERVER_ERROR,
+ StaleElementReference => StatusCode::NOT_FOUND,
+ Timeout => StatusCode::INTERNAL_SERVER_ERROR,
+ UnableToCaptureScreen => StatusCode::BAD_REQUEST,
+ UnableToSetCookie => StatusCode::INTERNAL_SERVER_ERROR,
+ UnexpectedAlertOpen => StatusCode::INTERNAL_SERVER_ERROR,
+ UnknownCommand => StatusCode::NOT_FOUND,
+ UnknownError => StatusCode::INTERNAL_SERVER_ERROR,
+ UnknownMethod => StatusCode::METHOD_NOT_ALLOWED,
+ UnknownPath => StatusCode::NOT_FOUND,
+ UnsupportedOperation => StatusCode::INTERNAL_SERVER_ERROR,
+ }
+ }
+}
+
+/// Deserialises error type from string.
+impl From<String> for ErrorStatus {
+ fn from(s: String) -> ErrorStatus {
+ use self::ErrorStatus::*;
+ match &*s {
+ "detached shadow root" => DetachedShadowRoot,
+ "element click intercepted" => ElementClickIntercepted,
+ "element not interactable" | "element not visible" => ElementNotInteractable,
+ "element not selectable" => ElementNotSelectable,
+ "insecure certificate" => InsecureCertificate,
+ "invalid argument" => InvalidArgument,
+ "invalid cookie domain" => InvalidCookieDomain,
+ "invalid coordinates" | "invalid element coordinates" => InvalidCoordinates,
+ "invalid element state" => InvalidElementState,
+ "invalid selector" => InvalidSelector,
+ "invalid session id" => InvalidSessionId,
+ "javascript error" => JavascriptError,
+ "move target out of bounds" => MoveTargetOutOfBounds,
+ "no such alert" => NoSuchAlert,
+ "no such element" => NoSuchElement,
+ "no such frame" => NoSuchFrame,
+ "no such shadow root" => NoSuchShadowRoot,
+ "no such window" => NoSuchWindow,
+ "script timeout" => ScriptTimeout,
+ "session not created" => SessionNotCreated,
+ "stale element reference" => StaleElementReference,
+ "timeout" => Timeout,
+ "unable to capture screen" => UnableToCaptureScreen,
+ "unable to set cookie" => UnableToSetCookie,
+ "unexpected alert open" => UnexpectedAlertOpen,
+ "unknown command" => UnknownCommand,
+ "unknown error" => UnknownError,
+ "unsupported operation" => UnsupportedOperation,
+ _ => UnknownError,
+ }
+ }
+}
+
+pub type WebDriverResult<T> = Result<T, WebDriverError>;
+
+#[derive(Debug, PartialEq, Serialize)]
+#[serde(remote = "Self")]
+pub struct WebDriverError {
+ pub error: ErrorStatus,
+ pub message: Cow<'static, str>,
+ #[serde(rename = "stacktrace")]
+ pub stack: Cow<'static, str>,
+ #[serde(skip)]
+ pub delete_session: bool,
+}
+
+impl Serialize for WebDriverError {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ #[derive(Serialize)]
+ struct Wrapper<'a> {
+ #[serde(with = "WebDriverError")]
+ value: &'a WebDriverError,
+ }
+
+ Wrapper { value: self }.serialize(serializer)
+ }
+}
+
+impl WebDriverError {
+ pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
+ where
+ S: Into<Cow<'static, str>>,
+ {
+ WebDriverError {
+ error,
+ message: message.into(),
+ stack: "".into(),
+ delete_session: false,
+ }
+ }
+
+ pub fn new_with_stack<S>(error: ErrorStatus, message: S, stack: S) -> WebDriverError
+ where
+ S: Into<Cow<'static, str>>,
+ {
+ WebDriverError {
+ error,
+ message: message.into(),
+ stack: stack.into(),
+ delete_session: false,
+ }
+ }
+
+ pub fn error_code(&self) -> &'static str {
+ self.error.error_code()
+ }
+
+ pub fn http_status(&self) -> StatusCode {
+ self.error.http_status()
+ }
+}
+
+impl Error for WebDriverError {
+ fn description(&self) -> &str {
+ self.error_code()
+ }
+
+ fn cause(&self) -> Option<&dyn Error> {
+ None
+ }
+}
+
+impl fmt::Display for WebDriverError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.message.fmt(f)
+ }
+}
+
+impl From<serde_json::Error> for WebDriverError {
+ fn from(err: serde_json::Error) -> WebDriverError {
+ WebDriverError::new(ErrorStatus::InvalidArgument, err.to_string())
+ }
+}
+
+impl From<io::Error> for WebDriverError {
+ fn from(err: io::Error) -> WebDriverError {
+ WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
+ }
+}
+
+impl From<DecodeError> for WebDriverError {
+ fn from(err: DecodeError) -> WebDriverError {
+ WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
+ }
+}
+
+impl From<Box<dyn Error>> for WebDriverError {
+ fn from(err: Box<dyn Error>) -> WebDriverError {
+ WebDriverError::new(ErrorStatus::UnknownError, err.to_string())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use serde_json::json;
+
+ use super::*;
+ use crate::test::assert_ser;
+
+ #[test]
+ fn test_json_webdriver_error() {
+ let json = json!({"value": {
+ "error": "unknown error",
+ "message": "foo bar",
+ "stacktrace": "foo\nbar",
+ }});
+ let error = WebDriverError {
+ error: ErrorStatus::UnknownError,
+ message: "foo bar".into(),
+ stack: "foo\nbar".into(),
+ delete_session: true,
+ };
+
+ assert_ser(&error, json);
+ }
+
+ #[test]
+ fn test_json_error_status() {
+ assert_ser(&ErrorStatus::UnknownError, json!("unknown error"));
+ }
+}