# 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

```
Test timeout of 30000ms exceeded.
```

```
Error: locator.click: Test timeout of 30000ms exceeded.
Call log:
  - waiting for getByRole('region', { name: 'Тестовое блюдо для проверки запасов' }).getByRole('button', { name: 'Готово' })
    - locator resolved to <button type="button" class="sc-laROWl kbYwqA">…</button>
  - attempting click action
    - waiting for element to be visible, enabled and stable

```

# Page snapshot

```yaml
- generic [ref=e4]:
  - generic [ref=e6]:
    - generic [ref=e8]:
      - generic [ref=e9]:
        - img [ref=e10]
        - generic [ref=e12]: 9 мая
      - generic [ref=e14]:
        - generic:
          - generic [ref=e17]:
            - generic [ref=e20]:
              - generic [ref=e21]: пн
              - generic [ref=e22]: "27"
            - generic [ref=e25]:
              - generic [ref=e26]: вт
              - generic [ref=e27]: "28"
            - generic [ref=e30]:
              - generic [ref=e31]: ср
              - generic [ref=e32]: "29"
            - generic [ref=e35]:
              - generic [ref=e36]: чт
              - generic [ref=e37]: "30"
            - generic [ref=e40]:
              - generic [ref=e41]: пт
              - generic [ref=e42]: "1"
            - generic [ref=e45]:
              - generic [ref=e46]: сб
              - generic [ref=e47]: "2"
            - generic [ref=e50]:
              - generic [ref=e51]: вс
              - generic [ref=e52]: "3"
          - generic [ref=e54]:
            - generic [ref=e57]:
              - generic [ref=e58]: пн
              - generic [ref=e59]: "4"
            - generic [ref=e62]:
              - generic [ref=e63]: вт
              - generic [ref=e64]: "5"
            - generic [ref=e67]:
              - generic [ref=e68]: ср
              - generic [ref=e69]: "6"
            - generic [ref=e72]:
              - generic [ref=e73]: чт
              - generic [ref=e74]: "7"
            - generic [ref=e77]:
              - generic [ref=e78]: пт
              - generic [ref=e79]: "8"
            - generic [ref=e80]:
              - generic [ref=e82]:
                - generic [ref=e83]: сб
                - generic [ref=e84]: "9"
              - img [ref=e86]
            - generic [ref=e90]:
              - generic [ref=e91]: вс
              - generic [ref=e92]: "10"
          - generic [ref=e94]:
            - generic [ref=e97]:
              - generic [ref=e98]: пн
              - generic [ref=e99]: "11"
            - generic [ref=e102]:
              - generic [ref=e103]: вт
              - generic [ref=e104]: "12"
            - generic [ref=e107]:
              - generic [ref=e108]: ср
              - generic [ref=e109]: "13"
            - generic [ref=e112]:
              - generic [ref=e113]: чт
              - generic [ref=e114]: "14"
            - generic [ref=e117]:
              - generic [ref=e118]: пт
              - generic [ref=e119]: "15"
            - generic [ref=e122]:
              - generic [ref=e123]: сб
              - generic [ref=e124]: "16"
            - generic [ref=e127]:
              - generic [ref=e128]: вс
              - generic [ref=e129]: "17"
    - generic [ref=e132]:
      - generic [ref=e134]:
        - region "Завтрак" [ref=e135]:
          - generic [ref=e137]: Завтрак
          - generic [ref=e139]:
            - generic [ref=e140]: Что готовили?
            - button "Отметить блюдо" [ref=e141]:
              - generic [ref=e142]:
                - img [ref=e143]
                - generic [ref=e145]: Отметить блюдо
        - region "Обед" [ref=e146]:
          - generic [ref=e148]: Обед
          - generic [ref=e150]:
            - generic [ref=e151]: Что готовили?
            - button "Отметить блюдо" [ref=e152]:
              - generic [ref=e153]:
                - img [ref=e154]
                - generic [ref=e156]: Отметить блюдо
        - region "Ужин" [ref=e157]:
          - generic [ref=e159]: Ужин
          - generic [ref=e161]:
            - generic [ref=e162]: Что готовили?
            - button "Отметить блюдо" [ref=e163]:
              - generic [ref=e164]:
                - img [ref=e165]
                - generic [ref=e167]: Отметить блюдо
        - region "Прочее" [ref=e168]:
          - generic [ref=e170]: Прочее
          - generic [ref=e172]:
            - generic [ref=e173]: Что готовили?
            - button "Отметить блюдо" [ref=e174]:
              - generic [ref=e175]:
                - img [ref=e176]
                - generic [ref=e178]: Отметить блюдо
      - generic [ref=e180]:
        - region "Завтрак" [ref=e181]:
          - generic [ref=e183]: Завтрак
          - generic [ref=e185]:
            - generic [ref=e186]: Что планируете?
            - button "Добавить блюдо" [ref=e187]:
              - generic [ref=e188]:
                - img [ref=e189]
                - generic [ref=e191]: Добавить блюдо
        - region "Обед" [ref=e192]:
          - generic [ref=e194]: Обед
          - generic [ref=e196]:
            - generic [ref=e197]: Что планируете?
            - button "Добавить блюдо" [ref=e198]:
              - generic [ref=e199]:
                - img [ref=e200]
                - generic [ref=e202]: Добавить блюдо
        - region "Ужин" [ref=e203]:
          - generic [ref=e204]:
            - generic [ref=e205]: Ужин
            - button "Добавить блюдо" [ref=e206]:
              - img [ref=e208]
          - region "Тестовое блюдо для проверки запасов" [ref=e211]:
            - button "Тестовое блюдо для проверки запасов" [ref=e212] [cursor=pointer]:
              - generic "Тестовое блюдо для проверки запасов" [ref=e213]:
                - generic [ref=e214]:
                  - img [ref=e218]
                  - generic [ref=e220]:
                    - generic [ref=e221]: Тестовое блюдо для проверки запасов
                    - generic [ref=e222]: 2 порции
            - generic [ref=e223]:
              - button "Готово" [ref=e224]:
                - generic [ref=e225]:
                  - img [ref=e226]
                  - generic [ref=e228]: Готово
              - button [ref=e229]:
                - img [ref=e231]
        - region "Прочее" [ref=e233]:
          - generic [ref=e235]: Прочее
          - generic [ref=e237]:
            - generic [ref=e238]: Что планируете?
            - button "Добавить блюдо" [ref=e239]:
              - generic [ref=e240]:
                - img [ref=e241]
                - generic [ref=e243]: Добавить блюдо
      - generic [ref=e245]:
        - region "Завтрак" [ref=e246]:
          - generic [ref=e248]: Завтрак
          - generic [ref=e250]:
            - generic [ref=e251]: Что планируете?
            - button "Добавить блюдо" [ref=e252]:
              - generic [ref=e253]:
                - img [ref=e254]
                - generic [ref=e256]: Добавить блюдо
        - region "Обед" [ref=e257]:
          - generic [ref=e259]: Обед
          - generic [ref=e261]:
            - generic [ref=e262]: Что планируете?
            - button "Добавить блюдо" [ref=e263]:
              - generic [ref=e264]:
                - img [ref=e265]
                - generic [ref=e267]: Добавить блюдо
        - region "Ужин" [ref=e268]:
          - generic [ref=e270]: Ужин
          - generic [ref=e272]:
            - generic [ref=e273]: Что планируете?
            - button "Добавить блюдо" [ref=e274]:
              - generic [ref=e275]:
                - img [ref=e276]
                - generic [ref=e278]: Добавить блюдо
        - region "Прочее" [ref=e279]:
          - generic [ref=e281]: Прочее
          - generic [ref=e283]:
            - generic [ref=e284]: Что планируете?
            - button "Добавить блюдо" [ref=e285]:
              - generic [ref=e286]:
                - img [ref=e287]
                - generic [ref=e289]: Добавить блюдо
  - navigation [ref=e290]:
    - button "Рецепты" [ref=e291] [cursor=pointer]:
      - img [ref=e292]
    - button "Журнал" [ref=e294] [cursor=pointer]:
      - img [ref=e295]
    - button "Есть дома" [ref=e297] [cursor=pointer]:
      - img [ref=e298]
    - button "Покупки" [ref=e300] [cursor=pointer]:
      - img [ref=e301]
    - button "Профиль" [ref=e303] [cursor=pointer]:
      - img [ref=e304]
```

