Spaces:
Paused
Paused
| """E2E tests for form interactions.""" | |
| import pytest | |
| from playwright.sync_api import Page, expect | |
| def select_sample(page: Page, sample_text: str): | |
| """Helper to select a sample from the dropdown.""" | |
| page.locator("#sample_dropdown input[role='listbox']").click() | |
| page.wait_for_timeout(300) | |
| page.locator(f"[role='option']:has-text('{sample_text}')").click() | |
| # Wait for form to be populated - check that project_name has a value | |
| # (status gets cleared when dropdown resets, so we can't rely on it) | |
| page.wait_for_function( | |
| """() => { | |
| const textarea = document.querySelector('#project_name textarea'); | |
| return textarea && textarea.value && textarea.value.includes('Sample:'); | |
| }""", | |
| timeout=30000 | |
| ) | |
| class TestProjectForm: | |
| """Test Tab 1 project form.""" | |
| def test_zip_validation_invalid(self, page: Page): | |
| """Test ZIP code format validation with invalid input.""" | |
| # zip_code uses input (max_lines=1), not textarea | |
| zip_input = page.locator("#zip_code input") | |
| # Invalid ZIP (too short) | |
| zip_input.fill("123") | |
| zip_input.blur() | |
| page.wait_for_timeout(300) | |
| # Should show invalid message (✗ indicates error) | |
| expect(page.locator("#zip_validation")).to_contain_text("✗") | |
| def test_zip_validation_valid(self, page: Page): | |
| """Test ZIP code format validation with valid input.""" | |
| # zip_code uses input (max_lines=1), not textarea | |
| zip_input = page.locator("#zip_code input") | |
| # Valid 5-digit ZIP | |
| zip_input.fill("12345") | |
| zip_input.blur() | |
| page.wait_for_timeout(300) | |
| # Should show valid checkmark | |
| expect(page.locator("#zip_validation")).to_contain_text("Valid") | |
| def test_zip_validation_valid_plus4(self, page: Page): | |
| """Test ZIP+4 format validation.""" | |
| # zip_code uses input (max_lines=1), not textarea | |
| zip_input = page.locator("#zip_code input") | |
| # Valid ZIP+4 | |
| zip_input.fill("12345-6789") | |
| zip_input.blur() | |
| page.wait_for_timeout(300) | |
| expect(page.locator("#zip_validation")).to_contain_text("Valid") | |
| def test_facility_classification_radio(self, page: Page): | |
| """Test facility classification radio buttons.""" | |
| # Use specific selector by value attribute to avoid substring matching | |
| facility_group = page.locator("#facility_classification") | |
| # Click Operational radio (use value attribute for exact match) | |
| facility_group.locator("input[value='Operational']").click() | |
| page.wait_for_timeout(200) | |
| # Verify it's selected | |
| expect(facility_group.locator("input[value='Operational']")).to_be_checked() | |
| # Click Non-Operational | |
| facility_group.locator("input[value='Non-Operational']").click() | |
| page.wait_for_timeout(200) | |
| expect(facility_group.locator("input[value='Non-Operational']")).to_be_checked() | |
| expect(facility_group.locator("input[value='Operational']")).not_to_be_checked() | |
| def test_construction_era_radio(self, page: Page): | |
| """Test construction era radio buttons.""" | |
| page.get_by_label("Pre-1980").click() | |
| expect(page.get_by_label("Pre-1980")).to_be_checked() | |
| page.get_by_label("Post-2000").click() | |
| expect(page.get_by_label("Post-2000")).to_be_checked() | |
| class TestRoomsForm: | |
| """Test Tab 2 rooms form.""" | |
| def test_room_exists_after_sample_load(self, page: Page): | |
| """Test room is created when sample is loaded.""" | |
| select_sample(page, "Bar & Dining Area") | |
| # Go to Rooms tab | |
| page.locator("#tab-rooms-button").click() | |
| page.wait_for_timeout(500) | |
| # Room should exist in table | |
| expect(page.locator("#rooms_table")).to_contain_text("Bar & Dining Area") | |
| def test_custom_height_visibility_toggle(self, page: Page): | |
| """Test custom height field appears when 'Custom' selected.""" | |
| page.locator("#tab-rooms-button").click() | |
| page.wait_for_timeout(300) | |
| # Select a standard height first - click dropdown input to open | |
| dropdown_input = page.locator("#room_height_preset input[role='listbox']") | |
| dropdown_input.click() | |
| page.wait_for_timeout(300) | |
| page.locator("[role='option']:has-text('10 ft')").click() | |
| page.wait_for_timeout(300) | |
| # Custom height should be hidden | |
| expect(page.locator("#room_height_custom")).not_to_be_visible() | |
| # Select Custom - click dropdown input to open | |
| dropdown_input.click() | |
| page.wait_for_timeout(300) | |
| page.locator("[role='option']:has-text('Custom')").click() | |
| page.wait_for_timeout(300) | |
| # Custom height should appear | |
| expect(page.locator("#room_height_custom")).to_be_visible() | |
| def test_room_validation_requires_name(self, page: Page): | |
| """Test that room name is required.""" | |
| page.locator("#tab-rooms-button").click() | |
| page.wait_for_timeout(300) | |
| # Try to add room with empty name | |
| page.locator("#room_length input").fill("20") | |
| page.locator("#room_width input").fill("15") | |
| # Select height from dropdown - click the input to open | |
| page.locator("#room_height_preset input[role='listbox']").click() | |
| page.wait_for_timeout(300) | |
| page.locator("[role='option']:has-text('10 ft')").click() | |
| page.wait_for_timeout(300) | |
| page.get_by_role("button", name="Add Room").click() | |
| page.wait_for_timeout(300) | |
| # Should show validation error about room name | |
| expect(page.locator("#tab2_validation")).to_contain_text("Room name") | |
| class TestImagesForm: | |
| """Test Tab 3 images form.""" | |
| def test_images_gallery_shows_sample_images(self, page: Page): | |
| """Test gallery displays images after sample load.""" | |
| select_sample(page, "Bar & Dining Area") | |
| # Go to Images tab | |
| page.locator("#tab-images-button").click() | |
| page.wait_for_timeout(500) | |
| # Gallery should have images | |
| gallery = page.locator("#images_gallery") | |
| expect(gallery).to_be_visible() | |
| # Should have 3 images | |
| images = gallery.locator("img") | |
| expect(images).to_have_count(3) | |
| def test_room_dropdown_populated(self, page: Page): | |
| """Test room dropdown is populated after sample load.""" | |
| select_sample(page, "Bar & Dining Area") | |
| # Go to Images tab | |
| page.locator("#tab-images-button").click() | |
| page.wait_for_timeout(500) | |
| # Click dropdown input to open it and verify room is in options | |
| page.locator("#room_select input[role='listbox']").click() | |
| page.wait_for_timeout(300) | |
| # Room should appear in dropdown options | |
| expect(page.locator("[role='option']:has-text('Bar & Dining Area')")).to_be_visible() | |
| class TestObservationsForm: | |
| """Test Tab 4 observations form.""" | |
| def test_checkbox_interactions(self, page: Page): | |
| """Test observation checkboxes can be toggled.""" | |
| page.locator("#tab-observations-button").click() | |
| page.wait_for_timeout(300) | |
| # Check smoke odor | |
| smoke_checkbox = page.locator("#smoke_odor input[type='checkbox']") | |
| smoke_checkbox.check() | |
| expect(smoke_checkbox).to_be_checked() | |
| # Uncheck | |
| smoke_checkbox.uncheck() | |
| expect(smoke_checkbox).not_to_be_checked() | |
| def test_odor_intensity_radio(self, page: Page): | |
| """Test odor intensity radio buttons.""" | |
| page.locator("#tab-observations-button").click() | |
| page.wait_for_timeout(300) | |
| # Use specific selector within odor_intensity group to avoid matching char_density | |
| odor_group = page.locator("#odor_intensity") | |
| odor_group.get_by_label("Strong").click() | |
| expect(odor_group.get_by_label("Strong")).to_be_checked() | |
| odor_group.get_by_label("Moderate").click() | |
| expect(odor_group.get_by_label("Moderate")).to_be_checked() | |
| expect(odor_group.get_by_label("Strong")).not_to_be_checked() | |
| def test_observations_persist_after_sample_load(self, page: Page): | |
| """Test observations are populated from sample.""" | |
| select_sample(page, "Factory Area") | |
| # Go to Observations tab | |
| page.locator("#tab-observations-button").click() | |
| page.wait_for_timeout(500) | |
| # Factory sample has smoke odor = True | |
| smoke_checkbox = page.locator("#smoke_odor input[type='checkbox']") | |
| expect(smoke_checkbox).to_be_checked() | |
| class TestDebugSelectors: | |
| """Debug tests to verify Gradio HTML structure.""" | |
| def test_capture_dropdown_and_sample_load(self, page: Page): | |
| """Capture dropdown HTML and test full sample load. | |
| Run with: pytest tests/test_e2e_forms.py::TestDebugSelectors::test_capture_dropdown_and_sample_load -v -s | |
| """ | |
| page.wait_for_timeout(2000) | |
| # Click dropdown to open | |
| print("\n--- Opening dropdown ---") | |
| dropdown_input = page.locator("#sample_dropdown input[role='listbox']") | |
| dropdown_input.click() | |
| page.wait_for_timeout(500) | |
| # Click the Bar & Dining option | |
| print("Clicking Bar & Dining option...") | |
| page.locator("[role='option']:has-text('Bar & Dining Area')").click() | |
| page.wait_for_timeout(2000) # Wait for sample to load | |
| # Check sample_status HTML | |
| print("\n--- DEBUG: Status HTML ---") | |
| try: | |
| status_html = page.locator("#sample_status").evaluate("el => el.outerHTML") | |
| print("Sample status HTML:", status_html[:500]) | |
| except Exception as e: | |
| print(f"Error getting status: {e}") | |
| # Check if project name was populated | |
| print("\n--- DEBUG: Project Name ---") | |
| try: | |
| # Get the full HTML of project_name element | |
| pn_html = page.locator("#project_name").evaluate("el => el.outerHTML") | |
| print(f"Project name HTML:\n{pn_html[:800]}") | |
| # Try different selectors | |
| print("\nTrying different selectors:") | |
| print(f" #project_name input count: {page.locator('#project_name input').count()}") | |
| print(f" #project_name textarea count: {page.locator('#project_name textarea').count()}") | |
| print(f" #project_name [data-testid] count: {page.locator('#project_name [data-testid]').count()}") | |
| # Check if there's any input/textarea in the document | |
| all_inputs = page.locator("input[type='text']").count() | |
| print(f" Total text inputs on page: {all_inputs}") | |
| except Exception as e: | |
| print(f"Error: {e}") | |
| print("--- END DEBUG ---\n") | |
| def test_capture_full_page_structure(self, page: Page): | |
| """Capture key element structures. | |
| Run with: pytest tests/test_e2e_forms.py::TestDebugSelectors::test_capture_full_page_structure -v -s | |
| """ | |
| page.wait_for_timeout(2000) | |
| print("\n--- DEBUG: Page Structure ---") | |
| # Capture project name structure | |
| project_name = page.locator("#project_name") | |
| print("Project name HTML:", project_name.evaluate("el => el.outerHTML")[:500]) | |
| # Capture tab button structure | |
| try: | |
| tab_btn = page.locator("#tab-project-button") | |
| print("Tab button exists:", tab_btn.count() > 0) | |
| except Exception as e: | |
| print(f"Tab button error: {e}") | |
| # Try to find tab by different selectors | |
| tabs = page.locator('[role="tab"]').all() | |
| print(f"Found {len(tabs)} tabs with role=tab") | |
| print("--- END DEBUG ---\n") | |