Files
animaltrack/tests/e2e/pages/move.py
Petru Paler 51e502ed10
All checks were successful
Deploy / deploy (push) Successful in 1m49s
Add Playwright e2e tests for all 8 spec acceptance scenarios
Implement browser-based e2e tests covering:
- Tests 1-5: Stats progression (cohort, feed, eggs, moves, backdating)
- Test 6: Event viewing and deletion UI
- Test 7: Harvest outcomes with yield items
- Test 8: Optimistic lock selection validation

Includes page objects for reusable form interactions and fresh_db
fixtures for tests requiring isolated database state.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 17:30:26 +00:00

135 lines
4.5 KiB
Python

# ABOUTME: Page object for animal move page with selection handling.
# ABOUTME: Encapsulates navigation, filter, selection, and optimistic lock handling.
from playwright.sync_api import Page, expect
class MovePage:
"""Page object for animal move page."""
def __init__(self, page: Page, base_url: str):
self.page = page
self.base_url = base_url
def goto_move_page(self, filter_str: str = ""):
"""Navigate to the move animals page.
Args:
filter_str: Optional filter DSL query to pre-populate
"""
url = f"{self.base_url}/move"
if filter_str:
url += f"?filter={filter_str}"
self.page.goto(url)
expect(self.page.locator("body")).to_be_visible()
def set_filter(self, filter_str: str):
"""Set the filter field and wait for selection preview.
Args:
filter_str: Filter DSL query (e.g., 'location:"Strip 1"')
"""
self.page.fill("#filter", filter_str)
# Trigger change event and wait for HTMX preview
self.page.keyboard.press("Tab")
# Wait for selection container to update
self.page.wait_for_selector("#selection-container", state="visible")
self.page.wait_for_load_state("networkidle")
def get_selection_count(self) -> int:
"""Get the count of selected animals from the preview.
Returns number of animals in selection, or 0 if not found.
"""
container = self.page.locator("#selection-container")
if container.count() == 0:
return 0
# Try to find count text (e.g., "5 animals selected")
text = container.text_content() or ""
import re
match = re.search(r"(\d+)\s*animal", text.lower())
if match:
return int(match.group(1))
# Count checkboxes if present
checkboxes = container.locator('input[type="checkbox"]')
return checkboxes.count()
def move_to_location(self, destination_name: str, notes: str = ""):
"""Select destination and submit move.
Args:
destination_name: Human-readable location name
notes: Optional notes
"""
self.page.select_option("#to_location_id", label=destination_name)
if notes:
self.page.fill("#notes", notes)
self.page.click('button[type="submit"]')
self.page.wait_for_load_state("networkidle")
def move_animals(
self,
*,
filter_str: str,
destination_name: str,
notes: str = "",
):
"""Complete move flow: set filter, select destination, submit.
Args:
filter_str: Filter DSL query
destination_name: Human-readable destination location
notes: Optional notes
"""
self.goto_move_page()
self.set_filter(filter_str)
self.move_to_location(destination_name, notes)
def has_mismatch_error(self) -> bool:
"""Check if a selection mismatch (409) error is displayed."""
# Look for mismatch/conflict panel indicators
body_text = self.page.locator("body").text_content() or ""
return any(
indicator in body_text.lower()
for indicator in ["mismatch", "conflict", "changed", "removed", "added"]
)
def get_mismatch_diff(self) -> dict:
"""Get the diff information from a mismatch panel.
Returns dict with removed/added counts if mismatch found.
"""
# This depends on actual UI structure of mismatch panel
return {}
def confirm_mismatch(self):
"""Click confirm button to proceed despite mismatch."""
# Look for confirm button - text varies
confirm_btn = self.page.locator('button:has-text("Confirm")')
if confirm_btn.count() > 0:
confirm_btn.click()
self.page.wait_for_load_state("networkidle")
return
# Try alternative selectors
confirm_btn = self.page.locator('button:has-text("Proceed")')
if confirm_btn.count() > 0:
confirm_btn.click()
self.page.wait_for_load_state("networkidle")
def select_specific_animals(self, animal_ids: list[str]):
"""Select specific animals from checkbox list.
Args:
animal_ids: List of animal IDs to select
"""
for animal_id in animal_ids:
checkbox = self.page.locator(f'input[type="checkbox"][value="{animal_id}"]')
if checkbox.count() > 0:
checkbox.check()