optcg-explorer / tests /test_plot.py
t22000t's picture
Add Synergy Inspector tab: leader-anchored recs with color legality + family bonus
78c571c
"""TDD for spaceutil.plot.build_umap_figure."""
from __future__ import annotations
def test_returns_plotly_figure(synthetic_cards):
import plotly.graph_objects as go
from spaceutil.plot import build_umap_figure
fig = build_umap_figure(synthetic_cards)
assert isinstance(fig, go.Figure)
def test_base_trace_only_without_highlights(synthetic_cards):
from spaceutil.plot import build_umap_figure
fig = build_umap_figure(synthetic_cards, highlight_indices=None)
assert len(fig.data) == 1
def test_overlay_trace_added_for_highlights(synthetic_cards):
from spaceutil.plot import build_umap_figure
fig = build_umap_figure(synthetic_cards, highlight_indices=[0, 2, 5])
assert len(fig.data) == 2
def test_empty_highlights_treated_as_none(synthetic_cards):
from spaceutil.plot import build_umap_figure
fig = build_umap_figure(synthetic_cards, highlight_indices=[])
assert len(fig.data) == 1
def test_base_trace_customdata_carries_corpus_index(synthetic_cards):
"""Click events need to round-trip the corpus index from customdata,
so we can resolve any clicked point back to a corpus row regardless
of which trace it landed on."""
from spaceutil.plot import build_umap_figure
fig = build_umap_figure(synthetic_cards)
base = fig.data[0]
assert base.customdata is not None
assert len(base.customdata) == len(synthetic_cards)
first_row = base.customdata[0]
# customdata rows may be array-like; first element must be the index.
assert int(first_row[0]) == 0
assert int(base.customdata[5][0]) == 5
def test_overlay_trace_customdata_length_matches_highlights(synthetic_cards):
from spaceutil.plot import build_umap_figure
indices = [1, 3, 7, 11]
fig = build_umap_figure(synthetic_cards, highlight_indices=indices)
overlay = fig.data[1]
assert overlay.customdata is not None
assert len(overlay.customdata) == len(indices)
def test_hover_text_includes_card_name(synthetic_cards):
from spaceutil.plot import build_umap_figure
fig = build_umap_figure(synthetic_cards)
base = fig.data[0]
assert base.text is not None
assert len(base.text) == len(synthetic_cards)
assert synthetic_cards[0]["name"] in base.text[0]
def test_cost_curve_figure_with_hits():
import plotly.graph_objects as go
from spaceutil.plot import build_cost_curve_figure
from spaceutil.synergy import SynergyHit
hits = [
SynergyHit(1, "A", "A", 0.9, 0.0, 0.9, False, "Character", ["Red"], 3, "OP01"),
SynergyHit(2, "B", "B", 0.8, 0.0, 0.8, False, "Character", ["Red"], 3, "OP01"),
SynergyHit(3, "C", "C", 0.7, 0.0, 0.7, False, "Event", ["Red"], 7, "OP01"),
]
fig = build_cost_curve_figure(hits)
assert isinstance(fig, go.Figure)
assert len(fig.data) == 1
bar = fig.data[0]
# Two distinct cost buckets: 3 (count 2) and 7 (count 1)
assert list(bar.x) == ["3", "7"]
assert list(bar.y) == [2, 1]
def test_cost_curve_figure_empty_shows_placeholder():
import plotly.graph_objects as go
from spaceutil.plot import build_cost_curve_figure
fig = build_cost_curve_figure([])
assert isinstance(fig, go.Figure)
# Empty case has no bar trace
assert len(fig.data) == 0
# But carries an annotation
assert fig.layout.annotations is not None
assert any("pick a leader" in a.text.lower() for a in fig.layout.annotations)