mirror of
https://github.com/servo/servo.git
synced 2025-08-09 15:35:34 +01:00
Update web-platform-tests to revision d011702f368b88b3bae86e7a8fd2ddd22e18b33c
This commit is contained in:
parent
f9608022ca
commit
299ad0f9d0
573 changed files with 38776 additions and 14942 deletions
1
tests/wpt/web-platform-tests/tools/webdriver/.gitignore
vendored
Normal file
1
tests/wpt/web-platform-tests/tools/webdriver/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
webdriver.egg-info/
|
48
tests/wpt/web-platform-tests/tools/webdriver/README.md
Normal file
48
tests/wpt/web-platform-tests/tools/webdriver/README.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
# WebDriver client for Python
|
||||
|
||||
This package provides a WebDriver client compatible with
|
||||
the [W3C browser automation specification](https://w3c.github.io/webdriver/webdriver-spec.html).
|
||||
|
||||
The client is written with determining
|
||||
implementation compliance to the specification in mind,
|
||||
so that different remote end drivers
|
||||
can determine whether they meet the recognised standard.
|
||||
The client is used for the WebDriver specification tests
|
||||
in the [Web Platform Tests](https://github.com/w3c/web-platform-tests).
|
||||
|
||||
## Installation
|
||||
|
||||
To install the package individually
|
||||
in your virtualenv or system-wide::
|
||||
|
||||
% python setup.py install
|
||||
|
||||
Or if you want to contribute patches::
|
||||
|
||||
% python setup.py develop
|
||||
|
||||
If you are writing WebDriver specification tests for
|
||||
[WPT](https://github.com/w3c/web-platform-tests),
|
||||
there is no need to install the client manually
|
||||
as it is picked up as a submodule to
|
||||
[wpt-tools](https://github.com/w3c/wpt-tools)
|
||||
that is checked out in `./tools`.
|
||||
|
||||
## Usage
|
||||
|
||||
```py
|
||||
import webdriver
|
||||
|
||||
session = webdriver.Session("127.0.0.1", "4444")
|
||||
session.start()
|
||||
|
||||
session.url = "https://mozilla.org"
|
||||
print "The current URL is %s" % session.url
|
||||
|
||||
session.end()
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
This client has the benefit of only using standard library dependencies.
|
||||
No external PyPI dependencies are needed.
|
18
tests/wpt/web-platform-tests/tools/webdriver/setup.py
Normal file
18
tests/wpt/web-platform-tests/tools/webdriver/setup.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# 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/.
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name="webdriver",
|
||||
version="1.0",
|
||||
description="WebDriver client compatible with "
|
||||
"the W3C browser automation specification.",
|
||||
author="Mozilla Engineering Productivity",
|
||||
author_email="tools@lists.mozilla.org",
|
||||
license="MPL 2.0",
|
||||
packages=find_packages(),
|
||||
classifiers=["Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)",
|
||||
"Operating System :: OS Independent"])
|
|
@ -0,0 +1,31 @@
|
|||
# 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/.
|
||||
|
||||
from client import Cookies, Element, Find, Session, Timeouts, Window
|
||||
from error import (
|
||||
ElementNotSelectableException,
|
||||
ElementNotVisibleException,
|
||||
InvalidArgumentException,
|
||||
InvalidCookieDomainException,
|
||||
InvalidElementCoordinatesException,
|
||||
InvalidElementStateException,
|
||||
InvalidSelectorException,
|
||||
InvalidSessionIdException,
|
||||
JavascriptErrorException,
|
||||
MoveTargetOutOfBoundsException,
|
||||
NoSuchAlertException,
|
||||
NoSuchElementException,
|
||||
NoSuchFrameException,
|
||||
NoSuchWindowException,
|
||||
ScriptTimeoutException,
|
||||
SessionNotCreatedException,
|
||||
StaleElementReferenceException,
|
||||
TimeoutException,
|
||||
UnableToSetCookieException,
|
||||
UnexpectedAlertOpenException,
|
||||
UnknownCommandException,
|
||||
UnknownErrorException,
|
||||
UnknownMethodException,
|
||||
UnsupportedOperationException,
|
||||
WebDriverException)
|
|
@ -1,26 +0,0 @@
|
|||
"""WebDriver alert handling."""
|
||||
|
||||
class Alert(object):
|
||||
"""Class that provides access to the WebDriver alert handling functions."""
|
||||
|
||||
def __init__(self, driver):
|
||||
self._driver = driver
|
||||
|
||||
def _execute(self, method, path, name, body=None):
|
||||
return self._driver.execute(method, path, name, body)
|
||||
|
||||
def dismiss(self):
|
||||
"""Dismiss the alert."""
|
||||
self._execute('POST', '/dismiss_alert', 'dismiss')
|
||||
|
||||
def accept(self):
|
||||
"""Accept the alert."""
|
||||
self._execute('POST', '/accept_alert', 'accept')
|
||||
|
||||
def get_text(self):
|
||||
"""Get the text displayed in the alert."""
|
||||
return self._execute('GET', '/alert_text', 'getText')
|
||||
|
||||
def send_keys(self, keys):
|
||||
"""Type into the text input of the alert if available."""
|
||||
self._execute('POST', '/alert_text', 'sendKeys', { 'text': keys })
|
|
@ -1,30 +0,0 @@
|
|||
"""Definition of various capability-related constants."""
|
||||
|
||||
class Capability:
|
||||
"""Standard capability names."""
|
||||
BROWSER_NAME = 'browserName'
|
||||
BROWSER_VERSION = 'browserVersion'
|
||||
PAGE_LOADING_STRATEGY = 'pageLoadingStrategy'
|
||||
PLATFORM_NAME = 'platformName'
|
||||
PLATFORM_VERSION = 'platformVersion'
|
||||
SECURE_SSL = 'secureSsl'
|
||||
TAKES_SCREENSHOT = 'takesScreenshot'
|
||||
TAKE_ELEMENT_SCREENSHOT = 'takeElementScreenshot'
|
||||
TOUCH_ENABLED = 'touchEnabled'
|
||||
|
||||
class Platform:
|
||||
"""Standard OS names."""
|
||||
ANY = 'any'
|
||||
ANDROID = 'android'
|
||||
IOS = 'ios'
|
||||
LINUX = 'linux'
|
||||
MAC = 'mac'
|
||||
UNIX = 'unix'
|
||||
WINDOW = 'windows'
|
||||
|
||||
class PageLoadingStrategy:
|
||||
"""Standard page loading strategies."""
|
||||
CONSERVATIVE = 'conservative'
|
||||
NORMAL = 'normal'
|
||||
EAGER = 'eager'
|
||||
NONE = 'none'
|
409
tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py
Normal file
409
tests/wpt/web-platform-tests/tools/webdriver/webdriver/client.py
Normal file
|
@ -0,0 +1,409 @@
|
|||
# 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/.
|
||||
|
||||
import urlparse
|
||||
|
||||
import transport
|
||||
|
||||
|
||||
element_key = "element-6066-11e4-a52e-4f735466cecf"
|
||||
|
||||
|
||||
def command(func):
|
||||
def inner(self, *args, **kwargs):
|
||||
if hasattr(self, "session"):
|
||||
session = self.session
|
||||
else:
|
||||
session = self
|
||||
|
||||
if session.session_id is None:
|
||||
session.start()
|
||||
assert session.session_id != None
|
||||
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
inner.__name__ = func.__name__
|
||||
inner.__doc__ = func.__doc__
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
class Timeouts(object):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
self._script = 30
|
||||
self._load = 0
|
||||
self._implicit_wait = 0
|
||||
|
||||
def _set_timeouts(self, name, value):
|
||||
body = {"type": name,
|
||||
"ms": value * 1000}
|
||||
return self.session.send_command("POST", "timeouts", body)
|
||||
|
||||
@property
|
||||
def script(self):
|
||||
return self._script
|
||||
|
||||
@script.setter
|
||||
def script(self, value):
|
||||
self._set_timeouts("script", value)
|
||||
self._script = value
|
||||
|
||||
@property
|
||||
def load(self):
|
||||
return self._load
|
||||
|
||||
@load.setter
|
||||
def set_load(self, value):
|
||||
self._set_timeouts("page load", value)
|
||||
self._script = value
|
||||
|
||||
@property
|
||||
def implicit_wait(self):
|
||||
return self._implicit_wait
|
||||
|
||||
@implicit_wait.setter
|
||||
def implicit_wait(self, value):
|
||||
self._set_timeouts("implicit wait", value)
|
||||
self._implicit_wait = value
|
||||
|
||||
|
||||
class Window(object):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
@property
|
||||
@command
|
||||
def size(self):
|
||||
return self.session.send_command("GET", "window/size")
|
||||
|
||||
@size.setter
|
||||
@command
|
||||
def size(self, (height, width)):
|
||||
body = {"width": width,
|
||||
"height": height}
|
||||
|
||||
return self.session.send_command("POST", "window/size", body)
|
||||
|
||||
@property
|
||||
@command
|
||||
def maximize(self):
|
||||
return self.session.send_command("POST", "window/maximize")
|
||||
|
||||
|
||||
class Find(object):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
@command
|
||||
def css(self, selector, all=True):
|
||||
return self._find_element("css selector", selector, all)
|
||||
|
||||
def _find_element(self, strategy, selector, all):
|
||||
route = "elements" if all else "element"
|
||||
|
||||
body = {"using": strategy,
|
||||
"value": selector}
|
||||
|
||||
data = self.session.send_command("POST", route, body, key="value")
|
||||
|
||||
if all:
|
||||
rv = [self.session._element(item) for item in data]
|
||||
else:
|
||||
rv = self.session._element(data)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
class Cookies(object):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
def __getitem__(self, name):
|
||||
self.session.send_command("GET", "cookie/%s" % name, {}, key="value")
|
||||
|
||||
def __setitem__(self, name, value):
|
||||
cookie = {"name": name,
|
||||
"value": None}
|
||||
|
||||
if isinstance(name, (str, unicode)):
|
||||
cookie["value"] = value
|
||||
elif hasattr(value, "value"):
|
||||
cookie["value"] = value.value
|
||||
self.session.send_command("POST", "cookie/%s" % name, {}, key="value")
|
||||
|
||||
|
||||
class Session(object):
|
||||
def __init__(self, host, port, url_prefix="", desired_capabilities=None,
|
||||
required_capabilities=None, timeout=60, extension=None):
|
||||
self.transport = transport.HTTPWireProtocol(
|
||||
host, port, url_prefix, timeout=timeout)
|
||||
self.desired_capabilities = desired_capabilities
|
||||
self.required_capabilities = required_capabilities
|
||||
self.session_id = None
|
||||
self.timeouts = None
|
||||
self.window = None
|
||||
self.find = None
|
||||
self._element_cache = {}
|
||||
self.extension = None
|
||||
self.extension_cls = extension
|
||||
|
||||
def start(self):
|
||||
if self.session_id is not None:
|
||||
return
|
||||
|
||||
body = {}
|
||||
|
||||
caps = {}
|
||||
if self.desired_capabilities is not None:
|
||||
caps["desiredCapabilities"] = self.desired_capabilities
|
||||
if self.required_capabilities is not None:
|
||||
caps["requiredCapabilities"] = self.required_capabilities
|
||||
body["capabilities"] = caps
|
||||
|
||||
resp = self.transport.send("POST", "session", body=body)
|
||||
self.session_id = resp["sessionId"]
|
||||
|
||||
self.timeouts = Timeouts(self)
|
||||
self.window = Window(self)
|
||||
self.find = Find(self)
|
||||
if self.extension_cls:
|
||||
self.extension = self.extension_cls(self)
|
||||
|
||||
return resp["value"]
|
||||
|
||||
def end(self):
|
||||
if self.session_id is None:
|
||||
return
|
||||
|
||||
url = "session/%s" % self.session_id
|
||||
self.transport.send("DELETE", url)
|
||||
|
||||
self.session_id = None
|
||||
self.timeouts = None
|
||||
self.window = None
|
||||
self.find = None
|
||||
self.extension = None
|
||||
self.transport.disconnect()
|
||||
|
||||
def __enter__(self):
|
||||
resp = self.start()
|
||||
if resp.error:
|
||||
raise Exception(resp)
|
||||
return self
|
||||
|
||||
def __exit__(self, *args, **kwargs):
|
||||
resp = self.end()
|
||||
if resp.error:
|
||||
raise Exception(resp)
|
||||
|
||||
def send_command(self, method, url, body=None, key=None):
|
||||
url = urlparse.urljoin("session/%s/" % self.session_id, url)
|
||||
return self.transport.send(method, url, body, key=key)
|
||||
|
||||
@property
|
||||
@command
|
||||
def url(self):
|
||||
return self.send_command("GET", "url", key="value")
|
||||
|
||||
@url.setter
|
||||
@command
|
||||
def url(self, url):
|
||||
if urlparse.urlsplit(url).netloc is None:
|
||||
return self.url(url)
|
||||
body = {"url": url}
|
||||
return self.send_command("POST", "url", body)
|
||||
|
||||
@command
|
||||
def back(self):
|
||||
return self.send_command("POST", "back")
|
||||
|
||||
@command
|
||||
def forward(self):
|
||||
return self.send_command("POST", "forward")
|
||||
|
||||
@command
|
||||
def refresh(self):
|
||||
return self.send_command("POST", "refresh")
|
||||
|
||||
@property
|
||||
@command
|
||||
def title(self):
|
||||
return self.send_command("GET", "title", key="value")
|
||||
|
||||
@property
|
||||
@command
|
||||
def window_handle(self):
|
||||
return self.send_command("GET", "window_handle", key="value")
|
||||
|
||||
@window_handle.setter
|
||||
@command
|
||||
def window_handle(self, handle):
|
||||
body = {"handle": handle}
|
||||
return self.send_command("POST", "window", body=body)
|
||||
|
||||
def switch_frame(self, frame):
|
||||
if frame == "parent":
|
||||
url = "frame/parent"
|
||||
body = None
|
||||
else:
|
||||
url = "frame"
|
||||
if isinstance(frame, Element):
|
||||
body = {"id": frame.json()}
|
||||
else:
|
||||
body = {"id": frame}
|
||||
|
||||
return self.send_command("POST", url, body)
|
||||
|
||||
@command
|
||||
def close(self):
|
||||
return self.send_command("DELETE", "window_handle")
|
||||
|
||||
@property
|
||||
@command
|
||||
def handles(self):
|
||||
return self.send_command("GET", "window_handles", key="value")
|
||||
|
||||
@property
|
||||
@command
|
||||
def active_element(self):
|
||||
data = self.send_command("GET", "element/active", key="value")
|
||||
if data is not None:
|
||||
return self._element(data)
|
||||
|
||||
def _element(self, data):
|
||||
elem_id = data[element_key]
|
||||
assert elem_id
|
||||
if elem_id in self._element_cache:
|
||||
return self._element_cache[elem_id]
|
||||
return Element(self, elem_id)
|
||||
|
||||
@command
|
||||
def cookies(self, name=None):
|
||||
if name is None:
|
||||
url = "cookie"
|
||||
else:
|
||||
url = "cookie/%s" % name
|
||||
return self.send_command("GET", url, {}, key="value")
|
||||
|
||||
@command
|
||||
def set_cookie(self, name, value, path=None, domain=None, secure=None, expiry=None):
|
||||
body = {"name": name,
|
||||
"value": value}
|
||||
if path is not None:
|
||||
body["path"] = path
|
||||
if domain is not None:
|
||||
body["domain"] = domain
|
||||
if secure is not None:
|
||||
body["secure"] = secure
|
||||
if expiry is not None:
|
||||
body["expiry"] = expiry
|
||||
self.send_command("POST", "cookie", {"cookie": body})
|
||||
|
||||
def delete_cookie(self, name=None):
|
||||
if name is None:
|
||||
url = "cookie"
|
||||
else:
|
||||
url = "cookie/%s" % name
|
||||
self.send_command("DELETE", url, {}, key="value")
|
||||
|
||||
#[...]
|
||||
|
||||
@command
|
||||
def execute_script(self, script, args=None):
|
||||
if args is None:
|
||||
args = []
|
||||
|
||||
body = {
|
||||
"script": script,
|
||||
"args": args
|
||||
}
|
||||
return self.send_command("POST", "execute", body, key="value")
|
||||
|
||||
@command
|
||||
def execute_async_script(self, script, args=None):
|
||||
if args is None:
|
||||
args = []
|
||||
|
||||
body = {
|
||||
"script": script,
|
||||
"args": args
|
||||
}
|
||||
return self.send_command("POST", "execute_async", body, key="value")
|
||||
|
||||
#[...]
|
||||
|
||||
@command
|
||||
def screenshot(self):
|
||||
return self.send_command("GET", "screenshot", key="value")
|
||||
|
||||
|
||||
class Element(object):
|
||||
def __init__(self, session, id):
|
||||
self.session = session
|
||||
self.id = id
|
||||
assert id not in self.session._element_cache
|
||||
self.session._element_cache[self.id] = self
|
||||
|
||||
def json(self):
|
||||
return {element_key: self.id}
|
||||
|
||||
@property
|
||||
def session_id(self):
|
||||
return self.session.session_id
|
||||
|
||||
def url(self, suffix):
|
||||
return "element/%s/%s" % (self.id, suffix)
|
||||
|
||||
@command
|
||||
def find_element(self, strategy, selector):
|
||||
body = {"using": strategy,
|
||||
"value": selector}
|
||||
|
||||
elem = self.session.send_command("POST", self.url("element"), body, key="value")
|
||||
return self.session.element(elem)
|
||||
|
||||
@command
|
||||
def click(self):
|
||||
self.session.send_command("POST", self.url("click"), {})
|
||||
|
||||
@command
|
||||
def tap(self):
|
||||
self.session.send_command("POST", self.url("tap"), {})
|
||||
|
||||
@command
|
||||
def clear(self):
|
||||
self.session.send_command("POST", self.url("clear"), {})
|
||||
|
||||
@command
|
||||
def send_keys(self, keys):
|
||||
if isinstance(keys, (str, unicode)):
|
||||
keys = [char for char in keys]
|
||||
|
||||
body = {"value": keys}
|
||||
|
||||
return self.session.send_command("POST", self.url("value"), body)
|
||||
|
||||
@property
|
||||
@command
|
||||
def text(self):
|
||||
return self.session.send_command("GET", self.url("text"))
|
||||
|
||||
@property
|
||||
@command
|
||||
def name(self):
|
||||
return self.session.send_command("GET", self.url("name"))
|
||||
|
||||
@command
|
||||
def style(self, property_name):
|
||||
return self.session.send_command("GET", self.url("css/%s" % property_name))
|
||||
|
||||
@property
|
||||
@command
|
||||
def rect(self):
|
||||
return self.session.send_command("GET", self.url("rect"))
|
||||
|
||||
@command
|
||||
def attribute(self, name):
|
||||
return self.session.send_command("GET", self.url("attribute/%s" % name))
|
|
@ -1,111 +0,0 @@
|
|||
"""Dispatches requests to remote WebDriver endpoint."""
|
||||
|
||||
import exceptions
|
||||
|
||||
import httplib
|
||||
import json
|
||||
import urlparse
|
||||
import webelement
|
||||
|
||||
class CommandExecutor(object):
|
||||
"""Dispatches requests to remote WebDriver endpoint."""
|
||||
|
||||
_HEADERS = {
|
||||
"User-Agent": "Python WebDriver Local End",
|
||||
"Content-Type": "application/json;charset=\"UTF-8\"",
|
||||
"Accept": "application/json",
|
||||
"Accept-Charset": "utf-8",
|
||||
"Accept-Encoding": "identity",
|
||||
"Connection": "close",
|
||||
}
|
||||
|
||||
def __init__(self, url, mode='strict'):
|
||||
self._parsed_url = urlparse.urlparse(url)
|
||||
self._conn = httplib.HTTPConnection(self._parsed_url.hostname,
|
||||
self._parsed_url.port)
|
||||
self._mode = mode
|
||||
|
||||
def execute(self,
|
||||
method,
|
||||
path,
|
||||
session_id,
|
||||
name,
|
||||
parameters=None,
|
||||
object_hook=None):
|
||||
"""Execute a command against the WebDriver endpoint.
|
||||
|
||||
Arguments:
|
||||
method -- one of GET, POST, DELETE
|
||||
path -- the path of the url endpoint (needs to include
|
||||
session/<sessionId> if needed)
|
||||
session_id -- the sessionId to include in the JSON body
|
||||
name -- name of the command that is being executed to include in
|
||||
the JSON body
|
||||
parameters -- the JSON body to send with the command. Only used if
|
||||
method is POST
|
||||
object_hook -- function used by json.loads to properly deserialize
|
||||
objects in the request
|
||||
"""
|
||||
if self._mode == 'strict':
|
||||
return self._execute_strict(
|
||||
method, path, session_id, name, parameters, object_hook)
|
||||
elif self._mode == 'compatibility':
|
||||
return self._execute_compatibility(
|
||||
method, path, session_id, name, parameters, object_hook)
|
||||
else:
|
||||
raise Exception("Unknown mode: " + self._mode)
|
||||
|
||||
def _execute_compatibility(self,
|
||||
method,
|
||||
path,
|
||||
session_id,
|
||||
name,
|
||||
parameters,
|
||||
object_hook):
|
||||
body = {'sessionId': session_id, 'name': name }
|
||||
if parameters:
|
||||
body.update(parameters)
|
||||
|
||||
self._conn.request(
|
||||
method,
|
||||
self._parsed_url.path + path,
|
||||
json.dumps(body, default = self._json_encode).encode('utf-8'),
|
||||
self._HEADERS)
|
||||
resp = self._conn.getresponse()
|
||||
data = resp.read().decode('utf-8')
|
||||
if data:
|
||||
data = json.loads(data, object_hook = object_hook)
|
||||
if data['status'] != 0:
|
||||
raise exceptions.create_webdriver_exception_compatibility(
|
||||
data['status'], data['value']['message'])
|
||||
return data
|
||||
if resp.status < 200 or resp.status > 299:
|
||||
raise exceptions.create_webdriver_exception_compatibility(
|
||||
resp.status, resp.reason)
|
||||
|
||||
def _execute_strict(self,
|
||||
method,
|
||||
path,
|
||||
session_id,
|
||||
name,
|
||||
parameters,
|
||||
object_hook):
|
||||
body = {
|
||||
'sessionId': session_id,
|
||||
'name': name,
|
||||
'parameters': parameters }
|
||||
self._conn.request(
|
||||
method,
|
||||
self._parsed_url.path + path,
|
||||
json.dumps(body, default = self._json_encode).encode('utf-8'),
|
||||
self._HEADERS)
|
||||
resp = self._conn.getresponse()
|
||||
data = json.loads(
|
||||
resp.read().decode('utf-8'), object_hook = object_hook)
|
||||
if data['status'] != 'success':
|
||||
raise exceptions.create_webdriver_exception_strict(
|
||||
data['status'], data['value'])
|
||||
return data
|
||||
|
||||
def _json_encode(self, obj):
|
||||
return obj.to_json()
|
|
@ -1,199 +0,0 @@
|
|||
"""Entry point for WebDriver."""
|
||||
|
||||
import alert
|
||||
import command
|
||||
import searchcontext
|
||||
import webelement
|
||||
|
||||
import base64
|
||||
|
||||
|
||||
class WebDriver(searchcontext.SearchContext):
|
||||
"""Controls a web browser."""
|
||||
|
||||
def __init__(self, host, required, desired, mode='strict'):
|
||||
args = { 'desiredCapabilities': desired }
|
||||
if required:
|
||||
args['requiredCapabilities'] = required
|
||||
|
||||
self._executor = command.CommandExecutor(host, mode)
|
||||
|
||||
resp = self._executor.execute(
|
||||
'POST', '/session', None, 'newSession', args)
|
||||
self.capabilities = resp['value']
|
||||
self._session_id = resp['sessionId']
|
||||
self.mode = mode
|
||||
|
||||
def execute(self, method, path, name, parameters= None):
|
||||
"""Execute a command against the current WebDriver session."""
|
||||
data = self._executor.execute(
|
||||
method,
|
||||
'/session/' + self._session_id + path,
|
||||
self._session_id,
|
||||
name,
|
||||
parameters,
|
||||
self._object_hook)
|
||||
if data:
|
||||
return data['value']
|
||||
|
||||
def get(self, url):
|
||||
"""Navigate to url."""
|
||||
self.execute('POST', '/url', 'get', { 'url': url })
|
||||
|
||||
def get_current_url(self):
|
||||
"""Get the current value of the location bar."""
|
||||
return self.execute('GET', '/url', 'getCurrentUrl')
|
||||
|
||||
def go_back(self):
|
||||
"""Hit the browser back button."""
|
||||
self.execute('POST', '/back', 'goBack')
|
||||
|
||||
def go_forward(self):
|
||||
"""Hit the browser forward button."""
|
||||
self.execute('POST', '/forward', 'goForward')
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh the current page in the browser."""
|
||||
self.execute('POST', '/refresh', 'refresh')
|
||||
|
||||
def quit(self):
|
||||
"""Shutdown the current WebDriver session."""
|
||||
self.execute('DELETE', '', 'quit')
|
||||
|
||||
def get_window_handle(self):
|
||||
"""Get the handle for the browser window/tab currently accepting
|
||||
commands.
|
||||
"""
|
||||
return self.execute('GET', '/window_handle', 'getWindowHandle')
|
||||
|
||||
def get_window_handles(self):
|
||||
"""Get handles for all open windows/tabs."""
|
||||
return self.execute('GET', '/window_handles', 'getWindowHandles')
|
||||
|
||||
def close(self):
|
||||
"""Close the current tab or window.
|
||||
|
||||
If this is the last tab or window, then this is the same as
|
||||
calling quit.
|
||||
"""
|
||||
self.execute('DELETE', '/window', 'close')
|
||||
|
||||
def maximize_window(self):
|
||||
"""Maximize the current window."""
|
||||
return self._window_command('POST', '/maximize', 'maximize')
|
||||
|
||||
def get_window_size(self):
|
||||
"""Get the dimensions of the current window."""
|
||||
result = self._window_command('GET', '/size', 'getWindowSize')
|
||||
return {'height': result['height'], 'width': result['width']}
|
||||
|
||||
def set_window_size(self, height, width):
|
||||
"""Set the size of the current window."""
|
||||
self._window_command(
|
||||
'POST',
|
||||
'/size',
|
||||
'setWindowSize',
|
||||
{ 'height': height, 'width': width})
|
||||
|
||||
def fullscreen_window(self):
|
||||
"""Make the current window fullscreen."""
|
||||
pass # implement when end point is defined
|
||||
|
||||
def switch_to_window(self, name):
|
||||
"""Switch to the window with the given handle or name."""
|
||||
self.execute('POST', '/window', 'switchToWindow', { 'name': name })
|
||||
|
||||
def switch_to_frame(self, id):
|
||||
"""Switch to a frame.
|
||||
|
||||
id can be either a WebElement or an integer.
|
||||
"""
|
||||
self.execute('POST', '/frame', 'switchToFrame', { 'id': id})
|
||||
|
||||
def switch_to_parent_frame(self):
|
||||
"""Move to the browsing context containing the currently selected frame.
|
||||
|
||||
If in the top-level browsing context, this is a no-op.
|
||||
"""
|
||||
self.execute('POST', '/frame/parent', 'switchToParentFrame')
|
||||
|
||||
def switch_to_alert(self):
|
||||
"""Return an Alert object to interact with a modal dialog."""
|
||||
alert_ = alert.Alert(self)
|
||||
alert_.get_text()
|
||||
return alert_
|
||||
|
||||
def execute_script(self, script, args=[]):
|
||||
"""Execute a Javascript script in the current browsing context."""
|
||||
return self.execute(
|
||||
'POST',
|
||||
'/execute',
|
||||
'executeScript',
|
||||
{ 'script': script, 'args': args })
|
||||
|
||||
def execute_script_async(self, script, args=[]):
|
||||
"""Execute a Javascript script in the current browsing context."""
|
||||
return self.execute(
|
||||
'POST',
|
||||
'/execute_async',
|
||||
'executeScriptAsync',
|
||||
{ 'script': script, 'args': args })
|
||||
|
||||
def take_screenshot(self, element=None):
|
||||
"""Take a screenshot.
|
||||
|
||||
If element is not provided, the screenshot should be of the
|
||||
current page, otherwise the screenshot should be of the given element.
|
||||
"""
|
||||
if self.mode == 'strict':
|
||||
pass # implement when endpoint is defined
|
||||
elif self.mode == 'compatibility':
|
||||
if element:
|
||||
pass # element screenshots are unsupported in compatibility
|
||||
else:
|
||||
return base64.standard_b64decode(
|
||||
self.execute('GET', '/screenshot', 'takeScreenshot'))
|
||||
|
||||
def add_cookie(self, cookie):
|
||||
"""Add a cookie to the browser."""
|
||||
self.execute('POST', '/cookie', 'addCookie', { 'cookie': cookie })
|
||||
|
||||
def get_cookie(self, name = None):
|
||||
"""Get the cookies accessible from the current page."""
|
||||
if self.mode == 'compatibility':
|
||||
cookies = self.execute('GET', '/cookie', 'getCookie')
|
||||
if name:
|
||||
cookies_ = []
|
||||
for cookie in cookies:
|
||||
if cookie['name'] == name:
|
||||
cookies_.append(cookie)
|
||||
return cookies_
|
||||
return cookies
|
||||
elif self.mode == 'strict':
|
||||
pass # implement when wire protocol for this has been defined
|
||||
|
||||
def set_implicit_timeout(self, ms):
|
||||
self._set_timeout('implicit', ms)
|
||||
|
||||
def set_page_load_timeout(self, ms):
|
||||
self._set_timeout('page load', ms)
|
||||
|
||||
def set_script_timeout(self, ms):
|
||||
self._set_timeout('script', ms)
|
||||
|
||||
def _set_timeout(self, type, ms):
|
||||
params = { 'type': type, 'ms': ms }
|
||||
self.execute('POST', '/timeouts', 'timeouts', params)
|
||||
|
||||
def _window_command(self, method, path, name, parameters = None):
|
||||
if self.mode == 'compatibility':
|
||||
return self.execute(
|
||||
method, '/window/current' + path, name, parameters)
|
||||
elif self.mode == 'strict':
|
||||
pass # implement this when end-points are defined in doc
|
||||
|
||||
def _object_hook(self, obj):
|
||||
if 'ELEMENT' in obj:
|
||||
return webelement.WebElement(self, obj['ELEMENT'])
|
||||
return obj
|
||||
|
144
tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py
Normal file
144
tests/wpt/web-platform-tests/tools/webdriver/webdriver/error.py
Normal file
|
@ -0,0 +1,144 @@
|
|||
# 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/.
|
||||
|
||||
|
||||
import collections
|
||||
|
||||
|
||||
class WebDriverException(Exception):
|
||||
http_status = None
|
||||
status_code = None
|
||||
|
||||
|
||||
class ElementNotSelectableException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "element not selectable"
|
||||
|
||||
|
||||
class ElementNotVisibleException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "element not visible"
|
||||
|
||||
|
||||
class InvalidArgumentException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "invalid argument"
|
||||
|
||||
|
||||
class InvalidCookieDomainException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "invalid cookie domain"
|
||||
|
||||
|
||||
class InvalidElementCoordinatesException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "invalid element coordinates"
|
||||
|
||||
|
||||
class InvalidElementStateException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "invalid cookie domain"
|
||||
|
||||
|
||||
class InvalidSelectorException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "invalid selector"
|
||||
|
||||
|
||||
class InvalidSessionIdException(WebDriverException):
|
||||
http_status = 404
|
||||
status_code = "invalid session id"
|
||||
|
||||
|
||||
class JavascriptErrorException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "javascript error"
|
||||
|
||||
|
||||
class MoveTargetOutOfBoundsException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "move target out of bounds"
|
||||
|
||||
|
||||
class NoSuchAlertException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "no such alert"
|
||||
|
||||
|
||||
class NoSuchElementException(WebDriverException):
|
||||
http_status = 404
|
||||
status_code = "no such element"
|
||||
|
||||
|
||||
class NoSuchFrameException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "no such frame"
|
||||
|
||||
|
||||
class NoSuchWindowException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "no such window"
|
||||
|
||||
|
||||
class ScriptTimeoutException(WebDriverException):
|
||||
http_status = 408
|
||||
status_code = "script timeout"
|
||||
|
||||
|
||||
class SessionNotCreatedException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "session not created"
|
||||
|
||||
|
||||
class StaleElementReferenceException(WebDriverException):
|
||||
http_status = 400
|
||||
status_code = "stale element reference"
|
||||
|
||||
|
||||
class TimeoutException(WebDriverException):
|
||||
http_status = 408
|
||||
status_code = "timeout"
|
||||
|
||||
|
||||
class UnableToSetCookieException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "unable to set cookie"
|
||||
|
||||
|
||||
class UnexpectedAlertOpenException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "unexpected alert open"
|
||||
|
||||
|
||||
class UnknownErrorException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "unknown error"
|
||||
|
||||
|
||||
class UnknownCommandException(WebDriverException):
|
||||
http_status = 404
|
||||
status_code = "unknown command"
|
||||
|
||||
|
||||
class UnknownMethodException(WebDriverException):
|
||||
http_status = 405
|
||||
status_code = "unknown method"
|
||||
|
||||
|
||||
class UnsupportedOperationException(WebDriverException):
|
||||
http_status = 500
|
||||
status_code = "unsupported operation"
|
||||
|
||||
|
||||
def get(status_code):
|
||||
"""Gets exception from `status_code`, falling back to
|
||||
``WebDriverException`` if it is not found.
|
||||
"""
|
||||
return _errors.get(status_code, WebDriverException)
|
||||
|
||||
|
||||
_errors = collections.defaultdict()
|
||||
for item in locals().values():
|
||||
if type(item) == type and issubclass(item, WebDriverException):
|
||||
_errors[item.status_code] = item
|
|
@ -1,166 +0,0 @@
|
|||
"""Definition of WebDriverException classes."""
|
||||
|
||||
def create_webdriver_exception_strict(status_code, message):
|
||||
"""Create the appropriate WebDriverException given the status_code."""
|
||||
if status_code in _exceptions_strict:
|
||||
return _exceptions_strict[status_code](message)
|
||||
return UnknownStatusCodeException("[%s] %s" % (status_code, message))
|
||||
|
||||
def create_webdriver_exception_compatibility(status_code, message):
|
||||
"""Create the appropriate WebDriverException given the status_code."""
|
||||
if status_code in _exceptions_compatibility:
|
||||
return _exceptions_compatibility[status_code](message)
|
||||
return UnknownStatusCodeException("[%s] %s" % (status_code, message))
|
||||
|
||||
class WebDriverException(Exception):
|
||||
"""Base class for all WebDriverExceptions."""
|
||||
|
||||
class UnableToSetCookieException(WebDriverException):
|
||||
"""A request to set a cookie's value could not be satisfied."""
|
||||
|
||||
class InvalidElementStateException(WebDriverException):
|
||||
"""An element command could not be completed because the element is
|
||||
in an invalid state (e.g. attempting to click an element that is no
|
||||
longer attached to the DOM).
|
||||
"""
|
||||
|
||||
class NoSuchElementException(WebDriverException):
|
||||
"""An element could not be located on the page using the given
|
||||
search parameters.
|
||||
"""
|
||||
|
||||
class TimeoutException(WebDriverException):
|
||||
"""An operation did not complete before its timeout expired."""
|
||||
|
||||
class ElementNotSelectableException(InvalidElementStateException):
|
||||
"""An attempt was made to select an element that cannot be selected."""
|
||||
|
||||
class ElementNotVisibleException(InvalidElementStateException):
|
||||
"""An element command could not be completed because the element is
|
||||
not visible on the page.
|
||||
"""
|
||||
|
||||
class ImeEngineActivationFailedException(WebDriverException):
|
||||
"""An IME engine could not be started."""
|
||||
|
||||
class ImeNotAvailableException(ImeEngineActivationFailedException):
|
||||
"""IME was not available."""
|
||||
|
||||
class InvalidCookieDomainException(UnableToSetCookieException):
|
||||
"""An illegal attempt was made to set a cookie under a different
|
||||
domain than the current page.
|
||||
"""
|
||||
|
||||
class InvalidElementCoordinatesException(WebDriverException):
|
||||
"""The coordinates provided to an interactions operation are invalid."""
|
||||
|
||||
class InvalidSelectorException(NoSuchElementException):
|
||||
"""Argument was an invalid selector (e.g. XPath/CSS)."""
|
||||
|
||||
class JavascriptErrorException(WebDriverException):
|
||||
"""An error occurred while executing user supplied JavaScript."""
|
||||
|
||||
class MoveTargetOutOfBoundsException(InvalidElementStateException):
|
||||
"""The target for mouse interaction is not in the browser's viewport
|
||||
and cannot be brought into that viewport.
|
||||
"""
|
||||
|
||||
class NoSuchAlertException(WebDriverException):
|
||||
"""An attempt was made to operate on a modal dialog when one was not open."""
|
||||
|
||||
class NoSuchFrameException(WebDriverException):
|
||||
"""A request to switch to a frame could not be satisfied because
|
||||
the frame could not be found."""
|
||||
|
||||
class NoSuchWindowException(WebDriverException):
|
||||
"""A request to switch to a different window could not be satisfied
|
||||
because the window could not be found.
|
||||
"""
|
||||
|
||||
class ScriptTimeoutException(TimeoutException):
|
||||
"""A script did not complete before its timeout expired."""
|
||||
|
||||
class SessionNotCreatedException(WebDriverException):
|
||||
"""A new session could not be created."""
|
||||
|
||||
class StaleElementReferenceException(InvalidElementStateException):
|
||||
"""An element command failed because the referenced element is no
|
||||
longer attached to the DOM.
|
||||
"""
|
||||
|
||||
class UnexpectedAlertOpenException(WebDriverException):
|
||||
"""A modal dialog was open, blocking this operation."""
|
||||
|
||||
class UnknownCommandException(WebDriverException):
|
||||
"""A command could not be executed because the remote end is not
|
||||
aware of it.
|
||||
"""
|
||||
|
||||
class UnknownErrorException(WebDriverException):
|
||||
"""An unknown error occurred in the remote end while processing
|
||||
the command.
|
||||
"""
|
||||
|
||||
class UnsupportedOperationException(WebDriverException):
|
||||
"""Indicates that a command that should have executed properly
|
||||
cannot be supported for some reason.
|
||||
"""
|
||||
|
||||
class UnknownStatusCodeException(WebDriverException):
|
||||
"""Exception for all other status codes."""
|
||||
|
||||
_exceptions_strict = {
|
||||
"element not selectable": ElementNotSelectableException,
|
||||
"element not visible": ElementNotVisibleException,
|
||||
"ime engine activation failed": ImeEngineActivationFailedException,
|
||||
"ime not available": ImeNotAvailableException,
|
||||
"invalid cookie domain": InvalidCookieDomainException,
|
||||
"invalid element coordinates": InvalidElementCoordinatesException,
|
||||
"invalid element state": InvalidElementStateException,
|
||||
"invalid selector": InvalidSelectorException,
|
||||
"javascript error": JavascriptErrorException,
|
||||
"move target out of bounds": MoveTargetOutOfBoundsException,
|
||||
"no such alert": NoSuchAlertException,
|
||||
"no such element": NoSuchElementException,
|
||||
"no such frame": NoSuchFrameException,
|
||||
"no such window": NoSuchWindowException,
|
||||
"script timeout": ScriptTimeoutException,
|
||||
"session not created": SessionNotCreatedException,
|
||||
"stale element reference": StaleElementReferenceException,
|
||||
"success": None,
|
||||
"timeout": TimeoutException,
|
||||
"unable to set cookie": UnableToSetCookieException,
|
||||
"unexpected alert open": UnexpectedAlertOpenException,
|
||||
"unknown command": UnknownCommandException,
|
||||
"unknown error": UnknownErrorException,
|
||||
"unsupported operation": UnsupportedOperationException,
|
||||
}
|
||||
|
||||
_exceptions_compatibility = {
|
||||
15: ElementNotSelectableException,
|
||||
11: ElementNotVisibleException,
|
||||
31: ImeEngineActivationFailedException,
|
||||
30: ImeNotAvailableException,
|
||||
24: InvalidCookieDomainException,
|
||||
29: InvalidElementCoordinatesException,
|
||||
12: InvalidElementStateException,
|
||||
19: InvalidSelectorException,
|
||||
32: InvalidSelectorException,
|
||||
17: JavascriptErrorException,
|
||||
34: MoveTargetOutOfBoundsException,
|
||||
27: NoSuchAlertException,
|
||||
7: NoSuchElementException,
|
||||
8: NoSuchFrameException,
|
||||
23: NoSuchWindowException,
|
||||
28: ScriptTimeoutException,
|
||||
6: SessionNotCreatedException,
|
||||
33: SessionNotCreatedException,
|
||||
10: StaleElementReferenceException,
|
||||
0: None, # success
|
||||
21: TimeoutException,
|
||||
25: UnableToSetCookieException,
|
||||
26: UnexpectedAlertOpenException,
|
||||
9: UnknownCommandException,
|
||||
13: UnknownErrorException,
|
||||
# "unsupported operation": UnsupportedOperationException
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
"""Constants for special keys."""
|
||||
|
||||
class Keys:
|
||||
"""Constants for special keys."""
|
||||
NULL = '\uE000'
|
||||
CANCEL = '\uE001'
|
||||
HELP = '\uE002'
|
||||
BACK_SPACE = '\uE003'
|
||||
TAB = '\uE004'
|
||||
CLEAR = '\uE005'
|
||||
RETURN = '\uE006'
|
||||
ENTER = '\uE007'
|
||||
SHIFT = '\uE008'
|
||||
LEFT_SHIFT = '\uE008'
|
||||
CONTROL = '\uE009'
|
||||
LEFT_CONTROL = '\uE009'
|
||||
ALT = '\uE00A'
|
||||
LEFT_ALT = '\uE00A'
|
||||
PAUSE = '\uE00B'
|
||||
ESCAPE = '\uE00C'
|
||||
SPACE = '\uE00D'
|
||||
PAGE_UP = '\uE00E'
|
||||
PAGE_DOWN = '\uE00F'
|
||||
END = '\uE010'
|
||||
HOME = '\uE011'
|
||||
LEFT = '\uE012'
|
||||
ARROW_LEFT = '\uE012'
|
||||
UP = '\uE013'
|
||||
ARROW_UP = '\uE013'
|
||||
RIGHT = '\uE014'
|
||||
ARROW_RIGHT = '\uE014'
|
||||
DOWN = '\uE015'
|
||||
ARROW_DOWN = '\uE015'
|
||||
INSERT = '\uE016'
|
||||
DELETE = '\uE017'
|
||||
SEMICOLON = '\uE018'
|
||||
EQUALS = '\uE019'
|
||||
NUMPAD0 = '\uE01A'
|
||||
NUMPAD1 = '\uE01B'
|
||||
NUMPAD2 = '\uE01C'
|
||||
NUMPAD3 = '\uE01D'
|
||||
NUMPAD4 = '\uE01E'
|
||||
NUMPAD5 = '\uE01F'
|
||||
NUMPAD6 = '\uE020'
|
||||
NUMPAD7 = '\uE021'
|
||||
NUMPAD8 = '\uE022'
|
||||
NUMPAD9 = '\uE023'
|
||||
MULTIPLY = '\uE024'
|
||||
ADD = '\uE025'
|
||||
SEPARATOR = '\uE026'
|
||||
SUBTRACT = '\uE027'
|
||||
DECIMAL = '\uE028'
|
||||
DIVIDE = '\uE029'
|
||||
F1 = '\uE031'
|
||||
F2 = '\uE032'
|
||||
F3 = '\uE033'
|
||||
F4 = '\uE034'
|
||||
F5 = '\uE035'
|
||||
F6 = '\uE036'
|
||||
F7 = '\uE037'
|
||||
F8 = '\uE038'
|
||||
F9 = '\uE039'
|
||||
F10 = '\uE03A'
|
||||
F11 = '\uE03B'
|
||||
F12 = '\uE03C'
|
||||
META = '\uE03D'
|
||||
COMMAND = '\uE03D'
|
||||
ZENKAKU_HANKAKU = '\uE040'
|
|
@ -1,54 +0,0 @@
|
|||
"""WebDriver element location functionality."""
|
||||
|
||||
class SearchContext(object):
|
||||
"""Abstract class that provides the core element location functionality."""
|
||||
|
||||
def find_element_by_css(self, selector):
|
||||
"""Find the first element matching a css selector."""
|
||||
return self._find_element('css selector', selector)
|
||||
|
||||
def find_elements_by_css(self, selector):
|
||||
"""Find all elements matching a css selector."""
|
||||
return self._find_elements('css selector', selector)
|
||||
|
||||
def find_element_by_link_text(self, text):
|
||||
"""Find the first link with the given text."""
|
||||
return self._find_element('link text', text)
|
||||
|
||||
def find_elements_by_link_text(self, text):
|
||||
"""Find all links with the given text."""
|
||||
return self._find_elements('link text', text)
|
||||
|
||||
def find_element_by_partial_link_text(self, text):
|
||||
"""Find the first link containing the given text."""
|
||||
return self._find_element('partial link text', text)
|
||||
|
||||
def find_elements_by_partial_link_text(self, text):
|
||||
"""Find all links containing the given text."""
|
||||
return self._find_elements('partial link text', text)
|
||||
|
||||
def find_element_by_xpath(self, xpath):
|
||||
"""Find the first element matching the xpath."""
|
||||
return self._find_element('xpath', xpath)
|
||||
|
||||
def find_elements_by_xpath(self, xpath):
|
||||
"""Find all elements matching the xpath."""
|
||||
return self._find_elements('xpath', xpath)
|
||||
|
||||
def _find_element(self, strategy, value):
|
||||
return self.execute('POST',
|
||||
'/element',
|
||||
'findElement',
|
||||
self._get_locator(strategy, value))
|
||||
|
||||
def _find_elements(self, strategy, value):
|
||||
return self.execute('POST',
|
||||
'/elements',
|
||||
'findElements',
|
||||
self._get_locator(strategy, value))
|
||||
|
||||
def _get_locator(self, strategy, value):
|
||||
if self.mode == 'strict':
|
||||
return {'strategy': strategy, 'value': value}
|
||||
elif self.mode == 'compatibility':
|
||||
return {'using': strategy, 'value': value}
|
|
@ -0,0 +1,22 @@
|
|||
# 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/.
|
||||
|
||||
class ServoExtensionCommands(object):
|
||||
def __init__(self, session):
|
||||
self.session = session
|
||||
|
||||
@command
|
||||
def get_prefs(self, *prefs):
|
||||
body = {"prefs": list(prefs)}
|
||||
return self.session.send_command("POST", "servo/prefs/get", body)
|
||||
|
||||
@command
|
||||
def set_prefs(self, prefs):
|
||||
body = {"prefs": prefs}
|
||||
return self.session.send_command("POST", "servo/prefs/set", body)
|
||||
|
||||
@command
|
||||
def reset_prefs(self, *prefs):
|
||||
body = {"prefs": list(prefs)}
|
||||
return self.session.send_command("POST", "servo/prefs/reset", body)
|
|
@ -0,0 +1,117 @@
|
|||
# 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/.
|
||||
|
||||
|
||||
import errno
|
||||
import httplib
|
||||
import json
|
||||
import socket
|
||||
import time
|
||||
import urlparse
|
||||
|
||||
import error
|
||||
|
||||
|
||||
HTTP_TIMEOUT = 5
|
||||
|
||||
|
||||
class HTTPWireProtocol(object):
|
||||
"""Transports messages (commands and responses) over the WebDriver
|
||||
wire protocol.
|
||||
"""
|
||||
|
||||
def __init__(self, host, port, url_prefix="/", timeout=HTTP_TIMEOUT):
|
||||
"""Construct interface for communicating with the remote server.
|
||||
|
||||
:param url: URL of remote WebDriver server.
|
||||
:param wait: Duration to wait for remote to appear.
|
||||
"""
|
||||
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.path_prefix = url_prefix
|
||||
|
||||
self._timeout = timeout
|
||||
self._connection = None
|
||||
|
||||
def connect(self):
|
||||
wait_for_port(self.host, self.port, self._timeout)
|
||||
self._connection = httplib.HTTPConnection(
|
||||
self.host, self.port, timeout=self._timeout)
|
||||
|
||||
def disconnect(self):
|
||||
if self._connection:
|
||||
self._connection.close()
|
||||
self._connection = None
|
||||
|
||||
def url(self, suffix):
|
||||
return urlparse.urljoin(self.path_prefix, suffix)
|
||||
|
||||
def send(self, method, url, body=None, headers=None, key=None):
|
||||
"""Send a command to the remote.
|
||||
|
||||
:param method: "POST" or "GET".
|
||||
:param body: Body of the request. Defaults to an empty dictionary
|
||||
if ``method`` is "POST".
|
||||
:param headers: Additional headers to include in the request.
|
||||
:param key: Extract this key from the dictionary returned from
|
||||
the remote.
|
||||
"""
|
||||
|
||||
if not self._connection:
|
||||
self.connect()
|
||||
|
||||
if body is None and method == "POST":
|
||||
body = {}
|
||||
|
||||
if isinstance(body, dict):
|
||||
body = json.dumps(body)
|
||||
|
||||
if isinstance(body, unicode):
|
||||
body = body.encode("utf-8")
|
||||
|
||||
if headers is None:
|
||||
headers = {}
|
||||
|
||||
url = self.path_prefix + url
|
||||
self._connection.request(method, url, body, headers)
|
||||
|
||||
resp = self._connection.getresponse()
|
||||
resp_body = resp.read()
|
||||
|
||||
try:
|
||||
data = json.loads(resp_body)
|
||||
except:
|
||||
raise IOError("Could not parse response body as JSON: %s" % body)
|
||||
|
||||
if resp.status != 200:
|
||||
cls = error.get(data.get("error"))
|
||||
raise cls(data.get("message"))
|
||||
|
||||
if key is not None:
|
||||
data = data[key]
|
||||
if not data:
|
||||
data = None
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def wait_for_port(host, port, timeout=HTTP_TIMEOUT):
|
||||
"""Wait for a given host/port to be available."""
|
||||
starttime = time.time()
|
||||
poll_interval = 0.1
|
||||
while time.time() - starttime < timeout:
|
||||
sock = None
|
||||
try:
|
||||
sock = socket.socket()
|
||||
sock.connect((host, port))
|
||||
return True
|
||||
except socket.error as e:
|
||||
if e[0] != errno.ECONNREFUSED:
|
||||
raise
|
||||
finally:
|
||||
if sock:
|
||||
sock.close()
|
||||
time.sleep(poll_interval)
|
||||
return False
|
|
@ -1,87 +0,0 @@
|
|||
# Copyright 2011 Software Freedom Conservancy.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Waiting functionality."""
|
||||
|
||||
import time
|
||||
from exceptions import NoSuchElementException
|
||||
from exceptions import TimeoutException
|
||||
|
||||
POLL_FREQUENCY = 0.5 # How long to sleep inbetween calls to the method
|
||||
IGNORED_EXCEPTIONS = [NoSuchElementException] # list of exceptions ignored during calls to the method
|
||||
|
||||
class WebDriverWait(object):
|
||||
|
||||
def __init__(self, driver, timeout, poll_frequency=POLL_FREQUENCY, ignored_exceptions=None):
|
||||
"""Constructor, takes a WebDriver instance and timeout in seconds.
|
||||
|
||||
:Args:
|
||||
- driver - Instance of WebDriver (Ie, Firefox, Chrome or Remote)
|
||||
- timeout - Number of seconds before timing out
|
||||
- poll_frequency - sleep interval between calls
|
||||
By default, it is 0.5 second.
|
||||
- ignored_exceptions - iterable structure of exception classes ignored during calls.
|
||||
By default, it contains NoSuchElementException only.
|
||||
|
||||
Example:
|
||||
from selenium.webdriver.support.ui import WebDriverWait \n
|
||||
element = WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("someId")) \n
|
||||
is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).\ \n
|
||||
until_not(lambda x: x.find_element_by_id("someId").is_displayed())
|
||||
"""
|
||||
self._driver = driver
|
||||
self._timeout = timeout
|
||||
self._poll = poll_frequency
|
||||
# avoid the divide by zero
|
||||
if self._poll == 0:
|
||||
self._poll = POLL_FREQUENCY
|
||||
exceptions = IGNORED_EXCEPTIONS
|
||||
if ignored_exceptions is not None:
|
||||
try:
|
||||
exceptions.extend(iter(ignored_exceptions))
|
||||
except TypeError: # ignored_exceptions is not iterable
|
||||
exceptions.append(ignored_exceptions)
|
||||
self._ignored_exceptions = tuple(exceptions)
|
||||
|
||||
def until(self, method, message=''):
|
||||
"""Calls the method provided with the driver as an argument until the \
|
||||
return value is not False."""
|
||||
end_time = time.time() + self._timeout
|
||||
while(True):
|
||||
try:
|
||||
value = method(self._driver)
|
||||
if value:
|
||||
return value
|
||||
except self._ignored_exceptions:
|
||||
pass
|
||||
time.sleep(self._poll)
|
||||
if(time.time() > end_time):
|
||||
break
|
||||
raise TimeoutException(message)
|
||||
|
||||
def until_not(self, method, message=''):
|
||||
"""Calls the method provided with the driver as an argument until the \
|
||||
return value is False."""
|
||||
end_time = time.time() + self._timeout
|
||||
while(True):
|
||||
try:
|
||||
value = method(self._driver)
|
||||
if not value:
|
||||
return value
|
||||
except self._ignored_exceptions:
|
||||
return True
|
||||
time.sleep(self._poll)
|
||||
if(time.time() > end_time):
|
||||
break
|
||||
raise TimeoutException(message)
|
|
@ -1,56 +0,0 @@
|
|||
"""Element-level WebDriver operations."""
|
||||
|
||||
import searchcontext
|
||||
|
||||
class WebElement(searchcontext.SearchContext):
|
||||
"""Corresponds to a DOM element in the current page."""
|
||||
|
||||
def __init__(self, driver, id):
|
||||
self._driver = driver
|
||||
self._id = id
|
||||
# Set value of mode used by SearchContext
|
||||
self.mode = driver.mode
|
||||
|
||||
def execute(self, method, path, name, body=None):
|
||||
"""Execute a command against this WebElement."""
|
||||
return self._driver.execute(
|
||||
method, '/element/%s%s' % (self._id, path), name, body)
|
||||
|
||||
def is_displayed(self):
|
||||
"""Is this element displayed?"""
|
||||
return self.execute('GET', '/displayed', 'isDisplayed')
|
||||
|
||||
def is_selected(self):
|
||||
"""Is this checkbox, radio button, or option selected?"""
|
||||
return self.execute('GET', '/selected', 'isSelected')
|
||||
|
||||
def get_attribute(self, name):
|
||||
"""Get the value of an element property or attribute."""
|
||||
return self.execute('GET', '/attribute/%s' % name, 'getElementAttribute')
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
"""Get the visible text for this element."""
|
||||
return self.execute('GET', '/text', 'text')
|
||||
|
||||
@property
|
||||
def tag_name(self):
|
||||
"""Get the tag name for this element"""
|
||||
return self.execute('GET', '/name', 'getElementTagName')
|
||||
|
||||
def click(self):
|
||||
"""Click on this element."""
|
||||
return self.execute('POST', '/click', 'click')
|
||||
|
||||
def clear(self):
|
||||
"""Clear the contents of the this text input."""
|
||||
self.execute('POST', '/clear', 'clear')
|
||||
|
||||
def send_keys(self, keys):
|
||||
"""Send keys to this text input or body element."""
|
||||
if isinstance(keys, str):
|
||||
keys = [keys]
|
||||
self.execute('POST', '/value', 'sendKeys', {'value': keys})
|
||||
|
||||
def to_json(self):
|
||||
return {'ELEMENT': self.id}
|
Loading…
Add table
Add a link
Reference in a new issue