Bug 1259055 - Use window management class for handling of new tabs. draft
authorHenrik Skupin <mail@hskupin.info>
Wed, 19 Oct 2016 16:48:23 +0200
changeset 427738 9156f6d7d150e4d0f3feaadd5c551bb4846d5d17
parent 427476 bf7d8564625c25b550f8e91c3fd799ddc26bf7fe
child 427739 ecf05e1342b24fafb6433764b344b181bbdc640a
push id33107
push userbmo:hskupin@gmail.com
push dateThu, 20 Oct 2016 21:03:00 +0000
bugs1259055
milestone52.0a1
Bug 1259055 - Use window management class for handling of new tabs. MozReview-Commit-ID: Le8ltn1IbU4
testing/marionette/harness/marionette/tests/unit/mixin_utils.py
testing/marionette/harness/marionette/tests/unit/test_about_pages.py
testing/marionette/harness/marionette/tests/unit/test_window_handles.py
testing/marionette/harness/marionette/tests/unit/test_window_management.py
new file mode 100644
--- /dev/null
+++ b/testing/marionette/harness/marionette/tests/unit/mixin_utils.py
@@ -0,0 +1,68 @@
+# 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 sys
+import unittest
+
+from marionette_driver import By, Wait
+
+
+class WindowManagementMixin(unittest.TestCase):
+
+    _menu_item_new_tab = (By.ID, "menu_newNavigatorTab")
+
+    def setUp(self):
+        super(WindowManagementMixin, self).setUp()
+
+        self.start_tab = self.marionette.current_window_handle
+        self.start_tabs = self.marionette.window_handles
+
+    def tearDown(self):
+        try:
+            if len(self.marionette.window_handles) != len(self.start_tabs):
+                raise Exception("Not all tabs as opened by the test have been closed")
+        finally:
+            super(WindowManagementMixin, self).tearDown()
+
+    def close_all_tabs(self):
+        current_window_handles = self.marionette.window_handles
+
+        # If the start tab is not present anymore, use the next one of the list
+        if self.start_tab not in current_window_handles:
+            self.start_tab = current_window_handles[0]
+
+        current_window_handles.remove(self.start_tab)
+        for handle in current_window_handles:
+            self.marionette.switch_to_window(handle)
+            self.marionette.close()
+
+            # Bug 1311350 - close() doesn't wait for tab to be closed.
+            Wait(self.marionette).until(
+                lambda mn: handle not in mn.window_handles,
+                message="Failed to close tab with handle {}".format(handle)
+            )
+
+        self.marionette.switch_to_window(self.start_tab)
+
+    def open_tab(self, trigger="menu"):
+        current_tabs = self.marionette.window_handles
+
+        try:
+            if callable(trigger):
+                trigger()
+            elif trigger == 'menu':
+                with self.marionette.using_context("chrome"):
+                    self.marionette.find_element(*self._menu_item_new_tab).click()
+        except Exception:
+            exc, val, tb = sys.exc_info()
+            raise exc, 'Failed to trigger opening a new tab: {}'.format(val), tb
+        else:
+            Wait(self.marionette).until(
+                lambda mn: len(mn.window_handles) == len(current_tabs) + 1,
+                message="No new tab has been opened"
+            )
+
+            [new_tab] = list(set(self.marionette.window_handles) - set(current_tabs))
+
+            return new_tab
--- a/testing/marionette/harness/marionette/tests/unit/test_about_pages.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_about_pages.py
@@ -1,93 +1,113 @@
 # 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 os
+import sys
+
 from marionette import MarionetteTestCase
-from marionette.marionette_test import skip_if_e10s
 from marionette_driver.keys import Keys
 from marionette_driver.by import By
 
+sys.path.append(os.path.dirname(__file__))
+from mixin_utils import WindowManagementMixin
 
-class TestAboutPages(MarionetteTestCase):
+
+class TestAboutPages(MarionetteTestCase, WindowManagementMixin):
 
     def setUp(self):
-        MarionetteTestCase.setUp(self)
+        super(TestAboutPages, self).setUp()
+
         if self.marionette.session_capabilities['platformName'] == 'darwin':
             self.mod_key = Keys.META
         else:
             self.mod_key = Keys.CONTROL
 
         self.remote_uri = self.marionette.absolute_url("javascriptPage.html")
         self.marionette.navigate(self.remote_uri)
 
+    def tearDown(self):
+        self.close_all_tabs()
+
+        super(TestAboutPages, self).tearDown()
+
     def test_back_forward(self):
+        # Bug 1311041 - Prevent changing of window handle by forcing the test
+        # to be run in a new tab.
+        new_tab = self.open_tab(trigger='menu')
+        self.marionette.switch_to_window(new_tab)
+
         self.marionette.navigate("about:blank")
         self.marionette.navigate(self.remote_uri)
 
         self.marionette.navigate("about:preferences")
 
         self.marionette.go_back()
 
         self.wait_for_condition(
             lambda mn: mn.get_url() == self.remote_uri)
 
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
+
     def test_navigate_non_remote_about_pages(self):
+        # Bug 1311041 - Prevent changing of window handle by forcing the test
+        # to be run in a new tab.
+        new_tab = self.open_tab(trigger='menu')
+        self.marionette.switch_to_window(new_tab)
+
         self.marionette.navigate("about:blank")
         self.assertEqual(self.marionette.get_url(), "about:blank")
         self.marionette.navigate("about:preferences")
         self.assertEqual(self.marionette.get_url(), "about:preferences")
 
-    def test_navigate_shortcut_key(self):
-        self.marionette.navigate("about:preferences")
-        self.marionette.navigate(self.remote_uri)
-        self.marionette.navigate("about:blank")
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
 
-        start_win = self.marionette.current_window_handle
-        start_win_handles = self.marionette.window_handles
-        with self.marionette.using_context("chrome"):
-            main_win = self.marionette.find_element(By.ID, "main-window")
-            main_win.send_keys(self.mod_key, Keys.SHIFT, 'a')
+    def test_navigate_shortcut_key(self):
+        def open_with_shortcut():
+            with self.marionette.using_context("chrome"):
+                main_win = self.marionette.find_element(By.ID, "main-window")
+                main_win.send_keys(self.mod_key, Keys.SHIFT, 'a')
+
+        new_tab = self.open_tab(trigger=open_with_shortcut)
+        self.marionette.switch_to_window(new_tab)
 
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
-        self.assertEqual(start_win, self.marionette.current_window_handle)
-        [new_tab] = list(set(self.marionette.window_handles) - set(start_win_handles))
+        self.wait_for_condition(lambda mn: mn.get_url() == "about:addons")
+
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
+
+    def test_type_to_non_remote_tab(self):
+        # Bug 1311041 - Prevent changing of window handle by forcing the test
+        # to be run in a new tab.
+        new_tab = self.open_tab(trigger='menu')
         self.marionette.switch_to_window(new_tab)
-        self.wait_for_condition(lambda mn: mn.get_url() == "about:addons")
-        self.marionette.close()
-        self.marionette.switch_to_window(start_win)
 
-    @skip_if_e10s
-    def test_type_to_non_remote_tab(self):
         with self.marionette.using_context("chrome"):
             urlbar = self.marionette.find_element(By.ID, 'urlbar')
             urlbar.send_keys(self.mod_key + 'a')
             urlbar.send_keys(self.mod_key + 'x')
             urlbar.send_keys('about:preferences' + Keys.ENTER)
         self.wait_for_condition(lambda mn: mn.get_url() == "about:preferences")
 
+        self.marionette.close()
+        self.marionette.switch_to_window(self.start_tab)
+
     def test_type_to_remote_tab(self):
-        self.marionette.navigate("about:preferences")
+        # about:blank keeps remoteness from remote_uri
+        self.marionette.navigate("about:blank")
         with self.marionette.using_context("chrome"):
             urlbar = self.marionette.find_element(By.ID, 'urlbar')
