From 2b1a5171d071c2265c2ff6e753fef30522bf8cd3 Mon Sep 17 00:00:00 2001 From: Temidayo32 Date: Thu, 19 Dec 2024 09:44:56 +0100 Subject: [PATCH] Refactored Bookmark Moved all bookmark functionalities into a single class, Bookmark --- foxpuppet/region.py | 2 + .../windows/browser/bookmarks/bookmark.py | 287 ++++++++++-------- foxpuppet/windows/browser/navbar.py | 75 +---- foxpuppet/windows/browser/window.py | 64 +--- tests/test_bookmarks.py | 183 ++++------- 5 files changed, 240 insertions(+), 371 deletions(-) diff --git a/foxpuppet/region.py b/foxpuppet/region.py index 21ca1b3..f0c13c4 100644 --- a/foxpuppet/region.py +++ b/foxpuppet/region.py @@ -6,6 +6,7 @@ from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.remote.webdriver import WebDriver +from selenium.webdriver.common.action_chains import ActionChains from foxpuppet.windows import BaseWindow @@ -38,3 +39,4 @@ def __init__(self, window: BaseWindow, root: WebElement): self.selenium: WebDriver = window.selenium self.wait: WebDriverWait = window.wait self.window: BaseWindow = window + self.actions: ActionChains = ActionChains(self.selenium) diff --git a/foxpuppet/windows/browser/bookmarks/bookmark.py b/foxpuppet/windows/browser/bookmarks/bookmark.py index 25cb361..60a87ab 100644 --- a/foxpuppet/windows/browser/bookmarks/bookmark.py +++ b/foxpuppet/windows/browser/bookmarks/bookmark.py @@ -20,14 +20,14 @@ class BookmarkData(TypedDict): keyword: Optional[str] -class BasicBookmark(NavBar): - """Handles basic bookmark operations.""" +class Bookmark(NavBar): + """Handles Bookmark operations in Firefox.""" if TYPE_CHECKING: from foxpuppet.windows.browser.window import BrowserWindow @staticmethod - def create(window: "BrowserWindow", root: WebElement) -> Optional["BasicBookmark"]: + def create(window: "BrowserWindow", root: WebElement) -> "Bookmark": """Create a bookmark object. Args: @@ -35,168 +35,193 @@ def create(window: "BrowserWindow", root: WebElement) -> Optional["BasicBookmark root (:py:class:`~selenium.webdriver.remote.webelement.WebElement`): WebDriver element object for bookmark Returns: - :py:class:`BaseBookmark`: Bookmark instance or None + :py:class:`Bookmark`: Bookmark instance """ with window.selenium.context(window.selenium.CONTEXT_CHROME): - try: - return BasicBookmark(window, root) - except NoSuchElementException: - return None + return Bookmark(window, root) @property - def is_bookmarked(self) -> bool: - """Checks if the current page is bookmarked. + def is_bookmarked_star(self) -> bool: + """Checks if the current page is bookmarked using the star button. Returns: bool: True if the page is bookmarked, False otherwise. """ with self.selenium.context(self.selenium.CONTEXT_CHROME): - star_button_image = self.find_element(BookmarkLocators.STAR_BUTTON_IMAGE) + star_button_image = self.selenium.find_element( + *BookmarkLocators.STAR_BUTTON_IMAGE + ) return star_button_image.get_attribute("starred") == "true" - def add(self) -> None: - """Add a Bookmark using the star button.""" - self.click_element(BookmarkLocators.STAR_BUTTON) - self.click_element(BookmarkLocators.FOLDER_MENU) - self.click_element(BookmarkLocators.OTHER_BOOKMARKS_STAR) - self.click_element(BookmarkLocators.SAVE_BUTTON) - - def retrieve_bookmark(self, label: str) -> bool: - """ - Check if a bookmark with the given label exists. + @property + def is_bookmarked_menu(self) -> bool: + """Checks if the current page is bookmarked using the main bookmark menu. - Args: - label (str): The name of the bookmark to search for. + Returns: + bool: True if the page is bookmarked, False otherwise. """ + current_page_title = self.selenium.title with self.selenium.context(self.selenium.CONTEXT_CHROME): - self.open_bookmark_menu( - locator_panel=BookmarkLocators.PANEL_MENU, - locator_bookmark=BookmarkLocators.PANEL_BOOKMARK_MENU, + toolbar_element = self.selenium.find_element( + *BookmarkLocators.NAVIGATOR_TOOLBOX ) - panel_bookmarks = self.find_element(BookmarkLocators.PANEL_BOOKMARK_TOOLBAR) - if panel_bookmarks is not None: - menu_items = panel_bookmarks.find_elements( - By.CSS_SELECTOR, "toolbarbutton.bookmark-item" + self.actions.context_click(toolbar_element).perform() + + menu_bar = self.selenium.find_element(*BookmarkLocators.MENU_BAR) + menu_bar.click() + + bookmark_menu = self.selenium.find_element( + *BookmarkLocators.MAIN_MENU_BOOKMARK + ) + if bookmark_menu is not None: + bookmark_menu.click() + menu_items = bookmark_menu.find_elements( + By.CSS_SELECTOR, "menuitem.bookmark-item" ) - if menu_items is not None: - for item in menu_items: - item_label = item.get_attribute("label") - if item_label and label.lower() in item_label.lower(): - return True - return False - - def delete(self) -> None: - """Delete a bookmark using the star button.""" - with self.selenium.context(self.selenium.CONTEXT_CHROME): - star_button_image = self.find_element(BookmarkLocators.STAR_BUTTON_IMAGE) - if star_button_image and star_button_image.get_attribute("starred") == "true": - self.click_element(BookmarkLocators.STAR_BUTTON) - self.click_element(BookmarkLocators.REMOVE_BUTTON) + if any( + item_label.lower() in current_page_title.lower() + for item in menu_items + if (item_label := item.get_attribute("label")) + ): + return True + return False -class AdvancedBookmark(BasicBookmark): - """Handles advanced bookmark operations.""" + def add_bookmark( + self, bookmark_data: BookmarkData = None, is_detailed: bool = False + ) -> None: + """ + Add a bookmark using either quick add (star button) or detailed menu approach. - if TYPE_CHECKING: - from foxpuppet.windows.browser.window import BrowserWindow + Args: + detailed (bool, optional): Whether to use detailed menu approach. Defaults to False. + bookmark_data (BookmarkData, optional): Data for the bookmark when using detailed menu. + Required when detailed is True. + """ + with self.selenium.context(self.selenium.CONTEXT_CHROME): + if not is_detailed: + self.selenium.find_element(*BookmarkLocators.STAR_BUTTON).click() + self.selenium.find_element(*BookmarkLocators.FOLDER_MENU).click() + self.selenium.find_element(*BookmarkLocators.OTHER_BOOKMARKS_STAR).click() + self.selenium.find_element(*BookmarkLocators.SAVE_BUTTON).click() + else: + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.actions.context_click( + self.selenium.find_element(*BookmarkLocators.NAVIGATOR_TOOLBOX) + ).perform() + self.selenium.find_element(*BookmarkLocators.MENU_BAR).click() + self.selenium.find_element( + *BookmarkLocators.MAIN_MENU_BOOKMARK + ).click() + self.actions.context_click( + self.selenium.find_element(*BookmarkLocators.MANAGE_BOOKMARKS) + ).perform() + self.selenium.find_element(*BookmarkLocators.ADD_BOOKMARK).click() + + frame_element = self.selenium.find_element( + *BookmarkLocators.ADD_BOOKMARK_FRAME + ) + self.selenium.switch_to.frame(frame_element) + if bookmark_data["name"]: + self.actions.send_keys(bookmark_data["name"]).perform() + self.actions.send_keys(Keys.TAB).perform() + + if bookmark_data["url"]: + self.actions.send_keys(bookmark_data["url"] + Keys.TAB).perform() + + if (tags := bookmark_data["tags"]) is not None: + for tag in tags: + self.actions.send_keys(tag).perform() + self.actions.send_keys(",").perform() + self.actions.send_keys(Keys.TAB).perform() + + if bookmark_data.get("keyword"): + keyword = bookmark_data["keyword"] or "" + self.actions.send_keys(keyword + Keys.TAB).perform() + + self.actions.send_keys( + Keys.TAB, Keys.TAB, Keys.TAB, Keys.ENTER + ).perform() + if ( + folder := self.selenium.find_element( + *BookmarkLocators.BOOKMARK_FOLDER + ) + ) is not None: + folder.click() + self.selenium.switch_to.frame(folder) + self.actions.send_keys(Keys.TAB, Keys.ENTER).perform() - @staticmethod - def create(window: "BrowserWindow", root: WebElement) -> Optional["AdvancedBookmark"]: - """Create an advanced bookmark object. + def retrieve_bookmark(self, label: str) -> bool: + """ + Check if a bookmark with the given label exists. Args: - window (:py:class:`BrowserWindow`): The window object where the bookmark appears. - root (:py:class:`~selenium.webdriver.remote.webelement.WebElement`): WebElement for the bookmark panel. + label (str): The name of the bookmark to search for. + """ + with self.selenium.context(self.selenium.CONTEXT_CHROME): + self.selenium.find_element(*BookmarkLocators.PANEL_MENU).click() + self.selenium.find_element(*BookmarkLocators.PANEL_BOOKMARK_MENU).click() + panel_bookmarks = self.selenium.find_element( + *BookmarkLocators.PANEL_BOOKMARK_TOOLBAR + ) + menu_items = panel_bookmarks.find_elements( + By.CSS_SELECTOR, "toolbarbutton.bookmark-item" + ) + if any( + label.lower() in item_label.lower() + for item in menu_items + if (item_label := item.get_attribute("label")) + ): + return True + return False - Returns: - :py:class:`AdvancedBookmark`: An instance of AdvancedBookmark if successful, otherwise None. + def delete_bookmark(self, label: str = None, is_detailed: bool = False) -> None: """ - with window.selenium.context(window.selenium.CONTEXT_CHROME): - try: - return AdvancedBookmark(window, root) - except NoSuchElementException: - return None + Delete a bookmark using either quick delete (star button) or detailed menu approach. - @property - def is_bookmarked(self) -> bool: - """Checks if the current page is bookmarked. + Args: + detailed (bool, optional): Whether to use detailed menu approach. Defaults to False. + label (str, optional): Label of the bookmark to delete when using detailed approach. + Required when detailed is True. Returns: - bool: True if the page is bookmarked, False otherwise. + bool: True if bookmark was successfully deleted (always True for detailed approach) """ - current_page_title = self.selenium.title - self.open_main_menu( - locator_toolbar=BookmarkLocators.NAVIGATOR_TOOLBOX, - locator_menu_bar=BookmarkLocators.MENU_BAR, - ) - bookmark_menu = self.find_element(BookmarkLocators.MAIN_MENU_BOOKMARK) - if bookmark_menu is not None: - self.click_element(BookmarkLocators.MAIN_MENU_BOOKMARK) - with self.selenium.context(self.selenium.CONTEXT_CHROME): - menu_items = bookmark_menu.find_elements( - By.CSS_SELECTOR, "menuitem.bookmark-item" - ) - for item in menu_items: - item_label = item.get_attribute("label") - if item_label and item_label.lower() in current_page_title.lower(): - return True - return False - - def add_bookmark(self, bookmark_data: BookmarkData) -> None: - """Add a Bookmark using the main bookmark menu.""" - self.open_main_menu( - locator_toolbar=BookmarkLocators.NAVIGATOR_TOOLBOX, - locator_menu_bar=BookmarkLocators.MENU_BAR, - ) - self.click_element(BookmarkLocators.MAIN_MENU_BOOKMARK) - self.context_click(BookmarkLocators.MANAGE_BOOKMARKS) - self.click_element(BookmarkLocators.ADD_BOOKMARK) - self.switch_to_frame(BookmarkLocators.ADD_BOOKMARK_FRAME) - - if bookmark_data["name"]: - self.actions.send_keys(bookmark_data["name"]).perform() - self.actions.send_keys(Keys.TAB).perform() - - if bookmark_data["url"]: - self.actions.send_keys(bookmark_data["url"] + Keys.TAB).perform() - - tags = bookmark_data["tags"] - if tags is not None: - for tag in tags: - self.actions.send_keys(tag).perform() - self.actions.send_keys(",").perform() - self.actions.send_keys(Keys.TAB).perform() - - if bookmark_data.get("keyword"): - keyword = ( - bookmark_data["keyword"] if bookmark_data["keyword"] is not None else "" - ) - self.actions.send_keys(keyword + Keys.TAB).perform() - - self.actions.send_keys(Keys.TAB, Keys.TAB, Keys.TAB, Keys.ENTER).perform() - self.actions.send_keys(Keys.TAB, Keys.ENTER).perform() - self.switch_to_default_context() - - def delete_bookmark(self, label: str) -> bool: - """Delete a bookmark using the main bookmark menu.""" - self.open_main_menu( - locator_toolbar=BookmarkLocators.NAVIGATOR_TOOLBOX, - locator_menu_bar=BookmarkLocators.MENU_BAR, - ) - bookmark_menu = self.find_element(BookmarkLocators.MAIN_MENU_BOOKMARK) - self.click_element(BookmarkLocators.MAIN_MENU_BOOKMARK) with self.selenium.context(self.selenium.CONTEXT_CHROME): - menu_item = bookmark_menu.find_element( - By.CSS_SELECTOR, f"menuitem.bookmark-item[label='{label}']" - ) - self.actions.context_click(menu_item).perform() - self.click_element(BookmarkLocators.DELETE_MENU_ITEM) - return True + if not is_detailed: + star_button_image = self.selenium.find_element( + *BookmarkLocators.STAR_BUTTON_IMAGE + ) + if ( + star_button_image + and star_button_image.get_attribute("starred") == "true" + ): + self.selenium.find_element(*BookmarkLocators.STAR_BUTTON).click() + self.selenium.find_element(*BookmarkLocators.REMOVE_BUTTON).click() + else: + self.actions.context_click( + self.selenium.find_element(*BookmarkLocators.NAVIGATOR_TOOLBOX) + ).perform() + self.selenium.find_element(*BookmarkLocators.MENU_BAR).click() + bookmark_menu = self.selenium.find_element( + *BookmarkLocators.MAIN_MENU_BOOKMARK + ) + self.selenium.find_element(*BookmarkLocators.MAIN_MENU_BOOKMARK).click() + menu_item = bookmark_menu.find_element( + By.CSS_SELECTOR, f"menuitem.bookmark-item[label='{label}']" + ) + self.actions.context_click(menu_item).perform() + self.selenium.find_element(*BookmarkLocators.DELETE_MENU_ITEM).click() + return True class BookmarkLocators: ADD_BOOKMARK = (By.ID, "placesContext_new:bookmark") ADD_BOOKMARK_FRAME = (By.CSS_SELECTOR, "browser[class='dialogFrame']") + BOOKMARK_FOLDER = ( + By.CSS_SELECTOR, + "browser.dialogFrame[name='dialogFrame-window-modal-dialog-subdialog']", + ) BOOKMARK_PROPERTIES_DIALOG = (By.ID, "bookmarkproperties") DELETE_MENU_ITEM = (By.ID, "placesContext_deleteBookmark") FOLDER_MENU = (By.ID, "editBMPanel_folderMenuList") diff --git a/foxpuppet/windows/browser/navbar.py b/foxpuppet/windows/browser/navbar.py index 0fe3bee..09fcb93 100644 --- a/foxpuppet/windows/browser/navbar.py +++ b/foxpuppet/windows/browser/navbar.py @@ -4,12 +4,7 @@ """Creates Navbar object to interact with Firefox Navigation Bar.""" from selenium.webdriver.common.by import By -from selenium.webdriver.remote.webelement import WebElement -from selenium.webdriver.remote.webdriver import WebDriver -from selenium.webdriver.common.action_chains import ActionChains -from selenium.common.exceptions import NoSuchElementException from foxpuppet.region import Region -from typing import Tuple, Optional from foxpuppet.windows.base import BaseWindow @@ -25,15 +20,11 @@ class NavBar(Region): """ - _tracking_protection_shield_locator: Tuple[str, str] = ( + _tracking_protection_shield_locator = ( By.ID, "tracking-protection-icon-box", ) - def __init__(self, window: BaseWindow, root: WebElement) -> None: - super().__init__(window, root) - self.actions: ActionChains = ActionChains(self.selenium) - @property def is_tracking_shield_displayed(self) -> bool: """Tracking Protection shield. @@ -48,67 +39,3 @@ 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 click_element(self, locator: Tuple[str, str]) -> None: - """Click on an element by its locator.""" - with self.selenium.context(self.selenium.CONTEXT_CHROME): - self.find_element(locator).click() - - def find_element(self, locator: Tuple[str, str]) -> WebElement: - """Find and return a web element by its locator.""" - with self.selenium.context(self.selenium.CONTEXT_CHROME): - try: - element = self.root.find_element(*locator) - return element - except Exception as e: - raise NoSuchElementException( - f"Error locating element with locator {locator}: {e}" - ) - - def context_click(self, locator: Tuple[str, str]) -> None: - """ - Perform a right-click (context-click) on an element. - - Args: - locator (tuple): Locator for the element to right-click. - """ - with self.selenium.context(self.selenium.CONTEXT_CHROME): - element = self.find_element(locator) - self.actions.context_click(element).perform() - - def switch_to_frame(self, locator: Tuple[str, str]) -> None: - """ - Switch to an iFrame using its locator - - Args: - locator (tuple): Locator for the iFrame elemeent. - """ - with self.selenium.context(self.selenium.CONTEXT_CHROME): - frame_element = self.find_element(locator) - if frame_element is not None: - self.selenium.switch_to.frame(frame_element) - else: - raise NoSuchElementException(f"iFrame with locator {locator} not found.") - - def switch_to_default_context(self) -> None: - """Switch back to default context.""" - with self.selenium.context(self.selenium.CONTEXT_CHROME): - self.selenium.switch_to.default_content() - - def open_main_menu( - self, locator_toolbar: Tuple[str, str], locator_menu_bar: Tuple[str, str] - ) -> None: - """Activate main menu bar""" - with self.selenium.context(self.selenium.CONTEXT_CHROME): - self.context_click(locator_toolbar) - self.click_element(locator_menu_bar) - - def open_bookmark_menu( - self, locator_panel: Tuple[str, str], locator_bookmark: Tuple[str, str] - ) -> None: - """ - Opens the Bookmarks menu in Panel UI - """ - with self.selenium.context(self.selenium.CONTEXT_CHROME): - self.click_element(locator_panel) - self.click_element(locator_bookmark) diff --git a/foxpuppet/windows/browser/window.py b/foxpuppet/windows/browser/window.py index e934278..e7e7ffa 100644 --- a/foxpuppet/windows/browser/window.py +++ b/foxpuppet/windows/browser/window.py @@ -10,8 +10,7 @@ from foxpuppet.windows import BaseWindow from foxpuppet.windows.browser.navbar import NavBar from foxpuppet.windows.browser.notifications import BaseNotification -from foxpuppet.windows.browser.bookmarks.bookmark import BasicBookmark -from foxpuppet.windows.browser.bookmarks.bookmark import AdvancedBookmark +from foxpuppet.windows.browser.bookmarks.bookmark import Bookmark from selenium.webdriver.remote.webelement import WebElement from typing import Any, Optional, Union, TypeVar, Type @@ -21,7 +20,7 @@ class BrowserWindow(BaseWindow): """Representation of a browser window.""" - _bookmark_panel_locator = (By.ID, "main-window") # editBookmarkPanelTemplate + _bookmark_locator = (By.ID, "main-window") # editBookmarkPanelTemplate _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") @@ -71,7 +70,7 @@ def notification(self) -> BaseNotification | Any: return None # no notification is displayed @property - def basic_bookmark(self) -> Optional[BasicBookmark]: + def bookmark(self) -> Bookmark: """Provide access to the currently displayed bookmark. Returns: @@ -79,25 +78,8 @@ def basic_bookmark(self) -> Optional[BasicBookmark]: """ with self.selenium.context(self.selenium.CONTEXT_CHROME): - try: - root = self.selenium.find_element(*self._bookmark_panel_locator) - return BasicBookmark.create(self, root) - except NoSuchElementException: - return None - - @property - def advanced_bookmark(self) -> Optional[AdvancedBookmark]: - """Provide access to the currently displayed advanced bookmark. - - Returns: - :py:class:`AdvancedBookmark`: FoxPuppet AdvancedBookmark object. - """ - with self.selenium.context(self.selenium.CONTEXT_CHROME): - try: - root = self.selenium.find_element(*self._bookmark_panel_locator) - return AdvancedBookmark.create(self, root) - except NoSuchElementException: - return None + root = self.selenium.find_element(*self._bookmark_locator) + return Bookmark.create(self, root) def wait_for_notification( self, @@ -132,38 +114,20 @@ def wait_for_notification( ) return None - def wait_for_bookmark( - self, - bookmark_class: Optional[ - Type[Union[BasicBookmark, AdvancedBookmark]] - ] = BasicBookmark, - ) -> Optional[Union[BasicBookmark, AdvancedBookmark]]: - """Wait for the specified bookmark panel to be displayed. - - Args: - bookmark_class (:py:class:`BasicBookmark` | :py:class:`AdvancedBookmark`, optional): - The bookmark class to wait for. If `None` is specified, it will - wait for any bookmark object to be shown. Defaults to `BasicBookmark`. + def wait_for_bookmark(self) -> Bookmark: + """Wait for the bookmark panel to be displayed. - Optional[BasicBookmark | AdvancedBookmark]: Firefox Bookmark object (Basic or Advanced), or None if not found. + Returns: + Optional[Bookmark]: The Bookmark object if found, or None if not found. """ with self.selenium.context(self.selenium.CONTEXT_CHROME): message = "Bookmark panel was not shown." - if bookmark_class is BasicBookmark: - self.wait.until( - lambda _: self.basic_bookmark is not None, - message=message, - ) - return self.basic_bookmark - elif bookmark_class is AdvancedBookmark: - self.wait.until( - lambda _: self.advanced_bookmark is not None, - message=message, - ) - return self.advanced_bookmark - else: - raise ValueError(f"Unsupported bookmark class: {bookmark_class}") + self.wait.until( + lambda _: self.bookmark is not None, + message=message, + ) + return self.bookmark @property def is_private(self) -> bool | Any: diff --git a/tests/test_bookmarks.py b/tests/test_bookmarks.py index e03107f..58517df 100644 --- a/tests/test_bookmarks.py +++ b/tests/test_bookmarks.py @@ -4,21 +4,16 @@ """Tests for Bookmarks.""" import pytest -from unittest.mock import Mock, patch -from selenium.webdriver.remote.webelement import WebElement -from selenium.common.exceptions import NoSuchElementException from selenium.webdriver.remote.webdriver import WebDriver -from selenium.webdriver.common.by import By from foxpuppet.windows import BrowserWindow from foxpuppet.windows.browser.bookmarks.bookmark import ( - BasicBookmark, - AdvancedBookmark, + Bookmark, BookmarkData, ) -class Bookmark: - """Class representing a Firefox bookmark.""" +class BookmarkEntry: + """Class representing a Firefox bookmark entry.""" def __init__(self, url: str, name: str = "", keyword: str = "", tags: list[str] = []): self.url = url @@ -27,151 +22,107 @@ def __init__(self, url: str, name: str = "", keyword: str = "", tags: list[str] self._tags = tags or [] @property - def keyword(self): - """Returns the current list of keywords.""" + def keyword(self) -> str: + """Returns the current keyword.""" return self._keyword @keyword.setter def keyword(self, new_keyword: str): """Sets new keywords for the bookmark.""" - if isinstance(new_keyword, str): - self._keyword = new_keyword - else: - raise ValueError("Keyword must be a string.") + self._keyword = new_keyword @property - def tags(self): + def tags(self) -> list[str]: """Returns the current list of tags.""" return self._tags @tags.setter def tags(self, new_tags: list[str]): """Sets new tags for the bookmark.""" - if isinstance(new_tags, list) and all(isinstance(t, str) for t in new_tags): - self._tags = new_tags - else: - raise ValueError("Tags must be a list of strings.") + self._tags = new_tags @pytest.fixture -def bookmark() -> Bookmark: +def bookmark_entry() -> BookmarkEntry: """Fixture for creating a bookmark with only a URL.""" - return Bookmark(url="https://www.mozilla.org/") + return BookmarkEntry(url="https://www.mozilla.org/en-US/") @pytest.fixture -def basic_bookmark( - browser: BrowserWindow, bookmark: Bookmark, selenium: WebDriver -) -> BasicBookmark: - """Return a BasicBookmark instance.""" - selenium.get(bookmark.url) - bookmark_instance = browser.wait_for_bookmark(BasicBookmark) - if bookmark_instance is None: - raise ValueError("Failed to get BasicBookmark instance.") - bookmark_instance.add() - return bookmark_instance +def bookmark_star( + browser: BrowserWindow, bookmark_entry: BookmarkEntry, selenium: WebDriver +) -> Bookmark: + """Return a Bookmark instance created using the star button.""" + selenium.get(bookmark_entry.url) + bookmark_using_star = browser.wait_for_bookmark() + bookmark_using_star.add_bookmark() + return bookmark_using_star @pytest.fixture -def advanced_bookmark( - browser: BrowserWindow, bookmark: Bookmark, selenium: WebDriver -) -> AdvancedBookmark: - """Return a AdvancedBookmark instance.""" - selenium.get(bookmark.url) - bookmark_instance = browser.wait_for_bookmark(AdvancedBookmark) - if bookmark_instance is None: - raise ValueError("Failed to get AdvancedBookmark instance.") - if not isinstance(bookmark_instance, AdvancedBookmark): - raise ValueError("Expected AdvancedBookmark instance but got different type") - bookmark.name = "Internet fInternet for people" - bookmark.tags = ["Browser", "Open Source"] - bookmark.keyword = "Mozilla" - bookmark_data: BookmarkData = { - "name": bookmark.name, - "url": bookmark.url, - "tags": bookmark.tags, - "keyword": bookmark.keyword, - } - bookmark_instance.add_bookmark(bookmark_data) - return bookmark_instance - - -def test_invalid_keyword(bookmark: Bookmark) -> None: - """Test setting a non-string value to the keyword raises ValueError.""" - with pytest.raises(ValueError, match="Keyword must be a string."): - bookmark.keyword = 123 - - -def test_invalid_tags(bookmark: Bookmark) -> None: - """Test setting a non-list or invalid list to tags raises ValueError.""" - with pytest.raises(ValueError, match="Tags must be a list of strings."): - bookmark.tags = "tag1, tag2" - with pytest.raises(ValueError, match="Tags must be a list of strings."): - bookmark.tags = [123, "tag2"] +def label() -> str: + """Fixture for the bookmark label.""" + return "Internet for people" -def test_create_basic_bookmark_none(browser: BrowserWindow) -> None: - """Test BasicBookmark.create returns None on NoSuchElementException.""" - mock_root = Mock() - with patch( - "foxpuppet.windows.browser.bookmarks.bookmark.BasicBookmark.__init__" - ) as mock_init: - mock_init.side_effect = NoSuchElementException - result = BasicBookmark.create(browser, mock_root) - assert result is None - - -def test_create_advanced_bookmark_none(browser: BrowserWindow) -> None: - """Test AdvancedBookmark.create returns None on NoSuchElementException.""" - mock_root = Mock() - with patch( - "foxpuppet.windows.browser.bookmarks.bookmark.AdvancedBookmark.__init__" - ) as mock_init: - mock_init.side_effect = NoSuchElementException - result = AdvancedBookmark.create(browser, mock_root) - assert result is None +@pytest.fixture +def bookmark_menu( + browser: BrowserWindow, bookmark_entry: BookmarkEntry, selenium: WebDriver +) -> Bookmark: + """Return a Bookmark instance created using main menu.""" + selenium.get(bookmark_entry.url) + bookmark_using_menu = browser.wait_for_bookmark() + bookmark_entry.name = "Internet fInternet for people" + bookmark_entry.tags = ["Browser", "Open Source"] + bookmark_entry.keyword = "Mozilla" + bookmark_data: BookmarkData = { + "name": bookmark_entry.name, + "url": bookmark_entry.url, + "tags": bookmark_entry.tags, + "keyword": bookmark_entry.keyword, + } + bookmark_using_menu.add_bookmark(bookmark_data, is_detailed=True) + return bookmark_using_menu -def test_add_basic_bookmark( - basic_bookmark: BasicBookmark, +def test_page_is_bookmarked_using_star( + bookmark_star: Bookmark, ) -> None: - """Ensure the basic bookmark is added.""" - assert basic_bookmark.is_bookmarked + """Ensure the bookmark is added using the star button.""" + assert bookmark_star.is_bookmarked_star -def test_retrieve_basic_bookmark(basic_bookmark: BasicBookmark) -> None: - """Retrieve Basic Bookmark.""" - label = "Internet for people" - assert basic_bookmark.retrieve_bookmark(label) is True +def test_page_is_bookmarked_using_menu(bookmark_menu: Bookmark) -> None: + """Ensure the bookmark is added using the main bookmark menu.""" + assert bookmark_menu.is_bookmarked_menu -def test_delete_basic_bookmark(basic_bookmark: BasicBookmark) -> None: - """Delete Advanced bookmark""" - basic_bookmark.delete() - assert basic_bookmark.is_bookmarked is False +def test_retrieve_bookmark_star(bookmark_star: Bookmark, label: str) -> None: + """Retrieve bookmark added using star button.""" + assert bookmark_star.retrieve_bookmark(label) is True -def test_add_advanced_bookmark( - advanced_bookmark: AdvancedBookmark, -) -> None: - """Ensure the advanced bookmark is added.""" - assert advanced_bookmark.is_bookmarked +def test_delete_bookmark_star(bookmark_star: Bookmark, label: str) -> None: + """Delete bookmark added using star button.""" + bookmark_star.delete_bookmark() + assert bookmark_star.is_bookmarked_star is False + assert bookmark_star.retrieve_bookmark(label) is False -def test_retrieve_advanced_bookmark(advanced_bookmark: AdvancedBookmark) -> None: - """Retrieve Advanced Bookmark.""" - label = "Internet for people" - assert advanced_bookmark.retrieve_bookmark(label) is True +def test_delete_bookmark_menu(bookmark_menu: Bookmark, label: str) -> None: + """Delete bookmark added using the main bookmark menu""" + bookmark_menu.delete_bookmark(label, is_detailed=True) + assert bookmark_menu.is_bookmarked_menu is False + assert bookmark_menu.retrieve_bookmark(label) is False -def test_retrieve_deleted_bookmark(basic_bookmark: BasicBookmark) -> None: - """Test retrieve deleted bookmark.""" - basic_bookmark.delete() - assert basic_bookmark.retrieve_bookmark("any label") is False +def test_retrieve_deleted_bookmark_star(bookmark_star: Bookmark, label: str) -> None: + """Test retrieve deleted bookmark (star button).""" + bookmark_star.delete_bookmark() + assert bookmark_star.retrieve_bookmark(label) is False -def test_delete_advanced_bookmark(advanced_bookmark: AdvancedBookmark) -> None: - """Delete Advanced bookmark""" - label = "Internet for people" - advanced_bookmark.delete_bookmark(label) - assert advanced_bookmark.is_bookmarked is False +def test_retrieve_deleted_bookmark_menu(bookmark_menu: Bookmark, label: str) -> None: + """Test retrieve deleted bookmark (main bookmark menu).""" + bookmark_menu.delete_bookmark(label, is_detailed=True) + assert bookmark_menu.retrieve_bookmark(label) is False