File size: 5,169 Bytes
a8afc36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39c3669
 
a8afc36
 
39c3669
 
a8afc36
 
 
 
 
 
 
 
39c3669
a8afc36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39c3669
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
import forge
from app import (
    CSS,
    background_selector_html,
    refresh_screen,
    school_asset_name,
    school_selector_html,
    show_background,
    show_draft_from_rules,
    show_name,
    show_school,
    start_rules,
)
from ui import new_run_shell


class AppPackClient:
    # Return one deterministic draft pack for app-flow tests.
    def __init__(self) -> None:
        self.calls = 0

    # Return a three-card model payload.
    def create_pack(self, payload):
        self.calls += 1
        card = lambda index: {
            "name": f"Flow Card {self.calls}-{index}",
            "flavor": "The table waits.",
            "art_prompt": "glowing spell on a wooden desk",
            "effects": [{"primitive_id": "deal", "weight": 1}],
        }
        return {"cards": [card(index) for index in range(payload["pack_size"])]}


class AppArtClient:
    # Count image requests made after card text is forged.
    def __init__(self) -> None:
        self.calls = 0

    # Return a deterministic image URI.
    def create_art(self, prompt: str) -> str:
        self.calls += 1
        return f"uri:{prompt}"


# Verify setup advances through one prompt per screen.
def test_setup_screen_steps() -> None:
    assert show_name()[1] == "name"
    assert show_background()[1] == "background"
    assert show_school()[1] == "school"


# Verify the title CTA stays compact instead of full-width.
def test_play_now_button_is_compact() -> None:
    assert "#play-now-btn" in CSS
    assert "max-width: 220px" in CSS
    assert "#start-draft-btn" in CSS
    assert "max-width: 200px" in CSS


# Verify draft cards are compact enough for the draft table.
def test_draft_cards_are_compact() -> None:
    assert "minmax(0, 300px)" in CSS
    assert "min-height: 405px" in CSS


# Verify setup selectors are image-backed clickable panels with hover copy.
def test_setup_selectors_use_image_panels() -> None:
    background = background_selector_html()
    school = school_selector_html()
    assert "selector-panel" in background
    assert "selector-image" in background
    assert "selector-copy" in background
    assert "background-btn-dark-fantasy" in background
    assert "You are a mage" in background
    assert "school-btn-fire" in school
    assert "school-btn-ice" in school
    assert "school-btn-earth" in school


# Verify selector art resolves to the bundled user-provided filenames.
def test_setup_selectors_use_bundled_assets() -> None:
    background = background_selector_html()

    assert "data:image/png;base64" in background
    assert school_asset_name("dark-fantasy", "fire") == "darkFantasyFire.png"
    assert school_asset_name("dark-fantasy", "ice") == "darkFantasyIce.png"
    assert school_asset_name("dark-fantasy", "earth") == "darkFantasyEarth.png"
    assert school_asset_name("cyberpunk", "fire") == "cyberpunkFire.png"
    assert school_asset_name("cyberpunk", "ice") == "cyberpunkice.png"
    assert school_asset_name("cyberpunk", "earth") == "cyberpunkEarth.png"
    assert school_asset_name("anime", "fire") == "animeFire.png"
    assert school_asset_name("anime", "ice") == "animeIce.png"
    assert school_asset_name("anime", "earth") == "animeEarth.png"
    assert "data:image/png;base64" in school_selector_html("Cyberpunk")


# Verify the rules screen starts deck generation in the background.
def test_start_rules_queues_deck_generation(monkeypatch) -> None:
    forge.reset()
    client = AppPackClient()
    monkeypatch.setattr("app.card_client_from_env", lambda: client)
    monkeypatch.setattr("app.art_client_from_env", lambda: None)

    output = start_rules("Ada", "ice", "Anime")
    state = output[0]

    # start_rules now lands on the boss reveal screen (rules follow on Continue).
    assert output[1] == "reveal"
    assert state.player_name == "Ada"
    assert state.school == "ice"
    assert "reveal-screen" in output[10]
    assert "The Necrolich" in output[10]

    forge.drain()
    refresh_screen(state, "rules")
    assert client.calls > 0


# Verify the rules screen starts image work once card text is ready.
def test_rules_screen_starts_art_generation(monkeypatch) -> None:
    monkeypatch.setattr("ui.MIN_DRAFT_LOADING_SECONDS", 0.0)
    forge.reset()
    client = AppPackClient()
    art_client = AppArtClient()
    monkeypatch.setattr("app.card_client_from_env", lambda: client)
    monkeypatch.setattr("app.art_client_from_env", lambda: art_client)

    state = start_rules("Ada", "fire", "Cyberpunk")[0]
    forge.drain()
    state = refresh_screen(state, "rules")[0]
    forge.drain()
    refresh_screen(state, "rules")

    assert art_client.calls >= 3


# Verify skipping rules shows a deck-loading draft state if the pack is still pending.
def test_skip_rules_shows_loading_deck(monkeypatch) -> None:
    forge.reset()
    monkeypatch.setattr("app.card_client_from_env", lambda: None)
    monkeypatch.setattr("app.art_client_from_env", lambda: None)

    state = new_run_shell("Ada", "Anime", "fire", seed=1)
    output = show_draft_from_rules(state)

    assert output[1] == "draft"
    assert output[0].loading == "Loading your deck"
    assert "Loading your deck" in output[12]