668 lines
25 KiB
Python
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
|