fix(web): use oninput binding in EditSession for reliable E2E interaction
Deploy Telegram Bot / build-and-push (push) Successful in 20m24s
Deploy Telegram Bot / scan-images (push) Successful in 10m15s
Deploy Telegram Bot / deploy (push) Successful in 2m0s

Blazor Server's default change-event binding races with Playwright fills,
causing input values to revert before the form submits. Switch Title,
JoinLink and MaxPlayers to @bind-Value:event=oninput so the model stays
in sync while the test types.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 16:14:40 +03:00
parent f65c18f0d3
commit e6fae2907d
2 changed files with 31 additions and 7 deletions
@@ -36,7 +36,7 @@
<EditForm Model="@model" OnValidSubmit="HandleSubmit">
<div class="gm-form-group">
<label class="gm-form-label">Название игры</label>
<InputText @bind-Value="model.Title" class="gm-form-control" placeholder="например, D&D 5e: Dragon's Hoard" />
<InputText @bind-Value="model.Title" @bind-Value:event="oninput" class="gm-form-control" placeholder="например, D&D 5e: Dragon's Hoard" />
<div class="gm-form-hint">Изменение этого поля обновит все сессии в одной группе.</div>
</div>
@@ -48,12 +48,12 @@
<div class="gm-form-group">
<label class="gm-form-label">Ссылка для подключения</label>
<InputText @bind-Value="model.JoinLink" class="gm-form-control" placeholder="Ссылка на Discord или VTT" />
<InputText @bind-Value="model.JoinLink" @bind-Value:event="oninput" class="gm-form-control" placeholder="Ссылка на Discord или VTT" />
</div>
<div class="gm-form-group">
<label class="gm-form-label">Лимит мест</label>
<InputNumber @bind-Value="model.MaxPlayers" class="gm-form-control" min="1" placeholder="Без лимита" />
<InputNumber @bind-Value="model.MaxPlayers" @bind-Value:event="oninput" class="gm-form-control" min="1" placeholder="Без лимита" />
<div class="gm-form-hint">Пустое значение означает запись без лимита. Если лимит заполнен, новые игроки попадут в лист ожидания.</div>
</div>
@@ -334,20 +334,44 @@ def test_dashboard_session_edit_flow() -> None:
page.goto(f"{base_url}/session/edit/{session_id}")
page.wait_for_selector("text=Редактирование сессии", timeout=15000)
def _fill_blazor_input(locator, value: str) -> None:
"""Fill a Blazor-bound input, move focus out, and wait for the value to stick."""
locator.fill(value)
locator.press("Tab")
# Blazor Server round-trips on change; give it time to update the model
# and re-render before the next field is touched.
page.wait_for_timeout(500)
expect(locator).to_have_value(value)
title_input = page.locator(".gm-form-group").filter(has_text="Название игры").locator("input").first
title_input.fill(updated_title)
_fill_blazor_input(title_input, updated_title)
join_input = page.locator(".gm-form-group").filter(has_text="Ссылка для подключения").locator("input").first
join_input.fill(updated_join_link)
_fill_blazor_input(join_input, updated_join_link)
max_players_input = page.locator(".gm-form-group").filter(has_text="Лимит мест").locator("input").first
max_players_input.fill("3")
_fill_blazor_input(max_players_input, "3")
# Verify the form actually received the new values before Blazor submits.
filled_title = title_input.input_value()
filled_join = join_input.input_value()
filled_max = max_players_input.input_value()
print(f"Edit form values before save: title={filled_title!r}, join={filled_join!r}, max={filled_max!r}")
save_button = page.locator("button:has-text('Сохранить изменения')").first
save_button.click()
page.wait_for_url(f"{base_url}/group/{group_id}", timeout=15000)
page.wait_for_selector(f"text={updated_title}", timeout=15000)
try:
page.wait_for_selector(f"text={updated_title}", timeout=15000)
except Exception:
dump_path = "e2e_edit_group_debug.html"
with open(dump_path, "w", encoding="utf-8") as f:
f.write(page.content())
print("URL after edit save:", page.url)
print(f"Group page HTML dumped to {dump_path}")
page.screenshot(path="e2e_edit_group_debug.png")
raise
expect(page.locator(f"text={updated_title}").first).to_be_visible()
db_session = _get_session_from_db(session_id)