Skip to content

Commit

Permalink
Refactored Bookmark
Browse files Browse the repository at this point in the history
Moved all bookmark functionalities into a single class, Bookmark
  • Loading branch information
Temidayo32 committed Dec 22, 2024
1 parent 8400326 commit 2b1a517
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 371 deletions.
2 changes: 2 additions & 0 deletions foxpuppet/region.py
Original file line number Diff line number Diff line change
Expand Up @@ -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


Expand Down Expand Up @@ -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)
287 changes: 156 additions & 131 deletions foxpuppet/windows/browser/bookmarks/bookmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,183 +20,208 @@ 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:
window (:py:class:`BrowserWindow`): Window object this bookmark appears in
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")
Expand Down
Loading

0 comments on commit 2b1a517

Please sign in to comment.