-            urlbar.send_keys(self.mod_key + 'a')
-            urlbar.send_keys(self.mod_key + 'x')
             urlbar.send_keys(self.remote_uri + Keys.ENTER)
 
         self.wait_for_condition(lambda mn: mn.get_url() == self.remote_uri)
 
     def test_hang(self):
-        self.marionette.set_context('chrome')
-        initial_tab = self.marionette.window_handles[0]
-
         # Open a new tab and close the first one
-        new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
-        new_tab.click()
-
-        new_tab = [handle for handle in self.marionette.window_handles if
-                   handle != initial_tab][0]
+        new_tab = self.open_tab(trigger="menu")
 
         self.marionette.close()
         self.marionette.switch_to_window(new_tab)
 
-        with self.marionette.using_context('content'):
-            self.marionette.navigate(self.remote_uri)
+        self.marionette.navigate(self.remote_uri)
--- a/testing/marionette/harness/marionette/tests/unit/test_window_handles.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_window_handles.py
@@ -1,98 +1,105 @@
 # 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 os
+import sys
+
 from marionette import MarionetteTestCase
 from marionette_driver.keys import Keys
 from marionette_driver import By
 
+sys.path.append(os.path.dirname(__file__))
+from mixin_utils import WindowManagementMixin
 
-class TestWindowHandles(MarionetteTestCase):
+
+class TestWindowHandles(MarionetteTestCase, WindowManagementMixin):
+
+    def tearDown(self):
+        try:
+            self.close_all_tabs()
+        finally:
+            super(TestWindowHandles, self).tearDown()
 
     def test_new_tab_window_handles(self):
-
         keys = []
         if self.marionette.session_capabilities['platformName'] == 'darwin':
             keys.append(Keys.META)
         else:
             keys.append(Keys.CONTROL)
         keys.append('t')
 
-        origin_win = self.marionette.current_window_handle
+        def open_with_shortcut():
+            with self.marionette.using_context("chrome"):
+                main_win = self.marionette.find_element(By.ID, "main-window")
+                main_win.send_keys(*keys)
 
-        with self.marionette.using_context("chrome"):
-            main_win = self.marionette.find_element(By.ID, "main-window")
-            main_win.send_keys(*keys)
-
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
-        handles = self.marionette.window_handles
-        handles.remove(origin_win)
-        new_tab = handles.pop()
+        new_tab = self.open_tab(trigger=open_with_shortcut)
+        self.assertEqual(self.marionette.current_window_handle, self.start_tab)
         self.marionette.switch_to_window(new_tab)
         self.assertEqual(self.marionette.get_url(), "about:newtab")
+
         self.marionette.close()
-
-        self.marionette.switch_to_window(origin_win)
-        self.assertEqual(self.marionette.get_url(), "about:blank")
+        self.marionette.switch_to_window(self.start_tab)
 
     def test_new_tab_window_handles_no_switch(self):
         """Regression test for bug 1294456.
         This test is testing the case where Marionette attempts to send a
         command to a window handle when the browser has opened and selected
         a new tab. Before bug 1294456 landed, the Marionette driver was getting
         confused about which window handle the client cared about, and assumed
         it was the window handle for the newly opened and selected tab.
 
         This caused Marionette to think that the browser needed to do a remoteness
         flip in the e10s case, since the tab opened by menu_newNavigatorTab is
         about:newtab (which is currently non-remote). This meant that commands
         sent to what should have been the original window handle would be
         queued and never sent, since the remoteness flip in the new tab was
         never going to happen.
         """
+        def open_with_menu():
+            with self.marionette.using_context("chrome"):
+                menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
+                menu_new_tab.click()
 
-        with self.marionette.using_context("chrome"):
-            menu_new_tab = self.marionette.find_element(By.ID, 'menu_newNavigatorTab')
-            menu_new_tab.click()
-
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
+        new_tab = self.open_tab(trigger=open_with_menu)
+        self.assertEqual(self.marionette.current_window_handle, self.start_tab)
 
         # We still have the default tab set as our window handle. This
         # get_url command should be sent immediately, and not be forever-queued.
         self.assertEqual(self.marionette.get_url(), "about:blank")
 
