Bug 1144873 - Implement a UI class for notification popups r?sydpolk, maja_zf
MozReview-Commit-ID: EAi5Y1AIitS
--- a/testing/firefox-ui/tests/puppeteer/manifest.ini
+++ b/testing/firefox-ui/tests/puppeteer/manifest.ini
@@ -8,13 +8,14 @@ tags = local
[test_security.py]
tags = remote
[test_software_update.py]
[test_utils.py]
# UI tests
[test_about_window.py]
[test_menubar.py]
+[test_notifications.py]
[test_page_info_window.py]
[test_tabbar.py]
[test_toolbars.py]
tags = remote
[test_windows.py]
new file mode 100644
index 0000000000000000000000000000000000000000..c8877b55dec371fa6417fc626ef1a6f3113cc2bc
GIT binary patch
literal 1552
zc$^FHW@Zs#U|`^2xLcX!&XKXBU5t@|;S&(^Fvu_@<>!|amlP!y=w%g$hHx@4I|ev~
zfpBRBHv=Qf3uXoeFtN7(bk<=50oU*Uy1c{uJeFQcbqa5EQfzhD{W56U@r_Mu3@@!v
z<o)^ml;)SM@4Q}|n_Dcg_kj0%p?Q|>-Zu)oD;`)ZNYONB-^7&Y;V;nT%v65;yzqYO
z_q=60Kb{MVNO-N&>f*ge^UliX*5iC^(~e)helD+ju_uR4M(m^)vvjUJda*>mHR;Wq
z<xg3vpYmig?GSu?N|?j#LU)h$+IIi#k9J&dUD@rcty2;j$>&+%CieU?*X$ndcc%jn
zM{qb^+O=wz%o+<mkEBIUHqLt-voK_;_F9dvXTD8jpJnq<?b6m~nyRr6f)sYCzmJr?
zWH5XE>1R(;{7-$}x3ABu>gUUso43^P{MWi=y!~wFzZEe%?^W2()VrU_xBnA=05EL1
z7(hTQBFmit39tjhHaVjxKQ~n$9IR_ZoWis)gEasiIUob#4KJhw0(lugEPyZ|BQ+-{
z9~eKmaFak7%_MDaouGnVjt~4?g{7VReP2pHedi3@5ZV!T%H!<Gli`;F1H8_k_xD`o
z6WX=)nHIAaTQ_s4Mr(SAlclQUO4pTLUILXXiY9PvKeFgb(E_dsA`A$>1&3$3Prjz3
zFArpcFhAUBdbx>tnQ5uTC19870bQks>9XYfypq(s5{2A6-6EjU#G;a%)Z$_VsOPv4
zL68Ov0{z15Lsh`EX#mv74YVgS52zz2N3SR)4Qz*ls8bjSquG%=`LrK6dH&?meltO6
zd86;*E4o4oVmbk;3AvLpH+)duw0z6c*M&ZDtecA7&9N@F4LtQV(DM`LHO|H1AC=BN
zGVNYeR9)>~<GMTaPu~r(DRO-6Ukav5-M(<NXy=khzt1)szv>mL_x_oYQ7-7dq`GM%
zL+P{7XWkwsotte=uhU#{u2%4OVat^5545zSz8G@U8&}61tzO-IFO=i$1n-#|>yGlt
zRE3+bWC`t<r8zf3zo=Vi&TXE>T8q7YIro&*hfZ7C9Uy(r-JzH@$GWtdLG$eDjhpyt
zH2Mo{m(Hu^kP$4oKK)IPYFBhUi;Kbw#f7skWaY_OhlyE8oqkuMY|`-6pmNi)d*K>(
ztn-?R8x~#smM5}hPvi5q%CD!}MOPS{op|xNuz7=duD0vZ<toqRQ!X5<U2OQt`N6aA
z9f=p2SGCl={9ZkG`Siw_na_3X`KtK+4o&F%$0>TEUe7T0!xg5}^&8$<h~4XPY@fEB
z=dB!*f#=2fiK<`svvluc4De=Tl4HhIz({}-5Rl2Rq!GkIEpAvL#SKQD0GWs@TS-7!
z0t{~*V}J(3GZ`x+YhX1DR~AEd-du!XNEr^Fskm|nvZ)qK448R^6_Qu*I2AK1ARD>>
cXedg?V1;B13<Fu&K$=*Aa5hk9JPU{i03S06YXATM
new file mode 100644
--- /dev/null
+++ b/testing/firefox-ui/tests/puppeteer/test_notifications.py
@@ -0,0 +1,77 @@
+# 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
+
+from marionette_driver import By
+from marionette_driver.errors import TimeoutException
+
+from firefox_ui_harness.testcases import FirefoxTestCase
+from firefox_puppeteer.ui.browser.notifications import (
+ AddOnInstallFailedNotification)
+
+here = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestNotifications(FirefoxTestCase):
+
+ def setUp(self):
+ FirefoxTestCase.setUp(self)
+ # setting this pref prevents a notification from being displayed
+ # when e10s is enabled. see http://mzl.la/1TVNAXh
+ self.prefs.set_pref('browser.tabs.remote.force-enable', True)
+
+ def tearDown(self):
+ try:
+ if self.browser.notification is not None:
+ self.browser.notification.close()
+ finally:
+ FirefoxTestCase.tearDown(self)
+
+ def test_no_notification(self):
+ """No initial notifications are shown"""
+ self.assertIsNone(self.browser.notification)
+
+ def test_notification_with_origin(self):
+ """Trigger a notification with an origin"""
+ self.trigger_add_on_notification('mn-restartless-unsigned.xpi')
+ self.assertIsNotNone(self.browser.notification.origin)
+ self.assertIsNotNone(self.browser.notification.label)
+
+ def test_close_notification(self):
+ """Trigger and dismiss a notification"""
+ self.trigger_add_on_notification('mn-restartless-unsigned.xpi')
+ self.browser.notification.close()
+ self.assertIsNone(self.browser.notification)
+
+ def test_add_on_failed_notification(self):
+ """Trigger add-on failed notification using an unsigned add-on"""
+ self.trigger_add_on_notification('mn-restartless-unsigned.xpi')
+ self.assertIsInstance(self.browser.notification,
+ AddOnInstallFailedNotification)
+
+ def test_wait_for_any_notification_timeout(self):
+ """Wait for a notification when one is not shown"""
+ message = 'No notification was shown'
+ with self.assertRaisesRegexp(TimeoutException, message):
+ self.browser.wait_for_notification()
+
+ def test_wait_for_specific_notification_timeout(self):
+ """Wait for a notification when one is not shown"""
+ message = 'AddOnInstallFailedNotification was not shown'
+ with self.assertRaisesRegexp(TimeoutException, message):
+ self.browser.wait_for_notification(AddOnInstallFailedNotification)
+
+ def test_wait_for_no_notification_timeout(self):
+ """Wait for no notification when one is shown"""
+ message = 'Unexpected notification shown'
+ self.trigger_add_on_notification('mn-restartless-unsigned.xpi')
+ with self.assertRaisesRegexp(TimeoutException, message):
+ self.browser.wait_for_notification(None)
+
+ def trigger_add_on_notification(self, add_on):
+ with self.marionette.using_context('content'):
+ self.marionette.navigate('file://{0}'.format(here))
+ self.marionette.find_element(By.LINK_TEXT, add_on).click()
+ self.browser.wait_for_notification()
--- a/testing/puppeteer/firefox/docs/index.rst
+++ b/testing/puppeteer/firefox/docs/index.rst
@@ -33,16 +33,17 @@ The following libraries are currently im
future. Each library is available from an instance of the FirefoxTestCase class.
.. toctree::
ui/about_window/window
ui/deck
ui/menu
ui/pageinfo/window
+ ui/browser/notifications
ui/browser/tabbar
ui/browser/toolbars
ui/browser/window
ui/update_wizard/dialog
ui/windows
api/appinfo
api/keys
api/l10n
new file mode 100644
--- /dev/null
+++ b/testing/puppeteer/firefox/docs/ui/browser/notifications.rst
@@ -0,0 +1,44 @@
+.. py:currentmodule:: firefox_puppeteer.ui.browser.notifications
+
+Notifications
+=============
+
+AddOnInstallBlockedNotification
+-------------------------------
+
+.. autoclass:: AddOnInstallBlockedNotification
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+AddOnInstallConfirmationNotification
+------------------------------------
+
+.. autoclass:: AddOnInstallConfirmationNotification
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+AddOnInstallCompleteNotification
+--------------------------------
+
+.. autoclass:: AddOnInstallCompleteNotification
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+AddOnInstallFailedNotification
+------------------------------
+
+.. autoclass:: AddOnInstallFailedNotification
+ :members:
+ :inherited-members:
+ :show-inheritance:
+
+AddOnProgressNotification
+-------------------------
+
+.. autoclass:: AddOnProgressNotification
+ :members:
+ :inherited-members:
+ :show-inheritance:
new file mode 100644
--- /dev/null
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/notifications.py
@@ -0,0 +1,84 @@
+# 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 abc import ABCMeta
+
+from marionette_driver import By
+
+from firefox_puppeteer.ui_base_lib import UIBaseLib
+
+
+class BaseNotification(UIBaseLib):
+ """Abstract base class for any kind of notification."""
+ __metaclass__ = ABCMeta
+
+ @property
+ def label(self):
+ """Provides access to the notification label.
+
+ :returns: The notification label.
+ """
+ return self.element.get_attribute('label')
+
+ @property
+ def origin(self):
+ """Provides access to the notification origin.
+
+ :returns: The notification origin.
+ """
+ return self.element.get_attribute('origin')
+
+ def close(self):
+ """Close the notification."""
+ self.element.find_element(
+ By.ANON_ATTRIBUTE, {'anonid': 'closebutton'}).click()
+
+
+class AddOnInstallBlockedNotification(BaseNotification):
+ """Add-on install blocked notification."""
+
+ def allow(self):
+ """Allow the add-on to be installed."""
+ self.element.find_element(
+ By.ANON_ATTRIBUTE, {'anonid': 'button'}).find_element(
+ By.ANON_ATTRIBUTE, {'anonid': 'button'}).click()
+
+
+class AddOnInstallConfirmationNotification(BaseNotification):
+ """Add-on install confirmation notification."""
+
+ @property
+ def add_on(self):
+ """Provides access to the add-on name.
+
+ :returns: The add-on name.
+ """
+ label = self.element.find_element(
+ By.CSS_SELECTOR, '#addon-install-confirmation-content label')
+ return label.get_attribute('value')
+
+ def cancel(self):
+ """Cancel installation of the add-on."""
+ self.element.find_element(
+ By.ID, 'addon-install-confirmation-cancel').click()
+
+ def install(self):
+ """Proceed with installation of the add-on."""
+ self.element.find_element(
+ By.ID, 'addon-install-confirmation-accept').click()
+
+
+class AddOnInstallCompleteNotification(BaseNotification):
+ """Add-on install complete notification."""
+ pass
+
+
+class AddOnInstallFailedNotification(BaseNotification):
+ """Add-on install failed notification."""
+ pass
+
+
+class AddOnProgressNotification(BaseNotification):
+ """Add-on progress notification."""
+ pass
--- a/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
+++ b/testing/puppeteer/firefox/firefox_puppeteer/ui/browser/window.py
@@ -1,22 +1,31 @@
# 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 firefox_puppeteer.errors as errors
from marionette_driver import By, Wait
-from marionette_driver.errors import NoSuchWindowException
+from marionette_driver.errors import (
+ NoSuchElementException,
+ NoSuchWindowException)
from marionette_driver.keys import Keys
from firefox_puppeteer.api.l10n import L10n
from firefox_puppeteer.api.prefs import Preferences
from firefox_puppeteer.decorators import use_class_as_property
from firefox_puppeteer.ui.about_window.window import AboutWindow
+from firefox_puppeteer.ui.browser.notifications import (
+ AddOnInstallBlockedNotification,
+ AddOnInstallConfirmationNotification,
+ AddOnInstallCompleteNotification,
+ AddOnInstallFailedNotification,
+ AddOnProgressNotification,
+ BaseNotification)
from firefox_puppeteer.ui.browser.tabbar import TabBar
from firefox_puppeteer.ui.browser.toolbars import NavBar
from firefox_puppeteer.ui.pageinfo.window import PageInfoWindow
from firefox_puppeteer.ui.windows import BaseWindow, Windows
import firefox_puppeteer.errors as errors
class BrowserWindow(BaseWindow):
@@ -80,16 +89,50 @@ class BrowserWindow(BaseWindow):
if not self._navbar:
navbar = self.window_element.find_element(By.ID, 'nav-bar')
self._navbar = NavBar(lambda: self.marionette, self, navbar)
return self._navbar
@property
+ def notification(self):
+ """Provides access to the currently displayed notification."""
+
+ notifications_map = {
+ 'addon-install-blocked-notification': AddOnInstallBlockedNotification,
+ 'addon-install-confirmation-notification': AddOnInstallConfirmationNotification,
+ 'addon-install-complete-notification': AddOnInstallCompleteNotification,
+ 'addon-install-failed-notification': AddOnInstallFailedNotification,
+ 'addon-progress-notification': AddOnProgressNotification,
+ }
+
+ try:
+ notification = self.window_element.find_element(
+ By.CSS_SELECTOR, '#notification-popup popupnotification')
+ except NoSuchElementException:
+ return None # no notification is displayed
+ notification_id = notification.get_attribute('id')
+ return notifications_map[notification_id](
+ lambda: self.marionette, self, notification)
+
+ def wait_for_notification(self, notification_class=BaseNotification):
+ """Waits for the specified notification to be displayed."""
+ if notification_class is None:
+ Wait(self.marionette).until(
+ lambda _: self.notification is None,
+ message='Unexpected notification shown.')
+ else:
+ message = '{0} was not shown.'.format(notification_class.__name__)
+ if notification_class is BaseNotification:
+ message = 'No notification was shown.'
+ Wait(self.marionette).until(lambda _: isinstance(
+ self.notification, notification_class), message=message)
+
+ @property
def tabbar(self):
"""Provides access to the tab bar.
See the :class:`~ui.browser.tabbar.TabBar` reference.
"""
self.switch_to()
if not self._tabbar: