Skip to content

Commit

Permalink
made url method a property and fixed Progress notification
Browse files Browse the repository at this point in the history
  • Loading branch information
Temidayo32 committed Nov 16, 2024
1 parent f25e72c commit 0f0c70a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 77 deletions.
23 changes: 0 additions & 23 deletions foxpuppet/windows/browser/notifications/addons.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,6 @@ def close(self) -> None:
BaseNotification.close(self)


class AddOnInstallRestart(BaseNotification):
"""Add-on install restart notification."""

def restart_now(self):
"""Restart Firefox immediately to complete the add-on installation."""
with self.selenium.context(self.selenium.CONTEXT_CHROME):
self.find_primary_button().click()

def restart_later(self):
"""Defer Firefox restart for later."""
with self.selenium.context(self.selenium.CONTEXT_CHROME):
self.find_secondary_button().click()


class AddOnInstallFailed(BaseNotification):
"""Add-on install failed notification."""

Expand Down Expand Up @@ -103,14 +89,6 @@ def is_downloading(self):
with self.selenium.context(self.selenium.CONTEXT_CHROME):
return "Downloading and verifying add-on…" in self.find_description().text

def wait_until_complete(self, timeout=None):
"""Wait until the progress notification disappears, indicating completion.
Args:
timeout (int, optional): Maximum time to wait in seconds.
"""
self.window.wait_for_notification(None, timeout=timeout)


# Clean up of these notifications will happen once Firefox ESR is past version 63
# https://github.com/mozilla/FoxPuppet/issues/212
Expand All @@ -119,7 +97,6 @@ def wait_until_complete(self, timeout=None):
"addon-install-confirmation-notification": AddOnInstallConfirmation,
"addon-install-complete-notification": AddOnInstallComplete,
"appMenu-addon-installed-notification": AddOnInstallComplete,
"addon-install-restart-notification": AddOnInstallRestart,
"addon-install-failed-notification": AddOnInstallFailed,
"addon-installed-notification": AddOnInstallComplete,
"addon-progress-notification": AddOnProgress,
Expand Down
110 changes: 69 additions & 41 deletions tests/test_notifications.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@

import pytest
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from typing import Any

from foxpuppet.windows.browser.notifications import BaseNotification
from selenium.webdriver.common.by import By
from foxpuppet.windows.browser.notifications.addons import (
AddOnInstallBlocked,
AddOnInstallComplete,
AddOnInstallConfirmation,
AddOnInstallFailed,
AddOnProgress,
)
from selenium.webdriver.remote.webdriver import WebDriver
from foxpuppet.windows import BrowserWindow
Expand All @@ -22,47 +25,67 @@


@pytest.fixture
def firefox_options(firefox_options: FirefoxOptions) -> FirefoxOptions:
def firefox_options(request, firefox_options: FirefoxOptions) -> FirefoxOptions:
"""Fixture for configuring Firefox."""
# Due to https://bugzilla.mozilla.org/show_bug.cgi?id=1329939 we need the
# initial browser window to be in the foreground. Without this, the
# notifications will not be displayed.
firefox_options.add_argument("-foreground")
if getattr(request, "param", {}).get("page_load_strategy_none", False):
firefox_options.set_capability("pageLoadStrategy", "none")
return firefox_options


class AddOn:
"""Class representing an add-on."""

def __init__(self, name: str, path: str):
def __init__(self, name: str, path_key: str = "default"):
self.name = name
self.path = path
self._paths = {
"default": "webextension.xpi",
"corrupt": "corruptwebextension.xpi",
"large": "largewebextension.xpi",
}
if path_key not in self._paths:
raise ValueError(f"Invalid path key: {path_key}")
self._path_key = path_key

@property
def path(self):
"""Returns the current path based on the selected key."""
return self._paths.get(self._path_key)

@path.setter
def path(self, ext_path):
"""Sets the current path key if it exists in paths."""
if ext_path in self._paths:
self._path_key = ext_path
else:
raise ValueError(f"Invalid path key: {ext_path}")


@pytest.fixture
def addon() -> AddOn:
"""Fixture for creating an installable add-on.
"""Fixture for creating an installable add-on."""
return AddOn(name="WebExtension")

Returns:
:py:class:`AddOn`: Add-on object containing a name and a path to the
add-on.

"""

# https://github.com/ambv/black/issues/144#issuecomment-392149599
class AddOn:
def __init__(self):
self.name = "WebExtension"
self.paths = {
"default": "webextension.xpi",
"corrupt": "corruptwebextension.xpi",
}