-        self.marionette.switch_to_window(self.marionette.window_handles[1])
+        self.marionette.switch_to_window(new_tab)
         self.marionette.close()
-        self.marionette.switch_to_window(self.marionette.window_handles[0])
+        self.marionette.switch_to_window(self.start_tab)
 
     def test_link_opened_tab_window_handles(self):
         tab_testpage = self.marionette.absolute_url("windowHandles.html")
         self.marionette.navigate(tab_testpage)
-        start_win = self.marionette.current_window_handle
-        link = self.marionette.find_element(By.ID, "new-tab")
-        link.click()
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
+
+        def open_with_link():
+            link = self.marionette.find_element(By.ID, "new-tab")
+            link.click()
 
-        handles = self.marionette.window_handles
-        handles.remove(start_win)
-        dest_win = handles.pop()
+        new_tab = self.open_tab(trigger=open_with_link)
+        self.assertEqual(self.marionette.current_window_handle, self.start_tab)
 
-        self.marionette.switch_to_window(dest_win)
+        self.marionette.switch_to_window(new_tab)
         self.assertEqual(self.marionette.get_url(), "about:blank")
         self.assertEqual(self.marionette.title, "")
 
-        self.marionette.switch_to_window(start_win)
+        self.marionette.switch_to_window(self.start_tab)
 
-        self.assertIn('windowHandles.html', self.marionette.get_url())
+        self.assertEqual(self.marionette.get_url(), tab_testpage)
         self.assertEqual(self.marionette.title, "Marionette New Tab Link")
 
         self.marionette.close()
-        self.marionette.switch_to_window(dest_win)
+        self.marionette.switch_to_window(new_tab)
         self.assertEqual(self.marionette.get_url(), "about:blank")
 
     def test_chrome_windows(self):
         opener_page = self.marionette.absolute_url("windowHandles.html")
 
         self.marionette.navigate(opener_page)
 
         start_win = self.marionette.current_chrome_window_handle
@@ -146,68 +153,60 @@ class TestWindowHandles(MarionetteTestCa
 
         self.marionette.switch_to_window(start_tab)
 
         self.assertEqual(len(self.marionette.chrome_window_handles), 1)
         self.assertEqual(len(self.marionette.window_handles), 1)
         self.assertEqual(self.marionette.current_window_handle, start_tab)
 
     def test_tab_and_window_handles(self):
-        start_tab = self.marionette.current_window_handle
         start_chrome_window = self.marionette.current_chrome_window_handle
+
         tab_open_page = self.marionette.absolute_url("windowHandles.html")
         window_open_page = self.marionette.absolute_url("test_windows.html")
+        results_page = self.marionette.absolute_url("resultPage.html")
 
         # Open a new tab and switch to it.
-        self.marionette.navigate(tab_open_page)
-        link = self.marionette.find_element(By.ID, "new-tab")
-        link.click()
+        def open_tab_with_link():
+            self.marionette.navigate(tab_open_page)
+            link = self.marionette.find_element(By.ID, "new-tab")
+            link.click()
 
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 2)
+        second_tab = self.open_tab(trigger=open_tab_with_link)
         self.assertEqual(len(self.marionette.chrome_window_handles), 1)
         self.assertEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
 
-        handles = self.marionette.window_handles
-        handles.remove(start_tab)
-
-        new_tab = handles.pop()
-        self.marionette.switch_to_window(new_tab)
+        self.marionette.switch_to_window(second_tab)
         self.assertEqual(self.marionette.get_url(), "about:blank")
 
-        # Open a new window from the new tab.
-        self.marionette.navigate(window_open_page)
+        # Open a new window from the new tab and only care about the second new tab
+        def open_win_with_link():
+            self.marionette.navigate(window_open_page)
+            link = self.marionette.find_element(By.LINK_TEXT, "Open new window")
+            link.click()
 
