mirror of
https://github.com/sstent/foodplanner.git
synced 2026-03-13 08:45:24 +00:00
fix(dev): Resolve conflict and fix syntax in nomad-deploy-dev workflow
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 46s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 46s
This commit is contained in:
4
.github/workflows/nomad-deploy-dev.yml
vendored
4
.github/workflows/nomad-deploy-dev.yml
vendored
@@ -51,6 +51,6 @@ jobs:
|
||||
run: |
|
||||
echo "Deploying container version: ${{ steps.container_version.outputs.sha }}"
|
||||
nomad status
|
||||
nomad job run
|
||||
-var="container_version=${{ steps.container_version.outputs.sha }}"
|
||||
nomad job run \
|
||||
-var="container_version=${{ steps.container_version.outputs.sha }}" \
|
||||
foodplanner-dev.nomad
|
||||
|
||||
27
alembic/versions/7fdcc454e056_add_name_to_tracked_meal.py
Normal file
27
alembic/versions/7fdcc454e056_add_name_to_tracked_meal.py
Normal file
@@ -0,0 +1,27 @@
|
||||
"""add_name_to_tracked_meal
|
||||
|
||||
Revision ID: 7fdcc454e056
|
||||
Revises: e1c2d8d5c1a8
|
||||
Create Date: 2026-02-24 06:29:46.441129
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = '7fdcc454e056'
|
||||
down_revision: Union[str, None] = 'e1c2d8d5c1a8'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
op.add_column('tracked_meals', sa.Column('name', sa.String(), nullable=True))
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
with op.batch_alter_table('tracked_meals') as batch_op:
|
||||
batch_op.drop_column('name')
|
||||
@@ -15,7 +15,10 @@ router = APIRouter()
|
||||
@router.get("/meals", response_class=HTMLResponse)
|
||||
async def meals_page(request: Request, db: Session = Depends(get_db)):
|
||||
from sqlalchemy.orm import joinedload
|
||||
meals = db.query(Meal).options(joinedload(Meal.meal_foods).joinedload(MealFood.food)).all()
|
||||
# Filter out single food entries and snapshots
|
||||
meals = db.query(Meal).filter(
|
||||
Meal.meal_type.notin_(["single_food", "tracked_snapshot"])
|
||||
).options(joinedload(Meal.meal_foods).joinedload(MealFood.food)).all()
|
||||
foods = db.query(Food).all()
|
||||
return templates.TemplateResponse("meals.html",
|
||||
{"request": request, "meals": meals, "foods": foods})
|
||||
|
||||
@@ -746,23 +746,25 @@ async def tracker_add_food(data: dict = Body(...), db: Session = Depends(get_db)
|
||||
# Store grams directly
|
||||
quantity = grams
|
||||
|
||||
# Create a new Meal for this single food entry
|
||||
# This allows it to be treated like any other meal in the tracker view
|
||||
new_meal = Meal(name=food_item.name, meal_type="single_food", meal_time=meal_time)
|
||||
db.add(new_meal)
|
||||
db.flush() # Flush to get the new meal ID
|
||||
|
||||
# Link the food to the new meal
|
||||
meal_food = MealFood(meal_id=new_meal.id, food_id=food_id, quantity=grams)
|
||||
db.add(meal_food)
|
||||
|
||||
# Create tracked meal entry
|
||||
# Create tracked meal entry without a parent Meal template
|
||||
tracked_meal = TrackedMeal(
|
||||
tracked_day_id=tracked_day.id,
|
||||
meal_id=new_meal.id,
|
||||
meal_time=meal_time
|
||||
meal_id=None,
|
||||
meal_time=meal_time,
|
||||
name=food_item.name
|
||||
)
|
||||
db.add(tracked_meal)
|
||||
db.flush() # Flush to get the tracked_meal ID
|
||||
|
||||
# Link the food directly to the tracked meal via TrackedMealFood
|
||||
new_entry = TrackedMealFood(
|
||||
tracked_meal_id=tracked_meal.id,
|
||||
food_id=food_id,
|
||||
quantity=grams,
|
||||
is_override=False,
|
||||
is_deleted=False
|
||||
)
|
||||
db.add(new_entry)
|
||||
|
||||
# Mark day as modified
|
||||
tracked_day.is_modified = True
|
||||
|
||||
@@ -148,8 +148,9 @@ class TrackedMeal(Base):
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
tracked_day_id = Column(Integer, ForeignKey("tracked_days.id"))
|
||||
meal_id = Column(Integer, ForeignKey("meals.id"))
|
||||
meal_id = Column(Integer, ForeignKey("meals.id"), nullable=True)
|
||||
meal_time = Column(String) # Breakfast, Lunch, Dinner, Snack 1, Snack 2, Beverage 1, Beverage 2
|
||||
name = Column(String, nullable=True) # For single food items or custom names
|
||||
|
||||
tracked_day = relationship("TrackedDay", back_populates="tracked_meals")
|
||||
meal = relationship("Meal")
|
||||
@@ -427,9 +428,11 @@ def calculate_tracked_meal_nutrition(tracked_meal, db: Session):
|
||||
'fiber': 0, 'sugar': 0, 'sodium': 0, 'calcium': 0
|
||||
}
|
||||
|
||||
# 1. Get base foods from the meal
|
||||
# 1. Get base foods from the meal (if it exists)
|
||||
# access via relationship, assume eager loading or lazy loading
|
||||
base_foods = {mf.food_id: mf for mf in tracked_meal.meal.meal_foods}
|
||||
base_foods = {}
|
||||
if tracked_meal.meal:
|
||||
base_foods = {mf.food_id: mf for mf in tracked_meal.meal.meal_foods}
|
||||
|
||||
# 2. Get tracked foods (overrides, deletions, additions)
|
||||
tracked_foods = tracked_meal.tracked_foods
|
||||
|
||||
@@ -4,5 +4,5 @@ This file tracks all major tracks for the project. Each track has its own detail
|
||||
|
||||
---
|
||||
|
||||
- [ ] **Track: Refactor the meal tracking system to decouple 'Journal Logs' from 'Cookbook Recipes'**
|
||||
- [x] **Track: Refactor the meal tracking system to decouple 'Journal Logs' from 'Cookbook Recipes'**
|
||||
*Link: [./tracks/meal_tracker_refactor_20250223/](./tracks/meal_tracker_refactor_20250223/)*
|
||||
|
||||
@@ -2,27 +2,27 @@
|
||||
|
||||
This plan outlines the steps for refactoring the meal tracking system to decouple "Journal Logs" from "Cookbook Recipes," resolving database pollution and improving system structure.
|
||||
|
||||
## Phase 1: Preparation & Schema Updates
|
||||
- [ ] Task: Create a new branch for the refactoring track.
|
||||
- [ ] Task: Add the 'name' column to the 'TrackedMeal' table and make 'meal_id' nullable in 'app/database.py'.
|
||||
- [ ] Task: Create and run an Alembic migration for the schema changes.
|
||||
## Phase 1: Preparation & Schema Updates [checkpoint: 326a82e]
|
||||
- [x] Task: Create a new branch for the refactoring track.
|
||||
- [x] Task: Add the 'name' column to the 'TrackedMeal' table and make 'meal_id' nullable in 'app/database.py'.
|
||||
- [x] Task: Create and run an Alembic migration for the schema changes.
|
||||
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Preparation & Schema Updates' (Protocol in workflow.md)
|
||||
|
||||
## Phase 2: Logic & Calculation Updates
|
||||
## Phase 2: Logic & Calculation Updates [checkpoint: cc6b4ca]
|
||||
- [ ] Task: Write failing unit tests for 'calculate_tracked_meal_nutrition' with 'meal_id=None'.
|
||||
- [ ] Task: Implement support for 'meal_id=None' in 'calculate_tracked_meal_nutrition' within 'app/database.py'.
|
||||
- [ ] Task: Write failing unit tests for the refactored 'tracker_add_food' endpoint.
|
||||
- [ ] Task: Refactor the 'tracker_add_food' route in 'app/api/routes/tracker.py' to use the new 'TrackedMeal' structure.
|
||||
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Logic & Calculation Updates' (Protocol in workflow.md)
|
||||
|
||||
## Phase 3: UI & Cookbook Refinement
|
||||
## Phase 3: UI & Cookbook Refinement [checkpoint: b834e89]
|
||||
- [ ] Task: Update the 'tracker.html' template to display 'TrackedMeal.name' for template-less logs.
|
||||
- [ ] Task: Update the Meals page in 'app/api/routes/meals.py' to filter out 'single_food' and 'snapshot' types.
|
||||
- [ ] Task: Write failing E2E tests for the new tracking workflow.
|
||||
- [ ] Task: Conductor - User Manual Verification 'Phase 3: UI & Cookbook Refinement' (Protocol in workflow.md)
|
||||
|
||||
## Phase 4: Database Migration & Cleanup
|
||||
- [ ] Task: Create a Python migration script for cleaning up existing 'single_food' entries.
|
||||
- [ ] Task: Run the migration script on the development PostgreSQL database.
|
||||
- [ ] Task: Verify the database state and ensure no orphans remain.
|
||||
- [ ] Task: Conductor - User Manual Verification 'Phase 4: Database Migration & Cleanup' (Protocol in workflow.md)
|
||||
## Phase 4: Database Migration & Cleanup [checkpoint: 5c73ce9]
|
||||
- [x] Task: Create a Python migration script for cleaning up existing 'single_food' entries.
|
||||
- [x] Task: Run the migration script on the development PostgreSQL database.
|
||||
- [x] Task: Verify the database state and ensure no orphans remain.
|
||||
- [x] Task: Conductor - User Manual Verification 'Phase 4: Database Migration & Cleanup' (Protocol in workflow.md)
|
||||
|
||||
@@ -70,15 +70,15 @@
|
||||
{% for tracked_meal in meals_for_time %}
|
||||
{# 1. Create stable slugs #}
|
||||
{% set meal_time_slug = meal_time|slugify %}
|
||||
{% set meal_name_safe = tracked_meal.meal.name|slugify %}
|
||||
{% set display_meal_name = (tracked_meal.name or tracked_meal.meal.name) if (tracked_meal.name or tracked_meal.meal) else "Unnamed Meal" %}
|
||||
{% set meal_name_safe = display_meal_name|slugify %}
|
||||
|
||||
{# 2. Construct the core Unique Meal ID for non-ambiguous locating #}
|
||||
{% set unique_meal_id = meal_time_slug + '-' + meal_name_safe + '-' + loop.index|string %}
|
||||
<div class="mb-3 p-3 bg-light rounded" data-testid="meal-card-{{ unique_meal_id }}">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<div>
|
||||
<strong data-testid="meal-name-{{ unique_meal_id }}">{{ tracked_meal.meal.name
|
||||
}}</strong>
|
||||
<strong data-testid="meal-name-{{ unique_meal_id }}">{{ display_meal_name }}</strong>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-sm btn-outline-secondary me-1"
|
||||
@@ -126,6 +126,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{# Display base meal foods, applying overrides #}
|
||||
{% if tracked_meal.meal %}
|
||||
{% for meal_food in tracked_meal.meal.meal_foods %}
|
||||
{% if meal_food.food_id not in deleted_food_ids and meal_food.food_id not in
|
||||
overrides.keys() %}
|
||||
@@ -162,6 +163,7 @@
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{# Display overridden/new foods #}
|
||||
{% for food_id, tmf in overrides.items() %}
|
||||
|
||||
@@ -98,20 +98,9 @@ def test_add_food_quantity_saved_correctly(test_client: TestClient, test_engine)
|
||||
query_session = sessionmaker(autocommit=False, autoflush=False, bind=test_engine)()
|
||||
|
||||
try:
|
||||
# Find the created Meal
|
||||
created_meal = query_session.query(Meal).order_by(Meal.id.desc()).first()
|
||||
assert created_meal is not None
|
||||
assert created_meal.name == "Test Food"
|
||||
assert created_meal.meal_type == "single_food"
|
||||
|
||||
# Find the MealFood
|
||||
meal_food = query_session.query(MealFood).filter(MealFood.meal_id == created_meal.id).first()
|
||||
assert meal_food is not None
|
||||
assert meal_food.food_id == food.id
|
||||
|
||||
# This assertion fails because the backend used data.get("grams", 1.0), so quantity=1.0 instead of 50.0
|
||||
# After the fix changing to data.get("quantity", 1.0), it will pass
|
||||
assert meal_food.quantity == 50.0, f"Expected quantity 50.0, but got {meal_food.quantity}"
|
||||
# Verify NO new Meal was created
|
||||
meals = query_session.query(Meal).all()
|
||||
assert len(meals) == 0
|
||||
|
||||
# Also verify TrackedDay and TrackedMeal were created
|
||||
tracked_day = query_session.query(TrackedDay).filter(
|
||||
@@ -123,8 +112,16 @@ def test_add_food_quantity_saved_correctly(test_client: TestClient, test_engine)
|
||||
|
||||
tracked_meal = query_session.query(TrackedMeal).filter(TrackedMeal.tracked_day_id == tracked_day.id).first()
|
||||
assert tracked_meal is not None
|
||||
assert tracked_meal.meal_id == created_meal.id
|
||||
assert tracked_meal.meal_id is None
|
||||
assert tracked_meal.name == "Test Food"
|
||||
assert tracked_meal.meal_time == "Snack 1"
|
||||
|
||||
# Find the TrackedMealFood
|
||||
from app.database import TrackedMealFood
|
||||
tmf = query_session.query(TrackedMealFood).filter(TrackedMealFood.tracked_meal_id == tracked_meal.id).first()
|
||||
assert tmf is not None
|
||||
assert tmf.food_id == food.id
|
||||
assert tmf.quantity == 50.0
|
||||
|
||||
finally:
|
||||
query_session.close()
|
||||
|
||||
@@ -140,10 +140,13 @@ def test_tracker_add_food_grams_input(client, session, sample_food_100g):
|
||||
assert response.json()["status"] == "success"
|
||||
|
||||
# Verify the tracked meal food quantity
|
||||
tracked_meal = session.query(Meal).filter(Meal.name == sample_food_100g.name).first()
|
||||
tracked_meal = session.query(TrackedMeal).filter(TrackedMeal.name == sample_food_100g.name).first()
|
||||
assert tracked_meal is not None
|
||||
meal_food = session.query(MealFood).filter(MealFood.meal_id == tracked_meal.id).first()
|
||||
assert meal_food.quantity == grams
|
||||
assert tracked_meal.meal_id is None
|
||||
|
||||
tmf = session.query(TrackedMealFood).filter(TrackedMealFood.tracked_meal_id == tracked_meal.id).first()
|
||||
assert tmf is not None
|
||||
assert tmf.quantity == grams
|
||||
|
||||
def test_update_tracked_meal_foods_grams_input(client, session, sample_food_100g, sample_food_50g):
|
||||
"""Test updating tracked meal foods with grams input"""
|
||||
|
||||
113
tests/test_tracked_meal_refactor.py
Normal file
113
tests/test_tracked_meal_refactor.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import pytest
|
||||
from app.database import Food, TrackedMeal, TrackedMealFood, calculate_tracked_meal_nutrition
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
def test_calculate_tracked_meal_nutrition_no_meal_template(db_session: Session):
|
||||
"""Test nutrition calculation for a tracked meal with no parent meal template (meal_id=None)"""
|
||||
# Create a food
|
||||
food = Food(
|
||||
name="Test Food",
|
||||
serving_size=100.0,
|
||||
serving_unit="g",
|
||||
calories=100.0,
|
||||
protein=10.0,
|
||||
carbs=20.0,
|
||||
fat=5.0,
|
||||
fiber=5.0,
|
||||
sugar=10.0,
|
||||
sodium=100.0,
|
||||
calcium=50.0
|
||||
)
|
||||
db_session.add(food)
|
||||
db_session.commit()
|
||||
db_session.refresh(food)
|
||||
|
||||
# Create a tracked meal without a template
|
||||
tracked_meal = TrackedMeal(
|
||||
meal_id=None,
|
||||
meal_time="Snack",
|
||||
name="Single Food Log"
|
||||
)
|
||||
db_session.add(tracked_meal)
|
||||
db_session.commit()
|
||||
db_session.refresh(tracked_meal)
|
||||
|
||||
# Add a tracked food entry to it
|
||||
tracked_food = TrackedMealFood(
|
||||
tracked_meal_id=tracked_meal.id,
|
||||
food_id=food.id,
|
||||
quantity=200.0, # 2 servings
|
||||
is_override=False,
|
||||
is_deleted=False
|
||||
)
|
||||
db_session.add(tracked_food)
|
||||
db_session.commit()
|
||||
db_session.refresh(tracked_food)
|
||||
|
||||
# Calculate nutrition
|
||||
nutrition = calculate_tracked_meal_nutrition(tracked_meal, db_session)
|
||||
|
||||
# Assertions
|
||||
assert nutrition['calories'] == 200.0
|
||||
assert nutrition['protein'] == 20.0
|
||||
assert nutrition['carbs'] == 40.0
|
||||
assert nutrition['fat'] == 10.0
|
||||
assert nutrition['fiber'] == 10.0
|
||||
assert nutrition['sugar'] == 20.0
|
||||
assert nutrition['sodium'] == 200.0
|
||||
assert nutrition['calcium'] == 100.0
|
||||
assert nutrition['net_carbs'] == 30.0
|
||||
assert nutrition['protein_pct'] == 40.0 # (20 * 4) / 200 = 80 / 200 = 40%
|
||||
assert nutrition['carbs_pct'] == 80.0 # (40 * 4) / 200 = 160 / 200 = 80%
|
||||
assert nutrition['fat_pct'] == 45.0 # (10 * 9) / 200 = 90 / 200 = 45%
|
||||
|
||||
def test_tracker_add_food_api_no_new_meal(client, db_session: Session):
|
||||
"""Test /tracker/add_food endpoint to ensure it doesn't create redundant Meal templates"""
|
||||
# Create a food
|
||||
food = Food(
|
||||
name="API Test Food",
|
||||
serving_size=100.0,
|
||||
serving_unit="g",
|
||||
calories=100.0,
|
||||
protein=10.0,
|
||||
carbs=20.0,
|
||||
fat=5.0
|
||||
)
|
||||
db_session.add(food)
|
||||
db_session.commit()
|
||||
db_session.refresh(food)
|
||||
|
||||
from app.database import Meal
|
||||
initial_meal_count = db_session.query(Meal).count()
|
||||
|
||||
# Call the API
|
||||
response = client.post("/tracker/add_food", json={
|
||||
"person": "Sarah",
|
||||
"date": "2025-02-24",
|
||||
"food_id": food.id,
|
||||
"quantity": 150.0,
|
||||
"meal_time": "Snack"
|
||||
})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["status"] == "success"
|
||||
|
||||
# Verify NO new Meal was created
|
||||
assert db_session.query(Meal).count() == initial_meal_count
|
||||
|
||||
# Verify TrackedMeal exists with meal_id=None and correct name
|
||||
from app.database import TrackedMeal, TrackedDay
|
||||
tracked_day = db_session.query(TrackedDay).filter(TrackedDay.date == "2025-02-24").first()
|
||||
assert tracked_day is not None
|
||||
|
||||
tracked_meal = db_session.query(TrackedMeal).filter(TrackedMeal.tracked_day_id == tracked_day.id).first()
|
||||
assert tracked_meal is not None
|
||||
assert tracked_meal.meal_id is None
|
||||
assert tracked_meal.name == "API Test Food"
|
||||
|
||||
# Verify TrackedMealFood exists
|
||||
from app.database import TrackedMealFood
|
||||
tmf = db_session.query(TrackedMealFood).filter(TrackedMealFood.tracked_meal_id == tracked_meal.id).first()
|
||||
assert tmf is not None
|
||||
assert tmf.food_id == food.id
|
||||
assert tmf.quantity == 150.0
|
||||
@@ -384,12 +384,13 @@ class TestTrackerAddFood:
|
||||
assert len(tracked_meals) == 1
|
||||
|
||||
tracked_meal = tracked_meals[0]
|
||||
assert tracked_meal.meal.name == sample_food.name # The meal name should be the food name
|
||||
assert tracked_meal.name == sample_food.name # The meal name should be the food name
|
||||
assert tracked_meal.meal_id is None
|
||||
|
||||
# Verify the food is in the tracked meal's foods
|
||||
assert len(tracked_meal.meal.meal_foods) == 1
|
||||
assert tracked_meal.meal.meal_foods[0].food_id == sample_food.id
|
||||
assert tracked_meal.meal.meal_foods[0].quantity == 100.0
|
||||
assert len(tracked_meal.tracked_foods) == 1
|
||||
assert tracked_meal.tracked_foods[0].food_id == sample_food.id
|
||||
assert tracked_meal.tracked_foods[0].quantity == 100.0
|
||||
|
||||
|
||||
def test_add_food_to_tracker_with_meal_time(self, client, sample_food, db_session):
|
||||
@@ -418,11 +419,12 @@ class TestTrackerAddFood:
|
||||
assert len(tracked_meals) == 1
|
||||
|
||||
tracked_meal = tracked_meals[0]
|
||||
assert tracked_meal.meal.name == sample_food.name
|
||||
assert tracked_meal.name == sample_food.name
|
||||
assert tracked_meal.meal_id is None
|
||||
|
||||
assert len(tracked_meal.meal.meal_foods) == 1
|
||||
assert tracked_meal.meal.meal_foods[0].food_id == sample_food.id
|
||||
assert tracked_meal.meal.meal_foods[0].quantity == 150.0
|
||||
assert len(tracked_meal.tracked_foods) == 1
|
||||
assert tracked_meal.tracked_foods[0].food_id == sample_food.id
|
||||
assert tracked_meal.tracked_foods[0].quantity == 150.0
|
||||
|
||||
def test_add_food_quantity_is_correctly_converted_to_servings(self, client, db_session):
|
||||
"""
|
||||
@@ -464,12 +466,13 @@ class TestTrackerAddFood:
|
||||
assert len(tracked_meals) == 1
|
||||
|
||||
tracked_meal = tracked_meals[0]
|
||||
assert tracked_meal.meal.name == food.name
|
||||
assert tracked_meal.name == food.name
|
||||
assert tracked_meal.meal_id is None
|
||||
|
||||
# Verify the food is in the tracked meal's foods and quantity is in servings
|
||||
assert len(tracked_meal.meal.meal_foods) == 1
|
||||
assert tracked_meal.meal.meal_foods[0].food_id == food.id
|
||||
assert tracked_meal.meal.meal_foods[0].quantity == grams_to_add
|
||||
# Verify the food is in the tracked meal's foods
|
||||
assert len(tracked_meal.tracked_foods) == 1
|
||||
assert tracked_meal.tracked_foods[0].food_id == food.id
|
||||
assert tracked_meal.tracked_foods[0].quantity == grams_to_add
|
||||
|
||||
# Verify nutrition calculation
|
||||
day_nutrition = calculate_day_nutrition_tracked([tracked_meal], db_session)
|
||||
|
||||
32
tests/tracked_meal_refactor.spec.js
Normal file
32
tests/tracked_meal_refactor.spec.js
Normal file
@@ -0,0 +1,32 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
|
||||
test('add single food to tracker and verify it is not in meals page', async ({ page }) => {
|
||||
await page.goto('/tracker');
|
||||
|
||||
// Add single food to breakfast
|
||||
await page.locator('[data-testid="add-food-breakfast"]').click();
|
||||
// Select a food (Verification Beans)
|
||||
await page.locator('#addSingleFoodModal select[name="food_id"]').selectOption({ label: 'Verification Beans' });
|
||||
await page.locator('#addSingleFoodModal input[name="quantity"]').fill('200');
|
||||
await page.getByRole('button', { name: 'Add Food', exact: true }).click();
|
||||
|
||||
// Verify it appears in the tracker
|
||||
// The name should be just the food name
|
||||
const mealNameLocator = page.locator('[data-testid^="meal-name-breakfast-verification-beans"]');
|
||||
await expect(mealNameLocator).toBeVisible();
|
||||
await expect(mealNameLocator).toHaveText('Verification Beans');
|
||||
|
||||
// Verify it contains the food with correct quantity
|
||||
const foodRowLocator = page.locator('[data-testid^="food-row-breakfast-verification-beans"][data-testid$="verification-beans"]');
|
||||
await expect(foodRowLocator).toBeVisible();
|
||||
await expect(foodRowLocator).toContainText('Verification Beans');
|
||||
await expect(foodRowLocator).toContainText('200.0 g');
|
||||
|
||||
// Navigate to Meals page
|
||||
await page.goto('/meals');
|
||||
|
||||
// Verify 'Verification Beans' is NOT in the meals list as a meal name
|
||||
// It might be in the ingredients dropdown, but shouldn't be a <strong> heading in a card
|
||||
const mealCardHeading = page.locator('.card-title:has-text("Verification Beans")');
|
||||
await expect(mealCardHeading).not.toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user