Bug 1378121 - Add WebDriver:MinimizeWindow command to Marionette; r=ato draft
authorYangyi Peng <yangyi_peng@htc.com>
Fri, 21 Jul 2017 17:00:41 +0800
changeset 616693 47c421b3218d
parent 616647 e5693cea1ec9
child 639566 7adc21138982
push id70783
push userbmo:ato@sny.no
push dateThu, 27 Jul 2017 11:33:43 +0000
reviewersato
bugs1378121
milestone56.0a1
Bug 1378121 - Add WebDriver:MinimizeWindow command to Marionette; r=ato Implements the backend for the WebDriver standard's Minimize Window command. Signed-off-by: Andreas Tolfsen <ato@sny.no> MozReview-Commit-ID: F5Z38LxhOJm
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/driver.js
testing/marionette/harness/marionette_harness/tests/unit/test_window_minimize.py
testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -2012,17 +2012,17 @@ class Marionette(object):
     @property
     def window_size(self):
         """Get the current browser window size.
 
         Will return the current browser window size in pixels. Refers to
         window outerWidth and outerHeight values, which include scroll bars,
         title bars, etc.
 
-        :returns: dictionary representation of current window width and height
+        :returns: Window rect.
         """
         warnings.warn("window_size property has been deprecated, please use get_window_rect()",
                       DeprecationWarning)
         return self._send_message("getWindowSize",
                                   key="value" if self.protocol == 1 else None)
 
     def set_window_size(self, width, height):
         """Resize the browser window currently in focus.
@@ -2031,28 +2031,48 @@ class Marionette(object):
         and `outerHeight` values, which include scroll bars, title bars, etc.
 
         An error will be returned if the requested window size would result
         in the window being in the maximised state.
 
         :param width: The width to resize the window to.
         :param height: The height to resize the window to.
 
+        :returns Window rect.
         """
         warnings.warn("set_window_size() has been deprecated, please use set_window_rect()",
                       DeprecationWarning)
         body = {"width": width, "height": height}
         return self._send_message("setWindowSize", body)
 
