[Pkg-mozext-commits] [requestpolicy] 127/257: [tst][ref] Add-On Library: Replace UI lib by API lib

David Prévot taffit at moszumanska.debian.org
Thu Jan 28 03:20:05 UTC 2016


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository requestpolicy.

commit e2b116acdb56cff727239baeb5e29e27e9d25e7e
Author: Martin Kimmerle <dev at 256k.de>
Date:   Wed Nov 4 18:27:19 2015 +0100

    [tst][ref] Add-On Library: Replace UI lib by API lib
    
    Replace the Puppeteer library "ui/addons.py" by "api/addon.py".
    The old library used the UI of the "about:addons` page, whereas
    the new library uses directly the `AddonManager` API inside
    Firefox. This is a much cleaner way of doing install, uninstall,
    enable and disable actions to Add-Ons, and therefore the library
    is also easier to maintain.
---
 tests/marionette/rp_puppeteer/__init__.py          |   4 +
 tests/marionette/rp_puppeteer/api/addon.py         | 257 ++++++++++++++++
 tests/marionette/rp_puppeteer/tests/manifest.ini   |   2 +-
 tests/marionette/rp_puppeteer/tests/test_addon.py  |  68 +++++
 tests/marionette/rp_puppeteer/tests/test_addons.py | 172 -----------
 tests/marionette/rp_puppeteer/ui/addons.py         | 339 ---------------------
 .../test_multiple_installations.py                 | 214 +++++++------
 .../test_restartlessness.py                        |  74 +++--
 tests/marionette/tests/test_setup_page.py          |  30 +-
 9 files changed, 516 insertions(+), 644 deletions(-)

diff --git a/tests/marionette/rp_puppeteer/__init__.py b/tests/marionette/rp_puppeteer/__init__.py
index c770a28..5b778f8 100644
--- a/tests/marionette/rp_puppeteer/__init__.py
+++ b/tests/marionette/rp_puppeteer/__init__.py
@@ -28,6 +28,10 @@ class RequestPolicyPuppeteer(object):
     def requests(self):
         pass
 
+    @use_class_as_property('api.addon.RequestPolicy')
+    def rp_addon(self):
+        pass
+
     @use_class_as_property('api.rules.Rules')
     def rules(self):
         pass
