Files
blindxfishandClaude Fable 5 50baf9d6f1 Fix battles join docs param location and the fight-flow ticker assertion
The auth matrix probes documented endpoints with their documented form
params; the faction param was declared with location body instead of
form, so the anonymous probe sent an empty body and hit 422
validation before the auth guard. And the first fight always triggers
a lead-change event that outranks the fight event at the top of the
ticker, so the e2e flow now asserts on the ticker as a whole.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-21 00:05:00 +02:00

157 lines
5.9 KiB
Python

# retoor <retoor@molodetz.nl>
import time
import requests
from playwright.sync_api import expect
from tests.conftest import BASE_URL
_counter_wars = [0]
def _seed_war(faction_a="Tabs", faction_b="Spaces"):
_counter_wars[0] += 1
name = f"ewar{int(time.time() * 1000)}{_counter_wars[0]}"
s = requests.Session()
s.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@t.dev",
"password": "secret123",
"confirm_password": "secret123",
"birth_date": "1990-01-01",
"accept_terms": "1",
},
allow_redirects=True,
)
r = s.post(
f"{BASE_URL}/posts/create",
data={
"content": "Seeded battle post for browser tests.",
"title": f"battle-{name}",
"topic": "question",
"war_faction_a": faction_a,
"war_faction_b": faction_b,
},
allow_redirects=False,
)
return r.headers["location"].split("/posts/")[-1]
def _open_composer(page):
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
page.locator(".feed-fab").first.wait_for(state="visible")
page.locator(".feed-fab").first.click()
page.locator("#create-post-modal.visible").wait_for(state="visible")
def test_battles_page_lists_cards(alice):
page, _ = alice
_seed_war("Vikings", "Knights")
page.goto(f"{BASE_URL}/battles", wait_until="domcontentloaded")
page.locator("dp-opinion-war").first.wait_for(state="visible")
expect(page.locator(".topnav-link.active:has-text('Battles')")).to_be_visible()
expect(page.locator("dp-opinion-war .war-vs").first).to_have_text("VS")
def test_battle_card_on_post_page(alice):
page, _ = alice
slug = _seed_war("Ninjas", "Pirates")
page.goto(f"{BASE_URL}/posts/{slug}", wait_until="domcontentloaded")
card = page.locator("dp-opinion-war")
card.wait_for(state="visible")
expect(card.locator(".war-side-a .war-side-name")).to_have_text("Ninjas")
expect(card.locator(".war-field")).to_be_visible()
expect(card.locator(".war-all-link")).to_have_attribute("href", "/battles")
def test_composer_war_toggle_and_poll_exclusivity(alice):
page, _ = alice
_open_composer(page)
modal = page.locator("#create-post-modal")
expect(modal.locator("[data-war-builder]")).to_be_hidden()
modal.locator("[data-war-toggle]").click()
expect(modal.locator("[data-war-builder]")).to_be_visible()
expect(modal.locator("input[name='war_faction_a']")).to_be_enabled()
modal.locator("[data-poll-toggle]").click()
expect(modal.locator("[data-poll-builder]")).to_be_visible()
expect(modal.locator("[data-war-builder]")).to_be_hidden()
modal.locator("[data-war-toggle]").click()
expect(modal.locator("[data-war-builder]")).to_be_visible()
expect(modal.locator("[data-poll-builder]")).to_be_hidden()
def test_composer_war_validation_blocks_submit(alice):
page, _ = alice
_open_composer(page)
modal = page.locator("#create-post-modal")
page.fill("#post-content", "A war with only one named faction must not submit.")
modal.locator("[data-war-toggle]").click()
modal.locator("input[name='war_faction_a']").fill("Solo")
modal.locator("button.btn-primary:has-text('Post')").click()
expect(modal.locator("[data-war-error]")).to_be_visible()
assert "/feed" in page.url
def test_create_join_and_fight_flow(alice):
page, _ = alice
from devplacepy.database import get_table, refresh_snapshot
from devplacepy.services.game.store import ensure_farm
refresh_snapshot()
row = get_table("users").find_one(username="alice_test")
farm = ensure_farm(row["uid"])
get_table("game_farms").update({"uid": farm["uid"], "coins": 1000}, ["uid"])
_open_composer(page)
modal = page.locator("#create-post-modal")
page.fill("#post-content", "Full battle flow: create, join and fight in browser.")
modal.locator("[data-war-toggle]").click()
modal.locator("input[name='war_faction_a']").fill("Coffee")
modal.locator("input[name='war_faction_b']").fill("Tea")
modal.locator("button.btn-primary:has-text('Post')").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
card = page.locator("dp-opinion-war")
card.wait_for(state="visible")
card.locator(".war-join-a").click()
card.locator("[data-war-mine]").wait_for(state="visible")
hp_before = card.locator("[data-war-hp-a]").text_content()
card.locator("[data-war-fight]").click()
page.wait_for_function(
"before => document.querySelector('[data-war-hp-a]').textContent !== before",
arg=hp_before,
)
expect(card.locator("[data-war-fight]")).to_be_disabled()
expect(card.locator("[data-war-ticker]")).to_contain_text("dealt")
def test_switch_faction_shows_confirm(alice):
page, _ = alice
slug = _seed_war("Left", "Right")
page.goto(f"{BASE_URL}/posts/{slug}", wait_until="domcontentloaded")
card = page.locator("dp-opinion-war")
card.wait_for(state="visible")
card.locator(".war-join-b").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
card = page.locator("dp-opinion-war")
card.locator(".war-switch-btn").wait_for(state="visible")
card.locator(".war-switch-btn").click()
page.locator(".dialog-overlay.visible .dialog-confirm").wait_for(state="visible")
page.locator(".dialog-overlay.visible .dialog-confirm").click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
card = page.locator("dp-opinion-war")
expect(card.locator(".war-mine-faction")).to_have_text("Left")
def test_guest_sees_disabled_actions(page, app_server):
slug = _seed_war("Sun", "Moon")
page.goto(f"{BASE_URL}/posts/{slug}", wait_until="domcontentloaded")
card = page.locator("dp-opinion-war")
card.wait_for(state="visible")
expect(card.locator(".war-join-a")).to_be_disabled()
expect(card.locator(".war-join-b")).to_be_disabled()