# Instructions

- Following Playwright test failed.
- Explain why, be concise, respect Playwright best practices.
- Provide a snippet of code with the fix, if possible.

# Test info

- Name: journal.spec.ts >> user can cook recipe and see stock deducted correctly
- Location: tests/journal.spec.ts:40:5

# Error details

```
Error: expect(locator).toHaveText(expected) failed

Locator: getByTestId('LeftoverLabel-Помидоры')
Expected: "есть"
Timeout: 5000ms
Error: element(s) not found

Call log:
  - Expect "toHaveText" with timeout 5000ms
  - waiting for getByTestId('LeftoverLabel-Помидоры')

```

# Page snapshot

```yaml
- generic [ref=e4]:
  - generic [ref=e6]:
    - banner [ref=e8]:
      - generic [ref=e9]:
        - heading "Есть дома" [level=1] [ref=e10]
        - generic [ref=e11]:
          - button "search" [ref=e12] [cursor=pointer]:
            - img [ref=e13]
          - link "history" [ref=e15] [cursor=pointer]:
            - /url: /leftovers/history
            - img [ref=e16]
    - generic [ref=e21]:
      - list [ref=e22]:
        - listitem [ref=e23]: Соусы и специи
        - listitem [ref=e24]:
          - link "Соль есть 5 секунд назад" [ref=e25] [cursor=pointer]:
            - /url: /ingredients/kljzpwqwhewr8esjo3slyb64/details
            - img [ref=e27]
            - generic [ref=e29]:
              - generic [ref=e30]: Соль
              - paragraph [ref=e31]:
                - generic [ref=e32]:
                  - generic [ref=e34]: есть
                  - generic [ref=e35]:
                    - img [ref=e36]
                    - generic [ref=e38]: 5 секунд назад
          - button "menu" [ref=e40] [cursor=pointer]:
            - img [ref=e41]
        - listitem [ref=e43]:
          - link "Перец чёрный есть 3 секунды назад" [ref=e44] [cursor=pointer]:
            - /url: /ingredients/cey6kje9i08st65pawjfnoxq/details
            - img [ref=e46]
            - generic [ref=e48]:
              - generic [ref=e49]: Перец чёрный
              - paragraph [ref=e50]:
                - generic [ref=e51]:
                  - generic [ref=e53]: есть
                  - generic [ref=e54]:
                    - img [ref=e55]
                    - generic [ref=e57]: 3 секунды назад
          - button "menu" [ref=e59] [cursor=pointer]:
            - img [ref=e60]
      - list [ref=e62]:
        - listitem [ref=e63]: Молочные продукты
        - listitem [ref=e64]:
          - link "Яйцо куриное 2 шт 11 секунд назад" [ref=e65] [cursor=pointer]:
            - /url: /ingredients/dd42iyqm8ygog5xtz76o2qwc/details
            - img [ref=e67]
            - generic [ref=e69]:
              - generic [ref=e70]: Яйцо куриное
              - paragraph [ref=e71]:
                - generic [ref=e72]:
                  - generic [ref=e75]: 2 шт
                  - generic [ref=e76]:
                    - img [ref=e77]
                    - generic [ref=e79]: 11 секунд назад
          - button "menu" [ref=e81] [cursor=pointer]:
            - img [ref=e82]
        - listitem [ref=e84]:
          - link "Молоко 500 мл 7 секунд назад" [ref=e85] [cursor=pointer]:
            - /url: /ingredients/ucth1rxcjo22w6ydthhs1yng/details
            - img [ref=e87]
            - generic [ref=e89]:
              - generic [ref=e90]: Молоко
              - paragraph [ref=e91]:
                - generic [ref=e92]:
                  - generic [ref=e95]: 500 мл
                  - generic [ref=e96]:
                    - img [ref=e97]
                    - generic [ref=e99]: 7 секунд назад
          - button "menu" [ref=e101] [cursor=pointer]:
            - img [ref=e102]
  - navigation [ref=e104]:
    - button "Рецепты" [ref=e105] [cursor=pointer]:
      - img [ref=e106]
    - button "Журнал" [ref=e108] [cursor=pointer]:
      - img [ref=e109]
    - button "Есть дома" [active] [ref=e111] [cursor=pointer]:
      - img [ref=e112]
    - button "Покупки" [ref=e114] [cursor=pointer]:
      - img [ref=e115]
    - button "Профиль" [ref=e117] [cursor=pointer]:
      - img [ref=e118]
```

# Test source

```ts
  80  | 
  81  |   // Set quantity for "Яйцо куриное" (4)
  82  |   await openIngredientBottomSheet(page, "Яйцо куриное");
  83  |   await markIngredientAsInStock(page, "Яйцо куриное", "Штуки", 4);
  84  | 
  85  |   // Set quantity for "Помидоры" (left some)
  86  |   await openIngredientBottomSheet(page, "Помидоры");
  87  |   await markIngredientAsInStock(page, "Помидоры", "Примерно");
  88  | 
  89  |   // Set quantity for "Молоко" (1000 мл = 1 л) — same unit as recipe (volume)
  90  |   await openIngredientBottomSheet(page, "Молоко");
  91  |   await markIngredientAsInStock(page, "Молоко", "Миллилитры", 1000);
  92  | 
  93  |   // Set quantity for "Соль" (500g)
  94  |   await openIngredientBottomSheet(page, "Соль");
  95  |   await markIngredientAsInStock(page, "Соль", "Граммы", 500);
  96  | 
  97  |   // Set quantity for "Перец чёрный" (left some)
  98  |   await openIngredientBottomSheet(page, "Перец чёрный");
  99  |   await markIngredientAsInStock(page, "Перец чёрный", "Примерно");
  100 | 
  101 |   // Navigate to journal and plan recipe
  102 |   await goToJournal(page);
  103 | 
  104 |   // Add recipe to meal plan
  105 |   await addRecipeToMealPlan(page, recipeTitle, "Ужин");
  106 | 
  107 |   // Mark recipe as cooked
  108 |   await page
  109 |     .getByRole("region", { name: recipeTitle })
  110 |     .getByRole("button", {
  111 |       name: "Готово",
  112 |     })
  113 |     .click();
  114 | 
  115 |   // Modal should appear
  116 |   await expect(page.getByRole("presentation")).toBeVisible();
  117 | 
  118 |   // Говядина: 300 г - 300 г = не осталось
  119 |   await expect(page.getByTestId(`QuantityUsed-Говядина`)).toHaveText("-300 г");
  120 |   await expect(page.getByTestId(`QuantityBefore-Говядина`)).toHaveText("300 г");
  121 |   // await expect(page.getByTestId(`QuantityAfter-Говядина`)).toHaveText(
  122 |   //   "не осталось",
  123 |   // );
  124 | 
  125 |   // Яйцо куриное: 4 шт - 2 шт = 2 шт
  126 |   await expect(page.getByTestId(`QuantityUsed-Яйцо куриное`)).toHaveText(
  127 |     "-2 шт",
  128 |   );
  129 |   await expect(page.getByTestId(`QuantityBefore-Яйцо куриное`)).toHaveText(
  130 |     "4 шт",
  131 |   );
  132 |   await expect(page.getByTestId(`QuantityAfter-Яйцо куриное`)).toHaveText(
  133 |     "2 шт",
  134 |   );
  135 | 
  136 |   // Помидоры: есть - 200 г = есть
  137 |   await expect(page.getByTestId(`QuantityUsed-Помидоры`)).toHaveText("-200 г");
  138 |   await expect(page.getByTestId(`QuantityBefore-Помидоры`)).toHaveText("есть");
  139 |   await expect(page.getByTestId(`QuantityAfter-Помидоры`)).not.toBeVisible();
  140 | 
  141 |   // Молоко: 1 л - 500 мл = 500 мл (same unit, no cross-unit conversion)
  142 |   await expect(page.getByTestId(`QuantityUsed-Молоко`)).toHaveText("-500 мл");
  143 |   await expect(page.getByTestId(`QuantityBefore-Молоко`)).toHaveText("1 л");
  144 |   await expect(page.getByTestId(`QuantityAfter-Молоко`)).toHaveText("500 мл");
  145 | 
  146 |   // Соль: 500 г - по вкусу = есть
  147 |   await expect(page.getByTestId(`QuantityUsed-Соль`)).toHaveText("по вкусу");
  148 |   await expect(page.getByTestId(`QuantityBefore-Соль`)).toHaveText("500 г");
  149 |   await expect(page.getByTestId(`QuantityAfter-Соль`)).toHaveText("есть");
  150 | 
  151 |   // Перец чёрный: есть - по вкусу = есть
  152 |   await expect(page.getByTestId(`QuantityUsed-Перец чёрный`)).toHaveText(
  153 |     "по вкусу",
  154 |   );
  155 |   await expect(page.getByTestId(`QuantityBefore-Перец чёрный`)).toHaveText(
  156 |     "есть",
  157 |   );
  158 |   await expect(
  159 |     page.getByTestId(`QuantityAfter-Перец чёрный`),
  160 |   ).not.toBeVisible();
  161 | 
  162 |   // Confirm cooking
  163 |   await page.getByRole("button", { name: "Подтвердить" }).click();
  164 | 
  165 |   // "Mark as cooked" button should not be visible
  166 |   await expect(
  167 |     page.getByRole("region", { name: recipeTitle }).getByRole("button", {
  168 |       name: "Готово",
  169 |     }),
  170 |   ).not.toBeVisible();
  171 | 
  172 |   // Navigate to leftovers to verify stock levels
  173 |   await goToLeftovers(page);
  174 | 
  175 |   const getLeftoverLabel = (ingredientName: string) =>
  176 |     page.getByTestId(`LeftoverLabel-${ingredientName}`);
  177 | 
  178 |   // 5 ingredients should stay in stock
  179 |   await expect(getLeftoverLabel("Яйцо куриное")).toHaveText("2 шт");
> 180 |   await expect(getLeftoverLabel("Помидоры")).toHaveText("есть");
      |                                              ^ Error: expect(locator).toHaveText(expected) failed
  181 |   await expect(getLeftoverLabel("Молоко")).toHaveText("500 мл");
  182 |   await expect(getLeftoverLabel("Соль")).toHaveText("есть");
  183 |   await expect(getLeftoverLabel("Перец чёрный")).toHaveText("есть");
  184 | 
  185 |   // One ingredient should be gone
  186 |   await expect(getLeftoverLabel("Говядина")).not.toBeVisible();
  187 | });
  188 | 
  189 | test("confirm cooking should not send Infinity when ingredient unit is changed to roughly", async ({
  190 |   page,
  191 |   user,
  192 | }) => {
  193 |   await user.kitchen.cleanMealPlans();
  194 | 
  195 |   const recipeTitle = "Тест Infinity бага";
  196 |   await user.createRecipe({
  197 |     title: recipeTitle,
  198 |     servings: 2,
  199 |     ingredients: [["Говядина", "WEIGHT", 300]],
  200 |     dishTypes: ["Main course"],
  201 |   });
  202 | 
  203 |   await loginByEmailAndPassword(page, user.email, user.password);
  204 | 
  205 |   // Navigate to journal and add recipe to meal plan
  206 |   await goToJournal(page);
  207 |   await addRecipeToMealPlan(page, recipeTitle, "Ужин");
  208 | 
  209 |   // Click "Готово" to navigate to confirm cooking page
  210 |   await page
  211 |     .getByRole("region", { name: recipeTitle })
  212 |     .getByRole("button", { name: "Готово" })
  213 |     .click();
  214 | 
  215 |   // Wait for confirm cooking page (renders as full-screen MUI Modal)
  216 |   await expect(page.getByRole("presentation")).toBeVisible();
  217 | 
  218 |   // Enter edit mode for ingredients
  219 |   await page.getByRole("button", { name: "Изменить" }).click();
  220 | 
  221 |   // Click the edit icon on "Говядина" to open DeductedQuantityDrawer
  222 |   await page
  223 |     .getByRole("listitem", { name: "Говядина" })
  224 |     .getByRole("button", { name: "Изменить" })
  225 |     .click();
  226 | 
  227 |   // Wait for the drawer to open
  228 |   const drawer = page.getByRole("dialog", { name: /Говядина/ });
  229 |   await expect(drawer).toBeVisible();
  230 | 
  231 |   // Change unit to "Примерно" — this triggers the Infinity bug
  232 |   // because onChange(null) → handleQuantityChange(null) → { value: Infinity }
  233 |   await drawer.getByRole("combobox").click();
  234 |   await page.getByRole("option", { name: "Примерно" }).click();
  235 | 
  236 |   // Confirm in drawer
  237 |   await drawer.getByRole("button", { name: "Подтвердить" }).click();
  238 |   await expect(drawer).not.toBeVisible();
  239 | 
  240 |   // Exit edit mode
  241 |   await page.getByRole("button", { name: "Готово" }).click();
  242 | 
  243 |   // Click main confirm button
  244 |   await page.getByRole("button", { name: "Подтвердить" }).click();
  245 | 
  246 |   // Cooking confirmation should succeed: the confirm cooking page (MUI Modal)
  247 |   // should close and we should be back on the journal.
  248 |   // When the Infinity bug is present, the mutation fails with an error alert
  249 |   // and the confirm cooking page stays open.
  250 |   await expect(page.getByRole("region", { name: recipeTitle })).toBeVisible({
  251 |     timeout: 5000,
  252 |   });
  253 | 
  254 |   // The "Готово" (mark as cooked) button should no longer be visible
  255 |   await expect(
  256 |     page
  257 |       .getByRole("region", { name: recipeTitle })
  258 |       .getByRole("button", { name: "Готово" }),
  259 |   ).not.toBeVisible();
  260 | });
  261 | 
```