mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-16 23:50:12 +01:00
feat(web): refine commander search and theme UX
This commit is contained in:
parent
fad6ceb13b
commit
0448419d9f
12 changed files with 764 additions and 116 deletions
|
|
@ -1,5 +1,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -69,3 +71,61 @@ def test_commander_catalog_cache_invalidation(tmp_path: Path, monkeypatch: pytes
|
|||
updated = loader.load_commander_catalog()
|
||||
assert updated is not first
|
||||
assert "zada-hedron-grinder" in updated.by_slug
|
||||
|
||||
|
||||
def test_commander_theme_labels_unescape(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
custom_dir = tmp_path / "csv_custom"
|
||||
custom_dir.mkdir()
|
||||
csv_path = custom_dir / "commander_cards.csv"
|
||||
with csv_path.open("w", encoding="utf-8", newline="") as handle:
|
||||
writer = csv.writer(handle)
|
||||
writer.writerow(
|
||||
[
|
||||
"name",
|
||||
"faceName",
|
||||
"edhrecRank",
|
||||
"colorIdentity",
|
||||
"colors",
|
||||
"manaCost",
|
||||
"manaValue",
|
||||
"type",
|
||||
"creatureTypes",
|
||||
"text",
|
||||
"power",
|
||||
"toughness",
|
||||
"keywords",
|
||||
"themeTags",
|
||||
"layout",
|
||||
"side",
|
||||
]
|
||||
)
|
||||
theme_value = json.dumps([r"\+2/\+2 Counters", "+1/+1 Counters"])
|
||||
writer.writerow(
|
||||
[
|
||||
"Escape Tester",
|
||||
"Escape Tester",
|
||||
"1234",
|
||||
"R",
|
||||
"R",
|
||||
"{3}{R}",
|
||||
"4",
|
||||
"Legendary Creature — Archer",
|
||||
"['Archer']",
|
||||
"Test",
|
||||
"2",
|
||||
"2",
|
||||
"",
|
||||
theme_value,
|
||||
"normal",
|
||||
"",
|
||||
]
|
||||
)
|
||||
|
||||
_set_csv_dir(monkeypatch, custom_dir)
|
||||
|
||||
catalog = loader.load_commander_catalog()
|
||||
assert len(catalog.entries) == 1
|
||||
|
||||
record = catalog.entries[0]
|
||||
assert record.themes == ("+2/+2 Counters", "+1/+1 Counters")
|
||||
assert "+2/+2 counters" in record.theme_tokens
|
||||
|
|
|
|||
|
|
@ -76,7 +76,15 @@ def _install_paginated_catalog(monkeypatch: pytest.MonkeyPatch, total: int) -> N
|
|||
search_haystack=f"{name.lower()}"
|
||||
)
|
||||
records.append(record)
|
||||
fake_catalog = SimpleNamespace(entries=tuple(records))
|
||||
_install_custom_catalog(monkeypatch, records)
|
||||
|
||||
|
||||
def _install_custom_catalog(monkeypatch: pytest.MonkeyPatch, records: list) -> None:
|
||||
fake_catalog = SimpleNamespace(
|
||||
entries=tuple(records),
|
||||
by_slug={record.slug: record for record in records},
|
||||
)
|
||||
|
||||
def loader() -> SimpleNamespace:
|
||||
return fake_catalog
|
||||
|
||||
|
|
@ -120,13 +128,7 @@ def test_commanders_show_all_themes_without_overflow(client: TestClient, monkeyp
|
|||
themes=themes,
|
||||
theme_tokens=tuple(theme.lower() for theme in themes),
|
||||
)
|
||||
fake_catalog = SimpleNamespace(entries=(enriched,))
|
||||
|
||||
def loader() -> SimpleNamespace:
|
||||
return fake_catalog
|
||||
|
||||
monkeypatch.setattr(commander_catalog_loader, "load_commander_catalog", loader)
|
||||
monkeypatch.setattr(commanders, "load_commander_catalog", loader)
|
||||
_install_custom_catalog(monkeypatch, [enriched])
|
||||
|
||||
response = client.get("/commanders")
|
||||
assert response.status_code == 200
|
||||
|
|
@ -135,3 +137,155 @@ def test_commanders_show_all_themes_without_overflow(client: TestClient, monkeyp
|
|||
assert "commander-theme-chip-more" not in body # no overflow badge rendered
|
||||
for name in themes:
|
||||
assert name in body
|
||||
|
||||
|
||||
def _commander_fixture(sample, *, name: str, slug: str, themes: tuple[str, ...] = ()):
|
||||
return replace(
|
||||
sample,
|
||||
name=name,
|
||||
face_name=name,
|
||||
display_name=name,
|
||||
slug=slug,
|
||||
themes=themes,
|
||||
theme_tokens=tuple(theme.lower() for theme in themes),
|
||||
search_haystack="|".join([name.lower(), *[theme.lower() for theme in themes]]),
|
||||
)
|
||||
|
||||
|
||||
def test_commanders_search_ignores_theme_tokens(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
catalog = load_commander_catalog()
|
||||
sample = catalog.entries[0]
|
||||
target = _commander_fixture(
|
||||
sample,
|
||||
name="Avatar Aang // Aang, Master of Elements",
|
||||
slug="avatar-aang",
|
||||
themes=("Elemental", "Avatar"),
|
||||
)
|
||||
other = _commander_fixture(
|
||||
sample,
|
||||
name="Generic Guardian",
|
||||
slug="generic-guardian",
|
||||
themes=("Avatar", "Guardian"),
|
||||
)
|
||||
_install_custom_catalog(monkeypatch, [target, other])
|
||||
|
||||
response = client.get("/commanders", params={"q": "Avatar Aang"})
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
|
||||
assert 'data-commander-slug="avatar-aang"' in body
|
||||
assert 'data-commander-slug="generic-guardian"' not in body
|
||||
|
||||
|
||||
def test_commanders_search_supports_token_reordering(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
catalog = load_commander_catalog()
|
||||
sample = catalog.entries[0]
|
||||
target = _commander_fixture(
|
||||
sample,
|
||||
name="Avatar Aang // Aang, Master of Elements",
|
||||
slug="avatar-aang",
|
||||
themes=("Elemental",),
|
||||
)
|
||||
fallback = _commander_fixture(
|
||||
sample,
|
||||
name="Master of Avatar Arts",
|
||||
slug="master-of-avatar-arts",
|
||||
themes=("Avatar",),
|
||||
)
|
||||
_install_custom_catalog(monkeypatch, [target, fallback])
|
||||
|
||||
response = client.get("/commanders", params={"q": "Aang Avatar"})
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
|
||||
assert 'data-commander-slug="avatar-aang"' in body
|
||||
assert 'data-commander-slug="master-of-avatar-arts"' not in body
|
||||
|
||||
|
||||
def test_commanders_theme_search_filters(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
catalog = load_commander_catalog()
|
||||
sample = catalog.entries[0]
|
||||
aggro_commander = _commander_fixture(
|
||||
sample,
|
||||
name="Aggro Ace",
|
||||
slug="aggro-ace",
|
||||
themes=("Aggro", "Combat"),
|
||||
)
|
||||
control_commander = _commander_fixture(
|
||||
sample,
|
||||
name="Control Keeper",
|
||||
slug="control-keeper",
|
||||
themes=("Control", "Value"),
|
||||
)
|
||||
_install_custom_catalog(monkeypatch, [aggro_commander, control_commander])
|
||||
|
||||
response = client.get("/commanders", params={"theme": "Aggo"})
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
|
||||
assert 'data-commander-slug="aggro-ace"' in body
|
||||
assert 'data-commander-slug="control-keeper"' not in body
|
||||
assert 'data-theme-suggestion="Aggro"' in body
|
||||
assert 'id="theme-suggestions"' in body
|
||||
assert 'option value="Aggro"' in body
|
||||
|
||||
|
||||
def test_commanders_theme_recommendations_render_in_fragment(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
catalog = load_commander_catalog()
|
||||
sample = catalog.entries[0]
|
||||
aggro_commander = _commander_fixture(
|
||||
sample,
|
||||
name="Aggro Ace",
|
||||
slug="aggro-ace",
|
||||
themes=("Aggro", "Combat"),
|
||||
)
|
||||
control_commander = _commander_fixture(
|
||||
sample,
|
||||
name="Control Keeper",
|
||||
slug="control-keeper",
|
||||
themes=("Control", "Value"),
|
||||
)
|
||||
_install_custom_catalog(monkeypatch, [aggro_commander, control_commander])
|
||||
|
||||
response = client.get(
|
||||
"/commanders",
|
||||
params={"theme": "Aggo"},
|
||||
headers={"HX-Request": "true"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
|
||||
assert 'data-theme-suggestion="Aggro"' in body
|
||||
assert 'data-commander-slug="aggro-ace"' in body
|
||||
|
||||
|
||||
def test_commander_name_fuzzy_tightened(client: TestClient, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
catalog = load_commander_catalog()
|
||||
sample = catalog.entries[0]
|
||||
finneas = _commander_fixture(
|
||||
sample,
|
||||
name="Finneas, Ace Archer",
|
||||
slug="finneas-ace-archer",
|
||||
themes=("Aggro", "Counters"),
|
||||
)
|
||||
torgal = _commander_fixture(
|
||||
sample,
|
||||
name="Torgal, A Fine Hound",
|
||||
slug="torgal-a-fine-hound",
|
||||
themes=("Aggro", "Combat"),
|
||||
)
|
||||
gorbag = _commander_fixture(
|
||||
sample,
|
||||
name="Gorbag of Minas Morgul",
|
||||
slug="gorbag-of-minas-morgul",
|
||||
themes=("Aggro", "Treasure"),
|
||||
)
|
||||
_install_custom_catalog(monkeypatch, [finneas, torgal, gorbag])
|
||||
|
||||
response = client.get("/commanders", params={"q": "Finneas"})
|
||||
assert response.status_code == 200
|
||||
body = response.text
|
||||
|
||||
assert 'data-commander-slug="finneas-ace-archer"' in body
|
||||
assert 'data-commander-slug="torgal-a-fine-hound"' not in body
|
||||
assert 'data-commander-slug="gorbag-of-minas-morgul"' not in body
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue