@@ -0,0 +1,667 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user