# Test source

```ts
  13  | import markIngredientAsInStock from "../helpers/ingredient/markIngredientAsInStock";
  14  | 
  15  | test("user can mark planned recipe as cooked and find cooked meal in leftovers", async ({
  16  |   page,
  17  |   user,
  18  | }) => {
  19  |   const RECIPE_TITLE = "Макароны по-флотски";
  20  | 
  21  |   await loginByEmailAndPassword(page, user.email, user.password);
  22  | 
  23  |   // Step 1: Navigate to journal and cook recipe directly
  24  |   await goToJournal(page);
  25  |   await cookRecipeFromJournal(page, RECIPE_TITLE);
  26  | 
  27  |   // Step 2: Navigate to leftovers and verify cooked meal behavior
  28  |   await goToLeftovers(page);
  29  | 
  30  |   // Verify that cooked does not appear in leftovers under "Готовая еда"
  31  |   await expect(page.getByText("Готовая еда")).not.toBeVisible();
  32  |   await expect(
  33  |     page.getByRole("link").filter({ hasText: COOKED_MEAL_INGREDIENT_TITLE }),
  34  |   ).not.toBeVisible();
  35  |   await expect(
  36  |     page.getByRole("link").filter({ hasText: RECIPE_TITLE }),
  37  |   ).not.toBeVisible();
  38  | });
  39  | 
  40  | test("user can cook recipe and see stock deducted correctly", async ({
  41  |   page,
  42  |   user,
  43  | }) => {
  44  |   // Clean meal plans
  45  |   await user.kitchen.cleanMealPlans();
  46  | 
  47  |   // Create a test recipe with 4 ingredients with different quantity types
  48  |   const recipeTitle = "Тестовое блюдо для проверки запасов";
  49  |   await user.createRecipe({
  50  |     title: recipeTitle,
  51  |     servings: 2,
  52  |     ingredients: [
  53  |       ["Говядина", "WEIGHT", 300], // Will add exact match (300g)
  54  |       ["Яйцо куриное", "QUANTITY", 2], // Will add double (4)
  55  |       ["Помидоры", "WEIGHT", 200], // Will add rough "left some"
  56  |       ["Молоко", "VOLUME", 0.5], // Will add in same unit (milliliters) so the assertion doesn't depend on a learned weight↔volume conversion rule
  57  |       "Соль", // Will add specific quantity (500g)
  58  |       "Перец чёрный", // Will add rough "left some"
  59  |     ],
  60  |     dishTypes: ["Main course"],
  61  |   });
  62  | 
  63  |   // Login to the app
  64  |   await loginByEmailAndPassword(page, user.email, user.password);
  65  | 
  66  |   // Navigate to recipes to access the created recipe
  67  |   await goToRecipes(page);
  68  |   await page
  69  |     .getByRole("button", {
  70  |       name: recipeTitle,
  71  |     })
  72  |     .click();
  73  | 
  74  |   // Wait for recipe details page to load
  75  |   await expect(page.getByRole("heading", { name: recipeTitle })).toBeVisible();
  76  | 
  77  |   // Set quantity for "Говядина" (300g)
  78  |   await openIngredientBottomSheet(page, "Говядина");
  79  |   await markIngredientAsInStock(page, "Говядина", "Граммы", 300);
  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();
      |      ^ Error: locator.click: Test timeout of 30000ms exceeded.
  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("есть");
  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();
```