From 23bcee4e41537a1fa876638f8a6d5fc79c3a0692 Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Sun, 26 Jan 2025 22:52:11 +0100 Subject: [PATCH 1/8] Implemented PanelUI Classes and Tests --- .../windows/browser/panel_ui/__init__.py | 4 + .../windows/browser/panel_ui/panel_ui.py | 225 ++++++++++++++++++ foxpuppet/windows/browser/window.py | 62 +++++ tests/test_panel_ui.py | 47 ++++ 4 files changed, 338 insertions(+) create mode 100644 foxpuppet/windows/browser/panel_ui/__init__.py create mode 100644 foxpuppet/windows/browser/panel_ui/panel_ui.py create mode 100644 tests/test_panel_ui.py diff --git a/foxpuppet/windows/browser/panel_ui/__init__.py b/foxpuppet/windows/browser/panel_ui/__init__.py new file mode 100644 index 0000000..499934f --- /dev/null +++ b/foxpuppet/windows/browser/panel_ui/__init__.py @@ -0,0 +1,4 @@ +# 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/. +"""Contains the Panel UI API and supporting files.""" diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py new file mode 100644 index 0000000..fec609c --- /dev/null +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -0,0 +1,225 @@ +# 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/. +"""Contains classes for handling Firefox Panel UI (Hamburger menu).""" + +from abc import ABCMeta + +from selenium.webdriver.common.by import By + +from foxpuppet.region import Region +from selenium.webdriver.remote.webelement import WebElement +from typing import Type, Any, TYPE_CHECKING, Optional + + +class PanelUI(Region): + """Handles interaction with Panel UI.""" + + __metaclass__ = ABCMeta + if TYPE_CHECKING: + from foxpuppet.windows import BrowserWindow + + @staticmethod + def create( + window: Optional["BrowserWindow"], root: WebElement + ) -> Type["PanelUI"] | Any: + """Create a Panel UI object. + + Args: + window (:py:class:`BrowserWindow`): Window object this region + appears in. + root + (:py:class:`~selenium.webdriver.remote.webelement.WebElement`): + WebDriver element object that serves as the root for the + Panel UI. + + Returns: + :py:class:`PanelUI`: Firefox Panel UI. + + """ + panel_items: dict = {} + _id: str | bool | WebElement | dict = root.get_property("id") + + panel_items.update(PANEL_ITEMS) + return panel_items.get(_id, PanelUI)(window, root) + + def open_panel_menu(self) -> None: + """ + Opens the Panel UI menu. + """ + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*PanelUILocators.PANEL_UI_BUTTON).click() + + def open_private_window(self) -> None: + """ + Opens a new window in private browsing mode using the Panel UI menu. + """ + # self.open_panel_menu() + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*PanelUILocators.PRIVATE_WINDOW).click() + + def open_history_menu(self) -> None: + """ + Opens the History in Panel UI Menu + """ + # self.open_panel_menu() + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*PanelUILocators.HISTORY).click() + + def wait_for_num_windows_or_tabs(self, expected_count: int) -> None: + """ + Waits until the number of open browser windows or tabs matches the expected value. + + Args: + expected_count (int): The expected number of windows or tabs. + """ + self.wait.until( + lambda _: len(self.driver.window_handles) == expected_count, + f"Expected {expected_count} windows or tabs, but found {len(self.driver.window_handles)}", + ) + + def switch_to_new_window_or_tab(self) -> None: + """Get list of all window handles, switch to the newly opened tab/window""" + handles = self.selenium.window_handles + self.selenium.switch_to.window(handles[-1]) + + +class History(PanelUI): + """Handles interactions with Firefox History.""" + + def is_present(self, link: str) -> bool: + """ + Checks if a specific link is present in the recent history. + Args: + link `str`: The URL or part of the URL to check for in the recent history. + + Returns: + bool: `True` if the link is present in the recent history, `False` otherwise. + """ + with self.selenium.context(self.selenium.CONTEXT_CHROME): + history_items = self.selenium.find_elements( + *PanelUILocators.RECENT_HISTORY_ITEMS + ) + for item in history_items: + item_src = item.get_attribute("image") + if item_src and link in item_src: + print(item_src) + return True + return False + + def clear_history(self): + """ + Clears the browsing history. + """ + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*PanelUILocators.CLEAR_RECENT_HISTORY).click() + self.selenium.switch_to.frame( + self.selenium.find_element(*PanelUILocators.HISTORY_IFRAME) + ) + with self.selenium.context(self.selenium.CONTEXT_CONTENT): + self.selenium.find_element(*PanelUILocators.DROPDOWN_HISTORY).click() + self.selenium.find_element( + *PanelUILocators.CLEAR_HISTORY_EVERYTHING + ).click() + self.selenium.execute_script( + """ + const shadowHost = arguments[0]; + const shadowRoot = shadowHost.shadowRoot; + const clearRecentHistoryButton = shadowRoot.querySelector('button[dlgtype="accept"]'); + clearRecentHistoryButton.click(); + """, + self.selenium.find_element(*PanelUILocators.HISTORY_DIALOG_BUTTON), + ) + self.selenium.switch_to.default_content() + + +class PrivateWindow(PanelUI): + """Handles interactions with Firefox Private Window.""" + + def verify_private_browsing_links_not_in_awesome_bar(self, links: list) -> list: + """ + Verifies that the provided links visited in private browing session do not appear in the awesome bar. + Args: + links `list`: A list of links to be verified. + + Returns: + list: A list of links that appeared in the awesome bar during private browsing. + """ + invalid_links = [] + initial_window_handle = self.driver.current_window_handle + # self.open_private_window() + self.switch_to_new_window_or_tab() + + for link in links: + self.selenium.get(link) + + self.driver.switch_to.window(initial_window_handle) + + with self.selenium.context(self.selenium.CONTEXT_CHROME): + for link in links: + awesome_bar = self.selenium.find_element(*PanelUILocators.INPUT_FIELD) + awesome_bar.clear() + awesome_bar.send_keys(link) + + self.wait.until( + lambda _: self.selenium.find_elements(*PanelUILocators.SEARCH_RESULTS) + ) + + search_results = self.selenium.find_elements( + *PanelUILocators.SEARCH_RESULTS + ) + if any(link in result.text for result in search_results): + invalid_links.append(link) + + return invalid_links + + def verify_private_browsing_links_not_in_history( + self, links: list, history: History + ) -> list: + """ + Verifies that the provided links visited in private browsing session do not appear in the history. + Args: + links (`list[str]`): List of URLs visited during private browsing session + history (`History`): An instance of the History class used to check if a link is present in the history. + + Returns: + list: A list of links that were found in the history during the private browsing session. + """ + invalid_links = [] + initial_window_handle = self.driver.current_window_handle + # self.open_private_window() + self.switch_to_new_window_or_tab() + + for link in links: + self.selenium.get(link) + + self.selenium.switch_to.window(initial_window_handle) + self.open_panel_menu() + self.open_history_menu() + for link in links: + if history.is_present(link): + invalid_links.append(link) + return invalid_links + + +class PanelUILocators: + PANEL_UI_BUTTON = (By.ID, "PanelUI-menu-button") + PRIVATE_WINDOW = (By.ID, "appMenu-new-private-window-button2") + HISTORY = (By.ID, "appMenu-history-button") + CLEAR_RECENT_HISTORY = (By.ID, "appMenuClearRecentHistory") + CLEAR_RECENT_HISTORY_BUTTON = (By.CSS_SELECTOR, "button[dlgtype='accept']") + CLEAR_HISTORY_EVERYTHING = (By.CSS_SELECTOR, "menuitem[value='0']") + HISTORY_DIALOG_BUTTON = (By.CSS_SELECTOR, "dialog[defaultButton='accept']") + DROPDOWN_HISTORY = (By.ID, "sanitizeDurationChoice") + RECENT_HISTORY_ITEMS = ( + By.CSS_SELECTOR, + "#appMenu_historyMenu toolbarbutton.subviewbutton", + ) + HISTORY_IFRAME = (By.CSS_SELECTOR, "browser.dialogFrame") + + +PANEL_ITEMS = { + "PanelUI-menu-button": PanelUI, + "appMenu-history-button": History, + "appMenu-new-private-window-button2": PrivateWindow, +} diff --git a/foxpuppet/windows/browser/window.py b/foxpuppet/windows/browser/window.py index 34cbc62..af41545 100644 --- a/foxpuppet/windows/browser/window.py +++ b/foxpuppet/windows/browser/window.py @@ -11,10 +11,12 @@ from foxpuppet.windows.browser.navbar import NavBar from foxpuppet.windows.browser.notifications import BaseNotification from foxpuppet.windows.browser.bookmarks.bookmark import Bookmark +from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI from selenium.webdriver.remote.webelement import WebElement from typing import Any, Optional, Union, TypeVar, Type T = TypeVar("T", bound="BaseNotification") +P = TypeVar("P", bound="PanelUI") class BrowserWindow(BaseWindow): @@ -26,10 +28,15 @@ class BrowserWindow(BaseWindow): _file_menu_new_window_button_locator = (By.ID, "menu_newNavigator") _nav_bar_locator = (By.ID, "nav-bar") _notification_locator = (By.CSS_SELECTOR, "#notification-popup popupnotification") + _panel_ui_locator = (By.ID, "PanelUI-menu-button") _app_menu_notification_locator = ( By.CSS_SELECTOR, "#appMenu-notification-popup popupnotification", ) + _app_menu_panel_ui_locator = ( + By.CSS_SELECTOR, + "#appMenu-mainView .panel-subview-body toolbarbutton", + ) _tab_browser_locator = (By.ID, "tabbrowser-tabs") @property @@ -81,6 +88,31 @@ def bookmark(self) -> Bookmark: root = self.selenium.find_element(*self._bookmark_locator) return Bookmark.create(self, root) + @property + def panel_ui(self) -> PanelUI | Any: + panel_root = None + with self.selenium.context(self.selenium.CONTEXT_CHROME): + try: + root = self.selenium.find_element(*self._panel_ui_locator) + panel_root = PanelUI.create(self, root) + except NoSuchElementException: + pass + + try: + panel_items = self.selenium.find_elements( + *self._app_menu_panel_ui_locator + ) + for item in panel_items: + _id = item.get_property("id") + from foxpuppet.windows.browser.panel_ui.panel_ui import PANEL_ITEMS + + if _id in PANEL_ITEMS and item.is_displayed(): + panel_root = PANEL_ITEMS[_id].create(self, item) # type: ignore + except StopIteration: + pass + + return panel_root + def wait_for_notification( self, notification_class: Optional[Type[T]] = BaseNotification, # type: ignore @@ -129,6 +161,36 @@ def wait_for_bookmark(self) -> Bookmark: ) return self.bookmark + def wait_for_panel_ui( + self, panel_ui_class: Optional[Type[P]] = PanelUI # type: ignore + ) -> Optional[P]: + """Wait for the specified PanelUI to be displayed. + + Args: + panel_ui_class (:py:class:`PanelUI`, optional): + The PanelUI subclass to wait for. If `None` is specified, it + will wait for any panel UI to be displayed. Defaults to `PanelUI`. + + Returns: + Optional[:py:class:`PanelUI`]: The displayed PanelUI or `None` if not found. + """ + if panel_ui_class: + if panel_ui_class is PanelUI: + message = "No panel UI was shown." + else: + message = f"{panel_ui_class.__name__} was not shown." + self.wait.until( + lambda _: isinstance(self.panel_ui, panel_ui_class), + message=message, + ) + return self.panel_ui # type: ignore + else: + self.wait.until( + lambda _: self.panel_ui is None, + message="Unexpected panel UI was shown.", + ) + return None + @property def is_private(self) -> bool | Any: """Property that checks if the specified window is private or not. diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py new file mode 100644 index 0000000..294ed9e --- /dev/null +++ b/tests/test_panel_ui.py @@ -0,0 +1,47 @@ +# 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/. +"""Tests for Panel UI.""" + +import pytest +from selenium.webdriver.remote.webdriver import WebDriver +from foxpuppet.windows import BrowserWindow +from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI, History + + +@pytest.fixture +def history(browser: BrowserWindow) -> History | None: + """Create History panel object. + + Args: + browser: BrowserWindow instance + + Returns: + :py:class:`History`: FoxPuppet History panel object + """ + panel_ui = browser.wait_for_panel_ui(PanelUI) + if panel_ui is not None: + panel_ui.open_panel_menu() + return browser.wait_for_panel_ui(History) + + +def test_url_is_present_in_history(history: History, selenium: WebDriver) -> None: + """Test that visited URL appears in browser history.""" + url = "https://www.mozilla.org/en-US/?v=a" + selenium.get(url) + history.open_history_menu() + assert history.is_present(url) + + +def test_clear_recent_history(history: History, selenium: WebDriver) -> None: + """Test clearing browser history removes visited URLs.""" + url = "https://www.mozilla.org/en-US/?v=a" + selenium.get(url) + history.open_history_menu() + history.clear_history() + import time + + time.sleep(1) + history.open_panel_menu() + history.open_history_menu() + assert not history.is_present(url) From c45b7dba188e99dbb4560749e3bf19fdad1e7ac8 Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Mon, 3 Feb 2025 23:00:15 +0100 Subject: [PATCH 2/8] Extended Panel UI methods and tests --- .../windows/browser/panel_ui/panel_ui.py | 133 +++++++++++++----- foxpuppet/windows/browser/window.py | 20 +-- tests/test_panel_ui.py | 87 +++++++++--- 3 files changed, 169 insertions(+), 71 deletions(-) diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index fec609c..470d6b2 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -50,11 +50,27 @@ def open_panel_menu(self) -> None: with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.PANEL_UI_BUTTON).click() + def open_new_tab(self) -> None: + """ + Opens a new tab using the Panel UI menu. + """ + self.open_panel_menu() + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*PanelUILocators.NEW_TAB).click() + + def open_new_window(self) -> None: + """ + Opens a new window using the Panel UI menu. + """ + self.open_panel_menu() + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*PanelUILocators.NEW_WINDOW).click() + def open_private_window(self) -> None: """ Opens a new window in private browsing mode using the Panel UI menu. """ - # self.open_panel_menu() + self.open_panel_menu() with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.PRIVATE_WINDOW).click() @@ -62,11 +78,11 @@ def open_history_menu(self) -> None: """ Opens the History in Panel UI Menu """ - # self.open_panel_menu() + self.open_panel_menu() with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.HISTORY).click() - def wait_for_num_windows_or_tabs(self, expected_count: int) -> None: + def wait_for_num_windows_or_tabs(self, expected_count: int) -> bool: """ Waits until the number of open browser windows or tabs matches the expected value. @@ -74,18 +90,40 @@ def wait_for_num_windows_or_tabs(self, expected_count: int) -> None: expected_count (int): The expected number of windows or tabs. """ self.wait.until( - lambda _: len(self.driver.window_handles) == expected_count, - f"Expected {expected_count} windows or tabs, but found {len(self.driver.window_handles)}", + lambda _: len(self.selenium.window_handles) == expected_count, + f"Expected {expected_count} windows or tabs, but found {len(self.selenium.window_handles)}", ) + return True def switch_to_new_window_or_tab(self) -> None: """Get list of all window handles, switch to the newly opened tab/window""" handles = self.selenium.window_handles self.selenium.switch_to.window(handles[-1]) + def awesome_bar(self, links: list) -> list: + """Check if the provided links are present in the awesome bar's suggestion links.""" + urls = [] + with self.selenium.context(self.selenium.CONTEXT_CHROME): + for link in links: + awesome_bar = self.selenium.find_element(*PanelUILocators.INPUT_FIELD) + awesome_bar.clear() + awesome_bar.send_keys(link) + + self.wait.until( + lambda _: self.selenium.find_elements(*PanelUILocators.SEARCH_RESULTS) + ) + + search_results = self.selenium.find_elements( + *PanelUILocators.SEARCH_RESULT_ITEMS + ) -class History(PanelUI): - """Handles interactions with Firefox History.""" + for result in search_results: + url_span = result.find_element(*PanelUILocators.SEARCH_RESULT_ITEM) + if url_span.text in link: + if len(url_span.text) != 0: + urls.append(link) + break + return urls def is_present(self, link: str) -> bool: """ @@ -132,9 +170,23 @@ def clear_history(self): ) self.selenium.switch_to.default_content() + def verify_links_in_awesome_bar(self, links: list, new_window: bool = False) -> list: + """ + Verifies that the provided links appear in the awesome bar. + Args: + links `list`: A list of links to be verified. -class PrivateWindow(PanelUI): - """Handles interactions with Firefox Private Window.""" + Returns: + list: A list of links that were not found in the awesome bar. + """ + if new_window: + self.open_new_window() + else: + self.open_new_tab() + self.switch_to_new_window_or_tab() + for link in links: + self.selenium.get(link) + return self.awesome_bar(links) def verify_private_browsing_links_not_in_awesome_bar(self, links: list) -> list: """ @@ -145,36 +197,43 @@ def verify_private_browsing_links_not_in_awesome_bar(self, links: list) -> list: Returns: list: A list of links that appeared in the awesome bar during private browsing. """ - invalid_links = [] - initial_window_handle = self.driver.current_window_handle - # self.open_private_window() + initial_window_handle = self.selenium.current_window_handle + self.open_private_window() self.switch_to_new_window_or_tab() for link in links: self.selenium.get(link) - self.driver.switch_to.window(initial_window_handle) - - with self.selenium.context(self.selenium.CONTEXT_CHROME): - for link in links: - awesome_bar = self.selenium.find_element(*PanelUILocators.INPUT_FIELD) - awesome_bar.clear() - awesome_bar.send_keys(link) - - self.wait.until( - lambda _: self.selenium.find_elements(*PanelUILocators.SEARCH_RESULTS) - ) + self.selenium.switch_to.window(initial_window_handle) + return self.awesome_bar(links) - search_results = self.selenium.find_elements( - *PanelUILocators.SEARCH_RESULTS - ) - if any(link in result.text for result in search_results): - invalid_links.append(link) + def verify_links_in_history(self, links: list, new_window: bool = False) -> list: + """ + Verifies that the provided links appear in the history. + Args: + links `list`: A list of links to be verified. - return invalid_links + Returns: + list: A list of links that were not found in the history. + """ + if new_window: + self.open_new_window() + else: + self.open_new_tab() + self.switch_to_new_window_or_tab() + for link in links: + self.selenium.get(link) + self.open_panel_menu() + self.open_history_menu() + urls = [] + for link in links: + if not self.is_present(link): + urls.append(link) + return urls def verify_private_browsing_links_not_in_history( - self, links: list, history: History + self, + links: list, ) -> list: """ Verifies that the provided links visited in private browsing session do not appear in the history. @@ -186,8 +245,8 @@ def verify_private_browsing_links_not_in_history( list: A list of links that were found in the history during the private browsing session. """ invalid_links = [] - initial_window_handle = self.driver.current_window_handle - # self.open_private_window() + initial_window_handle = self.selenium.current_window_handle + self.open_private_window() self.switch_to_new_window_or_tab() for link in links: @@ -197,15 +256,18 @@ def verify_private_browsing_links_not_in_history( self.open_panel_menu() self.open_history_menu() for link in links: - if history.is_present(link): + if self.is_present(link): invalid_links.append(link) return invalid_links class PanelUILocators: PANEL_UI_BUTTON = (By.ID, "PanelUI-menu-button") + NEW_TAB = (By.ID, "appMenu-new-tab-button2") + NEW_WINDOW = (By.ID, "appMenu-new-window-button2") PRIVATE_WINDOW = (By.ID, "appMenu-new-private-window-button2") HISTORY = (By.ID, "appMenu-history-button") + INPUT_FIELD = (By.ID, "urlbar-input") CLEAR_RECENT_HISTORY = (By.ID, "appMenuClearRecentHistory") CLEAR_RECENT_HISTORY_BUTTON = (By.CSS_SELECTOR, "button[dlgtype='accept']") CLEAR_HISTORY_EVERYTHING = (By.CSS_SELECTOR, "menuitem[value='0']") @@ -216,10 +278,11 @@ class PanelUILocators: "#appMenu_historyMenu toolbarbutton.subviewbutton", ) HISTORY_IFRAME = (By.CSS_SELECTOR, "browser.dialogFrame") + SEARCH_RESULTS = (By.ID, "urlbar-results") + SEARCH_RESULT_ITEMS = (By.CSS_SELECTOR, "div.urlbarView-row[role='presentation']") + SEARCH_RESULT_ITEM = (By.CSS_SELECTOR, "span.urlbarView-url") PANEL_ITEMS = { "PanelUI-menu-button": PanelUI, - "appMenu-history-button": History, - "appMenu-new-private-window-button2": PrivateWindow, } diff --git a/foxpuppet/windows/browser/window.py b/foxpuppet/windows/browser/window.py index af41545..80c4995 100644 --- a/foxpuppet/windows/browser/window.py +++ b/foxpuppet/windows/browser/window.py @@ -22,7 +22,7 @@ class BrowserWindow(BaseWindow): """Representation of a browser window.""" - _bookmark_locator = (By.ID, "main-window") # editBookmarkPanelTemplate + _bookmark_locator = (By.ID, "main-window") _file_menu_button_locator = (By.ID, "file-menu") _file_menu_private_window_locator = (By.ID, "menu_newPrivateWindow") _file_menu_new_window_button_locator = (By.ID, "menu_newNavigator") @@ -33,10 +33,6 @@ class BrowserWindow(BaseWindow): By.CSS_SELECTOR, "#appMenu-notification-popup popupnotification", ) - _app_menu_panel_ui_locator = ( - By.CSS_SELECTOR, - "#appMenu-mainView .panel-subview-body toolbarbutton", - ) _tab_browser_locator = (By.ID, "tabbrowser-tabs") @property @@ -97,20 +93,6 @@ def panel_ui(self) -> PanelUI | Any: panel_root = PanelUI.create(self, root) except NoSuchElementException: pass - - try: - panel_items = self.selenium.find_elements( - *self._app_menu_panel_ui_locator - ) - for item in panel_items: - _id = item.get_property("id") - from foxpuppet.windows.browser.panel_ui.panel_ui import PANEL_ITEMS - - if _id in PANEL_ITEMS and item.is_displayed(): - panel_root = PANEL_ITEMS[_id].create(self, item) # type: ignore - except StopIteration: - pass - return panel_root def wait_for_notification( diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py index 294ed9e..8deb6c8 100644 --- a/tests/test_panel_ui.py +++ b/tests/test_panel_ui.py @@ -6,42 +6,95 @@ import pytest from selenium.webdriver.remote.webdriver import WebDriver from foxpuppet.windows import BrowserWindow -from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI, History +from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI + + +links = [ + "https://www.mozilla.org/en-US/?v=a", + "https://www.youtube.com", + "https://www.facebook.com/", +] @pytest.fixture -def history(browser: BrowserWindow) -> History | None: - """Create History panel object. +def panel_ui(browser: BrowserWindow) -> PanelUI | None: + """Create Panel UI object. Args: browser: BrowserWindow instance Returns: - :py:class:`History`: FoxPuppet History panel object + :py:class:`PanelUI`: FoxPuppet Panel UI object """ - panel_ui = browser.wait_for_panel_ui(PanelUI) - if panel_ui is not None: - panel_ui.open_panel_menu() - return browser.wait_for_panel_ui(History) + return browser.wait_for_panel_ui(PanelUI) + + +def test_open_new_tab(panel_ui: PanelUI) -> None: + """Test opening a new tab using the Panel UI.""" + panel_ui.open_new_tab() + assert panel_ui.wait_for_num_windows_or_tabs(2) + + +def test_open_new_window(panel_ui: PanelUI) -> None: + """Test opening a new window using the Panel UI.""" + panel_ui.open_new_window() + assert panel_ui.wait_for_num_windows_or_tabs(2) + + +def test_verify_links_open_in_new_tab_in_awesome_bar(panel_ui: PanelUI) -> None: + """Test that links opened in new tab are present in the awesome bar.""" + link = ["https://www.mozilla.org/en-US/?v=a"] + urls = panel_ui.verify_links_in_awesome_bar(link) + assert len(urls) == 1 + +def test_verify_links_open_in_new_tab_in_history(panel_ui: PanelUI) -> None: + """Test that links opened in new tab are present in browser history.""" + urls = panel_ui.verify_links_in_history(links) + assert len(urls) == 3 -def test_url_is_present_in_history(history: History, selenium: WebDriver) -> None: + +def test_verify_links_open_in_new_window_in_history(panel_ui: PanelUI) -> None: + """Test that links opened in new window are present in browser history.""" + urls = panel_ui.verify_links_in_history(links, new_window=True) + assert len(urls) == 3 + + +def test_verify_links_open_in_new_window_in_awesome_bar(panel_ui: PanelUI) -> None: + """Test that links opened in new window are present in the awesome bar.""" + link = ["https://www.mozilla.org/en-US/?v=a"] + urls = panel_ui.verify_links_in_awesome_bar(link, new_window=True) + assert len(urls) == 1 + + +def test_url_is_present_in_history(panel_ui: PanelUI, selenium: WebDriver) -> None: """Test that visited URL appears in browser history.""" url = "https://www.mozilla.org/en-US/?v=a" selenium.get(url) - history.open_history_menu() - assert history.is_present(url) + panel_ui.open_history_menu() + assert panel_ui.is_present(url) -def test_clear_recent_history(history: History, selenium: WebDriver) -> None: +def test_clear_recent_history(panel_ui: PanelUI, selenium: WebDriver) -> None: """Test clearing browser history removes visited URLs.""" url = "https://www.mozilla.org/en-US/?v=a" selenium.get(url) - history.open_history_menu() - history.clear_history() + panel_ui.open_history_menu() + panel_ui.clear_history() import time time.sleep(1) - history.open_panel_menu() - history.open_history_menu() - assert not history.is_present(url) + panel_ui.open_history_menu() + assert not panel_ui.is_present(url) + + +def test_verify_private_browsing_links_not_in_history(panel_ui: PanelUI) -> None: + """Test that links opened in private window are not present in browser history.""" + invalid_links = panel_ui.verify_private_browsing_links_not_in_history(links) + assert not invalid_links + + +def test_verify_private_browsing_links_not_in_awesome_bar(panel_ui: PanelUI) -> None: + """Test that links opened in private window are not present in the awesome bar.""" + invalid_links = panel_ui.verify_private_browsing_links_not_in_awesome_bar(links) + assert not invalid_links From 5008d76bbd8dc7596febda55d17f3d2eeb1806a1 Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Mon, 10 Feb 2025 17:37:39 +0100 Subject: [PATCH 3/8] Added History SubClass --- foxpuppet/windows/browser/navbar.py | 32 ++++ .../windows/browser/panel_ui/panel_ui.py | 176 +++--------------- foxpuppet/windows/browser/window.py | 50 +++-- tests/test_panel_ui.py | 139 ++++++++++---- 4 files changed, 203 insertions(+), 194 deletions(-) diff --git a/foxpuppet/windows/browser/navbar.py b/foxpuppet/windows/browser/navbar.py index 09fcb93..93a2c02 100644 --- a/foxpuppet/windows/browser/navbar.py +++ b/foxpuppet/windows/browser/navbar.py @@ -39,3 +39,35 @@ def is_tracking_shield_displayed(self) -> bool: return el.get_attribute("active") is not None el = self.root.find_element(By.ID, "tracking-protection-icon") return bool(el.get_attribute("state")) + + def url_bar(self, links: list) -> list: + """Check if the provided links are present in the url bar's suggestions.""" + urls = [] + with self.selenium.context(self.selenium.CONTEXT_CHROME): + for link in links: + url_bar = self.selenium.find_element(*NavBarLocators.INPUT_FIELD) + url_bar.clear() + url_bar.send_keys(link) + + self.wait.until( + lambda _: self.selenium.find_elements(*NavBarLocators.SEARCH_RESULTS) + ) + + search_results = self.selenium.find_elements( + *NavBarLocators.SEARCH_RESULT_ITEMS + ) + + for result in search_results: + url_span = result.find_element(*NavBarLocators.SEARCH_RESULT_ITEM) + if url_span.text in link: + if len(url_span.text) != 0: + urls.append(link) + break + return urls + + +class NavBarLocators: + INPUT_FIELD = (By.ID, "urlbar-input") + SEARCH_RESULTS = (By.ID, "urlbar-results") + SEARCH_RESULT_ITEM = (By.CSS_SELECTOR, "span.urlbarView-url") + SEARCH_RESULT_ITEMS = (By.CSS_SELECTOR, "div.urlbarView-row[role='presentation']") diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index 470d6b2..ddb2bd6 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -3,19 +3,16 @@ # You can obtain one at http://mozilla.org/MPL/2.0/. """Contains classes for handling Firefox Panel UI (Hamburger menu).""" -from abc import ABCMeta - from selenium.webdriver.common.by import By -from foxpuppet.region import Region +from foxpuppet.windows.browser.navbar import NavBar from selenium.webdriver.remote.webelement import WebElement from typing import Type, Any, TYPE_CHECKING, Optional -class PanelUI(Region): +class PanelUI(NavBar): """Handles interaction with Panel UI.""" - __metaclass__ = ABCMeta if TYPE_CHECKING: from foxpuppet.windows import BrowserWindow @@ -43,6 +40,20 @@ def create( panel_items.update(PANEL_ITEMS) return panel_items.get(_id, PanelUI)(window, root) + @property + def is_barged(self) -> bool: + """ + Checks if the Panel UI button indicates a pending Firefox update. + + Returns: + bool: True if an update notification (barge) is present, False otherwise. + """ + with self.selenium.context(self.selenium.CONTEXT_CHROME): + barged_status = self.selenium.find_element( + *PanelUILocators.PANEL_UI_BUTTON + ).get_attribute("barged") + return barged_status == "true" + def open_panel_menu(self) -> None: """ Opens the Panel UI menu. @@ -78,53 +89,11 @@ def open_history_menu(self) -> None: """ Opens the History in Panel UI Menu """ - self.open_panel_menu() with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.HISTORY).click() - def wait_for_num_windows_or_tabs(self, expected_count: int) -> bool: - """ - Waits until the number of open browser windows or tabs matches the expected value. - - Args: - expected_count (int): The expected number of windows or tabs. - """ - self.wait.until( - lambda _: len(self.selenium.window_handles) == expected_count, - f"Expected {expected_count} windows or tabs, but found {len(self.selenium.window_handles)}", - ) - return True - - def switch_to_new_window_or_tab(self) -> None: - """Get list of all window handles, switch to the newly opened tab/window""" - handles = self.selenium.window_handles - self.selenium.switch_to.window(handles[-1]) - - def awesome_bar(self, links: list) -> list: - """Check if the provided links are present in the awesome bar's suggestion links.""" - urls = [] - with self.selenium.context(self.selenium.CONTEXT_CHROME): - for link in links: - awesome_bar = self.selenium.find_element(*PanelUILocators.INPUT_FIELD) - awesome_bar.clear() - awesome_bar.send_keys(link) - - self.wait.until( - lambda _: self.selenium.find_elements(*PanelUILocators.SEARCH_RESULTS) - ) - - search_results = self.selenium.find_elements( - *PanelUILocators.SEARCH_RESULT_ITEMS - ) - - for result in search_results: - url_span = result.find_element(*PanelUILocators.SEARCH_RESULT_ITEM) - if url_span.text in link: - if len(url_span.text) != 0: - urls.append(link) - break - return urls +class History(PanelUI): def is_present(self, link: str) -> bool: """ Checks if a specific link is present in the recent history. @@ -170,119 +139,26 @@ def clear_history(self): ) self.selenium.switch_to.default_content() - def verify_links_in_awesome_bar(self, links: list, new_window: bool = False) -> list: - """ - Verifies that the provided links appear in the awesome bar. - Args: - links `list`: A list of links to be verified. - - Returns: - list: A list of links that were not found in the awesome bar. - """ - if new_window: - self.open_new_window() - else: - self.open_new_tab() - self.switch_to_new_window_or_tab() - for link in links: - self.selenium.get(link) - return self.awesome_bar(links) - - def verify_private_browsing_links_not_in_awesome_bar(self, links: list) -> list: - """ - Verifies that the provided links visited in private browing session do not appear in the awesome bar. - Args: - links `list`: A list of links to be verified. - - Returns: - list: A list of links that appeared in the awesome bar during private browsing. - """ - initial_window_handle = self.selenium.current_window_handle - self.open_private_window() - self.switch_to_new_window_or_tab() - - for link in links: - self.selenium.get(link) - - self.selenium.switch_to.window(initial_window_handle) - return self.awesome_bar(links) - - def verify_links_in_history(self, links: list, new_window: bool = False) -> list: - """ - Verifies that the provided links appear in the history. - Args: - links `list`: A list of links to be verified. - - Returns: - list: A list of links that were not found in the history. - """ - if new_window: - self.open_new_window() - else: - self.open_new_tab() - self.switch_to_new_window_or_tab() - for link in links: - self.selenium.get(link) - self.open_panel_menu() - self.open_history_menu() - urls = [] - for link in links: - if not self.is_present(link): - urls.append(link) - return urls - - def verify_private_browsing_links_not_in_history( - self, - links: list, - ) -> list: - """ - Verifies that the provided links visited in private browsing session do not appear in the history. - Args: - links (`list[str]`): List of URLs visited during private browsing session - history (`History`): An instance of the History class used to check if a link is present in the history. - - Returns: - list: A list of links that were found in the history during the private browsing session. - """ - invalid_links = [] - initial_window_handle = self.selenium.current_window_handle - self.open_private_window() - self.switch_to_new_window_or_tab() - - for link in links: - self.selenium.get(link) - - self.selenium.switch_to.window(initial_window_handle) - self.open_panel_menu() - self.open_history_menu() - for link in links: - if self.is_present(link): - invalid_links.append(link) - return invalid_links - class PanelUILocators: - PANEL_UI_BUTTON = (By.ID, "PanelUI-menu-button") - NEW_TAB = (By.ID, "appMenu-new-tab-button2") - NEW_WINDOW = (By.ID, "appMenu-new-window-button2") - PRIVATE_WINDOW = (By.ID, "appMenu-new-private-window-button2") - HISTORY = (By.ID, "appMenu-history-button") - INPUT_FIELD = (By.ID, "urlbar-input") + CLEAR_HISTORY_EVERYTHING = (By.CSS_SELECTOR, "menuitem[value='0']") CLEAR_RECENT_HISTORY = (By.ID, "appMenuClearRecentHistory") CLEAR_RECENT_HISTORY_BUTTON = (By.CSS_SELECTOR, "button[dlgtype='accept']") - CLEAR_HISTORY_EVERYTHING = (By.CSS_SELECTOR, "menuitem[value='0']") - HISTORY_DIALOG_BUTTON = (By.CSS_SELECTOR, "dialog[defaultButton='accept']") DROPDOWN_HISTORY = (By.ID, "sanitizeDurationChoice") + HISTORY = (By.ID, "appMenu-history-button") + HISTORY_DIALOG_BUTTON = (By.CSS_SELECTOR, "dialog[defaultButton='accept']") + HISTORY_IFRAME = (By.CSS_SELECTOR, "browser.dialogFrame") + NEW_TAB = (By.ID, "appMenu-new-tab-button2") + NEW_WINDOW = (By.ID, "appMenu-new-window-button2") + PANEL_UI_BUTTON = (By.ID, "PanelUI-menu-button") + PRIVATE_WINDOW = (By.ID, "appMenu-new-private-window-button2") RECENT_HISTORY_ITEMS = ( By.CSS_SELECTOR, "#appMenu_historyMenu toolbarbutton.subviewbutton", ) - HISTORY_IFRAME = (By.CSS_SELECTOR, "browser.dialogFrame") - SEARCH_RESULTS = (By.ID, "urlbar-results") - SEARCH_RESULT_ITEMS = (By.CSS_SELECTOR, "div.urlbarView-row[role='presentation']") - SEARCH_RESULT_ITEM = (By.CSS_SELECTOR, "span.urlbarView-url") PANEL_ITEMS = { "PanelUI-menu-button": PanelUI, + "appMenu-history-button": History, } diff --git a/foxpuppet/windows/browser/window.py b/foxpuppet/windows/browser/window.py index 80c4995..3c55345 100644 --- a/foxpuppet/windows/browser/window.py +++ b/foxpuppet/windows/browser/window.py @@ -33,6 +33,10 @@ class BrowserWindow(BaseWindow): By.CSS_SELECTOR, "#appMenu-notification-popup popupnotification", ) + _app_menu_panel_ui_locator = ( + By.CSS_SELECTOR, + "#appMenu-mainView .panel-subview-body toolbarbutton", + ) _tab_browser_locator = (By.ID, "tabbrowser-tabs") @property @@ -85,14 +89,20 @@ def bookmark(self) -> Bookmark: return Bookmark.create(self, root) @property - def panel_ui(self) -> PanelUI | Any: + def panel(self) -> PanelUI | Any: panel_root = None with self.selenium.context(self.selenium.CONTEXT_CHROME): - try: - root = self.selenium.find_element(*self._panel_ui_locator) - panel_root = PanelUI.create(self, root) - except NoSuchElementException: - pass + root = self.selenium.find_element(*self._panel_ui_locator) + panel_root = PanelUI.create(self, root) + + panel_items = self.selenium.find_elements(*self._app_menu_panel_ui_locator) + for item in panel_items: + _id = item.get_property("id") + from foxpuppet.windows.browser.panel_ui.panel_ui import PANEL_ITEMS + + if _id in PANEL_ITEMS and item.is_displayed(): + panel_root = PANEL_ITEMS[_id].create(self, item) # type: ignore + return panel_root def wait_for_notification( @@ -143,10 +153,10 @@ def wait_for_bookmark(self) -> Bookmark: ) return self.bookmark - def wait_for_panel_ui( + def wait_for_panel( self, panel_ui_class: Optional[Type[P]] = PanelUI # type: ignore ) -> Optional[P]: - """Wait for the specified PanelUI to be displayed. + """Wait for the specified PanelUI item to be displayed. Args: panel_ui_class (:py:class:`PanelUI`, optional): @@ -162,13 +172,13 @@ def wait_for_panel_ui( else: message = f"{panel_ui_class.__name__} was not shown." self.wait.until( - lambda _: isinstance(self.panel_ui, panel_ui_class), + lambda _: isinstance(self.panel, panel_ui_class), message=message, ) - return self.panel_ui # type: ignore + return self.panel # type: ignore else: self.wait.until( - lambda _: self.panel_ui is None, + lambda _: self.panel is None, message="Unexpected panel UI was shown.", ) return None @@ -230,3 +240,21 @@ def open_window(self, private: bool = False) -> Union["BrowserWindow", Any]: expected.new_browser_window_is_opened(self.selenium, handles_before), message="No new browser window opened", ) + + def wait_for_num_windows_or_tabs(self, expected_count: int) -> bool: + """ + Waits until the number of open browser windows or tabs matches the expected value. + + Args: + expected_count (int): The expected number of windows or tabs. + """ + self.wait.until( + lambda _: len(self.selenium.window_handles) == expected_count, + f"Expected {expected_count} windows or tabs, but found {len(self.selenium.window_handles)}", + ) + return True + + def switch_to_new_window_or_tab(self) -> None: + """Get list of all window handles, switch to the newly opened tab/window""" + handles = self.selenium.window_handles + self.selenium.switch_to.window(handles[-1]) diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py index 8deb6c8..20c9e3c 100644 --- a/tests/test_panel_ui.py +++ b/tests/test_panel_ui.py @@ -4,9 +4,10 @@ """Tests for Panel UI.""" import pytest +import time from selenium.webdriver.remote.webdriver import WebDriver from foxpuppet.windows import BrowserWindow -from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI +from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI, History links = [ @@ -26,75 +27,147 @@ def panel_ui(browser: BrowserWindow) -> PanelUI | None: Returns: :py:class:`PanelUI`: FoxPuppet Panel UI object """ - return browser.wait_for_panel_ui(PanelUI) + return browser.wait_for_panel(PanelUI) -def test_open_new_tab(panel_ui: PanelUI) -> None: +@pytest.fixture +def history(panel_ui: PanelUI, browser: BrowserWindow) -> History | None: + """Create Panel UI object. + + Args: + browser: BrowserWindow instance + + Returns: + :py:class:`PanelUI`: FoxPuppet Panel UI object + """ + panel_ui.open_panel_menu() + return browser.wait_for_panel(History) + + +def test_open_new_tab(panel_ui: PanelUI, browser: BrowserWindow) -> None: """Test opening a new tab using the Panel UI.""" panel_ui.open_new_tab() - assert panel_ui.wait_for_num_windows_or_tabs(2) + assert browser.wait_for_num_windows_or_tabs(2) -def test_open_new_window(panel_ui: PanelUI) -> None: +def test_open_new_window(panel_ui: PanelUI, browser: BrowserWindow) -> None: """Test opening a new window using the Panel UI.""" panel_ui.open_new_window() - assert panel_ui.wait_for_num_windows_or_tabs(2) + assert browser.wait_for_num_windows_or_tabs(2) -def test_verify_links_open_in_new_tab_in_awesome_bar(panel_ui: PanelUI) -> None: - """Test that links opened in new tab are present in the awesome bar.""" - link = ["https://www.mozilla.org/en-US/?v=a"] - urls = panel_ui.verify_links_in_awesome_bar(link) +def test_verify_links_open_in_new_tab_in_url_bar( + panel_ui: PanelUI, browser: BrowserWindow, selenium: WebDriver +) -> None: + """Test that links opened in new tab are present in the url bar.""" + links = ["https://www.mozilla.org/en-US/?v=a"] + panel_ui.open_new_tab() + browser.switch_to_new_window_or_tab() + for link in links: + selenium.get(link) + urls = panel_ui.url_bar(links) assert len(urls) == 1 -def test_verify_links_open_in_new_tab_in_history(panel_ui: PanelUI) -> None: +def test_verify_links_open_in_new_tab_in_history( + panel_ui: PanelUI, history: History, browser: BrowserWindow, selenium: WebDriver +) -> None: """Test that links opened in new tab are present in browser history.""" - urls = panel_ui.verify_links_in_history(links) + urls = [] + panel_ui.open_new_tab() + browser.switch_to_new_window_or_tab() + for link in links: + selenium.get(link) + panel_ui.open_panel_menu() + panel_ui.open_history_menu() + for link in links: + if history.is_present(link): + urls.append(link) assert len(urls) == 3 -def test_verify_links_open_in_new_window_in_history(panel_ui: PanelUI) -> None: +def test_verify_links_open_in_new_window_in_history( + panel_ui: PanelUI, history: History, browser: BrowserWindow, selenium: WebDriver +) -> None: """Test that links opened in new window are present in browser history.""" - urls = panel_ui.verify_links_in_history(links, new_window=True) + urls = [] + panel_ui.open_new_window() + browser.switch_to_new_window_or_tab() + for link in links: + selenium.get(link) + panel_ui.open_panel_menu() + panel_ui.open_history_menu() + for link in links: + if history.is_present(link): + urls.append(link) assert len(urls) == 3 -def test_verify_links_open_in_new_window_in_awesome_bar(panel_ui: PanelUI) -> None: - """Test that links opened in new window are present in the awesome bar.""" - link = ["https://www.mozilla.org/en-US/?v=a"] - urls = panel_ui.verify_links_in_awesome_bar(link, new_window=True) - assert len(urls) == 1 +def test_verify_links_open_in_new_window_in_url_bar( + panel_ui: PanelUI, browser: BrowserWindow, selenium: WebDriver +) -> None: + """Test that links opened in new window are present in the url bar.""" + links = ["https://www.mozilla.org/en-US/?v=a"] + panel_ui.open_new_window() + browser.switch_to_new_window_or_tab() + time.sleep(1) + for link in links: + selenium.get(link) + url = panel_ui.url_bar(links) + assert len(url) == 1 -def test_url_is_present_in_history(panel_ui: PanelUI, selenium: WebDriver) -> None: +def test_url_is_present_in_history(history: History, selenium: WebDriver) -> None: """Test that visited URL appears in browser history.""" url = "https://www.mozilla.org/en-US/?v=a" selenium.get(url) - panel_ui.open_history_menu() - assert panel_ui.is_present(url) + history.open_history_menu() + assert history.is_present(url) -def test_clear_recent_history(panel_ui: PanelUI, selenium: WebDriver) -> None: +def test_clear_recent_history( + panel_ui: PanelUI, history: History, selenium: WebDriver +) -> None: """Test clearing browser history removes visited URLs.""" url = "https://www.mozilla.org/en-US/?v=a" selenium.get(url) panel_ui.open_history_menu() - panel_ui.clear_history() - import time - + history.clear_history() time.sleep(1) + panel_ui.open_panel_menu() panel_ui.open_history_menu() - assert not panel_ui.is_present(url) + assert not history.is_present(url) -def test_verify_private_browsing_links_not_in_history(panel_ui: PanelUI) -> None: +def test_verify_private_browsing_links_not_in_history( + panel_ui: PanelUI, history: History, browser: BrowserWindow, selenium: WebDriver +) -> None: """Test that links opened in private window are not present in browser history.""" - invalid_links = panel_ui.verify_private_browsing_links_not_in_history(links) + invalid_links = [] + initial_window_handle = selenium.current_window_handle + panel_ui.open_private_window() + browser.switch_to_new_window_or_tab() + for link in links: + selenium.get(link) + selenium.switch_to.window(initial_window_handle) + panel_ui.open_panel_menu() + panel_ui.open_history_menu() + for link in links: + if history.is_present(link): + invalid_links.append(link) assert not invalid_links -def test_verify_private_browsing_links_not_in_awesome_bar(panel_ui: PanelUI) -> None: - """Test that links opened in private window are not present in the awesome bar.""" - invalid_links = panel_ui.verify_private_browsing_links_not_in_awesome_bar(links) - assert not invalid_links +def test_verify_private_browsing_links_not_in_url_bar( + panel_ui: PanelUI, browser: BrowserWindow, selenium: WebDriver +) -> None: + """Test that links opened in private window are not present in the url bar.""" + initial_window_handle = selenium.current_window_handle + panel_ui.open_private_window() + browser.switch_to_new_window_or_tab() + + for link in links: + selenium.get(link) + + selenium.switch_to.window(initial_window_handle) + assert not panel_ui.url_bar(links) From dbab3336311422e1314efa12c5516bc039a536c7 Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Tue, 11 Feb 2025 18:06:04 +0100 Subject: [PATCH 4/8] Removed FoxPuppet's unrelated tests --- foxpuppet/region.py | 2 + foxpuppet/windows/browser/navbar.py | 37 +---- .../windows/browser/panel_ui/panel_ui.py | 2 +- foxpuppet/windows/browser/urlbar.py | 45 +++++++ foxpuppet/windows/browser/window.py | 18 --- tests/test_panel_ui.py | 126 +++++------------- 6 files changed, 89 insertions(+), 141 deletions(-) create mode 100644 foxpuppet/windows/browser/urlbar.py diff --git a/foxpuppet/region.py b/foxpuppet/region.py index f0c13c4..fa575fd 100644 --- a/foxpuppet/region.py +++ b/foxpuppet/region.py @@ -8,6 +8,7 @@ from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.common.action_chains import ActionChains from foxpuppet.windows import BaseWindow +from foxpuppet.windows.browser.urlbar import UrlBar class Region(object): @@ -40,3 +41,4 @@ def __init__(self, window: BaseWindow, root: WebElement): self.wait: WebDriverWait = window.wait self.window: BaseWindow = window self.actions: ActionChains = ActionChains(self.selenium) + self._url_bar: UrlBar = UrlBar(self.selenium, self.wait) diff --git a/foxpuppet/windows/browser/navbar.py b/foxpuppet/windows/browser/navbar.py index 93a2c02..db3b258 100644 --- a/foxpuppet/windows/browser/navbar.py +++ b/foxpuppet/windows/browser/navbar.py @@ -5,7 +5,7 @@ from selenium.webdriver.common.by import By from foxpuppet.region import Region -from foxpuppet.windows.base import BaseWindow +from foxpuppet.windows.browser.urlbar import UrlBar class NavBar(Region): @@ -40,34 +40,7 @@ def is_tracking_shield_displayed(self) -> bool: el = self.root.find_element(By.ID, "tracking-protection-icon") return bool(el.get_attribute("state")) - def url_bar(self, links: list) -> list: - """Check if the provided links are present in the url bar's suggestions.""" - urls = [] - with self.selenium.context(self.selenium.CONTEXT_CHROME): - for link in links: - url_bar = self.selenium.find_element(*NavBarLocators.INPUT_FIELD) - url_bar.clear() - url_bar.send_keys(link) - - self.wait.until( - lambda _: self.selenium.find_elements(*NavBarLocators.SEARCH_RESULTS) - ) - - search_results = self.selenium.find_elements( - *NavBarLocators.SEARCH_RESULT_ITEMS - ) - - for result in search_results: - url_span = result.find_element(*NavBarLocators.SEARCH_RESULT_ITEM) - if url_span.text in link: - if len(url_span.text) != 0: - urls.append(link) - break - return urls - - -class NavBarLocators: - INPUT_FIELD = (By.ID, "urlbar-input") - SEARCH_RESULTS = (By.ID, "urlbar-results") - SEARCH_RESULT_ITEM = (By.CSS_SELECTOR, "span.urlbarView-url") - SEARCH_RESULT_ITEMS = (By.CSS_SELECTOR, "div.urlbarView-row[role='presentation']") + @property + def url_bar(self) -> UrlBar: + """Returns an instance of the UrlBar class.""" + return self._url_bar diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index ddb2bd6..466ceb7 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -41,7 +41,7 @@ def create( return panel_items.get(_id, PanelUI)(window, root) @property - def is_barged(self) -> bool: + def is_update_available(self) -> bool: """ Checks if the Panel UI button indicates a pending Firefox update. diff --git a/foxpuppet/windows/browser/urlbar.py b/foxpuppet/windows/browser/urlbar.py new file mode 100644 index 0000000..edef8e7 --- /dev/null +++ b/foxpuppet/windows/browser/urlbar.py @@ -0,0 +1,45 @@ +# 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/. +"""Creates Navbar object to interact with Firefox URL Bar.""" + +from selenium.webdriver.common.by import By +from selenium.webdriver.remote.webdriver import WebDriver +from selenium.webdriver.support.wait import WebDriverWait + + +class UrlBar: + def __init__(self, selenium: WebDriver, wait: WebDriverWait): + self.selenium = selenium + self.wait = wait + + def check_suggestions(self, links: list) -> list: + """Check if the provided links are present in the URL bar's suggestions.""" + urls = [] + with self.selenium.context(self.selenium.CONTEXT_CHROME): + for link in links: + url_bar = self.selenium.find_element(*URLBarLocators.INPUT_FIELD) + url_bar.clear() + url_bar.send_keys(link) + + self.wait.until( + lambda _: self.selenium.find_elements(*URLBarLocators.SEARCH_RESULTS) + ) + + search_results = self.selenium.find_elements( + *URLBarLocators.SEARCH_RESULT_ITEMS + ) + + for result in search_results: + url_span = result.find_element(*URLBarLocators.SEARCH_RESULT_ITEM) + if url_span.text in link and len(url_span.text) != 0: + urls.append(link) + break + return urls + + +class URLBarLocators: + INPUT_FIELD = (By.ID, "urlbar-input") + SEARCH_RESULTS = (By.ID, "urlbar-results") + SEARCH_RESULT_ITEM = (By.CSS_SELECTOR, "span.urlbarView-url") + SEARCH_RESULT_ITEMS = (By.CSS_SELECTOR, "div.urlbarView-row[role='presentation']") diff --git a/foxpuppet/windows/browser/window.py b/foxpuppet/windows/browser/window.py index 3c55345..0e5e20f 100644 --- a/foxpuppet/windows/browser/window.py +++ b/foxpuppet/windows/browser/window.py @@ -240,21 +240,3 @@ def open_window(self, private: bool = False) -> Union["BrowserWindow", Any]: expected.new_browser_window_is_opened(self.selenium, handles_before), message="No new browser window opened", ) - - def wait_for_num_windows_or_tabs(self, expected_count: int) -> bool: - """ - Waits until the number of open browser windows or tabs matches the expected value. - - Args: - expected_count (int): The expected number of windows or tabs. - """ - self.wait.until( - lambda _: len(self.selenium.window_handles) == expected_count, - f"Expected {expected_count} windows or tabs, but found {len(self.selenium.window_handles)}", - ) - return True - - def switch_to_new_window_or_tab(self) -> None: - """Get list of all window handles, switch to the newly opened tab/window""" - handles = self.selenium.window_handles - self.selenium.switch_to.window(handles[-1]) diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py index 20c9e3c..56fd59c 100644 --- a/tests/test_panel_ui.py +++ b/tests/test_panel_ui.py @@ -10,11 +10,14 @@ from foxpuppet.windows.browser.panel_ui.panel_ui import PanelUI, History -links = [ - "https://www.mozilla.org/en-US/?v=a", - "https://www.youtube.com", - "https://www.facebook.com/", -] +@pytest.fixture +def links() -> list: + links = [ + "https://www.mozilla.org/en-US/?v=a", + "https://www.youtube.com", + "https://www.facebook.com/", + ] + return links @pytest.fixture @@ -31,7 +34,7 @@ def panel_ui(browser: BrowserWindow) -> PanelUI | None: @pytest.fixture -def history(panel_ui: PanelUI, browser: BrowserWindow) -> History | None: +def browser_history(panel_ui: PanelUI, browser: BrowserWindow) -> History | None: """Create Panel UI object. Args: @@ -44,130 +47,73 @@ def history(panel_ui: PanelUI, browser: BrowserWindow) -> History | None: return browser.wait_for_panel(History) -def test_open_new_tab(panel_ui: PanelUI, browser: BrowserWindow) -> None: +def test_open_new_tab(panel_ui: PanelUI, selenium: WebDriver) -> None: """Test opening a new tab using the Panel UI.""" panel_ui.open_new_tab() - assert browser.wait_for_num_windows_or_tabs(2) + assert len(selenium.window_handles) == 2 -def test_open_new_window(panel_ui: PanelUI, browser: BrowserWindow) -> None: +def test_open_new_window(panel_ui: PanelUI, selenium: WebDriver) -> None: """Test opening a new window using the Panel UI.""" panel_ui.open_new_window() - assert browser.wait_for_num_windows_or_tabs(2) + assert len(selenium.window_handles) == 2 -def test_verify_links_open_in_new_tab_in_url_bar( - panel_ui: PanelUI, browser: BrowserWindow, selenium: WebDriver -) -> None: - """Test that links opened in new tab are present in the url bar.""" - links = ["https://www.mozilla.org/en-US/?v=a"] - panel_ui.open_new_tab() - browser.switch_to_new_window_or_tab() - for link in links: - selenium.get(link) - urls = panel_ui.url_bar(links) - assert len(urls) == 1 +def test_url_is_present_in_history(browser_history: History, selenium: WebDriver) -> None: + """Test that visited URL appears in browser history.""" + url = "https://www.mozilla.org/en-US/?v=a" + selenium.get(url) + browser_history.open_history_menu() + assert browser_history.is_present(url) + + +def test_verify_url_bar_suggestions(panel_ui: PanelUI, selenium: WebDriver) -> None: + """Test that a link appears in url bar suggestions.""" + test_url = "https://www.mozilla.org/en-US/?v=a" + selenium.get(test_url) + suggestions = panel_ui.url_bar.check_suggestions([test_url]) + assert len(suggestions) == 1 def test_verify_links_open_in_new_tab_in_history( - panel_ui: PanelUI, history: History, browser: BrowserWindow, selenium: WebDriver + panel_ui: PanelUI, browser_history: History, selenium: WebDriver, links: list ) -> None: """Test that links opened in new tab are present in browser history.""" urls = [] panel_ui.open_new_tab() - browser.switch_to_new_window_or_tab() + selenium.switch_to.window(selenium.window_handles[-1]) for link in links: selenium.get(link) panel_ui.open_panel_menu() panel_ui.open_history_menu() - for link in links: - if history.is_present(link): - urls.append(link) + urls = [link for link in links if browser_history.is_present(link)] assert len(urls) == 3 def test_verify_links_open_in_new_window_in_history( - panel_ui: PanelUI, history: History, browser: BrowserWindow, selenium: WebDriver + panel_ui: PanelUI, browser_history: History, selenium: WebDriver, links: list ) -> None: """Test that links opened in new window are present in browser history.""" urls = [] panel_ui.open_new_window() - browser.switch_to_new_window_or_tab() + selenium.switch_to.window(selenium.window_handles[-1]) for link in links: selenium.get(link) panel_ui.open_panel_menu() panel_ui.open_history_menu() - for link in links: - if history.is_present(link): - urls.append(link) + urls = [link for link in links if browser_history.is_present(link)] assert len(urls) == 3 -def test_verify_links_open_in_new_window_in_url_bar( - panel_ui: PanelUI, browser: BrowserWindow, selenium: WebDriver -) -> None: - """Test that links opened in new window are present in the url bar.""" - links = ["https://www.mozilla.org/en-US/?v=a"] - panel_ui.open_new_window() - browser.switch_to_new_window_or_tab() - time.sleep(1) - for link in links: - selenium.get(link) - url = panel_ui.url_bar(links) - assert len(url) == 1 - - -def test_url_is_present_in_history(history: History, selenium: WebDriver) -> None: - """Test that visited URL appears in browser history.""" - url = "https://www.mozilla.org/en-US/?v=a" - selenium.get(url) - history.open_history_menu() - assert history.is_present(url) - - def test_clear_recent_history( - panel_ui: PanelUI, history: History, selenium: WebDriver + panel_ui: PanelUI, browser_history: History, selenium: WebDriver ) -> None: """Test clearing browser history removes visited URLs.""" url = "https://www.mozilla.org/en-US/?v=a" selenium.get(url) panel_ui.open_history_menu() - history.clear_history() + browser_history.clear_history() time.sleep(1) panel_ui.open_panel_menu() panel_ui.open_history_menu() - assert not history.is_present(url) - - -def test_verify_private_browsing_links_not_in_history( - panel_ui: PanelUI, history: History, browser: BrowserWindow, selenium: WebDriver -) -> None: - """Test that links opened in private window are not present in browser history.""" - invalid_links = [] - initial_window_handle = selenium.current_window_handle - panel_ui.open_private_window() - browser.switch_to_new_window_or_tab() - for link in links: - selenium.get(link) - selenium.switch_to.window(initial_window_handle) - panel_ui.open_panel_menu() - panel_ui.open_history_menu() - for link in links: - if history.is_present(link): - invalid_links.append(link) - assert not invalid_links - - -def test_verify_private_browsing_links_not_in_url_bar( - panel_ui: PanelUI, browser: BrowserWindow, selenium: WebDriver -) -> None: - """Test that links opened in private window are not present in the url bar.""" - initial_window_handle = selenium.current_window_handle - panel_ui.open_private_window() - browser.switch_to_new_window_or_tab() - - for link in links: - selenium.get(link) - - selenium.switch_to.window(initial_window_handle) - assert not panel_ui.url_bar(links) + assert not browser_history.is_present(url) From b963ae23a6ecd8c3fbe2515fae81edf9642b4c52 Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Sun, 16 Feb 2025 22:45:46 +0100 Subject: [PATCH 5/8] Adjusted Panel UI methods --- .../windows/browser/panel_ui/panel_ui.py | 45 +++++++++---- foxpuppet/windows/browser/urlbar.py | 52 ++++++++------- tests/test_panel_ui.py | 66 +++++++++++++++---- 3 files changed, 116 insertions(+), 47 deletions(-) diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index 466ceb7..ee6d00d 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -8,6 +8,7 @@ from foxpuppet.windows.browser.navbar import NavBar from selenium.webdriver.remote.webelement import WebElement from typing import Type, Any, TYPE_CHECKING, Optional +from selenium.webdriver.support import expected_conditions as EC class PanelUI(NavBar): @@ -60,30 +61,49 @@ def open_panel_menu(self) -> None: """ with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.PANEL_UI_BUTTON).click() + self.wait.until( + EC.visibility_of_element_located(*PanelUILocators.PANEL_POPUP), + message="Panel UI menu did not open", + ) def open_new_tab(self) -> None: """ Opens a new tab using the Panel UI menu. """ + initial_handles = set(self.selenium.window_handles) self.open_panel_menu() with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.NEW_TAB).click() + self.wait.until( + lambda _: set(self.selenium.window_handles) - initial_handles, + message="New Tab did not open", + ) def open_new_window(self) -> None: """ Opens a new window using the Panel UI menu. """ + initial_handles = set(self.selenium.window_handles) self.open_panel_menu() with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.NEW_WINDOW).click() + self.wait.until( + lambda _: set(self.selenium.window_handles) - initial_handles, + message="New window did not open", + ) def open_private_window(self) -> None: """ Opens a new window in private browsing mode using the Panel UI menu. """ + initial_handles = set(self.selenium.window_handles) self.open_panel_menu() with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.PRIVATE_WINDOW).click() + self.wait.until( + lambda _: set(self.selenium.window_handles) - initial_handles, + message="Private window did not open", + ) def open_history_menu(self) -> None: """ @@ -91,28 +111,29 @@ def open_history_menu(self) -> None: """ with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.HISTORY).click() + self.wait.until( + lambda _: self.selenium.find_element( + *PanelUILocators.PANEL_HISTORY + ).is_displayed(), + message="History menu did not open", + ) class History(PanelUI): - def is_present(self, link: str) -> bool: + def history_items(self) -> list[WebElement]: """ - Checks if a specific link is present in the recent history. - Args: - link `str`: The URL or part of the URL to check for in the recent history. + Retrieves all history items from the Panel UI history menu. Returns: - bool: `True` if the link is present in the recent history, `False` otherwise. + list[WebElement]: List of WebElement objects representing history items. + Returns an empty list if no history items are found. """ with self.selenium.context(self.selenium.CONTEXT_CHROME): history_items = self.selenium.find_elements( *PanelUILocators.RECENT_HISTORY_ITEMS ) - for item in history_items: - item_src = item.get_attribute("image") - if item_src and link in item_src: - print(item_src) - return True - return False + print(history_items) + return history_items def clear_history(self): """ @@ -150,6 +171,8 @@ class PanelUILocators: HISTORY_IFRAME = (By.CSS_SELECTOR, "browser.dialogFrame") NEW_TAB = (By.ID, "appMenu-new-tab-button2") NEW_WINDOW = (By.ID, "appMenu-new-window-button2") + PANEL_HISTORY = (By.ID, "PanelUI-history") + PANEL_POPUP = ((By.ID, "appMenu-popup"),) PANEL_UI_BUTTON = (By.ID, "PanelUI-menu-button") PRIVATE_WINDOW = (By.ID, "appMenu-new-private-window-button2") RECENT_HISTORY_ITEMS = ( diff --git a/foxpuppet/windows/browser/urlbar.py b/foxpuppet/windows/browser/urlbar.py index edef8e7..f3e8be4 100644 --- a/foxpuppet/windows/browser/urlbar.py +++ b/foxpuppet/windows/browser/urlbar.py @@ -13,29 +13,37 @@ def __init__(self, selenium: WebDriver, wait: WebDriverWait): self.selenium = selenium self.wait = wait - def check_suggestions(self, links: list) -> list: - """Check if the provided links are present in the URL bar's suggestions.""" - urls = [] + def suggestions(self, url: str) -> list[str]: + """ + Get all URL suggestions shown in the URL bar. + + Args: + url (str): The URL to type into the URL bar + + Returns: + list[str]: List of suggested URLs that appear in the URL bar + """ with self.selenium.context(self.selenium.CONTEXT_CHROME): - for link in links: - url_bar = self.selenium.find_element(*URLBarLocators.INPUT_FIELD) - url_bar.clear() - url_bar.send_keys(link) - - self.wait.until( - lambda _: self.selenium.find_elements(*URLBarLocators.SEARCH_RESULTS) - ) - - search_results = self.selenium.find_elements( - *URLBarLocators.SEARCH_RESULT_ITEMS - ) - - for result in search_results: - url_span = result.find_element(*URLBarLocators.SEARCH_RESULT_ITEM) - if url_span.text in link and len(url_span.text) != 0: - urls.append(link) - break - return urls + url_bar = self.selenium.find_element(*URLBarLocators.INPUT_FIELD) + url_bar.clear() + url_bar.send_keys(url) + + self.wait.until( + lambda _: self.selenium.find_elements(*URLBarLocators.SEARCH_RESULTS) + ) + + search_results = self.selenium.find_elements( + *URLBarLocators.SEARCH_RESULT_ITEMS + ) + + suggested_urls = [ + result.find_element(*URLBarLocators.SEARCH_RESULT_ITEM).text + for result in search_results + if result.find_element(*URLBarLocators.SEARCH_RESULT_ITEM).text + ] + print(suggested_urls) + + return suggested_urls class URLBarLocators: diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py index 56fd59c..8a05908 100644 --- a/tests/test_panel_ui.py +++ b/tests/test_panel_ui.py @@ -64,45 +64,74 @@ def test_url_is_present_in_history(browser_history: History, selenium: WebDriver url = "https://www.mozilla.org/en-US/?v=a" selenium.get(url) browser_history.open_history_menu() - assert browser_history.is_present(url) + history_items = browser_history.history_items() + with selenium.context(selenium.CONTEXT_CHROME): + is_present = False + for item in history_items: + image_attr = item.get_attribute("image") + if image_attr is not None: + is_present = url in image_attr + if is_present: + break + assert is_present def test_verify_url_bar_suggestions(panel_ui: PanelUI, selenium: WebDriver) -> None: """Test that a link appears in url bar suggestions.""" test_url = "https://www.mozilla.org/en-US/?v=a" selenium.get(test_url) - suggestions = panel_ui.url_bar.check_suggestions([test_url]) - assert len(suggestions) == 1 + all_suggestions = panel_ui.url_bar.suggestions(test_url) + matching_suggestions = [ + suggestion + for suggestion in all_suggestions + if suggestion in test_url and len(suggestion) != 0 + ] + assert len(matching_suggestions) == 1 -def test_verify_links_open_in_new_tab_in_history( +def test_verify_links_open_in_new_tab_from_history( panel_ui: PanelUI, browser_history: History, selenium: WebDriver, links: list ) -> None: """Test that links opened in new tab are present in browser history.""" - urls = [] panel_ui.open_new_tab() selenium.switch_to.window(selenium.window_handles[-1]) for link in links: selenium.get(link) panel_ui.open_panel_menu() panel_ui.open_history_menu() - urls = [link for link in links if browser_history.is_present(link)] - assert len(urls) == 3 - - -def test_verify_links_open_in_new_window_in_history( + history_items = browser_history.history_items() + with selenium.context(selenium.CONTEXT_CHROME): + found_urls = [] + for link in links: + for item in history_items: + image_attr = item.get_attribute("image") + if image_attr is not None and link in image_attr: + found_urls.append(link) + break + assert len(found_urls) == 3 + + +def test_verify_links_open_in_new_window_from_history( panel_ui: PanelUI, browser_history: History, selenium: WebDriver, links: list ) -> None: """Test that links opened in new window are present in browser history.""" - urls = [] panel_ui.open_new_window() selenium.switch_to.window(selenium.window_handles[-1]) + time.sleep(3) for link in links: selenium.get(link) panel_ui.open_panel_menu() panel_ui.open_history_menu() - urls = [link for link in links if browser_history.is_present(link)] - assert len(urls) == 3 + history_items = browser_history.history_items() + with selenium.context(selenium.CONTEXT_CHROME): + found_urls = [] + for link in links: + for item in history_items: + image_attr = item.get_attribute("image") + if image_attr is not None and link in image_attr: + found_urls.append(link) + break + assert len(found_urls) == 3 def test_clear_recent_history( @@ -116,4 +145,13 @@ def test_clear_recent_history( time.sleep(1) panel_ui.open_panel_menu() panel_ui.open_history_menu() - assert not browser_history.is_present(url) + history_items = browser_history.history_items() + with selenium.context(selenium.CONTEXT_CHROME): + is_present = False + for item in history_items: + image_attr = item.get_attribute("image") + if image_attr is not None: + is_present = url in image_attr + if is_present: + break + assert not is_present From fe817cd164d51f734198aaee70774e71f73ff39e Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Wed, 19 Feb 2025 16:56:54 +0100 Subject: [PATCH 6/8] Added pytest.ini --- Makefile | 4 +--- foxpuppet/windows/browser/panel_ui/panel_ui.py | 2 +- pytest.ini | 3 +++ 3 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 pytest.ini diff --git a/Makefile b/Makefile index a8e6b5f..96557a7 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,6 @@ export GECKODRIVER_LOG = $(shell pwd)/results/geckodriver.log BLACK_CHECK = black -l 90 --check --diff . BLACK_FIX = black -l 90 . -MINIMUM_COVERAGE = 95 -FOXPUPPET_TESTS = pytest -vvv --driver Firefox --cov --cov-fail-under=$(MINIMUM_COVERAGE) --html results/report.html check: install_poetry lint test @@ -18,7 +16,7 @@ install_poetry: curl -sSL https://install.python-poetry.org | python3 - test: install_dependencies - poetry run $(FOXPUPPET_TESTS) + poetry run pytest lint: install_dependencies poetry run $(BLACK_CHECK) diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index ee6d00d..f4284c5 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -62,7 +62,7 @@ def open_panel_menu(self) -> None: with self.selenium.context(self.selenium.CONTEXT_CHROME): self.selenium.find_element(*PanelUILocators.PANEL_UI_BUTTON).click() self.wait.until( - EC.visibility_of_element_located(*PanelUILocators.PANEL_POPUP), + EC.presence_of_element_located(*PanelUILocators.PANEL_POPUP), message="Panel UI menu did not open", ) diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..093c9b5 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,3 @@ +[pytest] +addopts = -vvv --driver Firefox --cov --cov-fail-under=95 --html=results/report.html --self-contained-html +testpaths = tests \ No newline at end of file From 098d5057a24e9b6b22740037d76b0d535526489c Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Thu, 20 Feb 2025 20:22:11 +0100 Subject: [PATCH 7/8] Added Private Window Test and Nit fixes --- foxpuppet/region.py | 2 -- foxpuppet/windows/browser/navbar.py | 2 +- .../windows/browser/panel_ui/panel_ui.py | 20 ++++++++++++++++--- foxpuppet/windows/browser/urlbar.py | 8 ++------ tests/test_panel_ui.py | 8 ++++++-- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/foxpuppet/region.py b/foxpuppet/region.py index fa575fd..f0c13c4 100644 --- a/foxpuppet/region.py +++ b/foxpuppet/region.py @@ -8,7 +8,6 @@ from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.common.action_chains import ActionChains from foxpuppet.windows import BaseWindow -from foxpuppet.windows.browser.urlbar import UrlBar class Region(object): @@ -41,4 +40,3 @@ def __init__(self, window: BaseWindow, root: WebElement): self.wait: WebDriverWait = window.wait self.window: BaseWindow = window self.actions: ActionChains = ActionChains(self.selenium) - self._url_bar: UrlBar = UrlBar(self.selenium, self.wait) diff --git a/foxpuppet/windows/browser/navbar.py b/foxpuppet/windows/browser/navbar.py index db3b258..50dfeb2 100644 --- a/foxpuppet/windows/browser/navbar.py +++ b/foxpuppet/windows/browser/navbar.py @@ -43,4 +43,4 @@ def is_tracking_shield_displayed(self) -> bool: @property def url_bar(self) -> UrlBar: """Returns an instance of the UrlBar class.""" - return self._url_bar + return UrlBar(self.window, self.root) diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index f4284c5..efd31e5 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -50,10 +50,10 @@ def is_update_available(self) -> bool: bool: True if an update notification (barge) is present, False otherwise. """ with self.selenium.context(self.selenium.CONTEXT_CHROME): - barged_status = self.selenium.find_element( + update_status = self.selenium.find_element( *PanelUILocators.PANEL_UI_BUTTON ).get_attribute("barged") - return barged_status == "true" + return update_status == "true" def open_panel_menu(self) -> None: """ @@ -78,6 +78,8 @@ def open_new_tab(self) -> None: lambda _: set(self.selenium.window_handles) - initial_handles, message="New Tab did not open", ) + new_tab = (set(self.selenium.window_handles) - initial_handles).pop() + self.selenium.switch_to.window(new_tab) def open_new_window(self) -> None: """ @@ -91,6 +93,8 @@ def open_new_window(self) -> None: lambda _: set(self.selenium.window_handles) - initial_handles, message="New window did not open", ) + new_window = (set(self.selenium.window_handles) - initial_handles).pop() + self.selenium.switch_to.window(new_window) def open_private_window(self) -> None: """ @@ -104,6 +108,17 @@ def open_private_window(self) -> None: lambda _: set(self.selenium.window_handles) - initial_handles, message="Private window did not open", ) + new_private_window = ( + set(self.selenium.window_handles) - initial_handles + ).pop() + self.selenium.switch_to.window(new_private_window) + + from foxpuppet.windows.browser.window import BrowserWindow + + new_window = BrowserWindow(self.selenium, new_private_window) + + if not new_window.is_private: + raise ValueError("The new window is not private.") def open_history_menu(self) -> None: """ @@ -132,7 +147,6 @@ def history_items(self) -> list[WebElement]: history_items = self.selenium.find_elements( *PanelUILocators.RECENT_HISTORY_ITEMS ) - print(history_items) return history_items def clear_history(self): diff --git a/foxpuppet/windows/browser/urlbar.py b/foxpuppet/windows/browser/urlbar.py index f3e8be4..0a4a331 100644 --- a/foxpuppet/windows/browser/urlbar.py +++ b/foxpuppet/windows/browser/urlbar.py @@ -6,13 +6,10 @@ from selenium.webdriver.common.by import By from selenium.webdriver.remote.webdriver import WebDriver from selenium.webdriver.support.wait import WebDriverWait +from foxpuppet.region import Region -class UrlBar: - def __init__(self, selenium: WebDriver, wait: WebDriverWait): - self.selenium = selenium - self.wait = wait - +class UrlBar(Region): def suggestions(self, url: str) -> list[str]: """ Get all URL suggestions shown in the URL bar. @@ -41,7 +38,6 @@ def suggestions(self, url: str) -> list[str]: for result in search_results if result.find_element(*URLBarLocators.SEARCH_RESULT_ITEM).text ] - print(suggested_urls) return suggested_urls diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py index 8a05908..63d1e57 100644 --- a/tests/test_panel_ui.py +++ b/tests/test_panel_ui.py @@ -59,6 +59,12 @@ def test_open_new_window(panel_ui: PanelUI, selenium: WebDriver) -> None: assert len(selenium.window_handles) == 2 +def test_open_new_private_window(panel_ui: PanelUI, selenium: WebDriver) -> None: + """Test opening a new window using the Panel UI.""" + panel_ui.open_private_window() + assert len(selenium.window_handles) == 2 + + def test_url_is_present_in_history(browser_history: History, selenium: WebDriver) -> None: """Test that visited URL appears in browser history.""" url = "https://www.mozilla.org/en-US/?v=a" @@ -94,7 +100,6 @@ def test_verify_links_open_in_new_tab_from_history( ) -> None: """Test that links opened in new tab are present in browser history.""" panel_ui.open_new_tab() - selenium.switch_to.window(selenium.window_handles[-1]) for link in links: selenium.get(link) panel_ui.open_panel_menu() @@ -116,7 +121,6 @@ def test_verify_links_open_in_new_window_from_history( ) -> None: """Test that links opened in new window are present in browser history.""" panel_ui.open_new_window() - selenium.switch_to.window(selenium.window_handles[-1]) time.sleep(3) for link in links: selenium.get(link) From db54174c62aee2d09bbe2b8626c22decbc00407a Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Fri, 21 Feb 2025 13:42:39 +0100 Subject: [PATCH 8/8] fixed private window method --- .../windows/browser/panel_ui/panel_ui.py | 19 ++++--- tests/test_panel_ui.py | 54 +++++++++---------- 2 files changed, 35 insertions(+), 38 deletions(-) diff --git a/foxpuppet/windows/browser/panel_ui/panel_ui.py b/foxpuppet/windows/browser/panel_ui/panel_ui.py index efd31e5..88cf407 100644 --- a/foxpuppet/windows/browser/panel_ui/panel_ui.py +++ b/foxpuppet/windows/browser/panel_ui/panel_ui.py @@ -108,17 +108,17 @@ def open_private_window(self) -> None: lambda _: set(self.selenium.window_handles) - initial_handles, message="Private window did not open", ) - new_private_window = ( - set(self.selenium.window_handles) - initial_handles - ).pop() - self.selenium.switch_to.window(new_private_window) - from foxpuppet.windows.browser.window import BrowserWindow - new_window = BrowserWindow(self.selenium, new_private_window) - - if not new_window.is_private: - raise ValueError("The new window is not private.") + new_private_window = self.selenium.window_handles[-1] + try: + private_window = BrowserWindow( + self.selenium, new_private_window + ).is_private + if private_window: + self.selenium.switch_to.window(new_private_window) + except Exception as e: + raise Exception(f"The new window is not private: {str(e)}") def open_history_menu(self) -> None: """ @@ -172,7 +172,6 @@ def clear_history(self): """, self.selenium.find_element(*PanelUILocators.HISTORY_DIALOG_BUTTON), ) - self.selenium.switch_to.default_content() class PanelUILocators: diff --git a/tests/test_panel_ui.py b/tests/test_panel_ui.py index 63d1e57..8ec837b 100644 --- a/tests/test_panel_ui.py +++ b/tests/test_panel_ui.py @@ -72,13 +72,10 @@ def test_url_is_present_in_history(browser_history: History, selenium: WebDriver browser_history.open_history_menu() history_items = browser_history.history_items() with selenium.context(selenium.CONTEXT_CHROME): - is_present = False - for item in history_items: - image_attr = item.get_attribute("image") - if image_attr is not None: - is_present = url in image_attr - if is_present: - break + is_present = any( + (image_attr := item.get_attribute("image")) and url in image_attr + for item in history_items + ) assert is_present @@ -106,13 +103,15 @@ def test_verify_links_open_in_new_tab_from_history( panel_ui.open_history_menu() history_items = browser_history.history_items() with selenium.context(selenium.CONTEXT_CHROME): - found_urls = [] - for link in links: - for item in history_items: - image_attr = item.get_attribute("image") - if image_attr is not None and link in image_attr: - found_urls.append(link) - break + found_urls = [ + link + for link in links + if any( + image_attr is not None and link in image_attr + for item in history_items + if (image_attr := item.get_attribute("image")) + ) + ] assert len(found_urls) == 3 @@ -128,13 +127,15 @@ def test_verify_links_open_in_new_window_from_history( panel_ui.open_history_menu() history_items = browser_history.history_items() with selenium.context(selenium.CONTEXT_CHROME): - found_urls = [] - for link in links: - for item in history_items: - image_attr = item.get_attribute("image") - if image_attr is not None and link in image_attr: - found_urls.append(link) - break + found_urls = [ + link + for link in links + if any( + image_attr is not None and link in image_attr + for item in history_items + if (image_attr := item.get_attribute("image")) + ) + ] assert len(found_urls) == 3 @@ -151,11 +152,8 @@ def test_clear_recent_history( panel_ui.open_history_menu() history_items = browser_history.history_items() with selenium.context(selenium.CONTEXT_CHROME): - is_present = False - for item in history_items: - image_attr = item.get_attribute("image") - if image_attr is not None: - is_present = url in image_attr - if is_present: - break + is_present = any( + (image_attr := item.get_attribute("image")) and url in image_attr + for item in history_items + ) assert not is_present