spaceship/tests/test_crafter_feedback.gd
Loïc Wisniewski 004808f067 Repair a broken working tree, and make every crafter press report
The crafter buttons doing nothing was not the crafter: Ship.gd was left
mid-edit and failed to parse (undeclared SHIELD_COLOR/SHIELD_RADIUS, and
a spawn() that only exists on the concrete VFX subclass). A script that
does not compile takes Main.tscn's whole node tree with it, so every
button in the game was dead. The project directory is the one being
played from, so an unfinished edit there is a broken build - finish or
revert before stopping.

With that fixed, the real gap underneath: an order the crafter cannot
pay for is ACCEPTED and queued (#16), which is indistinguishable from a
dead button. Ordering now reports either "building now" or "queued - N
more ore must be delivered here", and the transient message is rendered
LAST so it is the first line read rather than buried under the standing
fleet-capacity note.

test_crafter_feedback covers all three presses and asserts the message
survives the per-frame refresh, which is the specific way this looked
broken before.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 22:50:09 +02:00

121 lines
4.1 KiB
GDScript

extends SceneTree
# Headless gameplay test: run with
# godot --headless -s res://tests/test_crafter_feedback.gd
# Every press in the ship crafter must say what happened. Reported twice from
# play-testing as "the buttons do nothing".
#
# There is no silent-failure path left to press: storing with an empty hold,
# ordering something the crafter cannot pay for yet, and storing successfully
# all report. The queue in particular is invisible by nature - an order that
# cannot be afforded is accepted and waits (#16), which looks exactly like a
# dead button unless it says so.
#
# Verifies:
# 1. storing with an empty hold explains where ore comes from
# 2. ordering against an empty stock says it is queued AND how much more
# ore the building needs
# 3. a successful store reports the amount
# 4. the message survives the per-frame status refresh - it used to be
# overwritten on the very next frame, which is what made a working
# button look dead
# 5. the message is the FIRST line, not buried under the standing state
var game_state: Node
var scene: Node
var controller: Node
var crafter_id: int = 0
var stage: int = 0
var elapsed: float = 0.0
func _initialize() -> void:
game_state = root.get_node("GameState")
game_state.buildings.clear()
game_state.extractors.clear()
game_state.transporters.clear()
game_state.reset_progression()
game_state.unlocked = ["shipyards", "coordination", "wing"]
game_state.rocks = 100
game_state.fleet_cargo = 0
game_state.ships_owed = 0
scene = load("res://Main.tscn").instantiate()
root.add_child(scene)
controller = scene.get_node("BuildController")
func _first_line() -> String:
return controller.craft_status.text.split("\n")[0]
func _process(delta: float) -> bool:
elapsed += delta
match stage:
0:
scene.get_node("AsteroidField").set_process(false)
scene.get_node("EnemySpawner").set_process(false)
scene.get_node("BuildingField").place(GameState.BUILDING_SHIP_CRAFTER, Vector2(500.0, 0.0))
crafter_id = int(game_state.buildings[-1].id)
elapsed = 0.0
stage = 1
return false
1:
if elapsed < 0.3:
return false
controller.craft_building_id = crafter_id
controller.craft_menu.visible = true
controller._refresh_craft_status()
# --- 1. store with an empty hold ----------------------------
controller._on_store_ore()
var empty_msg: String = _first_line()
if "carrying nothing" not in empty_msg:
print("got: %s" % empty_msg)
print("TEST_CRAFTER_FEEDBACK: FAIL (storing with an empty hold said nothing useful)")
quit(1)
return true
print("empty hold -> %s" % empty_msg)
# --- 2. order against an empty stock ------------------------
controller._on_craft_interceptor()
var queued_msg: String = _first_line()
if "queued" not in queued_msg.to_lower() or "more ore" not in queued_msg:
print("got: %s" % queued_msg)
print("TEST_CRAFTER_FEEDBACK: FAIL (a queued order did not say it was queued and what it still needs)")
quit(1)
return true
if game_state.pending_orders(crafter_id) != 1:
print("TEST_CRAFTER_FEEDBACK: FAIL (the order was not actually queued)")
quit(1)
return true
print("unaffordable order -> %s" % queued_msg)
# --- 3. a successful store ----------------------------------
game_state.withdraw_to_fleet(30)
controller._on_store_ore()
var stored_msg: String = _first_line()
if "Stored" not in stored_msg:
print("got: %s" % stored_msg)
print("TEST_CRAFTER_FEEDBACK: FAIL (a successful store did not report)")
quit(1)
return true
print("successful store -> %s" % stored_msg)
elapsed = 0.0
stage = 2
return false
2:
# --- 4 + 5. it survives the refresh, and stays first --------
if elapsed < 0.5:
return false
controller._refresh_craft_status()
var still: String = _first_line()
if "Stored" not in still:
print("first line half a second later: %s" % still)
print("TEST_CRAFTER_FEEDBACK: FAIL (the message was overwritten by the per-frame refresh)")
quit(1)
return true
print("half a second later, still first: %s" % still)
print("TEST_CRAFTER_FEEDBACK: PASS")
quit(0)
return true
return false