def get_path(self, size="default"):
"""Returns web extension path."""
return self.paths.get(size, self.paths["default"])
@pytest.fixture
def progress_notification(
addon: AddOn, browser: BrowserWindow, webserver: WebServer, selenium: WebDriver
) -> BaseNotification:
"""Fixture that triggers the download progress notification.
return AddOn(name="WebExtension", path="webextension.xpi")
Returns:
:py:class:AddOnProgress: Firefox notification.
"""
addon.path = "large"
selenium.get(webserver.url)
element = WebDriverWait(selenium, 10).until(
EC.element_to_be_clickable((By.LINK_TEXT, addon.path))
)
element.click()
return browser.wait_for_notification(AddOnProgress)


@pytest.fixture
Expand All @@ -75,8 +98,8 @@ def blocked_notification(
:py:class:`AddOnInstallBlocked`: Firefox notification.
"""
selenium.get(webserver.url())
selenium.find_element(By.LINK_TEXT, addon.get_path()).click()
selenium.get(webserver.url)
selenium.find_element(By.LINK_TEXT, addon.path).click()
return browser.wait_for_notification(AddOnInstallBlocked)


Expand Down Expand Up @@ -109,26 +132,17 @@ def complete_notification(


@pytest.fixture
def failed_notification(addon, browser, webserver, selenium):
"""Fixture that triggers a failed installation notification.
Returns:
:py:class:`AddOnInstallFailed`: Firefox notification.
"""
selenium.get(webserver.url())
selenium.find_element(By.LINK_TEXT, addon.get_path("corrupt")).click()
return browser.wait_for_notification(AddOnInstallFailed)


@pytest.fixture
def failed_notification(addon, browser, webserver, selenium):
def failed_notification(
addon: AddOn, browser: BrowserWindow, webserver: WebServer, selenium: WebDriver
):
"""Fixture that triggers a failed installation notification.
Returns:
:py:class:`AddOnInstallFailed`: Firefox notification.
"""
selenium.get(webserver.url())
selenium.find_element(By.LINK_TEXT, addon.get_path("corrupt")).click()
addon.path = "corrupt"
selenium.get(webserver.url)
selenium.find_element(By.LINK_TEXT, addon.path).click()
return browser.wait_for_notification(AddOnInstallFailed)


Expand Down Expand Up @@ -172,6 +186,7 @@ def test_notification_with_origin(
blocked_notification: AddOnInstallBlocked,
) -> None:
"""Trigger a notification with an origin."""
assert blocked_notification.origin is not None, "Notification origin should not be None"
assert f"{webserver.host}" in blocked_notification.origin
assert blocked_notification.label is not None

Expand Down Expand Up @@ -211,7 +226,20 @@ def test_addon_install_complete(
browser.wait_for_notification(None)


def test_failed_installation_notification(failed_notification):
def test_failed_installation_notification(
failed_notification: AddOnInstallFailed,
) -> None:
"""Test that a failed installation notification is shown for a corrupt add-on."""
error_text = "The add-on downloaded from this site could not be installed because it appears to be corrupt."
assert failed_notification.error_message == error_text


@pytest.mark.parametrize(
"firefox_options", [{"page_load_strategy_none": True}], indirect=True
)
def test_progress_notification_downloading(
browser: BrowserWindow, progress_notification: AddOnProgress
) -> None:
"""Verify downloading status is reported correctly."""
description = progress_notification.is_downloading
assert description is not None
Binary file added tests/web/largewebextension.xpi
Binary file not shown.
19 changes: 6 additions & 13 deletions tests/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,12 @@ def port(self) -> int:
"""
return self.server.server_address[1]

def start(self) -> None:
@property
def url(self) -> str:
"""Web server URL."""
return "http://{0.host}:{0.port}/".format(self)

def start(self):
"""Start web server."""
self.thread.start()

Expand All @@ -64,18 +69,6 @@ def stop(self) -> None:
self.server.shutdown()
self.thread.join()

def url(self, path="/") -> str:
"""Web server URL.
Args:
path (str, optional): Path to append to the web server URL.
Returns:
str: URL of web server.
"""
return "http://{0.host}:{0.port}{1}".format(self, path)

@classmethod
def get_free_port(cls):
"""Find and return a free port on the system."""
Expand Down

0 comments on commit 0f0c70a

Please sign in to comment.