Bug 1150522: Add WebDriver string statuses to Marionette client

Adds string based statuses as defined by the W3C WebDriver protocol
to the Marionette Python client.  Importantly, it does not remove the
ability to look up errors by their Selenium protocol number for backwards
compatibility reasons.

r=dburns

--HG--
extra : rebase_source : 792e85d01ed6513370f448762c1c5bf8f13842a4
This commit is contained in:
Andreas Tolfsen 2015-04-02 20:07:20 +01:00
Родитель 08cbc9d8e1
Коммит 99a1fa9768
7 изменённых файлов: 175 добавлений и 125 удалений

Просмотреть файл

@ -16,7 +16,7 @@ import warnings
from marionette_driver.errors import (
ErrorCodes, MarionetteException, InstallGeckoError, TimeoutException, InvalidResponseException,
MarionetteException, InstallGeckoError, TimeoutException, InvalidResponseException,
JavascriptException, NoSuchElementException, XPathLookupException, NoSuchWindowException,
StaleElementException, ScriptTimeoutException, ElementNotVisibleException,
NoSuchFrameException, InvalidElementStateException, NoAlertPresentException,

Просмотреть файл

@ -6,7 +6,6 @@ import sys
from marionette import marionette_test
from marionette_driver import errors
from marionette_driver.errors import ErrorCodes
def fake_cause():
try:
@ -15,29 +14,26 @@ def fake_cause():
return sys.exc_info()
message = "foo"
status = ErrorCodes.TIMEOUT
cause = fake_cause()
stacktrace = "first\nsecond"
class TestMarionetteException(marionette_test.MarionetteTestCase):
class TestExceptionType(marionette_test.MarionetteTestCase):
def test_defaults(self):
exc = errors.MarionetteException()
self.assertIsNone(exc.msg)
self.assertEquals(exc.status, ErrorCodes.MARIONETTE_ERROR)
self.assertIsNone(exc.cause)
self.assertIsNone(exc.stacktrace)
def test_construction(self):
exc = errors.MarionetteException(
message=message, status=status, cause=cause, stacktrace=stacktrace)
message=message, cause=cause, stacktrace=stacktrace)
self.assertEquals(exc.msg, message)
self.assertEquals(exc.status, status)
self.assertEquals(exc.cause, cause)
self.assertEquals(exc.stacktrace, stacktrace)
def test_str(self):
exc = errors.MarionetteException(
message=message, status=status, cause=cause, stacktrace=stacktrace)
message=message, cause=cause, stacktrace=stacktrace)
r = str(exc)
self.assertIn(message, r)
self.assertIn(", caused by %r" % cause[0], r)
@ -55,3 +51,28 @@ class TestMarionetteException(marionette_test.MarionetteTestCase):
self.assertEqual(exc.cause, cause)
r = str(exc)
self.assertIn(", caused by %r" % cause[0], r)
class TestLookup(marionette_test.MarionetteTestCase):
def test_by_known_number(self):
self.assertEqual(errors.NoSuchElementException, errors.lookup(7))
def test_by_unknown_number(self):
self.assertEqual(errors.MarionetteException, errors.lookup(123456))
def test_by_known_string(self):
self.assertEqual(errors.NoSuchElementException,
errors.lookup("no such element"))
def test_by_unknown_string(self):
self.assertEqual(errors.MarionetteException, errors.lookup("barbera"))
class TestAllExceptions(marionette_test.MarionetteTestCase):
def test_properties(self):
for exc in errors.excs:
self.assertTrue(hasattr(exc, "code"),
"expected exception to have attribute `code'")
self.assertTrue(hasattr(exc, "status"),
"expected exception to have attribute `status'")
self.assertIsInstance(exc.code, tuple)

Просмотреть файл

@ -15,8 +15,17 @@ class TestHandleError(marionette_test.MarionetteTestCase):
def test_known_error_code(self):
with self.assertRaises(errors.NoSuchElementException):
self.marionette._handle_error(
{"error": {"status": errors.ErrorCodes.NO_SUCH_ELEMENT}})
{"error": {"status": errors.NoSuchElementException.code[0]}})
def test_known_error_status(self):
with self.assertRaises(errors.NoSuchElementException):
self.marionette._handle_error(
{"error": {"status": errors.NoSuchElementException.status}})
def test_unknown_error_code(self):
with self.assertRaises(errors.MarionetteException):
self.marionette._handle_error({"error": {"status": 123456}})
def test_unknown_error_status(self):
with self.assertRaises(errors.MarionetteException):
self.marionette._handle_error({"error": {"status": "barbera"}})

Просмотреть файл

@ -4,52 +4,19 @@
import traceback
class ErrorCodes(object):
SUCCESS = 0
NO_SUCH_ELEMENT = 7
NO_SUCH_FRAME = 8
UNKNOWN_COMMAND = 9
STALE_ELEMENT_REFERENCE = 10
ELEMENT_NOT_VISIBLE = 11
INVALID_ELEMENT_STATE = 12
UNKNOWN_ERROR = 13
ELEMENT_NOT_ACCESSIBLE = 56
ELEMENT_IS_NOT_SELECTABLE = 15
JAVASCRIPT_ERROR = 17
XPATH_LOOKUP_ERROR = 19
TIMEOUT = 21
NO_SUCH_WINDOW = 23
INVALID_COOKIE_DOMAIN = 24
UNABLE_TO_SET_COOKIE = 25
UNEXPECTED_ALERT_OPEN = 26
NO_ALERT_OPEN = 27
SCRIPT_TIMEOUT = 28
INVALID_ELEMENT_COORDINATES = 29
INVALID_SELECTOR = 32
MOVE_TARGET_OUT_OF_BOUNDS = 34
INVALID_XPATH_SELECTOR = 51
INVALID_XPATH_SELECTOR_RETURN_TYPER = 52
INVALID_RESPONSE = 53
FRAME_SEND_NOT_INITIALIZED_ERROR = 54
FRAME_SEND_FAILURE_ERROR = 55
SESSION_NOT_CREATED = 71
UNSUPPORTED_OPERATION = 405
MARIONETTE_ERROR = 500
class MarionetteException(Exception):
"""Raised when a generic non-recoverable exception has occured."""
def __init__(self, message=None,
status=ErrorCodes.MARIONETTE_ERROR, cause=None,
stacktrace=None):
code = (500,)
status = "webdriver error"
def __init__(self, message=None, cause=None, stacktrace=None):
"""Construct new MarionetteException instance.
:param message: An optional exception message.
:param status: A WebDriver status code given as an integer.
By default the generic Marionette error code 500 will be
used.
:param cause: An optional tuple of three values giving
information about the root exception cause. Expected
tuple values are (type, value, traceback).
@ -61,7 +28,6 @@ class MarionetteException(Exception):
"""
self.msg = message
self.status = status
self.cause = cause
self.stacktrace = stacktrace
@ -81,72 +47,175 @@ class MarionetteException(Exception):
return "".join(traceback.format_exception(self.__class__, msg, tb))
class InstallGeckoError(MarionetteException):
pass
class TimeoutException(MarionetteException):
pass
code = (21,)
status = "timeout"
class InvalidResponseException(MarionetteException):
pass
code = (53,)
status = "invalid response"
class JavascriptException(MarionetteException):
pass
code = (17,)
status = "javascript error"
class NoSuchElementException(MarionetteException):
pass
code = (7,)
status = "no such element"
class XPathLookupException(MarionetteException):
pass
code = (19,)
status = "invalid xpath selector"
class NoSuchWindowException(MarionetteException):
pass
code = (23,)
status = "no such window"
class StaleElementException(MarionetteException):
pass
code = (10,)
status = "stale element reference"
class ScriptTimeoutException(MarionetteException):
pass
code = (28,)
status = "script timeout"
class ElementNotVisibleException(MarionetteException):
def __init__(self, message="Element is not currently visible and may not be manipulated",
status=ErrorCodes.ELEMENT_NOT_VISIBLE,
code = (11,)
status = "element not visible"
def __init__(
self, message="Element is not currently visible and may not be manipulated",
stacktrace=None, cause=None):
super(ElementNotVisibleException, self).__init__(
message, status=status, cause=cause, stacktrace=stacktrace)
message, cause=cause, stacktrace=stacktrace)
class ElementNotAccessibleException(MarionetteException):
pass
code = (56,)
status = "element not accessible"
class NoSuchFrameException(MarionetteException):
pass
code = (8,)
status = "no such frame"
class InvalidElementStateException(MarionetteException):
pass
code = (12,)
status = "invalid element state"
class NoAlertPresentException(MarionetteException):
pass
code = (27,)
status = "no such alert"
class InvalidCookieDomainException(MarionetteException):
pass
code = (24,)
status = "invalid cookie domain"
class UnableToSetCookieException(MarionetteException):
pass
code = (25,)
status = "unable to set cookie"
class InvalidElementCoordinates(MarionetteException):
code = (29,)
status = "invalid element coordinates"
class InvalidSelectorException(MarionetteException):
pass
code = (32, 51, 52)
status = "invalid selector"
class MoveTargetOutOfBoundsException(MarionetteException):
pass
code = (34,)
status = "move target out of bounds"
class FrameSendNotInitializedError(MarionetteException):
pass
code = (54,)
status = "frame send not initialized"
class FrameSendFailureError(MarionetteException):
pass
code = (55,)
status = "frame send failure"
class UnsupportedOperationException(MarionetteException):
pass
code = (405,)
status = "unsupported operation"
class SessionNotCreatedException(MarionetteException):
pass
code = (33, 71)
status = "session not created"
class UnexpectedAlertOpen(MarionetteException):
code = (26,)
status = "unexpected alert open"
excs = [
MarionetteException,
TimeoutException,
InvalidResponseException,
JavascriptException,
NoSuchElementException,
XPathLookupException,
NoSuchWindowException,
StaleElementException,
ScriptTimeoutException,
ElementNotVisibleException,
ElementNotAccessibleException,
NoSuchFrameException,
InvalidElementStateException,
NoAlertPresentException,
InvalidCookieDomainException,
UnableToSetCookieException,
InvalidElementCoordinates,
InvalidSelectorException,
MoveTargetOutOfBoundsException,
FrameSendNotInitializedError,
FrameSendFailureError,
UnsupportedOperationException,
SessionNotCreatedException,
UnexpectedAlertOpen,
]
def lookup(identifier):
"""Finds error exception class by associated Selenium JSON wire
protocol number code, or W3C WebDriver protocol string."""
by_code = lambda exc: identifier in exc.code
by_status = lambda exc: exc.status == identifier
rv = None
if isinstance(identifier, int):
rv = filter(by_code, excs)
elif isinstance(identifier, str):
rv = filter(by_status, excs)
if not rv:
return MarionetteException
return rv[0]
__all__ = excs + ["lookup"]

