"""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")