+    def minimize_window(self):
+        """Iconify the browser window currently receiving commands.
+        The action should be equivalent to the user pressing the minimize
+        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("WebDriver:MinimizeWindow")
+
     def maximize_window(self):
-        """ Resize the browser window currently receiving commands. The action
-        should be equivalent to the user pressing the maximize button
+        """Resize the browser window currently receiving commands.
+        The action should be equivalent to the user pressing the maximize
+        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")
 
     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.
+        """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: dictionary representation of current window width and height
+        :returns: Window rect.
         """
         return self._send_message("fullscreen")
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -2886,16 +2886,59 @@ GeckoDriver.prototype.setScreenOrientati
   }
 
   if (!win.screen.mozLockOrientation(mozOr)) {
     throw new WebDriverError(`Unable to set screen orientation: ${or}`);
   }
 };
 
 /**
+ * Synchronously minimizes the user agent window as if the user pressed
+ * the minimize button, or restores it if it is already minimized.
+ *
+ * Not supported on Fennec.
+ *
+ * @return {Object.<string, number>}
+ *     Window rect and window state.
+ *
+ * @throws {UnsupportedOperationError}
+ *     Not available for current application.
+ * @throws {NoSuchWindowError}
+ *     Top-level browsing context has been discarded.
+ * @throws {UnexpectedAlertOpenError}
+ *     A modal dialog is open, blocking this operation.
+ */
+GeckoDriver.prototype.minimizeWindow = function* (cmd, resp) {
+  assert.firefox();
+  const win = assert.window(this.getCurrentWindow());
+  assert.noUserPrompt(this.dialog);
+
+  let state;
+  yield new Promise(resolve => {
+    win.addEventListener("sizemodechange", resolve, {once: true});
+
+    if (win.windowState == win.STATE_MINIMIZED) {
+      win.restore();
+      state = "normal";
+    } else {
+      win.minimize();
+      state = "minimized";
+    }
+  });
+
+  resp.body = {
+    x: win.screenX,
+    y: win.screenY,
+    width: win.outerWidth,
+    height: win.outerHeight,
+    state,
+  };
+};
+
+/**
  * Synchronously maximizes the user agent window as if the user pressed
  * the maximize button, or restores it if it is already maximized.
  *
  * Not supported on Fennec.
  *
  * @return {Map.<string, number>}
  *     Window rect.
  *
@@ -3421,16 +3464,17 @@ GeckoDriver.prototype.commands = {
   "WebDriver:GetTitle": GeckoDriver.prototype.getTitle,
   "WebDriver:GetWindowHandle": GeckoDriver.prototype.getWindowHandle,
   "WebDriver:GetWindowHandles": GeckoDriver.prototype.getWindowHandles,
   "WebDriver:GetWindowRect": GeckoDriver.prototype.getWindowRect,
   "WebDriver:GetWindowType": GeckoDriver.prototype.getWindowType,
   "WebDriver:IsElementDisplayed": GeckoDriver.prototype.isElementDisplayed,
   "WebDriver:IsElementEnabled": GeckoDriver.prototype.isElementEnabled,
   "WebDriver:IsElementSelected": GeckoDriver.prototype.isElementSelected,
+  "WebDriver:MinimizeWindow": GeckoDriver.prototype.minimizeWindow,
   "WebDriver:MaximizeWindow": GeckoDriver.prototype.maximizeWindow,
   "WebDriver:Navigate": GeckoDriver.prototype.get,
   "WebDriver:NewSession": GeckoDriver.prototype.newSession,
   "WebDriver:PerformActions": GeckoDriver.prototype.performActions,
   "WebDriver:Refresh":  GeckoDriver.prototype.refresh,
   "WebDriver:ReleaseActions": GeckoDriver.prototype.releaseActions,
   "WebDriver:SendAlertText": GeckoDriver.prototype.sendKeysToDialog,
   "WebDriver:SetScreenOrientation": GeckoDriver.prototype.setScreenOrientation,
new file mode 100644
--- /dev/null
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_minimize.py
@@ -0,0 +1,40 @@
+# 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 marionette_driver.errors import InvalidArgumentException
+
+from marionette_harness import MarionetteTestCase
+
+class TestWindowMinimize(MarionetteTestCase):
+
+    def setUp(self):
+        MarionetteTestCase.setUp(self)
+
+        self.original_size = self.marionette.window_size
+
+    def assert_window_minimized(self, resp):
+        self.assertEqual("minimized", resp["state"])
+
+    def assert_window_restored(self, actual):
+        self.assertEqual("normal", actual["state"])
+        self.assertEqual(self.original_size["width"], actual["width"])
+        self.assertEqual(self.original_size["height"], actual["height"])
+
+    def test_minimize_twice_restores(self):
+        resp = self.marionette.minimize_window()
+        self.assert_window_minimized(resp)
+
+        # restore the window
+        resp = self.marionette.minimize_window()
+        self.assert_window_restored(resp)
+
+    def test_minimize_stress(self):
+        for i in range(1, 25):
+            expect_minimized = bool(i % 2)
+
+            resp = self.marionette.minimize_window()
+            if expect_minimized:
+                self.assert_window_minimized(resp)
+            else:
+                self.assert_window_restored(resp)
--- a/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
+++ b/testing/marionette/harness/marionette_harness/tests/unit/unit-tests.ini
@@ -64,16 +64,18 @@ skip-if = appname == 'fennec'
 [test_window_handles_content.py]
 [test_window_close_chrome.py]
 skip-if = appname == 'fennec'
 [test_window_close_content.py]
 [test_window_rect.py]
 skip-if = appname == 'fennec'
 [test_window_maximize.py]
 skip-if = appname == 'fennec'
+[test_window_minimize.py]
+skip-if = appname == 'fennec' || headless
 [test_window_status_content.py]
 [test_window_status_chrome.py]
 
 [test_screenshot.py]
 skip-if = headless # Relies on native styling which headless doesn't support.
 [test_cookies.py]
 [test_title.py]
 [test_title_chrome.py]