/
vshmidt
/
streamlit
Обзор
Документация
Войти
/
vshmidt
/
streamlit
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
develop
e2e_playwright/custom_components/pdf_component_test.py
389 строк
14 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. """Test app for st.pdf component functionality and various PDF scenarios.""" import re from playwright.sync_api import Locator, Page, expect from e2e_playwright.conftest import ImageCompareFunction, wait_for_app_run, wait_until from e2e_playwright.shared.app_utils import reset_hovering, select_selectbox_option def _select_pdf_scenario(app: Page, scenario: str): """Select a PDF test scenario from the dropdown.""" select_selectbox_option(app, "PDF Test Scenarios", scenario) # reset hovering to avoid some flakiness: reset_hovering(app) def _expect_pdf_container_attached(app: Page): """Expect the PDF component container to be attached to the DOM.""" pdf_container = app.get_by_test_id("pdf-container") expect(pdf_container).to_be_attached() def _wait_for_slider_to_be_ready(app: Page, timeout: int = 5000): """Wait for the slider to be ready for interaction. Parameters ---------- app : Page The page containing the slider timeout : int Maximum time to wait in milliseconds """ slider = app.get_by_test_id("stSlider") if slider.count() > 0: # If there's a slider on the page, wait for it to be ready expect(slider).to_be_visible(timeout=timeout) expect(slider.get_by_role("slider")).to_be_enabled(timeout=timeout) def _wait_for_pdf_to_load( app: Page, timeout: int = 15000, pdf_container: Locator | None = None ): """Wait for PDF content to finish loading inside the DOM container. The PDF component renders directly in the DOM (no iframe) and exposes a container with data-testid="pdf-container". Parameters ---------- app : Page The page containing the PDF component timeout : int Maximum time to wait in milliseconds pdf_container : Locator | None Optional locator for a specific PDF container. If not provided, the default container test id is used. """ if pdf_container is None: pdf_container = app.get_by_test_id("pdf-container") # First ensure the container is attached and visible expect(pdf_container).to_be_visible(timeout=timeout) # While loading, the component may render an element with data-testid="pdf-loading". # We wait for that loading indicator to be hidden or gone. loading_indicator = pdf_container.get_by_test_id("pdf-loading") expect(loading_indicator).to_be_hidden(timeout=timeout) # Wait for the first page to actually render in the DOM. # The PDF component uses a virtualized list with data-index attributes for pages. first_page = pdf_container.locator('[data-index="0"]') expect(first_page).to_be_visible(timeout=timeout) def test_st_pdf_basic_functionality(app: Page, assert_snapshot: ImageCompareFunction): """Test basic st.pdf component functionality with snapshot.""" _select_pdf_scenario(app, "basic") _expect_pdf_container_attached(app) # Wait for PDF to be fully loaded before taking snapshot _wait_for_pdf_to_load(app) pdf_container = app.get_by_test_id("pdf-container") assert_snapshot(pdf_container, name="st_pdf-basic_functionality") def test_st_pdf_file_upload_no_file(app: Page, assert_snapshot: ImageCompareFunction): """Test st.pdf with file upload when no file is uploaded.""" _select_pdf_scenario(app, "fileUpload") file_uploader = app.get_by_test_id("stFileUploader") expect(file_uploader).to_be_visible() # Should not display any PDF when no file is uploaded expect(app.get_by_test_id("pdf-container")).not_to_be_attached() # Take snapshot of just the file uploader state file_uploader = app.get_by_test_id("stFileUploader") assert_snapshot(file_uploader, name="st_pdf-file_upload_no_file") def test_st_pdf_custom_size(app: Page, assert_snapshot: ImageCompareFunction): """Test st.pdf with custom height.""" _select_pdf_scenario(app, "customSize") height_slider = app.get_by_test_id("stSlider") expect(height_slider).to_be_visible() # Wait for slider to be ready for interaction _wait_for_slider_to_be_ready(app) _expect_pdf_container_attached(app) # Wait for PDF to be fully loaded _wait_for_pdf_to_load(app) # Capture just the PDF container to focus on the height setting pdf_container = app.get_by_test_id("pdf-container") assert_snapshot(pdf_container, name="st_pdf-custom_size") def test_st_pdf_base64_encoding(app: Page, assert_snapshot: ImageCompareFunction): """Test st.pdf with base64 encoded data.""" _select_pdf_scenario(app, "base64") base64_info = app.get_by_test_id("stMarkdown").filter(has_text="Base64 PDF length:") expect(base64_info).to_be_visible() code_block = app.get_by_test_id("stCode") expect(code_block).to_be_visible() _expect_pdf_container_attached(app) # Wait for PDF to be fully loaded _wait_for_pdf_to_load(app) # Take snapshot of just the PDF container, following the good example from bytes_io test pdf_container = app.get_by_test_id("pdf-container") assert_snapshot(pdf_container, name="st_pdf-base64_encoding") def test_st_pdf_bytes_io(app: Page, assert_snapshot: ImageCompareFunction): """Test st.pdf with BytesIO object.""" _select_pdf_scenario(app, "bytesIO") _expect_pdf_container_attached(app) # Wait for PDF to be fully loaded before taking snapshot _wait_for_pdf_to_load(app) pdf_container = app.get_by_test_id("pdf-container") assert_snapshot(pdf_container, name="st_pdf-bytes_io") def test_st_pdf_error_handling(app: Page, assert_snapshot: ImageCompareFunction): """Test st.pdf error handling with invalid data.""" _select_pdf_scenario(app, "errorHandling") warning_message = app.get_by_test_id("stAlert").filter( has_text="Attempting to display invalid PDF data" ) expect(warning_message).to_be_visible() # Even with invalid data, the component should still render the PDF container pdf_container = app.get_by_test_id("pdf-container") expect(pdf_container).to_be_visible() # Capture just the warning message - the error state in the PDF viewer isn't visually meaningful warning_message = app.get_by_test_id("stAlert") assert_snapshot(warning_message, name="st_pdf-error_handling") def test_st_pdf_in_columns(app: Page, assert_snapshot: ImageCompareFunction): """Test st.pdf in columns layout.""" _select_pdf_scenario(app, "columns") description = app.get_by_test_id("stMarkdown").filter( has_text="PDFs in Columns Layout" ) expect(description).to_be_visible() col1_header = app.get_by_test_id("stMarkdown").filter(has_text="PDF in Column 1") col2_header = app.get_by_test_id("stMarkdown").filter(has_text="PDF in Column 2") expect(col1_header).to_be_visible() expect(col2_header).to_be_visible() # Verify multiple PDFs are rendered in columns pdf_containers = app.get_by_test_id("pdf-container") expect(pdf_containers).to_have_count(2) # Wait for both containers to be visible first_container = pdf_containers.nth(0) second_container = pdf_containers.nth(1) expect(first_container).to_be_visible() expect(second_container).to_be_visible() # Wait for both PDFs to load _wait_for_pdf_to_load(app, pdf_container=first_container) _wait_for_pdf_to_load(app, pdf_container=second_container) # Take snapshot focusing on the column layout with PDFs columns_container = app.get_by_test_id("stHorizontalBlock") assert_snapshot(columns_container, name="st_pdf-in_columns") def test_st_pdf_interactive(app: Page, assert_snapshot: ImageCompareFunction): """Test interactive PDF features.""" _select_pdf_scenario(app, "interactive") subheader = app.get_by_test_id("stMarkdown").filter(has_text="Interactive PDF Test") expect(subheader).to_be_visible() height_slider = app.get_by_test_id("stSlider") expect(height_slider).to_be_visible() # Wait for slider to be ready for interaction _wait_for_slider_to_be_ready(app) reset_button = app.get_by_test_id("stButton").filter(has_text="Reset Height") expect(reset_button).to_be_visible() _expect_pdf_container_attached(app) # Wait for PDF to be fully loaded _wait_for_pdf_to_load(app) # Take snapshot of just the PDF container in initial state pdf_container = app.get_by_test_id("pdf-container") assert_snapshot(pdf_container, name="st_pdf-interactive_initial") # Test that the reset button actually works reset_button.click() wait_for_app_run(app) # After reset, the PDF should still be visible _expect_pdf_container_attached(app) # Wait for PDF to load again after reset _wait_for_pdf_to_load(app) # Take snapshot after reset to verify state assert_snapshot(pdf_container, name="st_pdf-interactive_after_reset") def test_st_pdf_app_title_and_selection(app: Page): """Test that the app title and selection dropdown work correctly.""" title = app.get_by_test_id("stMarkdown").filter(has_text="st.pdf Component Tests") expect(title).to_be_visible() description = app.get_by_test_id("stMarkdown").filter( has_text="Select a PDF test scenario to run:" ) expect(description).to_be_visible() selectbox = app.get_by_test_id("stSelectbox") expect(selectbox).to_be_visible() scenarios = [ "basic", "fileUpload", "customSize", ] for scenario in scenarios: _select_pdf_scenario(app, scenario) subheader = app.get_by_test_id("stMarkdown").filter( has_text=f"Running: {scenario}" ) expect(subheader).to_be_visible() def test_st_pdf_component_container_behavior(app: Page): """Test that st.pdf component creates a proper DOM container.""" _select_pdf_scenario(app, "basic") pdf_container = app.get_by_test_id("pdf-container") expect(pdf_container).to_be_attached() expect(pdf_container).to_be_visible() def test_st_pdf_widget_interactions(app: Page): """Test interactions with st.pdf widget controls.""" _select_pdf_scenario(app, "customSize") height_slider = app.get_by_test_id("stSlider") expect(height_slider).to_be_visible() # Wait for slider to be ready for interaction _wait_for_slider_to_be_ready(app) slider_thumb = height_slider.locator("[role='slider']") expect(slider_thumb).to_be_visible() expect(slider_thumb).to_have_attribute("aria-valuenow", re.compile(r".*")) # Verify that the PDF renders with the current slider value _expect_pdf_container_attached(app) def test_st_pdf_different_heights_snapshots( app: Page, assert_snapshot: ImageCompareFunction ): """Test PDF component with different height values for visual comparison.""" # Set a taller viewport to accommodate the maximum PDF height (800px) app.set_viewport_size({"width": 1280, "height": 1000}) _select_pdf_scenario(app, "customSize") height_slider = app.get_by_test_id("stSlider") expect(height_slider).to_be_visible() # Wait for slider to be ready for interaction _wait_for_slider_to_be_ready(app) # Wait for initial PDF to load _expect_pdf_container_attached(app) _wait_for_pdf_to_load(app, timeout=30000) pdf_container = app.get_by_test_id("pdf-container") # Record initial height and take snapshot at default height (500px) initial_box = pdf_container.bounding_box() assert initial_box is not None initial_height = initial_box["height"] assert_snapshot(pdf_container, name="st_pdf-height_default") # Get the actual slider element slider_element = height_slider.get_by_role("slider") expect(slider_element).to_be_visible() # Move slider to minimum (200px) using proper e2e slider interaction slider_element.hover() app.mouse.down() # Move mouse far to the left to reach minimum value app.mouse.move(0, 0) # Move to far left of screen app.mouse.up() wait_for_app_run(app) # Wait for PDF to adjust to new height and fully load _wait_for_pdf_to_load(app, timeout=30000) # Verify we actually reached a noticeably lower height than the initial one def _is_min_height_reached() -> bool: box = pdf_container.bounding_box() return box is not None and box["height"] < initial_height - 10 wait_until(app, _is_min_height_reached, timeout=7000) min_box = pdf_container.bounding_box() assert min_box is not None min_height = min_box["height"] assert_snapshot(pdf_container, name="st_pdf-height_minimum") # Move slider to maximum (800px) using proper e2e slider interaction slider_element.hover() app.mouse.down() # Move mouse far to the right to reach maximum value app.mouse.move(1000, 0) # Move to far right of screen app.mouse.up() wait_for_app_run(app) # Wait for PDF to adjust to new height and fully load _wait_for_pdf_to_load(app, timeout=30000) # Verify we actually reached a higher height than the minimum one def _is_max_height_reached() -> bool: box = pdf_container.bounding_box() return box is not None and box["height"] > min_height + 10 wait_until(app, _is_max_height_reached, timeout=7000) assert_snapshot(pdf_container, name="st_pdf-height_maximum")