/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
e2e_playwright/st_main_layout_test.py
339 строк
11 KB
Ken McGrady
Update copyright header to 2026 (#13491)
02 янв 2026, 16:21
Не верифицирован
02 янв 2026, 16:21
374540a
Код
Авторство
О чём код?
# Copyright (c) Streamlit Inc. (2018-2022) Snowflake Inc. (2022-2026) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. from __future__ import annotations from typing import TYPE_CHECKING, Any import pytest from playwright.sync_api import Page, expect from e2e_playwright.conftest import ( ImageCompareFunction, start_app_server, wait_for_app_loaded, wait_for_app_run, ) if TYPE_CHECKING: from collections.abc import Generator @pytest.fixture def sidebar_mode(request: pytest.FixtureRequest) -> str: """Fixture to provide sidebar_mode parameter to app fixture.""" return getattr(request, "param", "auto") # Disable the module-scoped app_server fixture for this test module @pytest.fixture(scope="module", autouse=True) def app_server(): """Override to disable the default module-scoped app_server fixture.""" return # Custom app fixture that starts a fresh server for each test with the correct sidebar mode @pytest.fixture def app( page: Page, app_port: int, request: pytest.FixtureRequest, sidebar_mode: str, ) -> Generator[Page, None, None]: """Start fresh server with correct sidebar mode for each test.""" from e2e_playwright.shared.performance import start_capture_traces # Start the Streamlit server with the custom environment variable streamlit_proc = start_app_server( app_port, request.module, extra_env={"STREAMLIT_SIDEBAR_TEST_MODE": sidebar_mode}, ) try: # Open the app page response = page.goto(f"http://localhost:{app_port}/") if response is None or response.status != 200: raise RuntimeError("Unable to load page") # Clear localStorage to ensure clean state for tests page.evaluate("() => window.localStorage.clear()") start_capture_traces(page) wait_for_app_loaded(page) yield page finally: # Clean up the server streamlit_stdout = streamlit_proc.terminate() print(streamlit_stdout, flush=True) def setup_viewport_and_verify_title( app: Page, width: int, height: int, sidebar_mode: str ) -> None: """Common setup for viewport, waiting, and title verification.""" app.set_viewport_size({"width": width, "height": height}) # Verify the fixture was applied correctly by checking the page title expected_title = f"Sidebar Test - {sidebar_mode.title()}" expect(app).to_have_title(expected_title) # Reload the page to allow viewport size change to take effect. app.reload() wait_for_app_run(app) def verify_sidebar_state(app: Page, expected_expanded: bool) -> None: """Verify sidebar exists and has expected expanded state.""" sidebar = app.get_by_test_id("stSidebar") expect(sidebar).to_be_attached() expect(sidebar).to_have_attribute("aria-expanded", str(expected_expanded).lower()) def verify_sidebar_content_visibility(app: Page, should_be_visible: bool) -> None: """Verify sidebar content visibility.""" sidebar_content = app.get_by_test_id("stSidebarContent") if should_be_visible: expect(sidebar_content).to_be_visible() else: # For collapsed state, we don't check visibility as it might vary pass def verify_expand_button_visible(app: Page) -> None: """Verify expand button is visible.""" expand_button = app.get_by_test_id("stExpandSidebarButton") expect(expand_button).to_be_visible() # Consolidated test for basic sidebar states across different modes and viewports @pytest.mark.parametrize( ("sidebar_mode", "viewport", "expected_expanded", "test_name"), [ ("auto", {"width": 375, "height": 667}, False, "auto_mobile_collapsed"), ("auto", {"width": 1280, "height": 800}, True, "auto_desktop_expanded"), ("collapsed", {"width": 375, "height": 667}, False, "collapsed_mobile"), ("collapsed", {"width": 1280, "height": 800}, False, "collapsed_desktop"), ("expanded", {"width": 375, "height": 667}, True, "expanded_mobile"), ("expanded", {"width": 1280, "height": 800}, True, "expanded_desktop"), ], indirect=["sidebar_mode"], ) def test_sidebar_basic_states( app: Page, assert_snapshot: ImageCompareFunction, sidebar_mode: str, viewport: dict[str, int], expected_expanded: bool, test_name: str, ): """Test sidebar basic states across different modes and viewport sizes.""" setup_viewport_and_verify_title( app, viewport["width"], viewport["height"], sidebar_mode ) # Verify sidebar state verify_sidebar_state(app, expected_expanded) # Verify content visibility for expanded states if expected_expanded: verify_sidebar_content_visibility(app, True) # Note: For collapsed states, we don't always verify expand button visibility # as behavior may vary between auto/collapsed modes # Take snapshot assert_snapshot(app, name=f"st_main_layout-{test_name}") # Test interaction functionality for auto mode @pytest.mark.parametrize("sidebar_mode", ["auto"], indirect=True) def test_sidebar_auto_mobile_expand_interaction( app: Page, assert_snapshot: ImageCompareFunction, sidebar_mode: str ): """Test sidebar expand interaction on mobile with auto mode.""" setup_viewport_and_verify_title(app, 375, 667, sidebar_mode) # Verify initial collapsed state verify_sidebar_state(app, False) verify_expand_button_visible(app) # Expand the sidebar expand_button = app.get_by_test_id("stExpandSidebarButton") expand_button.click() # Verify expanded state verify_sidebar_state(app, True) verify_sidebar_content_visibility(app, True) # Take snapshot of expanded state assert_snapshot(app, name="st_main_layout-auto_mobile_expanded") @pytest.mark.parametrize("sidebar_mode", ["auto"], indirect=True) def test_sidebar_auto_desktop_collapse_interaction( app: Page, assert_snapshot: ImageCompareFunction, sidebar_mode: str ): """Test sidebar collapse interaction on desktop with auto mode.""" setup_viewport_and_verify_title(app, 1280, 800, sidebar_mode) # Verify initial expanded state verify_sidebar_state(app, True) verify_sidebar_content_visibility(app, True) # Hover over sidebar to make collapse button visible sidebar_header = app.get_by_test_id("stSidebarHeader") sidebar_header.hover() # Collapse the sidebar collapse_button = app.get_by_test_id("stSidebarCollapseButton").locator("button") collapse_button.click() # Verify collapsed state verify_sidebar_state(app, False) verify_expand_button_visible(app) # Take snapshot of collapsed desktop state assert_snapshot(app, name="st_main_layout-auto_desktop_collapsed") # Tests for deploy button positioning @pytest.mark.parametrize( ("sidebar_mode", "viewport", "expected_expanded", "test_name"), [ ( "auto", {"width": 375, "height": 667}, False, "deploy_button_mobile_collapsed", ), ( "auto", {"width": 1280, "height": 800}, True, "deploy_button_desktop_expanded", ), ], indirect=["sidebar_mode"], ) def test_deploy_button_positioning( app: Page, assert_snapshot: ImageCompareFunction, sidebar_mode: str, viewport: dict[str, int], expected_expanded: bool, test_name: str, ): """Test deploy button positioning with different sidebar states.""" setup_viewport_and_verify_title( app, viewport["width"], viewport["height"], sidebar_mode ) # Verify sidebar state verify_sidebar_state(app, expected_expanded) # Verify header is visible header = app.get_by_test_id("stHeader") expect(header).to_be_visible() # Take screenshot focusing on header area assert_snapshot(header, name=f"st_main_layout-{test_name}") @pytest.mark.parametrize("sidebar_mode", ["auto"], indirect=True) def test_deploy_button_desktop_manually_collapsed( app: Page, assert_snapshot: ImageCompareFunction, sidebar_mode: str ): """Test deploy button positioning on desktop after manually collapsing sidebar.""" setup_viewport_and_verify_title(app, 1280, 800, sidebar_mode) # Manually collapse the sidebar sidebar_header = app.get_by_test_id("stSidebarHeader") sidebar_header.hover() collapse_button = app.get_by_test_id("stSidebarCollapseButton").locator("button") collapse_button.click() # Verify sidebar is collapsed verify_sidebar_state(app, False) # Verify header adjusts correctly header = app.get_by_test_id("stHeader") expect(header).to_be_visible() # Take screenshot focusing on header area assert_snapshot( header, name="st_main_layout-deploy_button_desktop_manually_collapsed" ) # Test responsive behavior during viewport size changes @pytest.mark.parametrize("sidebar_mode", ["auto"], indirect=True) def test_viewport_resize_responsive_behavior( app: Page, assert_snapshot: ImageCompareFunction, sidebar_mode: str ): """Test sidebar behavior when resizing viewport with auto mode.""" # Start with desktop - sidebar should be expanded setup_viewport_and_verify_title(app, 1280, 800, sidebar_mode) verify_sidebar_state(app, True) # Resize to mobile - sidebar should auto-collapse app.set_viewport_size({"width": 375, "height": 667}) # Verify sidebar collapsed on mobile verify_sidebar_state(app, False) verify_expand_button_visible(app) # Take final snapshot assert_snapshot(app, name="st_main_layout-responsive_mobile_final") # Comprehensive responsiveness test @pytest.mark.parametrize( ("sidebar_mode", "viewport_config"), [ ("auto", {"name": "mobile", "width": 375, "height": 667}), ("auto", {"name": "tablet", "width": 768, "height": 1024}), ("auto", {"name": "desktop", "width": 1280, "height": 800}), ("auto", {"name": "wide", "width": 1920, "height": 1080}), ], indirect=["sidebar_mode"], ) def test_layout_responsiveness_auto_mode( app: Page, viewport_config: dict[str, Any], assert_snapshot: ImageCompareFunction, sidebar_mode: str, ): """Test layout responsiveness with auto sidebar mode across different viewport sizes.""" setup_viewport_and_verify_title( app, viewport_config["width"], viewport_config["height"], sidebar_mode ) # Verify basic layout elements are present header = app.get_by_test_id("stHeader") expect(header).to_be_visible() main_content = app.get_by_test_id("stMain") expect(main_content).to_be_visible() sidebar = app.get_by_test_id("stSidebar") expect(sidebar).to_be_attached() expect(app.get_by_text("Info box 1")).to_be_visible() expect(app.get_by_text("Info box 2")).to_be_visible() # Take full app screenshot for this viewport assert_snapshot( app, name=f"st_main_layout-auto_{viewport_config['name']}_responsive" )