Просмотреть файл

@ -693,8 +693,7 @@ class Marionette(object):
self.session = None
self.window = None
self.client.close()
raise errors.TimeoutException(
"Connection timed out", status=errors.ErrorCodes.TIMEOUT)
raise errors.TimeoutException("Connection timed out")
# Process any emulator commands that are sent from a script
# while it's executing.
@ -745,60 +744,11 @@ class Marionette(object):
"Malformed packet, expected key 'error' to be a dict: %s" % response)
error = response["error"]
status = error.get("status", 500)
status = error.get("status")
message = error.get("message")
stacktrace = error.get("stacktrace")
# status numbers come from
# http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
if status == errors.ErrorCodes.NO_SUCH_ELEMENT:
raise errors.NoSuchElementException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.NO_SUCH_FRAME:
raise errors.NoSuchFrameException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.STALE_ELEMENT_REFERENCE:
raise errors.StaleElementException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.ELEMENT_NOT_VISIBLE:
raise errors.ElementNotVisibleException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.ELEMENT_NOT_ACCESSIBLE:
raise errors.ElementNotAccessibleException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.INVALID_ELEMENT_STATE:
raise errors.InvalidElementStateException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.UNKNOWN_ERROR:
raise errors.MarionetteException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.ELEMENT_IS_NOT_SELECTABLE:
raise errors.ElementNotSelectableException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.JAVASCRIPT_ERROR:
raise errors.JavascriptException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.XPATH_LOOKUP_ERROR:
raise errors.XPathLookupException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.TIMEOUT:
raise errors.TimeoutException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.NO_SUCH_WINDOW:
raise errors.NoSuchWindowException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.INVALID_COOKIE_DOMAIN:
raise errors.InvalidCookieDomainException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.UNABLE_TO_SET_COOKIE:
raise errors.UnableToSetCookieException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.NO_ALERT_OPEN:
raise errors.NoAlertPresentException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.SCRIPT_TIMEOUT:
raise errors.ScriptTimeoutException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.INVALID_SELECTOR \
or status == errors.ErrorCodes.INVALID_XPATH_SELECTOR \
or status == errors.ErrorCodes.INVALID_XPATH_SELECTOR_RETURN_TYPER:
raise errors.InvalidSelectorException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.MOVE_TARGET_OUT_OF_BOUNDS:
raise errors.MoveTargetOutOfBoundsException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.FRAME_SEND_NOT_INITIALIZED_ERROR:
raise errors.FrameSendNotInitializedError(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.FRAME_SEND_FAILURE_ERROR:
raise errors.FrameSendFailureError(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.UNSUPPORTED_OPERATION:
raise errors.UnsupportedOperationException(message=message, status=status, stacktrace=stacktrace)
elif status == errors.ErrorCodes.SESSION_NOT_CREATED:
raise errors.SessionNotCreatedException(message=message, status=status, stacktrace=stacktrace)
else:
raise errors.MarionetteException(message=message, status=status, stacktrace=stacktrace)
raise errors.lookup(status)(message, stacktrace=stacktrace)
def _reset_timeouts(self):
if self.timeout is not None:

Просмотреть файл

@ -124,6 +124,7 @@ this.WebDriverError = function(msg) {
Error.call(this, msg);
this.name = "WebDriverError";
this.message = msg;
this.status = "webdriver error";
this.code = 500; // overridden
};
WebDriverError.prototype = Object.create(Error.prototype);