diff --git a/tests/theme_and_toolbar/test_customize_themes_and_redirect.py b/tests/theme_and_toolbar/test_customize_themes_and_redirect.py index 67786a325..301a8e553 100644 --- a/tests/theme_and_toolbar/test_customize_themes_and_redirect.py +++ b/tests/theme_and_toolbar/test_customize_themes_and_redirect.py @@ -1,6 +1,5 @@ import pytest from selenium.webdriver import Firefox - from modules.browser_object import Navigation, PanelUi from modules.page_object import AboutAddons @@ -10,82 +9,121 @@ def test_case(): return "118173" -themes = { - "firefox-compact-dark_mozilla_org-heading": "rgb(43, 42, 51)", - "firefox-compact-light_mozilla_org-heading": "rgb(249, 249, 251)", +THEMES: dict[str, list[str]] = { + "firefox-compact-dark_mozilla_org-heading": [ + "rgb(43, 42, 51)", # classic darker tone + "rgb(143, 143, 148)", # focused dark + "rgb(120, 119, 126)", # dark without focus + ], + # Compact Light + "firefox-compact-light_mozilla_org-heading": [ + "rgb(249, 249, 251)", + ], +} + +ALPENGLOW_MAP: dict[str, str] = { + "light": "rgba(255, 255, 255, 0.76)", + "dark": "rgba(40, 29, 78, 0.96)", } -alpenglow_map = {"light": "rgba(255, 255, 255, 0.76)", "dark": "rgba(40, 29, 78, 0.96)"} +def colors_match(a: str, b: str, tolerance: float = 0.14) -> bool: + """ + Compare two CSS color strings and determine if they are close enough to be considered equal. -def colors_match(a, b): - """Determine if two colors are close enough to be considered matches""" - tolerance = 0.14 - a_colorstring = a.split("(")[1][:-1] - b_colorstring = b.split("(")[1][:-1] - a_colors = [float(n) for n in a_colorstring.split(",")] - b_colors = [float(n) for n in b_colorstring.split(",")] - for i in range(len(a_colors)): - diff = abs((a_colors[i] / b_colors[i]) - 1.0) + Args: + a (str): First CSS color string in 'rgb(r,g,b)' or 'rgba(r,g,b,a)' format. + b (str): Second CSS color string in 'rgb(r,g,b)' or 'rgba(r,g,b,a)' format. + tolerance (float, optional): Allowed relative difference between each color channel. + Defaults to 0.14. A higher value means colors can differ more and still match. + + Returns: + bool: True if the two colors are considered a match within the given tolerance. + False if the color strings are invalid. + """ + try: + a_vals = a.split("(")[1][:-1] + b_vals = b.split("(")[1][:-1] + a_nums = [float(n.strip()) for n in a_vals.split(",")] + b_nums = [float(n.strip()) for n in b_vals.split(",")] + except (IndexError, ValueError): + # Raised if string doesn't contain expected format or non-numeric parts + return False + + # Compare up to the shortest length (rgb vs rgba) + for i in range(min(len(a_nums), len(b_nums))): + base = b_nums[i] if b_nums[i] != 0 else 1.0 + diff = abs((a_nums[i] / base) - 1.0) if diff > tolerance: return False + return True @pytest.mark.ci -def test_redirect_to_addons(driver: Firefox): +def test_redirect_to_addons(driver: Firefox) -> None: """ - C118173, ensures that the user is redirected to about:addons through the ui panel + C118173: Ensure the user is redirected to about:addons via the UI panel. """ panel_ui = PanelUi(driver) panel_ui.open() panel_ui.open_panel_menu() panel_ui.navigate_to_about_addons() - windows = driver.window_handles - driver.switch_to.window(windows[2]) + + # remember original window, then switch to newly opened one + orig = driver.window_handles[0] + new = driver.window_handles[-1] + driver.switch_to.window(new) assert driver.current_url == "about:addons" + # cleanup: close the tab we opened and restore focus + driver.close() + driver.switch_to.window(orig) + -@pytest.mark.parametrize("theme_name", list(themes.keys())) -def test_open_addons(driver: Firefox, theme_name: str): +@pytest.mark.parametrize("theme_name", list(THEMES.keys())) +def test_activate_theme_background_matches_expected(driver: Firefox, theme_name: str) -> None: """ - C118173, continuation ensures that all the themes are set correctly + C118173: Ensure that activating each theme in about:addons applies the expected background color. + Handles Developer Edition vs standard Firefox defaults. """ + nav = Navigation(driver) abt_addons = AboutAddons(driver).open() abt_addons.choose_sidebar_option("theme") # Dynamically detect if running Developer Edition if abt_addons.is_devedition(): - # Adjust expectations for Developer Edition if theme_name == "firefox-compact-dark_mozilla_org-heading": - # Already default on Developer Edition; skip activation/assertion pytest.skip("Compact Dark is default on DevEdition, skipping.") else: - # Adjust expectations for standard Firefox if theme_name == "firefox-compact-light_mozilla_org-heading": - # Already default on Firefox standard; skip activation/assertion pytest.skip("Compact Light is default on Firefox, skipping.") current_bg = abt_addons.activate_theme( - nav, theme_name, themes[theme_name], perform_assert=False + nav, theme_name, "", perform_assert=False + ) + + expected_list = THEMES[theme_name] + assert any(colors_match(current_bg, exp) for exp in expected_list), ( + f"Got {current_bg} for {theme_name}; expected one of {expected_list}" ) - assert colors_match(current_bg, themes[theme_name]) -def test_alpenglow_theme(driver: Firefox): +def test_alpenglow_theme(driver: Firefox) -> None: """ - C118173, specifically for alpenglow theme because color can be two values for dark or light mode + C118173: Alpenglow theme can render two values depending on light / dark mode. + Accept either using the tolerance-based comparison. """ nav = Navigation(driver) abt_addons = AboutAddons(driver).open() abt_addons.choose_sidebar_option("theme") + current_bg = abt_addons.activate_theme( nav, "firefox-alpenglow_mozilla_org-heading", "", perform_assert=False ) - # assert current_bg == alpenglow_map["light"] or current_bg == alpenglow_map["dark"] - assert colors_match(current_bg, alpenglow_map["light"]) or colors_match( - current_bg, alpenglow_map["dark"] + assert colors_match(current_bg, ALPENGLOW_MAP["light"]) or colors_match( + current_bg, ALPENGLOW_MAP["dark"] ) diff --git a/tests/theme_and_toolbar/test_installed_theme_enabled.py b/tests/theme_and_toolbar/test_installed_theme_enabled.py index b6eeb5491..3116b59df 100644 --- a/tests/theme_and_toolbar/test_installed_theme_enabled.py +++ b/tests/theme_and_toolbar/test_installed_theme_enabled.py @@ -12,39 +12,46 @@ def test_case(): return "118174" -MAC_GHA = environ.get("GITHUB_ACTIONS") == "true" and sys.platform.startswith("darwin") +MAC_GHA: bool = environ.get("GITHUB_ACTIONS") == "true" and sys.platform.startswith("darwin") + +AMO_HOST: str = "addons.mozilla.org" +AMO_THEMES_PATH: str = "firefox/themes" @pytest.mark.skipif(MAC_GHA, reason="Test unstable in MacOS Github Actions") -def test_find_more_themes(driver: Firefox): +def test_find_more_themes(driver: Firefox) -> None: """ - C118174, first part + C118174 (part 1): From about:addons > Themes, 'Find more themes' opens AMO in a new tab. + Verify AMO host and themes path are present in the URL. """ about_addons = AboutAddons(driver).open() about_addons.choose_sidebar_option("theme") about_addons.get_element("find-more-themes-button").click() + driver.switch_to.window(driver.window_handles[-1]) - # Continuing to call the object "about_addons" is confusing base = about_addons - base.url_contains("addons.mozilla.org") - base.url_contains("firefox/themes") + base.url_contains(AMO_HOST) + base.url_contains(AMO_THEMES_PATH) @pytest.mark.skipif(MAC_GHA, reason="Test unstable in MacOS Github Actions") -def test_installed_theme_enabled(driver: Firefox): +def test_installed_theme_enabled(driver: Firefox) -> None: """ - C118174: install a theme and make sure it is set to enabled immediately + C118174 (part 2): Install a recommended theme from AMO and ensure it becomes enabled immediately. """ about_addons = AboutAddons(driver).open() about_addons.choose_sidebar_option("theme") - starting_theme = about_addons.get_element("enabled-theme-title").get_attribute( - "innerText" - ) - amo = AmoThemes(driver).open() - amo.install_recommended_theme() + + # Capture currently enabled theme title + starting_theme = about_addons.get_element("enabled-theme-title").get_attribute("innerText") + + # Go to AMO and install a recommended theme (POM encapsulates waits and flows) + AmoThemes(driver).open().install_recommended_theme() + + # Return to about:addons > Themes and verify the enabled theme changed about_addons = AboutAddons(driver).open() about_addons.choose_sidebar_option("theme") - # NOTE: AMO does not enforce that the listed theme name remains the same after installation + # AMO may change display names; we only assert that the enabled theme is different about_addons.check_theme_has_changed(starting_theme)