Bug 1270953 - Enhance notification class for Firefox Puppeteer. r=maja_zf

MozReview-Commit-ID: KE117HzhiXM

--HG--
extra : rebase_source : eefe2b6c7141784516bddf9e913162f3744e0b7e
This commit is contained in:
Henrik Skupin 2016-05-17 13:43:24 +02:00
parent b9da89429b
commit e07a01ca67
6 changed files with 116 additions and 66 deletions

View File

@ -2,56 +2,42 @@
# 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__))
AddOnInstallFailedNotification,
)
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)
self.prefs.set_pref('extensions.install.requireSecureOrigin', False)
self.addons_url = self.marionette.absolute_url('addons/extensions/')
self.utils.permissions.add(self.marionette.baseurl, 'install')
def tearDown(self):
try:
if self.browser.notification is not None:
self.browser.notification.close()
self.utils.permissions.remove(self.addons_url, 'install')
if self.browser.notification:
self.browser.notification.close(force=True)
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):
def test_open_close_notification(self):
"""Trigger and dismiss a notification"""
self.trigger_add_on_notification('mn-restartless-unsigned.xpi')
self.assertIsNone(self.browser.notification)
self.trigger_addon_notification('restartless_addon_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):
def test_wait_for_notification_timeout(self):
"""Wait for a notification when one is not shown"""
message = 'No notification was shown'
with self.assertRaisesRegexp(TimeoutException, message):
@ -66,12 +52,29 @@ class TestNotifications(FirefoxTestCase):
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')
self.trigger_addon_notification('restartless_addon_unsigned.xpi')
with self.assertRaisesRegexp(TimeoutException, message):
self.browser.wait_for_notification(None)
def trigger_add_on_notification(self, add_on):
def test_notification_with_origin(self):
"""Trigger a notification with an origin"""
self.trigger_addon_notification('restartless_addon_signed.xpi')
self.assertIn(self.browser.notification.origin, self.marionette.baseurl)
self.assertIsNotNone(self.browser.notification.label)
self.browser.notification.close()
def test_addon_install_failed_notification(self):
"""Trigger add-on blocked notification using an unsigned add-on"""
# Ensure that installing unsigned extensions will fail
self.prefs.set_pref('xpinstall.signatures.required', True)
self.trigger_addon_notification('restartless_addon_unsigned.xpi')
self.assertIsInstance(self.browser.notification,
AddOnInstallFailedNotification)
self.browser.notification.close()
def trigger_addon_notification(self, addon):
with self.marionette.using_context('content'):
self.marionette.navigate('file://{0}'.format(here))
self.marionette.find_element(By.LINK_TEXT, add_on).click()
self.marionette.navigate(self.addons_url)
self.marionette.find_element(By.LINK_TEXT, addon).click()
self.browser.wait_for_notification()

View File

@ -11,11 +11,21 @@ from firefox_puppeteer.ui_base_lib import UIBaseLib
class BaseNotification(UIBaseLib):
"""Abstract base class for any kind of notification."""
__metaclass__ = ABCMeta
@property
def close_button(self):
"""Provide access to the close button.
:returns: The close button.
"""
return self.element.find_element(By.ANON_ATTRIBUTE,
{'anonid': 'closebutton'})
@property
def label(self):
"""Provides access to the notification label.
"""Provide access to the notification label.
:returns: The notification label.
"""
@ -23,34 +33,47 @@ class BaseNotification(UIBaseLib):
@property
def origin(self):
"""Provides access to the notification origin.
"""Provide 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()
def close(self, force=False):
"""Close the notification.
:param force: Optional, if True force close the notification.
Defaults to False.
"""
if force:
self.marionette.execute_script('arguments[0].click()',
script_args=[self.close_button])
else:
self.close_button.click()
self.window.wait_for_notification(None)
class AddOnInstallBlockedNotification(BaseNotification):
"""Add-on install blocked notification."""
def allow(self):
"""Allow the add-on to be installed."""
self.element.find_element(
@property
def allow_button(self):
"""Provide access to the allow button.
:returns: The allow button.
"""
return self.element.find_element(
By.ANON_ATTRIBUTE, {'anonid': 'button'}).find_element(
By.ANON_ATTRIBUTE, {'anonid': 'button'}).click()
By.ANON_ATTRIBUTE, {'anonid': 'button'})
class AddOnInstallConfirmationNotification(BaseNotification):
"""Add-on install confirmation notification."""
@property
def add_on(self):
"""Provides access to the add-on name.
def addon_name(self):
"""Provide access to the add-on name.
:returns: The add-on name.
"""
@ -58,27 +81,36 @@ class AddOnInstallConfirmationNotification(BaseNotification):
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 cancel_button(self):
"""Provide access to the cancel button.
def install(self):
"""Proceed with installation of the add-on."""
self.element.find_element(
By.ID, 'addon-install-confirmation-accept').click()
:returns: The cancel button.
"""
return self.element.find_element(
By.ID, 'addon-install-confirmation-cancel')
def install_button(self):
"""Provide access to the install button.
:returns: The install button.
"""
return self.element.find_element(
By.ID, 'addon-install-confirmation-accept')
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

View File

@ -108,24 +108,39 @@ class BrowserWindow(BaseWindow):
try:
notification = self.window_element.find_element(
By.CSS_SELECTOR, '#notification-popup popupnotification')
notification_id = notification.get_attribute('id')
return notifications_map.get(notification_id, BaseNotification)(
lambda: self.marionette, self, notification)
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__)
def wait_for_notification(self, notification_class=BaseNotification,
timeout=5):
"""Waits for the specified notification to be displayed.
:param notification_class: Optional, the notification class to wait for.
If `None` is specified it will wait for any notification to be closed.
Defaults to `BaseNotification`.
:param timeout: Optional, how long to wait for the expected notification.
Defaults to 5 seconds.
"""
wait = Wait(self.marionette, timeout=timeout)
if notification_class:
if notification_class is BaseNotification:
message = 'No notification was shown.'
Wait(self.marionette).until(lambda _: isinstance(
self.notification, notification_class), message=message)
else:
message = '{0} was not shown.'.format(notification_class.__name__)
wait.until(
lambda _: isinstance(self.notification, notification_class),
message=message)
else:
message = 'Unexpected notification shown.'
wait.until(
lambda _: self.notification is None,
message='Unexpected notification shown.')
@property
def tabbar(self):