diff --git a/tests/marionette/rp_puppeteer/api/addon.py b/tests/marionette/rp_puppeteer/api/addon.py
new file mode 100644
index 0000000..797b5a4
--- /dev/null
+++ b/tests/marionette/rp_puppeteer/api/addon.py
@@ -0,0 +1,257 @@
+# 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.wait import Wait
+from firefox_puppeteer.base import BaseLib
+from rp_puppeteer.api.error_detection import (LoggingErrorDetection,
+                                              ConsoleErrorDetection)
+from contextlib import contextmanager
+
+
+class Addon(BaseLib):
+    """Class to install/uninstall/enable/disable an Add-On."""
+
+    sandbox = "rp-puppeteer-addons"
+
+    def __init__(self, marionette_getter, addon_id, install_url):
+        super(Addon, self).__init__(marionette_getter)
+        self.addon_id = addon_id
+        self.install_url = install_url
+
+    #################################
+    # Public Properties and Methods #
+    #################################
+
+    def install(self):
+        with self._addon_install():
+            # Start installing.
+            self.marionette.execute_script("""
+              var globalScope = this;
+              globalScope.addonInstall.install();
+            """, sandbox=self.sandbox, new_sandbox=False)
+
+            def is_installation_finished(_):
+                return self.marionette.execute_script("""
+                  Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+                  var globalScope = this;
+                  var state = globalScope.addonInstall.state;
+                  return state === AddonManager.STATE_INSTALLED;
+                """, sandbox=self.sandbox, new_sandbox=False)
+
+            # Wait until installed.
+            Wait(self.marionette).until(is_installation_finished)
+
+    def uninstall(self):
+        with self._addon():
+            return self.marionette.execute_script("""
+              var globalScope = this;
+              var addon = globalScope.addon;
+
+              if (addon !== null) {
+                addon.uninstall();
+              }
+            """, sandbox=self.sandbox, new_sandbox=False)
+
+        Wait(self.marionette).until(lambda _: not self.is_installed)
+
+    def enable(self):
+        self._set_user_disabled(False)
+        Wait(self.marionette).until(lambda _: self.is_active)
+
+    def disable(self):
+        self._set_user_disabled(True)
+        Wait(self.marionette).until(lambda _: not self.is_active)
+
+    @property
+    def is_installed(self):
+        with self._addon():
+            return self.marionette.execute_script("""
+              Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+              var globalScope = this;
+              var addon = globalScope.addon;
+
+              if (addon !== null) {
+                return true;
+              }
+              return false;
+            """, sandbox=self.sandbox, new_sandbox=False)
+
+    @property
+    def is_active(self):
+        with self._addon():
+            return self.marionette.execute_script("""
+              var globalScope = this;
+              var addon = globalScope.addon;
+
+              if (addon !== null) {
+                return addon.isActive;
+              }
+              return false;
+            """, sandbox=self.sandbox, new_sandbox=False)
+
+    ##################################
+    # Private Properties and Methods #
+    ##################################
+
+    @contextmanager
+    def _addon(self):
+        """Provide an `Addon` object in the sandbox."""
+
+        self.marionette.execute_script("""
+          Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+          var addonID = arguments[0];
+
+          var globalScope = this;
+          globalScope.addon = null;
+          globalScope.addonCallbackCalled = false;
+
+          var addonCallback = function (addon) {
+            globalScope.addon = addon;
+            globalScope.addonCallbackCalled = true;
+          };
+
+          AddonManager.getAddonByID(addonID, addonCallback);
+        """, sandbox=self.sandbox, new_sandbox=False, script_args=[self.addon_id])
+
+        self._wait_until_true("addonCallbackCalled")
+
+        try:
+            yield
+        finally:
+            # Cleanup
+            self.marionette.execute_script("""
+              var globalScope = this;
+              delete globalScope.addon;
+              delete globalScope.addonCallbackCalled;
+            """, sandbox=self.sandbox, new_sandbox=False)
+
+    @contextmanager
+    def _addon_install(self):
+        """Provide an `AddonInstall` object in the sandbox."""
+
+        self.marionette.execute_script("""
+          Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+          var installUrl = arguments[0];
+
+          var globalScope = this;
+          globalScope.addonInstall = null;
+          globalScope.installCallbackCalled = false;
+
+          var installCallback = function (addonInstall) {
+            globalScope.addonInstall = addonInstall;
+            globalScope.installCallbackCalled = true;
+          };
+
+          AddonManager.getInstallForURL(installUrl, installCallback,
+                                        "application/x-xpinstall");
+        """, sandbox=self.sandbox, new_sandbox=False, script_args=[self.install_url])
+
+        self._wait_until_true("installCallbackCalled")
+
+        try:
+            yield
+        finally:
+            # Cleanup
+            self.marionette.execute_script("""
+              var globalScope = this;
+              delete globalScope.addonInstall;
+              delete globalScope.installCallbackCalled;
+            """, sandbox=self.sandbox, new_sandbox=False)
+
+    def _wait_until_true(self, variable_name):
+        def is_true(_):
+            return self.marionette.execute_script(
+                """
+                  var globalScope = this;
+                  return globalScope[arguments[0]] === true;
+                """, sandbox=self.sandbox, new_sandbox=False,
+                script_args=[variable_name])
+
+        Wait(self.marionette).until(is_true)
+
+    def _set_user_disabled(self, value):
+        assert isinstance(value, bool)
+        with self._addon():
+            return self.marionette.execute_script("""
+              var globalScope = this;
+              var addon = globalScope.addon;
+
+              if (addon !== null) {
+                addon.userDisabled = arguments[0];
+              }
+            """, sandbox=self.sandbox, new_sandbox=False, script_args=[value])
+
+
+class RequestPolicy(Addon):
+    """Class to install/uninstall/enable/disable RequestPolicy."""
+
+    ignore_errors = False
+
+    def __init__(self, marionette_getter):
+        Addon.__init__(self, marionette_getter,
+                       addon_id="rpcontinued at non-amo.requestpolicy.org",
+                       install_url=("http://localhost/.dist/"
+                                    "requestpolicy-unit-testing.xpi"))
+
+        self.pref_welcome_win = "extensions.requestpolicy.welcomeWindowShown"
+
+        self.logging_error_detect = LoggingErrorDetection(marionette_getter)
+        self.console_error_detect = ConsoleErrorDetection(marionette_getter)
+
+    #################################
+    # Public Properties and Methods #
+    #################################
+
+    def install(self, ignore_errors=False):
+        with self._ensure_no_errors():
+            super(RequestPolicy, self).install()
+
+    def uninstall(self, ignore_errors=False):
+        with self._ensure_no_errors():
+            super(RequestPolicy, self).uninstall()
+
+    def enable(self, ignore_errors=False):
+        with self._ensure_no_errors():
+            super(RequestPolicy, self).enable()
+
+    def disable(self, ignore_errors=False):
+        with self._ensure_no_errors():
+            super(RequestPolicy, self).disable()
+
+    ##################################
+    # Private Properties and Methods #
+    ##################################
+
+    @contextmanager
+    def _ensure_no_errors(self):
+        if self.ignore_errors:
+            yield
+            return
+
+        logging_errors_before = self.logging_error_detect.n_errors
+        console_errors_before = self.console_error_detect.n_errors
+        yield
+        logging_error_count = (self.logging_error_detect.n_errors -
+                               logging_errors_before)
+        console_error_count = (self.console_error_detect.n_errors -
+                               console_errors_before)
+        console_error_messages = self.console_error_detect.messages
+
+        if logging_error_count > 0 and console_error_count > 0:
+            raise Exception("There have been {} Logging errors and "
+                            "{} Console errors! "
+                            "Console error messages were: {}"
+                            .format(logging_error_count, console_error_count,
+                                    console_error_messages))
+        elif logging_error_count > 0:
+            raise Exception("There have been {} Logging "
+                            "errors!".format(logging_error_count))
+        elif console_error_count > 0:
+            raise Exception("There have been {} Console  errors! "
+                            "Messages were: {}".format(console_error_count,
+                                                       console_error_messages))
diff --git a/tests/marionette/rp_puppeteer/tests/manifest.ini b/tests/marionette/rp_puppeteer/tests/manifest.ini
index ffd1e0f..bf2d45a 100644
--- a/tests/marionette/rp_puppeteer/tests/manifest.ini
+++ b/tests/marionette/rp_puppeteer/tests/manifest.ini
@@ -1,4 +1,4 @@
-[test_addons.py]
+[test_addon.py]
 [test_context_menu.py]
 [test_error_detection.py]
 [test_l10n.py]
diff --git a/tests/marionette/rp_puppeteer/tests/test_addon.py b/tests/marionette/rp_puppeteer/tests/test_addon.py
new file mode 100644
index 0000000..49df254
--- /dev/null
+++ b/tests/marionette/rp_puppeteer/tests/test_addon.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/.
+
+from rp_ui_harness import RequestPolicyTestCase
+from rp_puppeteer.api.addon import Addon
+
+
+class TestAddons(RequestPolicyTestCase):
+
+    def setUp(self):
+        super(TestAddons, self).setUp()
+        self.addon = Addon(lambda: self.marionette,
+                           addon_id="dummy-ext at requestpolicy.org",
+                           install_url="http://localhost/.dist/dummy-ext.xpi")
+        self.addon.install()
+
+    def tearDown(self):
+        try:
+            self.addon.uninstall()
+        finally:
+            super(TestAddons, self).tearDown()
+
+    def test_uninstall_install(self):
+        self.assertTrue(self.addon.is_installed)
+        self.addon.uninstall()
+        self.assertFalse(self.addon.is_installed)
+        self.addon.install()
+        self.assertTrue(self.addon.is_installed)
+
+    def test_multiple_uninstall_install(self):
+        self.addon.uninstall()
+        self.addon.uninstall()
+        self.addon.install()
+        self.addon.install()
+
+    def test_disable_enable(self):
+        self.assertTrue(self.addon.is_active)
+        self.addon.disable()
+        self.assertFalse(self.addon.is_active)
+        self.addon.enable()
+        self.assertTrue(self.addon.is_active)
+
+    def test_multiple_disable_enable(self):
+        self.addon.disable()
+        self.addon.disable()
+        self.addon.enable()
+        self.addon.enable()
+
+    def test_is_installed(self):
+        self.assertTrue(self.addon.is_installed)
+        self.addon.disable()
+        self.assertTrue(self.addon.is_installed)
+        self.addon.uninstall()
+        self.assertFalse(self.addon.is_installed)
+        self.addon.install()
+        self.assertTrue(self.addon.is_installed)
+        self.addon.enable()
+
+    def test_is_active(self):
+        self.assertTrue(self.addon.is_active)
+        self.addon.disable()
+        self.assertFalse(self.addon.is_active)
+        self.addon.uninstall()
+        self.assertFalse(self.addon.is_active)
+        self.addon.install()
+        self.addon.enable()
+        self.assertTrue(self.addon.is_active)
diff --git a/tests/marionette/rp_puppeteer/tests/test_addons.py b/tests/marionette/rp_puppeteer/tests/test_addons.py
deleted file mode 100644
index e28fba3..0000000
--- a/tests/marionette/rp_puppeteer/tests/test_addons.py
+++ /dev/null
@@ -1,172 +0,0 @@
-# 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 rp_ui_harness import RequestPolicyTestCase
-
-from rp_puppeteer.ui.addons import Addons, AboutAddonsTab
-
-
-ADDON_ID = "dummy-ext at requestpolicy.org"
-INSTALL_URL = "http://localhost/link.html?.dist/dummy-ext.xpi"
-
-
-class AddonsTestCase(RequestPolicyTestCase):
-    def selected_tab_is_about_addons(self):
-        location = self.browser.tabbar.selected_tab.location
-        return location == "about:addons"
-
-    def extension_category_is_selected(self):
-        if not self.selected_tab_is_about_addons():
-            return False
-        with self.marionette.using_context("content"):
-
-            # Using `get_attribute` does not work here. It raises
-            # an exception: "Element is not selectable".
-            rv = self.marionette.execute_script(
-                """
-                let category = document.getElementById("category-extension");
-                return category.getAttribute("selected");
-                """);
-            return rv == "true"
-
-    def count_tabs(self):
-        return len(self.browser.tabbar.tabs)
-
-
-class TestAddons(AddonsTestCase):
-    def setUp(self):
-        AddonsTestCase.setUp(self)
-        self.addons = Addons(lambda: self.marionette)
-        self.addons.install_addon(INSTALL_URL)
-
-    def tearDown(self):
-        try:
-            self.addons.remove_addon_by_id(ADDON_ID)
-        finally:
-            AddonsTestCase.tearDown(self)
-
-    def test_using_addon_list(self):
-        num_tabs = self.count_tabs()
-        self.assertFalse(self.selected_tab_is_about_addons())
-        with self.addons.using_addon_list() as about_addons:
-            self.assertIsInstance(about_addons, AboutAddonsTab)
-            self.assertTrue(self.selected_tab_is_about_addons())
-            self.assertTrue(self.extension_category_is_selected())
-            self.assertEqual(self.count_tabs(), num_tabs + 1,
-                             msg="The number of tabs has increased by one.")
-        self.assertFalse(self.selected_tab_is_about_addons())
-        self.assertEqual(self.count_tabs(), num_tabs,
-                         msg="The number of tabs is like in the beginning.")
-
-    def test_install_in_two_steps(self):
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.remove_addon_by_id(ADDON_ID)
-        self.assertFalse(self.addons.is_addon_installed(ADDON_ID))
-
-        tab = None
-        with self.addons.install_addon_in_two_steps(INSTALL_URL):
-            # when a tab is opened and selected, there shouldn't be an exception
-            tab = self.browser.tabbar.open_tab()
-            tab.select()
-        tab.close()
-
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-
-    def test_uninstall_install(self):
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.remove_addon_by_id(ADDON_ID)
-        self.assertFalse(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.install_addon(INSTALL_URL)
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-
-    def test_multiple_uninstall_install(self):
-        self.addons.remove_addon_by_id(ADDON_ID)
-        self.addons.remove_addon_by_id(ADDON_ID)
-        self.addons.install_addon(INSTALL_URL)
-        self.addons.install_addon(INSTALL_URL)
-
-    def test_disable_enable(self):
-        self.assertTrue(self.addons.is_addon_enabled(ADDON_ID))
-        self.addons.disable_addon_by_id(ADDON_ID)
-        self.assertFalse(self.addons.is_addon_enabled(ADDON_ID))
-        self.addons.enable_addon_by_id(ADDON_ID)
-        self.assertTrue(self.addons.is_addon_enabled(ADDON_ID))
-
-    def test_multiple_disable_enable(self):
-        self.addons.disable_addon_by_id(ADDON_ID)
-        self.addons.disable_addon_by_id(ADDON_ID)
-        self.addons.enable_addon_by_id(ADDON_ID)
-        self.addons.enable_addon_by_id(ADDON_ID)
-
-    def test_is_installed(self):
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.disable_addon_by_id(ADDON_ID)
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.remove_addon_by_id(ADDON_ID)
-        self.assertFalse(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.install_addon(INSTALL_URL)
-        self.assertTrue(self.addons.is_addon_installed(ADDON_ID))
-        self.addons.enable_addon_by_id(ADDON_ID)
-
-    def test_is_enabled(self):
-        self.assertTrue(self.addons.is_addon_enabled(ADDON_ID))
-        self.addons.disable_addon_by_id(ADDON_ID)
-        self.assertFalse(self.addons.is_addon_enabled(ADDON_ID))
-        self.addons.remove_addon_by_id(ADDON_ID)
-        self.assertFalse(self.addons.is_addon_enabled(ADDON_ID))
-        self.addons.install_addon(INSTALL_URL)
-        self.addons.enable_addon_by_id(ADDON_ID)
-        self.assertTrue(self.addons.is_addon_enabled(ADDON_ID))
-
-
-class TestAboutAddons(AddonsTestCase):
-    def setUp(self):
-        RequestPolicyTestCase.setUp(self)
-
-        Addons(lambda: self.marionette).install_addon(INSTALL_URL)
-
-        self.about_addons = AboutAddonsTab(lambda: self.marionette)
-        self.about_addons.open_tab()
-        self.about_addons.set_category_by_id("extension")
-
-    def tearDown(self):
-        try:
-            if self.about_addons.is_addon_installed(ADDON_ID):
-                self.about_addons.remove_addon(self.addon)
-            self.about_addons.close_tab()
-        finally:
-            RequestPolicyTestCase.tearDown(self)
-
-    @property
-    def addon(self):
-        return self.about_addons.get_addon_by_id(ADDON_ID)
-
-    def test_close_tab_open_tab(self):
-        self.assertTrue(self.selected_tab_is_about_addons())
-        self.about_addons.close_tab()
-        self.assertFalse(self.selected_tab_is_about_addons())
-        self.about_addons.open_tab()
-        self.assertTrue(self.selected_tab_is_about_addons())
-
-    def test_disable_enable(self):
-        """Test the methods `disable`, `enable` and `is_enabled`.
-        """
-        self.assertTrue(self.addon.is_enabled(), msg="The addon is enabled.")
-        self.about_addons.disable_addon(self.addon)
-        self.assertFalse(self.addon.is_enabled(),
-                         msg="The addon has been disabled.")
-        self.about_addons.enable_addon(self.addon)
-        self.assertTrue(self.addon.is_enabled(),
-                        msg="The addon is enabled again.")
-
-    def test_uninstall(self):
-        def is_installed():
-            return self.about_addons.is_addon_installed(ADDON_ID)
-
-        self.assertTrue(is_installed(), msg="The addon is installed.")
-        self.about_addons.remove_addon(self.addon)
-
-        # The addon should be already uninstalled when `remove`
-        # returns. Therefore there is no "wait until".
-        self.assertFalse(is_installed(), msg="The addon has been removed.")
diff --git a/tests/marionette/rp_puppeteer/ui/addons.py b/tests/marionette/rp_puppeteer/ui/addons.py
deleted file mode 100644
index 838493f..0000000
--- a/tests/marionette/rp_puppeteer/ui/addons.py
+++ /dev/null
@@ -1,339 +0,0 @@
-# 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 NoSuchElementException
-from marionette_driver.wait import Wait
-from firefox_puppeteer.base import BaseLib
-from firefox_puppeteer.ui.windows import Windows
-from rp_puppeteer.api.error_detection import (LoggingErrorDetection,
-                                              ConsoleErrorDetection)
-from contextlib import contextmanager
-
-
-class _InstallNotifications(object):
-    """This class provides methods for managing the install notificaitons.
-    """
-
-    def __init__(self, marionette):
-        self.marionette = marionette
-        self.wait = Wait(self.marionette)
-
-    def is_hidden(self, notification_id):
-        with self.marionette.using_context("chrome"):
-            try:
-                hidden_attr = (
-                        self.marionette
-                        .find_element("id", notification_id)
-                        .get_attribute("hidden")
-                    )
-                return hidden_attr == "true"
-            except NoSuchElementException:
-                return True
-
-    def wait_until_not_hidden(self, notification_id):
-        self.wait.until(
-            lambda m: not self.is_hidden(notification_id),
-            message=("Notification with ID {} is shown."
-                     .format(notification_id)))
-
-    def wait_until_hidden(self, notification_id):
-        self.wait.until(
-            lambda m: self.is_hidden(notification_id),
-            message=("Notification with ID {} is hidden."
-                     .format(notification_id)))
-
-    @contextmanager
-    def wrap_lifetime(self, notification_id):
-        """Yields as soon as the notification is shown, and
-        finally returns as soon as the notification is hidden."""
-
-        self.wait_until_not_hidden(notification_id)
-        try:
-            yield
-        finally:
-            self.wait_until_hidden(notification_id)
-
-
-class RequestPolicy(BaseLib):
-    """With this class, RequestPolicy can be installed, enabled, disabled
-    or removed.
-    """
-
-    def __init__(self, marionette_getter):
-        BaseLib.__init__(self, marionette_getter)
-
-        self.pref_welcome_win = "extensions.requestpolicy.welcomeWindowShown"
-        self.addon_id = "rpcontinued at non-amo.requestpolicy.org"
-        self.install_url = ("http://localhost/link.html?"
-                            ".dist/requestpolicy-unit-testing.xpi")
-
-        self.addons = Addons(marionette_getter)
-
-        self.logging_error_detect = LoggingErrorDetection(marionette_getter)
-        self.console_error_detect = ConsoleErrorDetection(marionette_getter)
-
-    @contextmanager
-    def _ensure_no_errors(self):
-        logging_errors_before = self.logging_error_detect.n_errors
-        console_errors_before = self.console_error_detect.n_errors
-        yield
-        logging_error_count = (self.logging_error_detect.n_errors -
-                               logging_errors_before)
-        console_error_count = (self.console_error_detect.n_errors -
-                               console_errors_before)
-        console_error_messages = self.console_error_detect.messages
-
-        if logging_error_count > 0 and console_error_count > 0:
-            raise Exception("There have been {} Logging errors and "
-                            "{} Console errors! "
-                            "Console error messages were: {}"
-                            .format(logging_error_count, console_error_count,
-                                    console_error_messages))
-        elif logging_error_count > 0:
-            raise Exception("There have been {} Logging "
-                            "errors!".format(logging_error_count))
-        elif console_error_count > 0:
-            raise Exception("There have been {} Console  errors! "
-                            "Messages were: {}".format(console_error_count,
-                                                       console_error_messages))
-
-    @contextmanager
-    def install_in_two_steps(self):
-        with self._ensure_no_errors():
-            with self.addons.install_addon_in_two_steps(self.install_url):
-                yield
-
-    def install(self):
-        with self._ensure_no_errors():
-            self.addons.install_addon(self.install_url)
-
-    def remove(self):
-        with self._ensure_no_errors():
-            self.addons.remove_addon_by_id(self.addon_id)
-
-    def enable(self):
-        with self._ensure_no_errors():
-            self.addons.enable_addon_by_id(self.addon_id)
-
-    def disable(self):
-        with self._ensure_no_errors():
-            self.addons.disable_addon_by_id(self.addon_id)
-
-    def is_installed(self):
-        return self.addons.is_addon_installed(self.addon_id)
-
-    def is_enabled(self):
-        return self.addons.is_addon_enabled(self.addon_id)
-
-
-class Addons(BaseLib):
-    """With this class, an addon can be installed, enabled, disabled
-    or removed. All actions required, such as opening `about:addons`,
-    is done automatically.
-    """
-
-    def __init__(self, marionette_getter):
-        BaseLib.__init__(self, marionette_getter)
-
-    @contextmanager
-    def using_addon_list(self):
-        about_addons = AboutAddonsTab(lambda: self.marionette)
-        about_addons.open_tab()
-        about_addons.set_category_by_id("extension")
-        try:
-            yield about_addons
-        finally:
-            about_addons.close_tab()
-
-    @contextmanager
-    def install_addon_in_two_steps(self, install_url):
-        # open a new tab where the install URL will be opened
-        install_tab = Windows(lambda: self.marionette).current.tabbar.open_tab()
-
-        with self.marionette.using_context("content"):
-            # open the install URL
-            self.marionette.navigate(install_url)
-            # open the first link
-            self.marionette.find_element("tag name", "a").click()
-
-        with self.marionette.using_context("chrome"):
-            notif = _InstallNotifications(self.marionette)
-
-            with notif.wrap_lifetime("addon-install-blocked-notification"):
-                # Allow the XPI to be downloaded.  ("allow" button)
-                (
-                    self.marionette
-                    .find_element("id", "addon-install-blocked-notification")
-                    .find_element("anon attribute", {"anonid": "button"})
-                    .click()
-                )
-
-            with notif.wrap_lifetime("addon-install-confirmation-notification"):
-                # Confirm installation.
-                (
-                    self.marionette
-                    .find_element("id", "addon-install-confirmation-accept")
-                    .click()
-                )
-
-            if install_tab.selected:
-                # If the installation tab is still selected, the
-                # "install complete" notification is shown.
-                # If the selected tab has changed, there is no such
-                # notification.
-
-                with notif.wrap_lifetime("addon-install-complete-notification"):
-                    # Close the "install complete" notification.
-                    (
-                        self.marionette
-                        .find_element("id",
-                                      "addon-install-complete-notification")
-                        .find_element("anon attribute",
-                                      {"anonid": "closebutton"})
-                        .click()
-                    )
-
-        try:
-            yield
-        finally:
-            install_tab.close()
-
-    def install_addon(self, install_url):
-        with self.install_addon_in_two_steps(install_url):
-            pass
-
-    def remove_addon_by_id(self, addon_id):
-        with self.using_addon_list() as about_addons:
-            addon = about_addons.get_addon_by_id(addon_id)
-            about_addons.remove_addon(addon)
-
-    def enable_addon_by_id(self, addon_id):
-        with self.using_addon_list() as about_addons:
-            addon = about_addons.get_addon_by_id(addon_id)
-            about_addons.enable_addon(addon)
-
-    def disable_addon_by_id(self, addon_id):
-        with self.using_addon_list() as about_addons:
-            addon = about_addons.get_addon_by_id(addon_id)
-            about_addons.disable_addon(addon)
-
-    def is_addon_installed(self, addon_id):
-        with self.using_addon_list() as about_addons:
-            addon = about_addons.get_addon_by_id(addon_id)
-            return addon != None
-
-    def is_addon_enabled(self, addon_id):
-        with self.using_addon_list() as about_addons:
-            addon = about_addons.get_addon_by_id(addon_id)
-            if addon == None:
-                return False
-            return addon.is_enabled()
-
-
-class AboutAddonsTab(BaseLib):
-    """This class helps handling an `about:addons` tab.
-    """
-
-    def open_tab(self):
-        self._tab = Windows(lambda: self.marionette).current.tabbar.open_tab()
-        with self.marionette.using_context("content"):
-            self.marionette.navigate("about:addons")
-
-    def close_tab(self):
-        with self.marionette.using_context("chrome"):
-            if self._tab != None:
-                self._tab.close()
-                self._tab = None
-
-    def set_category_by_id(self, category_id):
-        with self.marionette.using_context("content"):
-            (
-                self.marionette
-                .find_element("id", "category-" + category_id)
-                .click()
-            )
-
-    def get_addon_by_id(self, addon_id):
-        with self.marionette.using_context("content"):
-            try:
-                handle = (
-                        self.marionette
-                        .find_element("id", "list-view")
-                        .find_element("css selector",
-                                      ".addon[value='{}']".format(addon_id))
-                    )
-                return Addon(lambda: self.marionette, handle)
-            except NoSuchElementException:
-                return None
-
-    def is_addon_installed(self, addon_id):
-        # Switch categories to dispose of the undo link
-        # which is displayed after clicking the "remove" button.
-        self.set_category_by_id("theme")
-        self.set_category_by_id("extension")
-
-        return self.get_addon_by_id(addon_id) != None
-
-    def enable_addon(self, addon):
-        addon.click_enable()
-        (
-            Wait(self.marionette)
-            .until(lambda m: addon.is_enabled(),
-                   message="The addon has been enabled.")
-        )
-
-    def disable_addon(self, addon):
-        addon.click_disable()
-        (
-            Wait(self.marionette)
-            .until(lambda m: not addon.is_enabled(),
-                   message="The addon has been disabled.")
-        )
-
-    def remove_addon(self, addon):
-        if addon == None:
-            # the addon does not exist
-            return
-        addon_id = addon.addon_id
-        addon.click_remove()
-        (
-            Wait(self.marionette)
-            .until(lambda m: not self.is_addon_installed(addon_id),
-                   message="The addon has been uninstalled.")
-        )
-
-
-class Addon(BaseLib):
-    def __init__(self, marionette_getter, addon_handle):
-        BaseLib.__init__(self, marionette_getter)
-        self._handle = addon_handle
-
-    @property
-    def addon_id(self):
-        with self.marionette.using_context("content"):
-            return self._handle.get_attribute("value")
-
-    def is_enabled(self):
-        with self.marionette.using_context("content"):
-            return self._handle.get_attribute("active") == "true"
-
-    def _click_on_button(self, button_name):
-        with self.marionette.using_context("content"):
-            (
-                self._handle
-                .find_element("anon attribute",
-                              {"anonid": button_name + "-btn"})
-                .click()
-            )
-
-    def click_enable(self):
-        if not self.is_enabled():
-            self._click_on_button("enable")
-
-    def click_disable(self):
-        if self.is_enabled():
-            self._click_on_button("disable")
-
-    def click_remove(self):
-        self._click_on_button("remove")
diff --git a/tests/marionette/tests/addon_install_and_upgrade/test_multiple_installations.py b/tests/marionette/tests/addon_install_and_upgrade/test_multiple_installations.py
index 9c29487..9699c0e 100644
--- a/tests/marionette/tests/addon_install_and_upgrade/test_multiple_installations.py
+++ b/tests/marionette/tests/addon_install_and_upgrade/test_multiple_installations.py
@@ -3,36 +3,54 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from rp_ui_harness import RequestPolicyTestCase
-from rp_puppeteer.ui.addons import RequestPolicy, Addons
+from rp_puppeteer.api.addon import Addon
+from marionette_driver.errors import NoSuchElementException
 
 
-OLD_VERSION__ADDON_ID = "requestpolicy at requestpolicy.com"
-AMO_VERSION__ADDON_ID = "rpcontinued at requestpolicy.org"
-
-def _install_url(filename):
-    return "http://localhost/link.html?.dist/" + filename
-
-OLD_VERSION__INSTALL_URL = _install_url("requestpolicy-v1.0.beta9.3"
-                                        "__preprocess.py.xpi")
-AMO_VERSION__INSTALL_URL = _install_url("requestpolicy-amo.xpi")
-
+# v1.0.beta9.3
+RP_BETA_9 = {"id": "requestpolicy at requestpolicy.com",
+             "xpi": "requestpolicy-v1.0.beta9.3__preprocess.py.xpi"}
+# AMO version
+RP_AMO = {"id": "rpcontinued at requestpolicy.org",
+          "xpi": "requestpolicy-amo.xpi"}
 
 NOTICE_URL = "chrome://rpcontinued/content/multiple-installations.html"
 
 
 class MultipleInstallationsTestCase(RequestPolicyTestCase):
+    OTHER_ADDON = None
+
     def setUp(self):
-        RequestPolicyTestCase.setUp(self)
-        self.rp = RequestPolicy(lambda: self.marionette)
-        self.addons = Addons(lambda: self.marionette)
+        super(MultipleInstallationsTestCase, self).setUp()
+
+        self.rp_addon.ignore_errors = True
+
+        assert self.OTHER_ADDON is not None
+        self.other_rp = Addon(lambda: self.marionette,
+                              addon_id=self.OTHER_ADDON["id"],
+                              install_url=("http://localhost/.dist/" +
+                                           self.OTHER_ADDON["xpi"]))
 
     def tearDown(self):
-        self._close_notice_tabs()
-        # Restart the browser. (It's a method of FirefoxTestCase.)
-        # It's necessary to restart because multiple installed versions
-        # might have broken RequestPolicy's functionality.
-        self.restart()
-        RequestPolicyTestCase.tearDown(self)
+        try:
+            self._close_notice_tabs()
+
+            self.other_rp.uninstall()
+            self.rp_addon.install()
+
+            self.rp_addon.ignore_errors = False
+
+            # Restart the browser. (It's a method of FirefoxTestCase.)
+            # It's necessary to restart because multiple installed versions
+            # might have broken RequestPolicy's functionality.
+            self.restart()
+        finally:
+            # It's highly probable that errors occur. However, the tests
+            # in this file don't intend to avoid these errors.
+            self.logging_error_detect.reset()
+            self.console_error_detect.reset()
+
+            super(MultipleInstallationsTestCase, self).tearDown()
 
     def _get_notice_tabs(self):
         tabs = self.browser.tabbar.tabs
@@ -65,10 +83,11 @@ class MultipleInstallationsTestCase(RequestPolicyTestCase):
             tab.select()
 
         with self.marionette.using_context("content"):
-            # check if the word "Notice" is on the page
-            notice = self.marionette.find_element(
-                "xpath",
-                "//*[contains(text(),'Notice')]")
+            try:
+                self.marionette.find_element("xpath",
+                                             "//*[contains(text(),'Notice')]")
+            except NoSuchElementException:
+                self.fail("The Notice Tab should contain the word 'Notice'.")
 
     def _close_notice_tabs(self):
         tabs = self._get_notice_tabs()
@@ -76,90 +95,105 @@ class MultipleInstallationsTestCase(RequestPolicyTestCase):
             tab.close()
 
 
-class OldVersionTestCase(MultipleInstallationsTestCase):
-    INSTALL_URL = OLD_VERSION__INSTALL_URL
-    ADDON_ID = OLD_VERSION__ADDON_ID
-    def tearDown(self):
-        self.addons.remove_addon_by_id(self.ADDON_ID)
-        MultipleInstallationsTestCase.tearDown(self)
+class CommonTests:
+    """Common tests for all so-called "other" versions.
 
+    Idea from
+    https://stackoverflow.com/questions/1323455/python-unit-test-with-base-and-sub-class/25695512#25695512
+    """
 
-class TestOldVersionActive_ThenInstallCurrentVersion(OldVersionTestCase):
-    def setUp(self):
-        OldVersionTestCase.setUp(self)
-        self.rp.remove()
-        self.addons.install_addon(self.INSTALL_URL)
+    class OtherVersionActive_ThenInstallCurrentVersion(MultipleInstallationsTestCase):
+        def setUp(self):
+            MultipleInstallationsTestCase.setUp(self)
+            self.rp_addon.uninstall()
+            self.other_rp.install()
 
-    def test_notice_is_shown(self):
-        with self.rp.install_in_two_steps():
+        def test_notice_is_shown(self):
+            self.rp_addon.install()
             self._assert_notice_tab()
 
-class TestOldVersionActive_ThenEnableCurrentVersion(OldVersionTestCase):
-    def setUp(self):
-        OldVersionTestCase.setUp(self)
-        self.rp.disable()
-        self.addons.install_addon(self.INSTALL_URL)
+    class OtherVersionActive_ThenEnableCurrentVersion(MultipleInstallationsTestCase):
+        def setUp(self):
+            MultipleInstallationsTestCase.setUp(self)
+            self.rp_addon.disable()
+            self.other_rp.install()
 
-    def test_notice_is_shown(self):
-        self.rp.enable()
-        self._assert_notice_tab()
+        def test_notice_is_shown(self):
+            self.rp_addon.enable()
+            self._assert_notice_tab()
 
-class TestOldVersionNotActive_ThenInstall(OldVersionTestCase):
-    def test_notice_is_shown(self):
-        with self.addons.install_addon_in_two_steps(self.INSTALL_URL):
+    class OtherVersionNotActive_ThenInstall(MultipleInstallationsTestCase):
+        def test_notice_is_shown(self):
+            self.other_rp.install()
             self._assert_notice_tab()
 
-class TestOldVersionNotActive_ThenEnable(OldVersionTestCase):
-    def setUp(self):
-        OldVersionTestCase.setUp(self)
-        # After this preparation, both the current and the old version
-        # will be installed, but the old version will be disabled.
-        self.rp.disable()
-        self.addons.install_addon(self.INSTALL_URL)
-        self.addons.disable_addon_by_id(self.ADDON_ID)
-        self.rp.enable()
-        self.assertIsNone(self._notice_tab,
-                          msg=("No 'notice' tab has been opened during "
-                               "preparation"))
-
-    def test_notice_is_shown(self):
-        self.addons.enable_addon_by_id(self.ADDON_ID)
-        self._assert_notice_tab()
-
-class TestOldAndCurrentVersionActiveAfterRestart(OldVersionTestCase):
-    def setUp(self):
-        OldVersionTestCase.setUp(self)
-        self.addons.install_addon(self.INSTALL_URL)
-        self._close_notice_tabs()
+    class OtherVersionNotActive_ThenEnable(MultipleInstallationsTestCase):
+        def setUp(self):
+            MultipleInstallationsTestCase.setUp(self)
+            # After this preparation, both the current and the old version
+            # will be installed, but the old version will be disabled.
+            self.rp_addon.disable()
+            self.other_rp.install()
+            self.other_rp.disable()
+            self.rp_addon.enable()
+            self.assertIsNone(self._notice_tab,
+                              msg=("No 'notice' tab has been opened during "
+                                   "preparation"))
+
+        def test_notice_is_shown(self):
+            self.other_rp.enable()
+            self._assert_notice_tab()
+
+    class OtherAndCurrentVersionActiveAfterRestart(MultipleInstallationsTestCase):
+        def setUp(self):
+            MultipleInstallationsTestCase.setUp(self)
+            self.other_rp.install()
+            self._close_notice_tabs()
 
-    def test_notice_is_shown(self):
-        self.restart()
-        # Don't require the tab to be selected. It somehow doesn't get
-        # selected in the unit test, but it works when done manually.
-        self._assert_notice_tab(selected_required=False)
+        def test_notice_is_shown(self):
+            self.restart()
+            # Don't require the tab to be selected. It somehow doesn't get
+            # selected in the unit test, but it works when done manually.
+            self._assert_notice_tab(selected_required=False)
 
 
 class TestAMOVersionActive_ThenInstallCurrentVersion(\
-        TestOldVersionActive_ThenInstallCurrentVersion):
-    INSTALL_URL = AMO_VERSION__INSTALL_URL
-    ADDON_ID = AMO_VERSION__ADDON_ID
+        CommonTests.OtherVersionActive_ThenInstallCurrentVersion):
+    OTHER_ADDON = RP_AMO
 
 class TestAMOVersionActive_ThenEnableCurrentVersion(\
-        TestOldVersionActive_ThenEnableCurrentVersion):
-    INSTALL_URL = AMO_VERSION__INSTALL_URL
-    ADDON_ID = AMO_VERSION__ADDON_ID
+        CommonTests.OtherVersionActive_ThenEnableCurrentVersion):
+    OTHER_ADDON = RP_AMO
 
 class TestAMOVersionNotActive_ThenInstall(\
-        TestOldVersionNotActive_ThenInstall):
-    INSTALL_URL = AMO_VERSION__INSTALL_URL
-    ADDON_ID = AMO_VERSION__ADDON_ID
+        CommonTests.OtherVersionNotActive_ThenInstall):
+    OTHER_ADDON = RP_AMO
 
 class TestAMOVersionNotActive_ThenEnable(\
-        TestOldVersionNotActive_ThenEnable):
-    INSTALL_URL = AMO_VERSION__INSTALL_URL
-    ADDON_ID = AMO_VERSION__ADDON_ID
+        CommonTests.OtherVersionNotActive_ThenEnable):
+    OTHER_ADDON = RP_AMO
 
 class TestAMOAndNonAMOVersionActiveAfterRestart(\
-        TestOldAndCurrentVersionActiveAfterRestart):
-    INSTALL_URL = AMO_VERSION__INSTALL_URL
-    ADDON_ID = AMO_VERSION__ADDON_ID
+        CommonTests.OtherAndCurrentVersionActiveAfterRestart):
+    OTHER_ADDON = RP_AMO
+
+
+class TestOldVersionActive_ThenInstallCurrentVersion(\
+        CommonTests.OtherVersionActive_ThenInstallCurrentVersion):
+    OTHER_ADDON = RP_BETA_9
+
+class TestOldVersionActive_ThenEnableCurrentVersion(\
+        CommonTests.OtherVersionActive_ThenEnableCurrentVersion):
+    OTHER_ADDON = RP_BETA_9
+
+class TestOldVersionNotActive_ThenInstall(\
+        CommonTests.OtherVersionNotActive_ThenInstall):
+    OTHER_ADDON = RP_BETA_9
+
+class TestOldVersionNotActive_ThenEnable(\
+        CommonTests.OtherVersionNotActive_ThenEnable):
+    OTHER_ADDON = RP_BETA_9
+
+class TestOldAndCurrentVersionActiveAfterRestart(\
+        CommonTests.OtherAndCurrentVersionActiveAfterRestart):
+    OTHER_ADDON = RP_BETA_9
diff --git a/tests/marionette/tests/addon_install_and_upgrade/test_restartlessness.py b/tests/marionette/tests/addon_install_and_upgrade/test_restartlessness.py
index c60be8e..e878f2f 100644
--- a/tests/marionette/tests/addon_install_and_upgrade/test_restartlessness.py
+++ b/tests/marionette/tests/addon_install_and_upgrade/test_restartlessness.py
@@ -3,38 +3,54 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from rp_ui_harness import RequestPolicyTestCase
-from rp_puppeteer.ui.addons import RequestPolicy
+from marionette.marionette_test import SkipTest
+import time
 
 PREF_WELCOME_WIN_SHOWN = "extensions.requestpolicy.welcomeWindowShown"
 
 
