# HG changeset patch # User Henrik Skupin # Date 1522944528 -7200 # Node ID 7f5061bafd9798986894e1988b46ea89d3e55bc2 # Parent 88ae606bd4b7deeb4007262baa5a6634fcb13756 Bug 1451727 - [marionette] Update WebDriver specific commands to use the "WebDriver" prefix. r=ato MozReview-Commit-ID: 57wk5BD8F2m diff --git a/testing/marionette/client/marionette_driver/marionette.py b/testing/marionette/client/marionette_driver/marionette.py --- a/testing/marionette/client/marionette_driver/marionette.py +++ b/testing/marionette/client/marionette_driver/marionette.py @@ -63,100 +63,109 @@ class HTMLElement(object): """ return self.marionette.find_elements(method, target, self.id) def get_attribute(self, name): """Returns the requested attribute, or None if no attribute is set. """ body = {"id": self.id, "name": name} - return self.marionette._send_message("getElementAttribute", body, key="value") + return self.marionette._send_message("WebDriver:GetElementAttribute", + body, key="value") def get_property(self, name): """Returns the requested property, or None if the property is not set. """ try: body = {"id": self.id, "name": name} - return self.marionette._send_message("getElementProperty", body, key="value") + return self.marionette._send_message("WebDriver:GetElementProperty", + body, key="value") except errors.UnknownCommandException: # Keep backward compatibility for code which uses get_attribute() to # also retrieve element properties. # Remove when Firefox 55 is stable. return self.get_attribute(name) def click(self): """Simulates a click on the element.""" - self.marionette._send_message("clickElement", {"id": self.id}) + self.marionette._send_message("WebDriver:ElementClick", + {"id": self.id}) def tap(self, x=None, y=None): """Simulates a set of tap events on the element. :param x: X coordinate of tap event. If not given, default to the centre of the element. :param y: Y coordinate of tap event. If not given, default to the centre of the element. """ body = {"id": self.id, "x": x, "y": y} self.marionette._send_message("singleTap", body) @property def text(self): """Returns the visible text of the element, and its child elements.""" body = {"id": self.id} - return self.marionette._send_message("getElementText", body, key="value") + return self.marionette._send_message("WebDriver:GetElementText", + body, key="value") def send_keys(self, *strings): """Sends the string via synthesized keypresses to the element. If an array is passed in like `marionette.send_keys(Keys.SHIFT, "a")` it will be joined into a string. If an integer is passed in like `marionette.send_keys(1234)` it will be coerced into a string. """ keys = Marionette.convert_keys(*strings) - body = {"id": self.id, "text": keys} - self.marionette._send_message("sendKeysToElement", body) + self.marionette._send_message("WebDriver:ElementSendKeys", + {"id": self.id, "text": keys}) def clear(self): """Clears the input of the element.""" - self.marionette._send_message("clearElement", {"id": self.id}) + self.marionette._send_message("WebDriver:ElementClear", + {"id": self.id}) def is_selected(self): """Returns True if the element is selected.""" body = {"id": self.id} - return self.marionette._send_message("isElementSelected", body, key="value") + return self.marionette._send_message("WebDriver:IsElementSelected", + body, key="value") def is_enabled(self): """This command will return False if all the following criteria are met otherwise return True: * A form control is disabled. * A ``HTMLElement`` has a disabled boolean attribute. """ body = {"id": self.id} - return self.marionette._send_message("isElementEnabled", body, key="value") + return self.marionette._send_message("WebDriver:IsElementEnabled", + body, key="value") def is_displayed(self): """Returns True if the element is displayed, False otherwise.""" body = {"id": self.id} - return self.marionette._send_message("isElementDisplayed", body, key="value") + return self.marionette._send_message("WebDriver:IsElementDisplayed", + body, key="value") @property def size(self): """A dictionary with the size of the element.""" warnings.warn("The size property has been deprecated and will be removed in a future version. \ Please use HTMLElement#rect", DeprecationWarning) rect = self.rect return {"width": rect["width"], "height": rect["height"]} @property def tag_name(self): """The tag name of the element.""" body = {"id": self.id} - return self.marionette._send_message("getElementTagName", body, key="value") + return self.marionette._send_message("WebDriver:GetElementTagName", + body, key="value") @property def location(self): """Get an element's location on the page. The returned point will contain the x and y coordinates of the top left-hand corner of the given element. The point (0,0) refers to the upper-left corner of the document. @@ -174,27 +183,27 @@ class HTMLElement(object): This will return a dictionary with the following: * x and y represent the top left coordinates of the ``HTMLElement`` relative to top left corner of the document. * height and the width will contain the height and the width of the DOMRect of the ``HTMLElement``. """ - body = {"id": self.id} - return self.marionette._send_message("getElementRect", body) + return self.marionette._send_message("WebDriver:GetElementRect", + {"id": self.id}) def value_of_css_property(self, property_name): """Gets the value of the specified CSS property name. :param property_name: Property name to get the value of. """ body = {"id": self.id, "propertyName": property_name} - return self.marionette._send_message( - "getElementValueOfCssProperty", body, key="value") + return self.marionette._send_message("WebDriver:GetElementCSSValue", + body, key="value") class MouseButton(object): """Enum-like class for mouse button constants.""" LEFT = 0 MIDDLE = 1 RIGHT = 2 @@ -527,32 +536,34 @@ class Alert(object): Alert(marionette).dismiss() """ def __init__(self, marionette): self.marionette = marionette def accept(self): """Accept a currently displayed modal dialog.""" - self.marionette._send_message("acceptDialog") + # TODO: Switch to "WebDriver:AcceptAlert" in Firefox 62 + self.marionette._send_message("WebDriver:AcceptDialog") def dismiss(self): """Dismiss a currently displayed modal dialog.""" - self.marionette._send_message("dismissDialog") + self.marionette._send_message("WebDriver:DismissAlert") @property def text(self): """Return the currently displayed text in a tab modal.""" - return self.marionette._send_message("getTextFromDialog", key="value") + return self.marionette._send_message("WebDriver:GetAlertText", + key="value") def send_keys(self, *string): """Send keys to the currently displayed text input area in an open tab modal dialog.""" - body = {"text": Marionette.convert_keys(*string)} - self.marionette._send_message("sendKeysToDialog", body) + self.marionette._send_message("WebDriver:SendAlertText", + {"text": Marionette.convert_keys(*string)}) class Marionette(object): """Represents a Marionette connection to a browser or device.""" CONTEXT_CHROME = "chrome" # non-browser content: windows, dialogs, etc. CONTEXT_CONTENT = "content" # browser content: iframes, divs, etc. DEFAULT_STARTUP_TIMEOUT = 120 @@ -725,17 +736,17 @@ class Marionette(object): :param name: Requested command key. :param params: Optional dictionary of key/value arguments. :param key: Optional key to extract from response. :returns: Full response from the server, or if `key` is given, the value of said key in the response. """ - if not self.session_id and name != "newSession": + if not self.session_id and name != "WebDriver:NewSession": raise errors.MarionetteException("Please start a session") try: msg = self.client.request(name, params) except IOError: self.delete_session(send_request=False) raise @@ -991,17 +1002,18 @@ class Marionette(object): case prefInterface.PREF_INVALID: return false; }} """.format(pref, value)) if not pref_exists: break if not pref_exists: - context = self._send_message("getContext", key="value") + context = self._send_message("Marionette:GetContext", + key="value") self.delete_session() self.instance.restart(prefs) self.raise_for_port() self.start_session() # Restore the context as used before the restart self.set_context(context) @@ -1052,17 +1064,18 @@ class Marionette(object): if canceled: raise errors.MarionetteException( "Something cancelled the quit application request") body = None if len(flags) > 0: body = {"flags": list(flags)} - return self._send_message("quitApplication", body, key="cause") + return self._send_message("Marionette:Quit", + body, key="cause") @do_process_check def quit(self, clean=False, in_app=False, callback=None): """Terminate the currently running instance. This command will delete the active marionette session. It also allows manipulation of eg. the profile data while the application is not running. To start the application again, :func:`start_session` has to be called. @@ -1081,17 +1094,18 @@ class Marionette(object): "on Gecko instances launched by Marionette") cause = None if in_app: if callback is not None: if not callable(callback): raise ValueError("Specified callback '{}' is not callable".format(callback)) - self._send_message("acceptConnections", {"value": False}) + self._send_message("Marionette:AcceptConnections", + {"value": False}) callback() else: cause = self._request_in_app_shutdown() self.delete_session(send_request=False) # Give the application some time to shutdown returncode = self.instance.runner.wait(timeout=self.DEFAULT_SHUTDOWN_TIMEOUT) @@ -1124,28 +1138,30 @@ class Marionette(object): browser. Otherwise the browser will be restarted immediately by killing the process. :param callback: If provided and `in_app` is True, the callback will be used to trigger the restart. """ if not self.instance: raise errors.MarionetteException("restart() can only be called " "on Gecko instances launched by Marionette") - context = self._send_message("getContext", key="value") + context = self._send_message("Marionette:GetContext", + key="value") cause = None if in_app: if clean: raise ValueError("An in_app restart cannot be triggered with the clean flag set") if callback is not None: if not callable(callback): raise ValueError("Specified callback '{}' is not callable".format(callback)) - self._send_message("acceptConnections", {"value": False}) + self._send_message("Marionette:AcceptConnections", + {"value": False}) callback() else: cause = self._request_in_app_shutdown("eRestart") self.delete_session(send_request=False) try: timeout = self.DEFAULT_SHUTDOWN_TIMEOUT + self.DEFAULT_STARTUP_TIMEOUT @@ -1233,17 +1249,18 @@ class Marionette(object): # top-level, and after bug 1388424 removed support for overriding # the session ID, we also do this with this client. However, # because this client is used with older Firefoxen (through upgrade # tests et al.) we need to preserve backwards compatibility until # Firefox 60. if "capabilities" not in body and capabilities is not None: body["capabilities"] = dict(capabilities) - resp = self._send_message("newSession", body) + resp = self._send_message("WebDriver:NewSession", + body) self.session_id = resp["sessionId"] self.session = resp["capabilities"] # fallback to processId can be removed in Firefox 55 self.process_id = self.session.get("moz:processID", self.session.get("processId")) self.profile = self.session.get("moz:profile") return self.session @@ -1259,17 +1276,17 @@ class Marionette(object): """Close the current session and disconnect from the server. :param send_request: Optional, if `True` a request to close the session on the server side will be sent. Use `False` in case of eg. in_app restart() or quit(), which trigger a deletion themselves. Defaults to `True`. """ try: if send_request: - self._send_message("deleteSession") + self._send_message("WebDriver:DeleteSession") finally: self.process_id = None self.profile = None self.session = None self.session_id = None self.window = None if self.client is not None: @@ -1348,17 +1365,18 @@ class Marionette(object): Returns an opaque server-assigned identifier to this window that uniquely identifies it within this Marionette instance. This can be used to switch to this window at a later point. :returns: unique window handle :rtype: string """ - self.window = self._send_message("getWindowHandle", key="value") + self.window = self._send_message("WebDriver:GetWindowHandle", + key="value") return self.window @property def current_chrome_window_handle(self): """Get the current chrome window's handle. Corresponds to a chrome window that may itself contain tabs identified by window_handles. @@ -1404,68 +1422,70 @@ class Marionette(object): :param x: x coordinate for the top left of the window :param y: y coordinate for the top left of the window :param width: The width to resize the window to. :param height: The height to resize the window to. """ if (x is None and y is None) and (height is None and width is None): raise errors.InvalidArgumentException("x and y or height and width need values") - return self._send_message("setWindowRect", {"x": x, "y": y, - "height": height, - "width": width}) + body = {"x": x, "y": y, "height": height, "width": width} + return self._send_message("WebDriver:SetWindowRect", + body) @property def window_rect(self): - return self._send_message("getWindowRect") + return self._send_message("WebDriver:GetWindowRect") @property def title(self): """Current title of the active window.""" - return self._send_message("getTitle", key="value") + return self._send_message("WebDriver:GetTitle", + key="value") @property def window_handles(self): """Get list of windows in the current context. If called in the content context it will return a list of references to all available browser windows. Called in the chrome context, it will list all available windows, not just browser windows (e.g. not just navigator.browser). Each window handle is assigned by the server, and the list of strings returned does not have a guaranteed ordering. :returns: Unordered list of unique window handles as strings """ - return self._send_message("getWindowHandles") + return self._send_message("WebDriver:GetWindowHandles") @property def chrome_window_handles(self): """Get a list of currently open chrome windows. Each window handle is assigned by the server, and the list of strings returned does not have a guaranteed ordering. :returns: Unordered list of unique chrome window handles as strings """ return self._send_message("getChromeWindowHandles") @property def page_source(self): """A string representation of the DOM.""" - return self._send_message("getPageSource", key="value") + return self._send_message("WebDriver:GetPageSource", + key="value") def close(self): """Close the current window, ending the session if it's the last window currently open. :returns: Unordered list of remaining unique window handles as strings """ - return self._send_message("close") + return self._send_message("WebDriver:CloseWindow") def close_chrome_window(self): """Close the currently selected chrome window, ending the session if it's the last window open. :returns: Unordered list of remaining unique chrome window handles as strings """ return self._send_message("closeChromeWindow") @@ -1477,34 +1497,37 @@ class Marionette(object): `CONTEXT_CHROME` or `CONTEXT_CONTENT`. Usage example:: marionette.set_context(marionette.CONTEXT_CHROME) """ if context not in [self.CONTEXT_CHROME, self.CONTEXT_CONTENT]: raise ValueError("Unknown context: {}".format(context)) - self._send_message("setContext", {"value": context}) + + self._send_message("Marionette:SetContext", + {"value": context}) @contextmanager def using_context(self, context): """Sets the context that Marionette commands are running in using a `with` statement. The state of the context on the server is saved before entering the block, and restored upon exiting it. :param context: Context, may be one of the class properties `CONTEXT_CHROME` or `CONTEXT_CONTENT`. Usage example:: with marionette.using_context(marionette.CONTEXT_CHROME): # chrome scope ... do stuff ... """ - scope = self._send_message("getContext", key="value") + scope = self._send_message("Marionette:GetContext", + key="value") self.set_context(context) try: yield finally: self.set_context(scope) def switch_to_alert(self): """Returns an :class:`~marionette_driver.marionette.Alert` object for @@ -1522,34 +1545,35 @@ class Marionette(object): """Switch to the specified window; subsequent commands will be directed at the new window. :param window_id: The id or name of the window to switch to. :param focus: A boolean value which determins whether to focus the window that we just switched to. """ - body = {"focus": focus, "name": window_id} - self._send_message("switchToWindow", body) + self._send_message("WebDriver:SwitchToWindow", + {"focus": focus, "name": window_id}) self.window = window_id def get_active_frame(self): """Returns an :class:`~marionette_driver.marionette.HTMLElement` representing the frame Marionette is currently acting on.""" - return self._send_message("getActiveFrame", key="value") + return self._send_message("WebDriver:GetActiveFrame", + key="value") def switch_to_default_content(self): """Switch the current context to page's default content.""" return self.switch_to_frame() def switch_to_parent_frame(self): """ Switch to the Parent Frame """ - self._send_message("switchToParentFrame") + self._send_message("WebDriver:SwitchToParentFrame") def switch_to_frame(self, frame=None, focus=True): """Switch the current context to the specified frame. Subsequent commands will operate in the context of the specified frame, if applicable. :param frame: A reference to the frame to switch to. This can be an :class:`~marionette_driver.marionette.HTMLElement`, @@ -1560,46 +1584,51 @@ class Marionette(object): :param focus: A boolean value which determins whether to focus the frame that we just switched to. """ body = {"focus": focus} if isinstance(frame, HTMLElement): body["element"] = frame.id elif frame is not None: body["id"] = frame - self._send_message("switchToFrame", body) + + self._send_message("WebDriver:SwitchToFrame", + body) def switch_to_shadow_root(self, host=None): """Switch the current context to the specified host's Shadow DOM. Subsequent commands will operate in the context of the specified Shadow DOM, if applicable. :param host: A reference to the host element containing Shadow DOM. This can be an :class:`~marionette_driver.marionette.HTMLElement`. If you call ``switch_to_shadow_root`` without an argument, it will switch to the parent Shadow DOM or the top-level frame. """ body = {} if isinstance(host, HTMLElement): body["id"] = host.id - return self._send_message("switchToShadowRoot", body) + + return self._send_message("WebDriver:SwitchToShadowRoot", + body) def get_url(self): """Get a string representing the current URL. On Desktop this returns a string representation of the URL of the current top level browsing context. This is equivalent to document.location.href. When in the context of the chrome, this returns the canonical URL of the current resource. :returns: string representation of URL """ - return self._send_message("getCurrentUrl", key="value") + return self._send_message("WebDriver:GetCurrentURL", + key="value") def get_window_type(self): """Gets the windowtype attribute of the window Marionette is currently acting on. This command only makes sense in a chrome context. You might use this method to distinguish a browser window from an editor window. """ @@ -1623,29 +1652,30 @@ class Marionette(object): `window` triggers and `document.readyState` is "complete". In chrome context it will change the current `window`'s location to the supplied URL and wait until `document.readyState` equals "complete" or the page timeout duration has elapsed. :param url: The URL to navigate to. """ - self._send_message("get", {"url": url}) + self._send_message("WebDriver:Navigate", + {"url": url}) def go_back(self): """Causes the browser to perform a back navigation.""" - self._send_message("goBack") + self._send_message("WebDriver:Back") def go_forward(self): """Causes the browser to perform a forward navigation.""" - self._send_message("goForward") + self._send_message("WebDriver:Forward") def refresh(self): """Causes the browser to perform to refresh the current page.""" - self._send_message("refresh") + self._send_message("WebDriver:Refresh") def _to_json(self, args): if isinstance(args, list) or isinstance(args, tuple): wrapped = [] for arg in args: wrapped.append(self._to_json(arg)) elif isinstance(args, dict): wrapped = {} @@ -1763,17 +1793,19 @@ class Marionette(object): frame = stack[-2:-1][0] # grab the second-to-last frame body = {"script": script, "args": args, "newSandbox": new_sandbox, "sandbox": sandbox, "scriptTimeout": script_timeout, "line": int(frame[1]), "filename": os.path.basename(frame[0])} - rv = self._send_message("executeScript", body, key="value") + + rv = self._send_message("WebDriver:ExecuteScript", + body, key="value") return self._from_json(rv) def execute_async_script(self, script, script_args=(), new_sandbox=True, sandbox="default", script_timeout=None, debug_script=False): """Executes an asynchronous JavaScript script, and returns the result (or None if the script does return a value). @@ -1812,17 +1844,19 @@ class Marionette(object): body = {"script": script, "args": args, "newSandbox": new_sandbox, "sandbox": sandbox, "scriptTimeout": script_timeout, "line": int(frame[1]), "filename": os.path.basename(frame[0]), "debug_script": debug_script} - rv = self._send_message("executeAsyncScript", body, key="value") + + rv = self._send_message("WebDriver:ExecuteAsyncScript", + body, key="value") return self._from_json(rv) def find_element(self, method, target, id=None): """Returns an :class:`~marionette_driver.marionette.HTMLElement` instance that matches the specified method and target in the current context. An :class:`~marionette_driver.marionette.HTMLElement` instance may be @@ -1843,17 +1877,19 @@ class Marionette(object): "tag", target might equal "div". If method = "id", target would be an element id. :param id: If specified, search for elements only inside the element with the specified id. """ body = {"value": target, "using": method} if id: body["element"] = id - return self._send_message("findElement", body, key="value") + + return self._send_message("WebDriver:FindElement", + body, key="value") def find_elements(self, method, target, id=None): """Returns a list of all :class:`~marionette_driver.marionette.HTMLElement` instances that match the specified method and target in the current context. An :class:`~marionette_driver.marionette.HTMLElement` instance may be used to call other methods on the element, such as @@ -1871,20 +1907,23 @@ class Marionette(object): "tag", target might equal "div". If method = "id", target would be an element id. :param id: If specified, search for elements only inside the element with the specified id. """ body = {"value": target, "using": method} if id: body["element"] = id - return self._send_message("findElements", body) + + return self._send_message("WebDriver:FindElements", + body) def get_active_element(self): - el_or_ref = self._send_message("getActiveElement", key="value") + el_or_ref = self._send_message("WebDriver:GetActiveElement", + key="value") return el_or_ref def add_cookie(self, cookie): """Adds a cookie to your current session. :param cookie: A dictionary object, with required keys - "name" and "value"; optional keys - "path", "domain", "secure", "expiry". @@ -1893,42 +1932,43 @@ class Marionette(object): :: driver.add_cookie({"name": "foo", "value": "bar"}) driver.add_cookie({"name": "foo", "value": "bar", "path": "/"}) driver.add_cookie({"name": "foo", "value": "bar", "path": "/", "secure": True}) """ - body = {"cookie": cookie} - self._send_message("addCookie", body) + self._send_message("WebDriver:AddCookie", + {"cookie": cookie}) def delete_all_cookies(self): """Delete all cookies in the scope of the current session. Usage example: :: driver.delete_all_cookies() """ - self._send_message("deleteAllCookies") + self._send_message("WebDriver:DeleteAllCookies") def delete_cookie(self, name): """Delete a cookie by its name. :param name: Name of cookie to delete. Usage example: :: driver.delete_cookie("foo") """ - self._send_message("deleteCookie", {"name": name}) + self._send_message("WebDriver:DeleteCookie", + {"name": name}) def get_cookie(self, name): """Get a single cookie by name. Returns the cookie if found, None if not. :param name: Name of cookie to get. """ cookies = self.get_cookies() @@ -1940,17 +1980,17 @@ class Marionette(object): def get_cookies(self): """Get all the cookies for the current domain. This is the equivalent of calling `document.cookie` and parsing the result. :returns: A list of cookies for the current domain. """ - return self._send_message("getCookies") + return self._send_message("WebDriver:GetCookies") def screenshot(self, element=None, highlights=None, format="base64", full=True, scroll=True): """Takes a screenshot of a web element or the current frame. The screen capture is returned as a lossless PNG image encoded as a base 64 string by default. If the `element` argument is defined the capture area will be limited to the bounding box of that @@ -1986,17 +2026,19 @@ class Marionette(object): body = {"id": element, "highlights": lights, "full": full, "hash": False, "scroll": scroll} if format == "hash": body["hash"] = True - data = self._send_message("takeScreenshot", body, key="value") + + data = self._send_message("WebDriver:TakeScreenshot", + body, key="value") if format == "base64" or format == "hash": return data elif format == "binary": return base64.b64decode(data.encode("ascii")) else: raise ValueError("format parameter must be either 'base64'" " or 'binary', not {0}".format(repr(format))) @@ -2079,18 +2121,18 @@ class Marionette(object): button in the OS window. Note that this command is not available on Fennec. It may also not be available in certain window managers. :returns: Window rect. """ - return self._send_message("maximizeWindow") + return self._send_message("WebDriver:MaximizeWindow") def fullscreen(self): """Synchronously sets the user agent window to full screen as if the user had done "View > Enter Full Screen", or restores it if it is already in full screen. :returns: Window rect. """ - return self._send_message("fullscreen") + return self._send_message("WebDriver:FullscreenWindow") diff --git a/testing/marionette/client/marionette_driver/timeout.py b/testing/marionette/client/marionette_driver/timeout.py --- a/testing/marionette/client/marionette_driver/timeout.py +++ b/testing/marionette/client/marionette_driver/timeout.py @@ -25,20 +25,20 @@ class Timeouts(object): """ def __init__(self, marionette): self._marionette = marionette def _set(self, name, sec): ms = sec * 1000 - self._marionette._send_message("setTimeouts", {name: ms}) + self._marionette._send_message("WebDriver:SetTimeouts", {name: ms}) def _get(self, name): - ts = self._marionette._send_message("getTimeouts") + ts = self._marionette._send_message("WebDriver:GetTimeouts") if name not in ts: raise KeyError() ms = ts[name] return ms / 1000 @property def script(self): """Get the session's script timeout. This specifies the time