feat(web): refine commander search and theme UX

This commit is contained in:
matt 2025-10-01 10:54:32 -07:00
parent fad6ceb13b
commit 0448419d9f
12 changed files with 764 additions and 116 deletions

View file

@ -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