-class TestRequestPolicyRestartlessness(RequestPolicyTestCase):
-    """These tests ensure that RequestPolicy can be uninstalled/installed and
-    disabled/enabled restartless.
-    """
+class CommonTests:
+    class TestRequestPolicyRestartlessness(RequestPolicyTestCase):
+        """These tests ensure that RequestPolicy can be uninstalled/installed and
+        disabled/enabled restartless.
+        """
+
+        def test_disable_enable(self):
+            self.assertTrue(self.rp_addon.is_active, msg="The addon is enabled.")
+            self.rp_addon.disable()
+            self.assertFalse(self.rp_addon.is_active,
+                             msg="The addon has been disabled.")
+            self.rp_addon.enable()
+            self.assertTrue(self.rp_addon.is_active,
+                            msg="The addon has been re-enabled.")
+
+        def test_uninstall_install(self):
+            # remove:
+            self.assertTrue(self.rp_addon.is_installed,
+                            msg="The addon is installed.")
+            self.rp_addon.uninstall()
+            self.assertFalse(self.rp_addon.is_installed,
+                             msg="The addon has been removed.")
+
+            # re-install:
+            self.prefs.set_pref(PREF_WELCOME_WIN_SHOWN, True)
+            self.rp_addon.install()
+            self.assertTrue(self.rp_addon.is_installed,
+                            msg="The addon has been re-installed.")
+
+
+class TestNormal(CommonTests.TestRequestPolicyRestartlessness):
+    def setUp(self):
+        super(TestNormal, self).setUp()
+        # Wait some time in order to ensure the browser is in idle.
+        time.sleep(0.2)
+
 
