mirror of
https://github.com/Sea-Haven-Industries/meal-order-manager.git
synced 2026-09-30 05:23:13 +00:00
* feat(cart): add a local Redefine cart filler Match the admin order-list CSV to the live menu and add each meal to a guest cart so the weekly bulk order does not have to be typed in by hand. * fix(cart): report cart failures without a traceback A closed window or a failed cart request during the fill now prints a short failure and the Added/Failed/Skipped summary instead of crashing. * fix(cart): fail cleanly on a bad CSV and a closed browser A non-UTF-8 order list and a Playwright error while opening the page now print a short message and exit 1 instead of a traceback. * fix(cart): account for a closed window and an empty menu A closed page during add marks the remaining meals as not attempted, and an empty menu catalog raises the same error as the menu parser.
66 lines
2.2 KiB
Python
66 lines
2.2 KiB
Python
"""Parser for the catalog embedded on the Redefine menu page."""
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from scraper.parse_menu import MenuParseError, extract_catalog_products, parse_menu_html
|
|
|
|
FIXTURE = Path(__file__).resolve().parent / "fixtures" / "menu_page.html"
|
|
|
|
|
|
def test_parse_menu_html_maps_catalog_fields():
|
|
page = FIXTURE.read_text()
|
|
menu = parse_menu_html(
|
|
page,
|
|
menu_url="https://www.redefinemeals.com/menu",
|
|
scraped_at="2026-09-21T07:30:00-04:00",
|
|
)
|
|
|
|
assert menu["meal_count"] == 2
|
|
assert menu["scraped_at"] == "2026-09-21T07:30:00-04:00"
|
|
korean, sale = menu["meals"]
|
|
|
|
assert korean["name"] == "Korean Steak Bowl"
|
|
assert korean["price"] == 12.49
|
|
assert korean["calories"] == 590
|
|
assert korean["protein"] == "50g"
|
|
assert korean["dietary_tags"] == ["Gluten Free", "Dairy Free"]
|
|
assert korean["image_url"] == "https://example.com/korean.png"
|
|
assert korean["is_new"] is True
|
|
assert korean["description"] == "Shaved ribeye & rice."
|
|
|
|
assert sale["name"] == "Sale Bowl"
|
|
assert sale["price"] == 9.50
|
|
assert sale["calories"] == 400
|
|
assert sale["protein"] == "30g"
|
|
assert sale["is_new"] is False
|
|
assert sale["description"] == "On sale."
|
|
|
|
|
|
def test_parse_menu_html_rejects_a_missing_catalog():
|
|
with pytest.raises(MenuParseError, match="missing :products"):
|
|
parse_menu_html("<html></html>", menu_url="https://example.com/menu")
|
|
|
|
|
|
def test_extract_catalog_products_rejects_an_empty_catalog():
|
|
page = "<orders-page :products='[]' :newest-ids='[]'></orders-page>"
|
|
with pytest.raises(MenuParseError, match="empty"):
|
|
extract_catalog_products(page)
|
|
|
|
|
|
def test_parse_menu_html_rejects_an_empty_catalog():
|
|
page = "<orders-page :products='[]' :newest-ids='[]'></orders-page>"
|
|
with pytest.raises(MenuParseError, match="empty"):
|
|
parse_menu_html(page, menu_url="https://example.com/menu")
|
|
|
|
|
|
def test_parse_menu_html_rejects_a_meal_without_a_price():
|
|
page = """
|
|
<orders-page
|
|
:products='[{"id":1,"name":"Broken","price":null,"on_sale":false,"available":true}]'
|
|
:newest-ids='[]'
|
|
></orders-page>
|
|
"""
|
|
with pytest.raises(MenuParseError, match="missing a price"):
|
|
parse_menu_html(page, menu_url="https://example.com/menu")
|