Bug 1391691 - Make WebDriver:MaximizeWindow idempotent. r=automatedtester draft
authorAndreas Tolfsen <ato@sny.no>
Fri, 18 Aug 2017 18:31:42 +0100
changeset 650450 6b00586b1aa0e16f335ab730152466051e85fdca
parent 650447 310a76953028b6a64df55215abb02436d1c84fb7
child 650451 c24c4bb3977e3c21a8e4a70ed38b59277bc68012
push id75394
push userbmo:ato@sny.no
push dateTue, 22 Aug 2017 11:15:25 +0000
reviewersautomatedtester
bugs1391691
milestone57.0a1
Bug 1391691 - Make WebDriver:MaximizeWindow idempotent. r=automatedtester MozReview-Commit-ID: EJ0VQOTWysg
testing/marionette/driver.js
testing/marionette/harness/marionette_harness/tests/unit/test_window_maximize.py
testing/web-platform/meta/MANIFEST.json
testing/web-platform/tests/webdriver/tests/contexts/maximize_window.py
testing/web-platform/tests/webdriver/tests/set_window_rect.py
--- a/testing/marionette/driver.js
+++ b/testing/marionette/driver.js
@@ -3047,17 +3047,19 @@ GeckoDriver.prototype.minimizeWindow = a
     });
   }
 
   return this.curBrowser.rect;
 };
 
 /**
  * Synchronously maximizes the user agent window as if the user pressed
- * the maximize button, or restores it if it is already maximized.
+ * the maximize button.
+ *
+ * No action is taken if the window is already maximized.
  *
  * Not supported on Fennec.
  *
  * @return {Object.<string, number>}
  *     Window rect.
  *
  * @throws {UnsupportedOperationError}
  *     Not available for current application.
@@ -3087,58 +3089,54 @@ GeckoDriver.prototype.maximizeWindow = a
           curSize.outerHeight != origSize.outerHeight) {
         resolve();
       } else {
         reject();
       }
     });
   }
 
-  let modeChangeEv;
-  await new TimedPromise(resolve => {
-    modeChangeEv = resolve;
-    win.addEventListener("sizemodechange", modeChangeEv, {once: true});
-
-    if (win.windowState == win.STATE_MAXIMIZED) {
-      win.restore();
-    } else {
+  let state = WindowState.from(win.windowState);
+  if (state != WindowState.Maximized) {
+    await new TimedPromise(resolve => {
+      win.addEventListener("sizemodechange", resolve, {once: true});
       win.maximize();
-    }
-  }, {throws: null});
-  win.removeEventListener("sizemodechange", modeChangeEv);
-
-  // Transitioning into a window state is asynchronous on Linux, and we
-  // cannot rely on sizemodechange to accurately tell us when the
-  // transition has completed.
-  //
-  // To counter for this we wait for the window size to change, which
-  // it usually will.  On platforms where the transition is synchronous,
-  // the wait will have the cost of one iteration because the size will
-  // have changed as part of the transition.  Where the platform
-  // is asynchronous, the cost may be greater as we have to poll
-  // continuously until we see a change, but it ensures conformity in
-  // behaviour.
-  //
-  // Certain window managers, however, do not have a concept of maximised
-  // windows and here sizemodechange may never fire.  Indeed, if the
-  // window covers the maximum available screen real estate, the window
-  // size may also not change.  In this circumstance, which admittedly
-  // is a somewhat bizarre edge case, we assume that the timeout of
-  // waiting for sizemodechange to fire is sufficient to give the window
-  // enough time to transition itself into whatever form or shape the
-  // WM is programmed to give it.
-  await windowSizeChange();
+    }, {throws: null});
+
+    // Transitioning into a window state is asynchronous on Linux,
+    // and we cannot rely on sizemodechange to accurately tell us when
+    // the transition has completed.
+    //
+    // To counter for this we wait for the window size to change, which
+    // it usually will.  On platforms where the transition is synchronous,
+    // the wait will have the cost of one iteration because the size
+    // will have changed as part of the transition.  Where the platform is
+    // asynchronous, the cost may be greater as we have to poll
+    // continuously until we see a change, but it ensures conformity in
+    // behaviour.
+    //
+    // Certain window managers, however, do not have a concept of
+    // maximised windows and here sizemodechange may never fire.  Indeed,
+    // if the window covers the maximum available screen real estate,
+    // the window size may also not change.  In this circumstance,
+    // which admittedly is a somewhat bizarre edge case, we assume that
+    // the timeout of waiting for sizemodechange to fire is sufficient
+    // to give the window enough time to transition itself into whatever
+    // form or shape the WM is programmed to give it.
+    await windowSizeChange();
+  }
 
   return this.curBrowser.rect;
 };
 
 /**
  * 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.
+ * had done "View > Enter Full Screen".
+ *
+ * No action is taken if the window is already in full screen mode.
  *
  * Not supported on Fennec.
  *
  * @return {Map.<string, number>}
  *     Window rect.
  *
  * @throws {UnsupportedOperationError}
  *     Not available for current application.
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_maximize.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_maximize.py
@@ -40,20 +40,16 @@ class TestWindowMaximize(MarionetteTestC
                 "current width {current} should be greater than or equal to max width {max}"
                 .format(delta=delta, current=actual["width"], max=self.max["width"] - delta))
         self.assertGreaterEqual(
             actual["height"], self.max["height"] - delta,
             msg="Window height is not within {delta} px of availHeight: "
                 "current height {current} should be greater than or equal to max height {max}"
                 .format(delta=delta, current=actual["height"], max=self.max["height"] - delta))
 
-    def assert_window_restored(self, actual):
-        self.assertEqual(self.original_size["width"], actual["width"])
-        self.assertEqual(self.original_size["height"], actual["height"])
-
     def assert_window_rect(self, rect):
         self.assertIn("width", rect)
         self.assertIn("height", rect)
         self.assertIn("x", rect)
         self.assertIn("y", rect)
         self.assertIsInstance(rect["width"], int)
         self.assertIsInstance(rect["height"], int)
         self.assertIsInstance(rect["x"], int)
@@ -61,24 +57,26 @@ class TestWindowMaximize(MarionetteTestC
 
     def test_maximize(self):
         maximize_resp = self.marionette.maximize_window()
         self.assert_window_rect(maximize_resp)
         window_rect_resp = self.marionette.window_rect
         self.assertEqual(maximize_resp, window_rect_resp)
         self.assert_window_maximized(maximize_resp)
 
-    def test_maximize_twice_restores(self):
+    def test_maximize_twice_is_idempotent(self):
         maximized = self.marionette.maximize_window()
         self.assert_window_maximized(maximized)
 
-        restored = self.marionette.maximize_window()
-        self.assert_window_restored(restored)
+        still_maximized = self.marionette.maximize_window()
+        self.assert_window_maximized(still_maximized)
 
     def test_stress(self):
         for i in range(1, 25):
             expect_maximized = bool(i % 2)
 
-            rect = self.marionette.maximize_window()
             if expect_maximized:
+                rect = self.marionette.maximize_window()
                 self.assert_window_maximized(rect)
             else:
-                self.assert_window_restored(rect)
+                rect = self.marionette.set_window_rect(width=800, height=600)
+                self.assertEqual(800, rect["width"])
+                self.assertEqual(600, rect["height"])
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -626288,17 +626288,17 @@
    "94b6f474bb4417cf2c06cb5fc6042c01bde98aa2",
    "support"
   ],
   "webdriver/tests/conftest.py": [
    "ab95734024a8e152ed84128eb804344740be91d4",
    "support"
   ],
   "webdriver/tests/contexts/maximize_window.py": [
-   "de49d5d8cc03e863dfe7e29ca8c10678d9b45e83",
+   "905e748f0ab795a66ab41d70aebb22dbc6adbe7e",
    "wdspec"
   ],
   "webdriver/tests/contexts/resizing_and_positioning.py": [
    "479379109115668183643e8a050396219332887d",
    "wdspec"
   ],
   "webdriver/tests/cookies/cookies.py": [
    "e31177e638269864031e44808945fa1e7c46031c",
--- a/testing/web-platform/tests/webdriver/tests/contexts/maximize_window.py
+++ b/testing/web-platform/tests/webdriver/tests/contexts/maximize_window.py
@@ -1,67 +1,87 @@
 from tests.support.inline import inline
 from tests.support.asserts import assert_error, assert_success
 
+
 alert_doc = inline("<script>window.alert()</script>")
 
+
+def maximize(session):
+    return session.transport.send("POST", "session/%s/window/maximize" % session.session_id)
+
+
 # 10.7.3 Maximize Window
-def test_maximize_no_browsing_context(session, create_window):
-    # Step 1
+
+
+def test_no_browsing_context(session, create_window):
+    # step 1
     session.window_handle = create_window()
     session.close()
-    result = session.transport.send("POST", "session/%s/window/maximize" % session.session_id)
-    assert_error(result, "no such window")
+    response = maximize(session)
+    assert_error(response, "no such window")
 
 
 def test_handle_user_prompt(session):
-    # Step 2
+    # step 2
     session.url = alert_doc
-    result = session.transport.send("POST", "session/%s/window/maximize" % session.session_id)
-    assert_error(result, "unexpected alert open")
+    response = maximize(session)
+    assert_error(response, "unexpected alert open")
 
 
 def test_maximize(session):
     before_size = session.window.size
     assert session.window.state == "normal"
 
     # step 4
-    result = session.transport.send("POST", "session/%s/window/maximize" % session.session_id)
-    assert_success(result)
+    response = maximize(session)
+    assert_success(response)
 
     assert before_size != session.window.size
     assert session.window.state == "maximized"
 
 
 def test_payload(session):
     before_size = session.window.size
     assert session.window.state == "normal"
 
-    result = session.transport.send("POST", "session/%s/window/maximize" % session.session_id)
+    response = maximize(session)
 
     # step 5
-    assert result.status == 200
-    assert isinstance(result.body["value"], dict)
+    assert response.status == 200
+    assert isinstance(response.body["value"], dict)
 
-    rect = result.body["value"]
-    assert "width" in rect
-    assert "height" in rect
-    assert "x" in rect
-    assert "y" in rect
-    assert "state" in rect
-    assert isinstance(rect["width"], (int, float))
-    assert isinstance(rect["height"], (int, float))
-    assert isinstance(rect["x"], (int, float))
-    assert isinstance(rect["y"], (int, float))
-    assert isinstance(rect["state"], basestring)
+    value = response.body["value"]
+    assert "width" in value
+    assert "height" in value
+    assert "x" in value
+    assert "y" in value
+    assert "state" in value
+    assert isinstance(value["width"], (int, float))
+    assert isinstance(value["height"], (int, float))
+    assert isinstance(value["x"], (int, float))
+    assert isinstance(value["y"], (int, float))
+    assert isinstance(value["state"], basestring)
 
     assert before_size != session.window.size
     assert session.window.state == "maximized"
 
 
+def test_maximize_twice_is_idempotent(session):
+    assert session.window.state == "normal"
+
+    first_response = maximize(session)
+    assert_success(first_response)
+    assert session.window.state == "maximized"
+
+    second_response = maximize(session)
+    assert_success(second_response)
+    assert session.window.state == "maximized"
+
+
 def test_maximize_when_resized_to_max_size(session):
     # Determine the largest available window size by first maximising
     # the window and getting the window rect dimensions.
     #
     # Then resize the window to the maximum available size.
     session.end()
     assert session.window.state == "normal"
     available = session.window.maximize()
--- a/testing/web-platform/tests/webdriver/tests/set_window_rect.py
+++ b/testing/web-platform/tests/webdriver/tests/set_window_rect.py
@@ -82,37 +82,89 @@ def test_handle_prompt_missing_value(ses
     {"height": None, "width": None, "x": {}, "y": {}},
 ])
 def test_invalid_params(session, data):
     # step 8-9
     response = set_window_rect(session, data)
     assert_error(response, "invalid argument")
 
 
-def test_fullscreened(session):
-    # step 10
+def test_fully_exit_fullscreen(session):
+    """
+    10. Fully exit fullscreen.
+
+    [...]
+
+    To fully exit fullscreen a document document, run these steps:
+
+      1. If document's fullscreen element is null, terminate these steps.
+
+      2. Unfullscreen elements whose fullscreen flag is set, within
+      document's top layer, except for document's fullscreen element.
+
+      3. Exit fullscreen document.
+
+    """
+
     session.window.fullscreen()
-    assert session.window.state == "fullscreen"
+    assert session.execute_script("return window.fullScreen") is True
+
     response = set_window_rect(session, {"width": 400, "height": 400})
-    assert_success(response)
-    assert rect["width"] == 400
-    assert rect["height"] == 400
-    assert rect["state"] == "normal"
+    value = assert_success(response)
+    assert value["width"] == 400
+    assert value["height"] == 400
+    assert value["state"] == "normal"
 
 
-def test_minimized(session):
-    # step 11
+def test_restore_window_from_minimized(session):
+    """
+    11. Restore the window.
+
+    [...]
+
+    To restore the window, given an operating system level window with an
+    associated top-level browsing context, run implementation-specific
+    steps to restore or unhide the window to the visible screen. Do not
+    return from this operation until the visibility state of the top-level
+    browsing context's active document has reached the visible state,
+    or until the operation times out.
+    """
+
     session.window.minimize()
-    assert session.window.state == "minimized"
+    assert session.execute_script("return document.hidden") is True
+
+    response = set_window_rect(session, {"width": 450, "height": 450})
+    value = assert_success(response)
+    assert value["width"] == 450
+    assert value["height"] == 450
+    assert value["state"] == "normal"
 
-    response = set_window_rect(session, {"width": 400, "height": 400})
-    rect = assert_success(response)
-    assert rect["width"] == 400
-    assert rect["height"] == 400
-    assert rect["state"] == "normal"
+
+def test_restore_window_from_maximized(session):
+    """
+    11. Restore the window.
+
+    [...]
+
+    To restore the window, given an operating system level window with an
+    associated top-level browsing context, run implementation-specific
+    steps to restore or unhide the window to the visible screen. Do not
+    return from this operation until the visibility state of the top-level
+    browsing context’s active document has reached the visible state,
+    or until the operation times out.
+    """
+
+    session.window.maximize()
+    assert session.window.state == "maximized"
+
+    response = set_window_rect(session, {"width": 500, "height": 500})
+    value = assert_success(response)
+    assert value["width"] == 500
+    assert value["height"] == 500
+    assert value["state"] == "normal"
 
 
 def test_height_width(session):
     original = session.window.rect
     max = session.execute_script("""
         return {
           width: window.screen.availWidth,
           height: window.screen.availHeight,