SmokeScan / tests /test_e2e_forms.py
KinetoLabs's picture
Fix critical model implementations and add sample scenarios
f3ebc82
raw
history blame
11.7 kB
"""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")