All checks were successful
Deploy / deploy (push) Successful in 1m49s
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>
101 lines
3.2 KiB
Python
101 lines
3.2 KiB
Python
# ABOUTME: Page object for feed management pages (purchase, give feed).
|
|
# ABOUTME: Encapsulates navigation and form interactions for feed operations.
|
|
|
|
from playwright.sync_api import Page, expect
|
|
|
|
|
|
class FeedPage:
|
|
"""Page object for feed management pages."""
|
|
|
|
def __init__(self, page: Page, base_url: str):
|
|
self.page = page
|
|
self.base_url = base_url
|
|
|
|
def goto_feed_page(self):
|
|
"""Navigate to the feed quick capture page."""
|
|
self.page.goto(f"{self.base_url}/feed")
|
|
expect(self.page.locator("body")).to_be_visible()
|
|
|
|
def purchase_feed(
|
|
self,
|
|
*,
|
|
feed_type: str = "layer",
|
|
bag_size_kg: int,
|
|
bags_count: int,
|
|
bag_price_euros: float,
|
|
vendor: str = "",
|
|
notes: str = "",
|
|
):
|
|
"""Fill and submit the feed purchase form.
|
|
|
|
Args:
|
|
feed_type: Feed type code (e.g., "layer")
|
|
bag_size_kg: Size of each bag in kg
|
|
bags_count: Number of bags
|
|
bag_price_euros: Price per bag in EUR
|
|
vendor: Optional vendor name
|
|
notes: Optional notes
|
|
"""
|
|
self.goto_feed_page()
|
|
|
|
# The purchase form uses specific IDs
|
|
self.page.select_option("#purchase_feed_type_code", feed_type)
|
|
self.page.fill("#bag_size_kg", str(bag_size_kg))
|
|
self.page.fill("#bags_count", str(bags_count))
|
|
self.page.fill("#bag_price_euros", str(bag_price_euros))
|
|
|
|
if vendor:
|
|
self.page.fill("#vendor", vendor)
|
|
|
|
if notes:
|
|
self.page.fill("#purchase_notes", notes)
|
|
|
|
# Submit the purchase form (second form on page)
|
|
self.page.click('form[hx-post*="feed-purchased"] button[type="submit"]')
|
|
|
|
# Wait for HTMX response
|
|
self.page.wait_for_load_state("networkidle")
|
|
|
|
def give_feed(
|
|
self,
|
|
*,
|
|
location_name: str,
|
|
feed_type: str = "layer",
|
|
amount_kg: int,
|
|
notes: str = "",
|
|
):
|
|
"""Fill and submit the feed given form.
|
|
|
|
Args:
|
|
location_name: Human-readable location name (e.g., "Strip 1")
|
|
feed_type: Feed type code (e.g., "layer")
|
|
amount_kg: Amount of feed in kg
|
|
notes: Optional notes
|
|
"""
|
|
self.goto_feed_page()
|
|
|
|
# The give form uses specific IDs
|
|
self.page.select_option("#location_id", label=location_name)
|
|
self.page.select_option("#feed_type_code", feed_type)
|
|
self.page.fill("#amount_kg", str(amount_kg))
|
|
|
|
if notes:
|
|
self.page.fill("#notes", notes)
|
|
|
|
# Submit the give form (first form on page)
|
|
self.page.click('form[hx-post*="feed-given"] button[type="submit"]')
|
|
|
|
# Wait for HTMX response
|
|
self.page.wait_for_load_state("networkidle")
|
|
|
|
def get_feed_inventory_balance(self, feed_type: str = "layer") -> dict:
|
|
"""Get the current feed inventory from the page stats.
|
|
|
|
Returns dict with purchased_kg, given_kg, balance_kg if visible,
|
|
or empty dict if stats not found.
|
|
"""
|
|
# This depends on how stats are displayed on the page
|
|
# May need to parse text content from stats section
|
|
# For now, return empty - can be enhanced based on actual UI
|
|
return {}
|