Files
site/WESP_REL/tests/e2e/test_recipes_user_journeys.py
T
2026-07-17 12:57:18 +03:00

668 lines
25 KiB
Python

"""E2E user journeys: хаотичные длинные сценарии «полный еблан за терминалом»."""
from __future__ import annotations
import pytest
from tests.e2e.conftest import (
E2E_DISP_ID,
E2E_DISP_RECIPE_1,
E2E_DISP_RECIPE_2,
E2E_MILL_ID,
E2E_PERIOD_A,
E2E_PERIOD_B,
E2E_RECIPE_ID,
)
from tests.e2e.recipes_e2e_helpers import (
abort_transfer_via_escape,
assert_period_order,
click_copy_recipe,
click_move_recipe,
create_recipe_in_period,
delete_recipe_unlink,
drag_reorder_recipe,
drag_transfer_to_period,
ensure_two_recipe_order,
fetch_recipe,
list_dom_recipe_ids,
open_dispenser_period,
period_recipe_ids,
recipe_ingredient_snapshot,
restore_two_recipe_order_if_reversed,
select_period,
wait_editor_closed,
wait_editor_open,
wait_mobile_dashboard_step,
wait_period_order,
)
pytestmark = pytest.mark.e2e
MOBILE_VIEWPORT = {"width": 390, "height": 844}
E2E_DISP_ING_1 = "e2e-disp-ing-1"
SCENARIO_JOURNEY_MORNING_ROUTINE = "JOURNEY_MORNING_ROUTINE"
SCENARIO_JOURNEY_TRANSFER_REPEAT_SLOTS = "JOURNEY_TRANSFER_REPEAT_SLOTS"
SCENARIO_JOURNEY_ESCAPE_REORDER_MIX = "JOURNEY_ESCAPE_REORDER_MIX"
SCENARIO_JOURNEY_MILL_CREATE_DELETE = "JOURNEY_MILL_CREATE_DELETE"
SCENARIO_JOURNEY_SETTINGS_LOGOUT = "JOURNEY_SETTINGS_LOGOUT"
SCENARIO_JOURNEY_EMPTY_THEN_FULL = "JOURNEY_EMPTY_THEN_FULL"
SCENARIO_JOURNEY_MOBILE_FLIP = "JOURNEY_MOBILE_FLIP"
SCENARIO_JOURNEY_EDITOR_CANCEL_REOPEN = "JOURNEY_EDITOR_CANCEL_REOPEN"
SCENARIO_JOURNEY_MOVE_BUTTONS_YOYO = "JOURNEY_MOVE_BUTTONS_YOYO"
SCENARIO_JOURNEY_DOUBLE_COPY_PASTE = "JOURNEY_DOUBLE_COPY_PASTE"
SCENARIO_JOURNEY_MILL_DISPENSER_PINGPONG = "JOURNEY_MILL_DISPENSER_PINGPONG"
SCENARIO_JOURNEY_DISTRACTED_UI = "JOURNEY_DISTRACTED_UI"
SCENARIO_JOURNEY_MEGA_MIX = "JOURNEY_MEGA_MIX"
def _open_dispenser_period_a(page, live_server_url) -> None:
open_dispenser_period(
page,
E2E_PERIOD_A,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2],
)
def test_journey_morning_routine_save_reorder_copy_paste_unlink(
authenticated_page, live_server_url
) -> None:
SCENARIO_JOURNEY_MORNING_ROUTINE # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_DISP_RECIPE_1}"]').click()
wait_editor_open(page)
page.locator("#recipeName").fill("E2E Утренний рейс")
with page.expect_response(
lambda r: r.request.method == "PUT" and E2E_DISP_RECIPE_1 in r.url,
timeout=15000,
):
page.locator('[data-action="save-and-exit"]').click()
wait_editor_closed(page)
assert fetch_recipe(page, live_server_url, E2E_DISP_RECIPE_1)["name"] == "E2E Утренний рейс"
assert_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
_open_dispenser_period_a(page, live_server_url)
drag_reorder_recipe(
page,
E2E_DISP_RECIPE_1,
direction="down",
live_server_url=live_server_url,
period_id=E2E_PERIOD_A,
expected_order=[E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1],
)
click_copy_recipe(page, E2E_DISP_RECIPE_1)
select_period(page, E2E_PERIOD_B, live_server_url=live_server_url, expected_order=[])
page.locator(f'[data-action="paste-recipe"][data-period-id="{E2E_PERIOD_B}"]').click()
wait_editor_open(page)
with page.expect_response(
lambda r: r.request.method == "POST"
and f"/api/periods/{E2E_PERIOD_B}/recipes" in r.url,
timeout=20000,
) as paste_save:
page.locator('[data-action="save-and-exit"]').click()
pasted_id = paste_save.value.json().get("id")
assert pasted_id
wait_editor_closed(page)
wait_period_order(page, live_server_url, E2E_PERIOD_B, [pasted_id])
select_period(page, E2E_PERIOD_B, wait_recipe_id=pasted_id)
delete_recipe_unlink(
page,
pasted_id,
live_server_url=live_server_url,
period_id=E2E_PERIOD_B,
expected_period_order=[],
)
assert page.request.get(f"{live_server_url}/api/recipes/{pasted_id}").ok
def test_journey_transfer_repeat_with_slots(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_TRANSFER_REPEAT_SLOTS # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
drag_transfer_to_period(
page,
E2E_DISP_RECIPE_1,
E2E_PERIOD_B,
live_server_url=live_server_url,
source_period_id=E2E_PERIOD_A,
expected_source_order=[E2E_DISP_RECIPE_2],
expected_target_order=[E2E_DISP_RECIPE_1],
)
assert page.locator("button.recipe-transfer-slot").count() == 0
select_period(
page,
E2E_PERIOD_B,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1],
)
drag_transfer_to_period(
page,
E2E_DISP_RECIPE_1,
E2E_PERIOD_A,
slot_index=1,
live_server_url=live_server_url,
source_period_id=E2E_PERIOD_B,
expected_source_order=[],
expected_target_order=[E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1],
)
def test_journey_escape_then_reorder_still_works(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_ESCAPE_REORDER_MIX # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
abort_transfer_via_escape(page, E2E_DISP_RECIPE_1)
assert_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
drag_reorder_recipe(
page,
E2E_DISP_RECIPE_1,
direction="down",
live_server_url=live_server_url,
period_id=E2E_PERIOD_A,
expected_order=[E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1],
)
assert list_dom_recipe_ids(page) == [E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1]
def test_journey_mill_create_edit_delete_global(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_MILL_CREATE_DELETE # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_MILL_ID}"]').click()
page.locator("#createRecipeBtn").click()
wait_editor_open(page)
page.locator("#recipeName").fill("E2E Journey Mill")
with page.expect_response(
lambda r: r.request.method == "POST" and r.url.rstrip("/").endswith("/api/recipes"),
timeout=20000,
) as resp_info:
page.locator('[data-action="save-and-exit"]').click()
recipe_id = resp_info.value.json().get("id")
assert recipe_id
wait_editor_closed(page)
assert fetch_recipe(page, live_server_url, recipe_id)["name"] == "E2E Journey Mill"
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_MILL_ID}"]').click()
page.locator(f'[data-action="select-recipe"][data-recipe-id="{recipe_id}"]').click()
wait_editor_open(page)
before_ings = len(fetch_recipe(page, live_server_url, recipe_id).get("ingredients") or [])
page.locator('[data-action="add-ingredient"]').click()
page.wait_for_function(
f"() => document.querySelectorAll('#ingredientsTableBody .zt-data-grid__row').length > {before_ings}"
)
page.locator('[data-action="remove-ingredient"]').first.click()
with page.expect_response(
lambda r: r.request.method == "PUT" and recipe_id in r.url,
timeout=15000,
):
page.locator('[data-action="save-and-exit"]').click()
wait_editor_closed(page)
assert len(fetch_recipe(page, live_server_url, recipe_id).get("ingredients") or []) == before_ings
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_MILL_ID}"]').click()
page.on("dialog", lambda d: d.accept())
with page.expect_response(
lambda r: r.request.method == "DELETE" and recipe_id in r.url,
timeout=15000,
):
page.locator(f'[data-action="delete-recipe"][data-recipe-id="{recipe_id}"]').click()
assert page.request.get(f"{live_server_url}/api/recipes/{recipe_id}").status == 404
assert page.request.get(f"{live_server_url}/api/recipes/{E2E_RECIPE_ID}").ok
def test_journey_settings_then_logout(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_SETTINGS_LOGOUT # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
page.locator('[data-action="open-settings"]').first.click(force=True)
page.wait_for_selector('[data-action="toggle-sync-form"]', timeout=5000)
page.locator('[data-action="toggle-sync-form"]').click()
page.locator('[data-action="toggle-credentials-form"]').click()
page.locator('#settingsModal [data-action="close-settings"]').first.click()
page.wait_for_selector('[data-action="open-settings"]', state="visible", timeout=5000)
with page.expect_response(
lambda r: r.url.endswith("/api/auth/logout") and r.request.method == "POST",
timeout=10000,
):
page.locator('[data-action="logout"]').first.click()
page.wait_for_url(f"{live_server_url}/", timeout=10000)
check = page.request.get(f"{live_server_url}/api/auth/check")
assert check.ok
assert check.json().get("authenticated") is False
def test_journey_empty_period_create_then_delete_all(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_EMPTY_THEN_FULL # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_DISP_ID}"]').click()
select_period(
page,
E2E_PERIOD_B,
live_server_url=live_server_url,
expected_order=[],
)
created_ids: list[str] = []
for i in range(2):
page.wait_for_selector(
f'[data-action="create-recipe"][data-period-id="{E2E_PERIOD_B}"]',
state="visible",
timeout=10000,
)
rid = create_recipe_in_period(
page,
E2E_PERIOD_B,
f"E2E Пустой B {i}",
live_server_url=live_server_url,
)
created_ids.append(rid)
wait_period_order(page, live_server_url, E2E_PERIOD_B, created_ids.copy())
assert len(period_recipe_ids(page, live_server_url, E2E_PERIOD_B)) == 2
for rid in created_ids:
select_period(page, E2E_PERIOD_B, wait_recipe_id=rid)
remaining = [x for x in created_ids if x != rid]
delete_recipe_unlink(
page,
rid,
live_server_url=live_server_url,
period_id=E2E_PERIOD_B,
expected_period_order=remaining,
)
assert_period_order(page, live_server_url, E2E_PERIOD_B, [])
def test_journey_mobile_flip_back_and_button_reorder(
authenticated_page, live_server_url
) -> None:
SCENARIO_JOURNEY_MOBILE_FLIP # noqa: F841
page = authenticated_page
page.set_viewport_size(MOBILE_VIEWPORT)
open_dispenser_period(
page,
E2E_PERIOD_A,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2],
)
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_DISP_RECIPE_1}"]').click()
wait_editor_open(page)
page.locator('[data-action="show-recipe-edit-off"]').first.click()
wait_editor_closed(page)
page.locator('[data-action="mobile-dashboard-back"]').click()
wait_mobile_dashboard_step(page, "periods")
select_period(
page,
E2E_PERIOD_A,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2],
)
click_move_recipe(page, E2E_DISP_RECIPE_1, "down")
wait_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1]
)
page.locator('[data-action="mobile-dashboard-back"]').click()
wait_mobile_dashboard_step(page, "periods")
select_period(
page,
E2E_PERIOD_B,
live_server_url=live_server_url,
expected_order=[],
)
page.locator('[data-action="mobile-dashboard-back"]').click()
wait_mobile_dashboard_step(page, "periods")
select_period(
page,
E2E_PERIOD_A,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1],
)
click_move_recipe(page, E2E_DISP_RECIPE_1, "up")
wait_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
def test_journey_editor_cancel_then_save_other_recipe(
authenticated_page, live_server_url
) -> None:
SCENARIO_JOURNEY_EDITOR_CANCEL_REOPEN # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
before_name = fetch_recipe(page, live_server_url, E2E_DISP_RECIPE_1)["name"]
before_ings = recipe_ingredient_snapshot(page, live_server_url, E2E_DISP_RECIPE_1)
assert len(before_ings) == 1
assert before_ings[0][0] == E2E_DISP_ING_1
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_DISP_RECIPE_1}"]').click()
wait_editor_open(page)
page.locator("#recipeName").fill("E2E Не должно сохраниться")
page.locator('[data-action="add-ingredient"]').click()
page.wait_for_function(
"() => document.querySelectorAll('#ingredientsTableBody .ingredient-row').length > 1"
)
page.locator("#ingredientsTableBody .weight-per-head-input").first.fill("99.9")
page.locator('[data-action="show-recipe-edit-off"]').first.click()
wait_editor_closed(page)
assert fetch_recipe(page, live_server_url, E2E_DISP_RECIPE_1)["name"] == before_name
assert recipe_ingredient_snapshot(page, live_server_url, E2E_DISP_RECIPE_1) == before_ings
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_DISP_RECIPE_2}"]').click()
wait_editor_open(page)
page.locator("#recipeName").fill("E2E Второй переименован")
with page.expect_response(
lambda r: r.request.method == "PUT" and E2E_DISP_RECIPE_2 in r.url,
timeout=15000,
):
page.locator('[data-action="save-and-exit"]').click()
wait_editor_closed(page)
assert fetch_recipe(page, live_server_url, E2E_DISP_RECIPE_1)["name"] == before_name
assert recipe_ingredient_snapshot(page, live_server_url, E2E_DISP_RECIPE_1) == before_ings
assert fetch_recipe(page, live_server_url, E2E_DISP_RECIPE_2)["name"] == "E2E Второй переименован"
def test_journey_move_buttons_yoyo(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_MOVE_BUTTONS_YOYO # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
click_move_recipe(page, E2E_DISP_RECIPE_1, "down")
wait_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1]
)
click_move_recipe(page, E2E_DISP_RECIPE_1, "up")
wait_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
click_move_recipe(page, E2E_DISP_RECIPE_1, "down")
wait_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1]
)
def test_journey_double_copy_paste_same_buffer(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_DOUBLE_COPY_PASTE # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
click_copy_recipe(page, E2E_DISP_RECIPE_1)
select_period(page, E2E_PERIOD_B, live_server_url=live_server_url, expected_order=[])
pasted_ids: list[str] = []
for n in range(2):
page.locator(f'[data-action="paste-recipe"][data-period-id="{E2E_PERIOD_B}"]').click()
wait_editor_open(page)
page.locator("#recipeName").fill(f"E2E Двойная копия {n}")
with page.expect_response(
lambda r: r.request.method == "POST"
and f"/api/periods/{E2E_PERIOD_B}/recipes" in r.url,
timeout=20000,
) as resp:
page.locator('[data-action="save-and-exit"]').click()
rid = resp.value.json().get("id")
assert rid
pasted_ids.append(rid)
wait_editor_closed(page)
wait_period_order(page, live_server_url, E2E_PERIOD_B, pasted_ids.copy())
assert_period_order(page, live_server_url, E2E_PERIOD_B, pasted_ids)
for rid in pasted_ids:
select_period(page, E2E_PERIOD_B, wait_recipe_id=rid)
remaining = [x for x in pasted_ids if x != rid]
delete_recipe_unlink(
page,
rid,
live_server_url=live_server_url,
period_id=E2E_PERIOD_B,
expected_period_order=remaining,
)
assert_period_order(page, live_server_url, E2E_PERIOD_B, [])
def test_journey_mill_dispenser_pingpong(authenticated_page, live_server_url) -> None:
SCENARIO_JOURNEY_MILL_DISPENSER_PINGPONG # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
mill_before = fetch_recipe(page, live_server_url, E2E_RECIPE_ID)["name"]
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_MILL_ID}"]').click()
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_RECIPE_ID}"]').click()
wait_editor_open(page)
page.locator("#recipeName").fill("E2E Mill черновик")
page.locator('[data-action="show-recipe-edit-off"]').first.click()
wait_editor_closed(page)
assert fetch_recipe(page, live_server_url, E2E_RECIPE_ID)["name"] == mill_before
_open_dispenser_period_a(page, live_server_url)
disp_before = recipe_ingredient_snapshot(page, live_server_url, E2E_DISP_RECIPE_1)
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_DISP_RECIPE_1}"]').click()
wait_editor_open(page)
page.locator("#toggleDryMatterMode").click()
page.locator('[data-action="show-recipe-edit-off"]').first.click()
wait_editor_closed(page)
assert recipe_ingredient_snapshot(page, live_server_url, E2E_DISP_RECIPE_1) == disp_before
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_MILL_ID}"]').click()
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_RECIPE_ID}"]').click()
wait_editor_open(page)
page.locator("#recipeName").fill("E2E Mill сохранён")
with page.expect_response(
lambda r: r.request.method == "PUT" and E2E_RECIPE_ID in r.url,
timeout=15000,
):
page.locator('[data-action="save-and-exit"]').click()
wait_editor_closed(page)
assert fetch_recipe(page, live_server_url, E2E_RECIPE_ID)["name"] == "E2E Mill сохранён"
_open_dispenser_period_a(page, live_server_url)
drag_transfer_to_period(
page,
E2E_DISP_RECIPE_1,
E2E_PERIOD_B,
live_server_url=live_server_url,
source_period_id=E2E_PERIOD_A,
expected_source_order=[E2E_DISP_RECIPE_2],
expected_target_order=[E2E_DISP_RECIPE_1],
)
def test_journey_distracted_help_settings_then_transfer(
authenticated_page, live_server_url
) -> None:
SCENARIO_JOURNEY_DISTRACTED_UI # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
_open_dispenser_period_a(page, live_server_url)
page.locator(f'[data-action="select-recipe"][data-recipe-id="{E2E_DISP_RECIPE_1}"]').click()
wait_editor_open(page)
page.set_viewport_size(MOBILE_VIEWPORT)
page.locator('[data-action="open-recipe-help"]').first.click()
page.wait_for_selector("body.recipe-help-modal-open", timeout=5000)
page.locator('[data-action="close-recipe-help"]').first.click()
page.wait_for_function("() => !document.body.classList.contains('recipe-help-modal-open')")
page.set_viewport_size({"width": 1280, "height": 800})
page.locator('[data-action="open-settings"]').first.click(force=True)
page.locator('[data-action="toggle-sync-form"]').click()
page.locator('[data-action="toggle-credentials-form"]').click()
page.locator('#settingsModal [data-action="close-settings"]').first.click()
page.wait_for_selector("#settingsModal", state="hidden", timeout=5000)
page.locator('[data-action="show-recipe-edit-off"]').first.click()
wait_editor_closed(page)
restore_two_recipe_order_if_reversed(
page, live_server_url, E2E_PERIOD_A, E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2
)
assert_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
abort_transfer_via_escape(page, E2E_DISP_RECIPE_2)
restore_two_recipe_order_if_reversed(
page, live_server_url, E2E_PERIOD_A, E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2
)
assert_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
drag_transfer_to_period(
page,
E2E_DISP_RECIPE_2,
E2E_PERIOD_B,
live_server_url=live_server_url,
source_period_id=E2E_PERIOD_A,
expected_source_order=[E2E_DISP_RECIPE_1],
expected_target_order=[E2E_DISP_RECIPE_2],
)
def test_journey_mega_mix_all_operations(authenticated_page, live_server_url) -> None:
"""Мега-сценарий: всё подряд, как реальный зоотехник в понедельник утром."""
SCENARIO_JOURNEY_MEGA_MIX # noqa: F841
page = authenticated_page
page.set_viewport_size({"width": 1280, "height": 800})
page.locator('[data-action="open-settings"]').first.click(force=True)
page.locator('#settingsModal [data-action="close-settings"]').first.click()
page.wait_for_selector("#settingsModal", state="hidden", timeout=5000)
_open_dispenser_period_a(page, live_server_url)
select_period(page, E2E_PERIOD_B, live_server_url=live_server_url, expected_order=[])
select_period(
page,
E2E_PERIOD_A,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2],
)
click_move_recipe(page, E2E_DISP_RECIPE_1, "down")
wait_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1]
)
abort_transfer_via_escape(page, E2E_DISP_RECIPE_1)
ensure_two_recipe_order(
page,
live_server_url,
E2E_PERIOD_A,
[E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1],
)
assert_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_2, E2E_DISP_RECIPE_1]
)
ensure_two_recipe_order(
page,
live_server_url,
E2E_PERIOD_A,
[E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2],
)
assert_period_order(
page, live_server_url, E2E_PERIOD_A, [E2E_DISP_RECIPE_1, E2E_DISP_RECIPE_2]
)
click_copy_recipe(page, E2E_DISP_RECIPE_2)
drag_transfer_to_period(
page,
E2E_DISP_RECIPE_1,
E2E_PERIOD_B,
live_server_url=live_server_url,
source_period_id=E2E_PERIOD_A,
expected_source_order=[E2E_DISP_RECIPE_2],
expected_target_order=[E2E_DISP_RECIPE_1],
)
select_period(
page,
E2E_PERIOD_B,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1],
)
page.locator(f'[data-action="paste-recipe"][data-period-id="{E2E_PERIOD_B}"]').click()
wait_editor_open(page)
with page.expect_response(
lambda r: r.request.method == "POST"
and f"/api/periods/{E2E_PERIOD_B}/recipes" in r.url,
timeout=20000,
) as paste_resp:
page.locator('[data-action="save-and-exit"]').click()
pasted_id = paste_resp.value.json().get("id")
assert pasted_id
wait_editor_closed(page)
wait_period_order(
page, live_server_url, E2E_PERIOD_B, [E2E_DISP_RECIPE_1, pasted_id]
)
open_dispenser_period(
page,
E2E_PERIOD_B,
wait_recipe_id=E2E_DISP_RECIPE_1,
live_server_url=live_server_url,
expected_order=[E2E_DISP_RECIPE_1, pasted_id],
)
click_move_recipe(page, pasted_id, "up")
wait_period_order(
page, live_server_url, E2E_PERIOD_B, [pasted_id, E2E_DISP_RECIPE_1]
)
page.locator(f'[data-action="select-dispenser"][data-dispenser-id="{E2E_MILL_ID}"]').click()
page.wait_for_selector("#createRecipeBtn", timeout=10000)
assert fetch_recipe(page, live_server_url, E2E_RECIPE_ID).get("name")
open_dispenser_period(
page,
E2E_PERIOD_B,
wait_recipe_id=pasted_id,
live_server_url=live_server_url,
expected_order=[pasted_id, E2E_DISP_RECIPE_1],
)
delete_recipe_unlink(
page,
pasted_id,
live_server_url=live_server_url,
period_id=E2E_PERIOD_B,
expected_period_order=[E2E_DISP_RECIPE_1],
)
assert page.request.get(f"{live_server_url}/api/recipes/{pasted_id}").ok