+class TestAfterNavigate(CommonTests.TestRequestPolicyRestartlessness):
     def setUp(self):
-        RequestPolicyTestCase.setUp(self)
-        self.rp = RequestPolicy(lambda: self.marionette)
-
-    def test_disable_enable(self):
-        self.assertTrue(self.rp.is_enabled(), msg="The addon is enabled.")
-        self.rp.disable()
-        self.assertFalse(self.rp.is_enabled(),
-                         msg="The addon has been disabled.")
-        self.rp.enable()
-        self.assertTrue(self.rp.is_enabled(),
-                        msg="The addon has been re-enabled.")
-
-    def test_uninstall_install(self):
-        # remove:
-        self.assertTrue(self.rp.is_installed(), msg="The addon is installed.")
-        self.rp.remove()
-        self.assertFalse(self.rp.is_installed(),
-                         msg="The addon has been removed.")
-
-        # re-install:
-        self.prefs.set_pref(PREF_WELCOME_WIN_SHOWN, True)
-        self.rp.install()
-        self.assertTrue(self.rp.is_installed(),
-                        msg="The addon has been re-installed.")
+        super(TestAfterNavigate, self).setUp()
+
+        raise SkipTest("Skipping due to issue #728.")
+
+        with self.marionette.using_context("content"):
+            self.marionette.navigate("http://localhost/")
diff --git a/tests/marionette/tests/test_setup_page.py b/tests/marionette/tests/test_setup_page.py
index b7480fd..a5711ec 100644
--- a/tests/marionette/tests/test_setup_page.py
+++ b/tests/marionette/tests/test_setup_page.py
@@ -4,7 +4,7 @@
 
 from rp_ui_harness import RequestPolicyTestCase
 from marionette_driver.wait import Wait
-from rp_puppeteer.ui.addons import RequestPolicy
+import time
 
 
 PREF_WELCOME_WIN_SHOWN = "extensions.requestpolicy.welcomeWindowShown"
@@ -23,18 +23,22 @@ class TestSetupPageShowingUp(RequestPolicyTestCase):
 
 
     def test_on_install(self):
-        rp = RequestPolicy(lambda: self.marionette)
-        rp.remove()
+        # FIXME: Issue #728;   [Restartlessness] RP should not cause
+        #        JavaScript errors on shutdown while requests are being called.
+        time.sleep(0.2)
+
+        self.rp_addon.uninstall()
 
         self.prefs.set_pref(PREF_WELCOME_WIN_SHOWN, False)
 
-        with rp.install_in_two_steps():
-            setup_tab = Wait(self.marionette).until(
-                lambda m: self._get_setup_tab(),
-                message="RequestPolicy has opened its Setup page.")
-            self.assertTrue(self.prefs.get_pref(PREF_WELCOME_WIN_SHOWN),
-                            msg=("The 'welcome window shown' pref has "
-                                 "been set to `true`."))
-            self.assertTrue(setup_tab.selected,
-                            msg="The setup tab is selected.")
-            setup_tab.close()
+        self.rp_addon.install()
+
+        setup_tab = Wait(self.marionette).until(
+            lambda m: self._get_setup_tab(),
+            message="RequestPolicy has opened its Setup page.")
+        self.assertTrue(self.prefs.get_pref(PREF_WELCOME_WIN_SHOWN),
+                        msg=("The 'welcome window shown' pref has "
+                             "been set to `true`."))
+        self.assertTrue(setup_tab.selected,
+                        msg="The setup tab is selected.")
+        setup_tab.close()

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/requestpolicy.git



More information about the Pkg-mozext-commits mailing list