diff --git a/scripts/fill_redefine_cart.py b/scripts/fill_redefine_cart.py
index 4983fbb..3ff81f3 100644
--- a/scripts/fill_redefine_cart.py
+++ b/scripts/fill_redefine_cart.py
@@ -145,7 +145,8 @@ def _fill_page(page, plan: FillPlan, menu_url: str) -> int:
return 1
posted = []
- for item in plan.adds:
+ page_closed = False
+ for index, item in enumerate(plan.adds):
try:
_cart_request(
page,
@@ -161,27 +162,37 @@ def _fill_page(page, plan: FillPlan, menu_url: str) -> int:
failed.append(f"{item.name}: {exc}")
print(f"Failed: {item.name}: {exc}")
continue
- posted.append(item)
-
- try:
- current = cart_lines(_cart_request(page, "GET", "/api/cart"))
- except RuntimeError as exc:
- failed.append(f"Could not read the cart: {exc}")
- print(f"Failed: Could not read the cart: {exc}")
- else:
- for item in posted:
- found = quantity_for(current, item.uuid)
- if found != item.quantity:
- message = (
- f"{item.name}: requested {item.quantity}, cart has {found}"
- )
+ except PlaywrightError as exc:
+ failed.append(f"{item.name}: {exc}")
+ print(f"Failed: {item.name}: {exc}")
+ for rest in plan.adds[index + 1 :]:
+ message = f"{rest.name}: not attempted"
failed.append(message)
print(f"Failed: {message}")
- else:
- added += 1
- origin = _origin(page.url or menu_url)
- page.goto(f"{origin}/cart", wait_until="domcontentloaded", timeout=60000)
- ready = True
+ page_closed = True
+ break
+ posted.append(item)
+
+ if not page_closed:
+ try:
+ current = cart_lines(_cart_request(page, "GET", "/api/cart"))
+ except RuntimeError as exc:
+ failed.append(f"Could not read the cart: {exc}")
+ print(f"Failed: Could not read the cart: {exc}")
+ else:
+ for item in posted:
+ found = quantity_for(current, item.uuid)
+ if found != item.quantity:
+ message = (
+ f"{item.name}: requested {item.quantity}, cart has {found}"
+ )
+ failed.append(message)
+ print(f"Failed: {message}")
+ else:
+ added += 1
+ origin = _origin(page.url or menu_url)
+ page.goto(f"{origin}/cart", wait_until="domcontentloaded", timeout=60000)
+ ready = True
except (RuntimeError, PlaywrightError) as exc:
failed.append(str(exc))
print(f"Failed: {exc}")
diff --git a/src/scraper/parse_menu.py b/src/scraper/parse_menu.py
index 6b905cb..5ee1f6e 100644
--- a/src/scraper/parse_menu.py
+++ b/src/scraper/parse_menu.py
@@ -49,6 +49,8 @@ def extract_catalog_products(page: str) -> list:
products = _extract_json_attr(page, PRODUCTS_MARKER)
if not isinstance(products, list):
raise MenuParseError(":products must be a JSON array")
+ if not products:
+ raise MenuParseError("Menu catalog is empty")
return products
diff --git a/tests/test_fill_cart.py b/tests/test_fill_cart.py
index ac8011a..c19e0ec 100644
--- a/tests/test_fill_cart.py
+++ b/tests/test_fill_cart.py
@@ -205,6 +205,29 @@ def test_fill_page_prints_a_summary_when_the_window_closes_mid_add(capsys):
assert "Cart is ready" not in output
+def test_fill_page_counts_items_left_when_the_window_closes(capsys):
+ plan = build_plan(
+ "Item,Quantity\nKorean Steak Bowl,1\nSale Bowl,2\n",
+ PRODUCTS,
+ )
+ page = _CartPage(
+ [
+ {"ok": True, "status": 200, "data": {"items": []}},
+ PlaywrightError("Target page, context or browser has been closed"),
+ ]
+ )
+
+ code = fill_redefine_cart._fill_page(
+ page, plan, "https://www.redefinemeals.com/menu"
+ )
+ output = capsys.readouterr().out
+
+ assert code == 1
+ assert "Korean Steak Bowl:" in output
+ assert "Sale Bowl: not attempted" in output
+ assert "Added 0. Failed 2. Skipped 0." in output
+
+
def test_fill_page_prints_a_summary_when_the_first_cart_read_fails(capsys):
plan = build_plan("Item,Quantity\nKorean Steak Bowl,1\n", PRODUCTS)
page = _CartPage(
diff --git a/tests/test_parse_menu.py b/tests/test_parse_menu.py
index a06ad61..2256f33 100644
--- a/tests/test_parse_menu.py
+++ b/tests/test_parse_menu.py
@@ -4,7 +4,7 @@ from pathlib import Path
import pytest
-from scraper.parse_menu import MenuParseError, parse_menu_html
+from scraper.parse_menu import MenuParseError, extract_catalog_products, parse_menu_html
FIXTURE = Path(__file__).resolve().parent / "fixtures" / "menu_page.html"
@@ -43,6 +43,12 @@ def test_parse_menu_html_rejects_a_missing_catalog():
parse_menu_html("", menu_url="https://example.com/menu")
+def test_extract_catalog_products_rejects_an_empty_catalog():
+ page = ""
+ with pytest.raises(MenuParseError, match="empty"):
+ extract_catalog_products(page)
+
+
def test_parse_menu_html_rejects_an_empty_catalog():
page = ""
with pytest.raises(MenuParseError, match="empty"):