-        link = self.marionette.find_element(By.LINK_TEXT, "Open new window")
-        link.click()
-        self.wait_for_condition(lambda mn: len(mn.window_handles) == 3)
-
+        third_tab = self.open_tab(trigger=open_win_with_link)
         self.assertEqual(len(self.marionette.chrome_window_handles), 2)
         self.assertEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
 
-        # Find the new window and switch to it.
-        handles = self.marionette.window_handles
-        handles.remove(start_tab)
-        handles.remove(new_tab)
-        new_window = handles.pop()
-
-        self.marionette.switch_to_window(new_window)
-        results_page = self.marionette.absolute_url("resultPage.html")
+        # Check that the new tab has the correct page loaded
+        self.marionette.switch_to_window(third_tab)
         self.assertEqual(self.marionette.get_url(), results_page)
 
         self.assertEqual(len(self.marionette.chrome_window_handles), 2)
         self.assertNotEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
 
         # Return to our original tab and close it.
-        self.marionette.switch_to_window(start_tab)
+        self.marionette.switch_to_window(self.start_tab)
         self.marionette.close()
         self.assertEquals(len(self.marionette.window_handles), 2)
 
-        # Close the opened window and carry on in our new tab.
-        self.marionette.switch_to_window(new_window)
+        # Close the opened window and carry on in our second tab.
+        self.marionette.switch_to_window(third_tab)
         self.marionette.close()
         self.assertEqual(len(self.marionette.window_handles), 1)
 
-        self.marionette.switch_to_window(new_tab)
+        self.marionette.switch_to_window(second_tab)
         self.assertEqual(self.marionette.get_url(), results_page)
         self.marionette.navigate("about:blank")
 
         self.assertEqual(len(self.marionette.chrome_window_handles), 1)
         self.assertEqual(self.marionette.current_chrome_window_handle, start_chrome_window)
--- a/testing/marionette/harness/marionette/tests/unit/test_window_management.py
+++ b/testing/marionette/harness/marionette/tests/unit/test_window_management.py
@@ -4,38 +4,39 @@
 
 import time
 from marionette import MarionetteTestCase
 from marionette_driver import By, Wait
 
 
 class TestSwitchWindow(MarionetteTestCase):
     def open_new_window(self):
-        self.marionette.set_context("chrome")
-        self.marionette.set_script_timeout(5000)
-        self.marionette.execute_async_script("""
-var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                   .getService(Components.interfaces.nsIWindowWatcher);
-var win = ww.openWindow(null, "chrome://browser/content/browser.xul", "testWin", null, null);
-win.addEventListener("load", function() {
-  win.removeEventListener("load", arguments.callee, true);
-  marionetteScriptFinished();
-}, null);
-""")
-        self.marionette.set_context("content")
+        with self.marionette.using_context("chrome"):
+            print '**** %s' % self.marionette.execute_async_script("""
+              var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
+                                  getService(Components.interfaces.nsIWindowWatcher);
+              var win = ww.openWindow(null, "chrome://browser/content/browser.xul",
+                                      "testWin", null, null);
+              win.addEventListener("load", function() {
+                marionetteScriptFinished(true);
+              }, {once: true});
+              dump(win.readyState);
+              if (win.readyState == 'loaded')
+                marionetteScriptFinished(true);
+            """)
 
     def close_new_window(self):
         self.marionette.set_context("chrome")
         self.marionette.execute_script("""
-var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
-                   .getService(Components.interfaces.nsIWindowWatcher);
-var win = ww.getWindowByName("testWin", null);
-if (win != null)
-  win.close();
-""")
+          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
+                              getService(Components.interfaces.nsIWindowWatcher);
+          var win = ww.getWindowByName("testWin", null);
+          if (win != null)
+            win.close();
+        """)
         self.marionette.set_context("content")
 
     def test_windows(self):
         orig_win = self.marionette.current_chrome_window_handle
         orig_available = self.marionette.chrome_window_handles
         self.open_new_window()
         # assert we're still in the original window
         self.assertEqual(self.marionette.current_chrome_window_handle, orig_win)