Parcourir la source

add test for color mode (initial and toggle) (#4467)

* add test for color mode (initial and toggle)

* add css check

* add page reload in the tests

* update test to catch the appearance regression

* don't render the appearance prop of rx.theme
Thomas Brandého il y a 5 mois
Parent
commit
862d7ec807

+ 1 - 0
reflex/components/radix/themes/base.py

@@ -266,6 +266,7 @@ class Theme(RadixThemesComponent):
                 _js_expr="{...theme.styles.global[':root'], ...theme.styles.global.body}"
             ),
         )
+        tag.remove_props("appearance")
         return tag
 
 

+ 218 - 0
tests/integration/tests_playwright/test_appearance.py

@@ -0,0 +1,218 @@
+from typing import Generator
+
+import pytest
+from playwright.sync_api import Page, expect
+
+from reflex.testing import AppHarness
+
+
+def DefaultLightModeApp():
+    import reflex as rx
+    from reflex.style import color_mode
+
+    app = rx.App(theme=rx.theme(appearance="light"))
+
+    @app.add_page
+    def index():
+        return rx.text(color_mode)
+
+
+def DefaultDarkModeApp():
+    import reflex as rx
+    from reflex.style import color_mode
+
+    app = rx.App(theme=rx.theme(appearance="dark"))
+
+    @app.add_page
+    def index():
+        return rx.text(color_mode)
+
+
+def DefaultSystemModeApp():
+    import reflex as rx
+    from reflex.style import color_mode
+
+    app = rx.App()
+
+    @app.add_page
+    def index():
+        return rx.text(color_mode)
+
+
+def ColorToggleApp():
+    import reflex as rx
+    from reflex.style import color_mode, resolved_color_mode, set_color_mode
+
+    app = rx.App(theme=rx.theme(appearance="light"))
+
+    @app.add_page
+    def index():
+        return rx.box(
+            rx.segmented_control.root(
+                rx.segmented_control.item(
+                    rx.icon(tag="monitor", size=20),
+                    value="system",
+                ),
+                rx.segmented_control.item(
+                    rx.icon(tag="sun", size=20),
+                    value="light",
+                ),
+                rx.segmented_control.item(
+                    rx.icon(tag="moon", size=20),
+                    value="dark",
+                ),
+                on_change=set_color_mode,
+                variant="classic",
+                radius="large",
+                value=color_mode,
+            ),
+            rx.text(color_mode, id="current_color_mode"),
+            rx.text(resolved_color_mode, id="resolved_color_mode"),
+            rx.text(rx.color_mode_cond("LightMode", "DarkMode"), id="color_mode_cond"),
+        )
+
+
+@pytest.fixture()
+def light_mode_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
+    """Start DefaultLightMode app at tmp_path via AppHarness.
+
+    Args:
+        tmp_path_factory: pytest tmp_path_factory fixture
+
+    Yields:
+        running AppHarness instance
+
+    """
+    with AppHarness.create(
+        root=tmp_path_factory.mktemp("appearance_app"),
+        app_source=DefaultLightModeApp,  # type: ignore
+    ) as harness:
+        assert harness.app_instance is not None, "app is not running"
+        yield harness
+
+
+@pytest.fixture()
+def dark_mode_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
+    """Start DefaultDarkMode app at tmp_path via AppHarness.
+
+    Args:
+        tmp_path_factory: pytest tmp_path_factory fixture
+
+    Yields:
+        running AppHarness instance
+
+    """
+    with AppHarness.create(
+        root=tmp_path_factory.mktemp("appearance_app"),
+        app_source=DefaultDarkModeApp,  # type: ignore
+    ) as harness:
+        assert harness.app_instance is not None, "app is not running"
+        yield harness
+
+
+@pytest.fixture()
+def system_mode_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
+    """Start DefaultSystemMode app at tmp_path via AppHarness.
+
+    Args:
+        tmp_path_factory: pytest tmp_path_factory fixture
+
+    Yields:
+        running AppHarness instance
+
+    """
+    with AppHarness.create(
+        root=tmp_path_factory.mktemp("appearance_app"),
+        app_source=DefaultSystemModeApp,  # type: ignore
+    ) as harness:
+        assert harness.app_instance is not None, "app is not running"
+        yield harness
+
+
+@pytest.fixture()
+def color_toggle_app(tmp_path_factory) -> Generator[AppHarness, None, None]:
+    """Start ColorToggle app at tmp_path via AppHarness.
+
+    Args:
+        tmp_path_factory: pytest tmp_path_factory fixture
+
+    Yields:
+        running AppHarness instance
+
+    """
+    with AppHarness.create(
+        root=tmp_path_factory.mktemp("appearance_app"),
+        app_source=ColorToggleApp,  # type: ignore
+    ) as harness:
+        assert harness.app_instance is not None, "app is not running"
+        yield harness
+
+
+def test_appearance_light_mode(light_mode_app: AppHarness, page: Page):
+    assert light_mode_app.frontend_url is not None
+    page.goto(light_mode_app.frontend_url)
+
+    expect(page.get_by_text("light")).to_be_visible()
+
+
+def test_appearance_dark_mode(dark_mode_app: AppHarness, page: Page):
+    assert dark_mode_app.frontend_url is not None
+    page.goto(dark_mode_app.frontend_url)
+
+    expect(page.get_by_text("dark")).to_be_visible()
+
+
+def test_appearance_system_mode(system_mode_app: AppHarness, page: Page):
+    assert system_mode_app.frontend_url is not None
+    page.goto(system_mode_app.frontend_url)
+
+    expect(page.get_by_text("system")).to_be_visible()
+
+
+def test_appearance_color_toggle(color_toggle_app: AppHarness, page: Page):
+    assert color_toggle_app.frontend_url is not None
+    page.goto(color_toggle_app.frontend_url)
+
+    # Radio buttons locators.
+    radio_system = page.get_by_role("radio").nth(0)
+    radio_light = page.get_by_role("radio").nth(1)
+    radio_dark = page.get_by_role("radio").nth(2)
+
+    # Text locators to check.
+    current_color_mode = page.locator("id=current_color_mode")
+    resolved_color_mode = page.locator("id=resolved_color_mode")
+    color_mode_cond = page.locator("id=color_mode_cond")
+    root_body = page.locator('div[data-is-root-theme="true"]')
+
+    # Background colors.
+    dark_background = "rgb(17, 17, 19)"  # value based on dark native appearance, can change depending on the browser
+    light_background = "rgb(255, 255, 255)"
+
+    # check initial state
+    expect(current_color_mode).to_have_text("light")
+    expect(resolved_color_mode).to_have_text("light")
+    expect(color_mode_cond).to_have_text("LightMode")
+    expect(root_body).to_have_css("background-color", light_background)
+
+    # click dark mode
+    radio_dark.click()
+    expect(current_color_mode).to_have_text("dark")
+    expect(resolved_color_mode).to_have_text("dark")
+    expect(color_mode_cond).to_have_text("DarkMode")
+    expect(root_body).to_have_css("background-color", dark_background)
+
+    # click light mode
+    radio_light.click()
+    expect(current_color_mode).to_have_text("light")
+    expect(resolved_color_mode).to_have_text("light")
+    expect(color_mode_cond).to_have_text("LightMode")
+    expect(root_body).to_have_css("background-color", light_background)
+    page.reload()
+    expect(root_body).to_have_css("background-color", light_background)
+
+    # click system mode
+    radio_system.click()
+    expect(current_color_mode).to_have_text("system")
+    expect(resolved_color_mode).to_have_text("light")
+    expect(color_mode_cond).to_have_text("LightMode")
+    expect(root_body).to_have_css("background-color", light_background)