Files
aetherbound-guild/runtime/tests/dev3/guild_contract_choice_test.gd
T

167 lines
8.2 KiB
GDScript

extends SceneTree
const EntryShell = preload("res://entry/entry_shell.gd")
const SettingsStore = preload("res://entry/settings_store.gd")
const DraftService = preload("res://entry/initial_company_draft_service.gd")
const DRAFT_PATH := "user://dev3_guild_contract_draft.json"
const COMPANY_PATH := "user://dev3_guild_contract_company.json"
const SETTINGS_PATH := "user://dev3_guild_contract_settings.json"
const SEED := "11223344556677889900aabbccddeeff"
var failures: Array[String] = []
var parsed_touches := 0
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
root.content_scale_size = Vector2i(844, 390)
root.size = Vector2i(844, 390)
_cleanup()
var company := _create_company()
var settings_store := SettingsStore.new()
settings_store.path = SETTINGS_PATH
var settings := settings_store.defaults()
settings.setup_completed = true
settings.locale = "en"
settings.text_scale = 1.3
settings_store.save_settings(settings)
var shell = await _shell(settings)
shell.show_guild_handoff(company)
await _frames(3)
expect(shell.current_screen == "guild_handoff", "opening Company reaches the real Guild")
var contract_button := shell.find_child("GuildContractBoard", true, false) as Button
var market_unavailable := shell.find_child("GuildMarketUnavailable", true, false) as Button
expect(contract_button != null and not contract_button.disabled, "Guild identifies Contract Board as the available next action")
expect(market_unavailable != null and market_unavailable.disabled, "Market remains disabled before Contract creation")
expect("first Contract" in shell.find_child("GuildMarketReason", true, false).text, "disabled Market gives an exact reason")
await _touch(contract_button)
expect(shell.current_screen == "contract_choice", "parsed touch opens Contract review")
expect(shell.find_child("ContractDifficultyStandard", true, false).get_meta("selected", false), "Standard is the initial explicit difficulty")
await _touch(shell.find_child("ContractDifficultyWayfinder", true, false))
expect(shell.current_screen == "contract_choice" and shell.contract_difficulty == "wayfinder", "difficulty changes without committing")
expect(_draft_service().load_company().company.active_contract.is_empty(), "difficulty selection leaves persistence unchanged")
await _touch(shell.find_child("ContractCancel", true, false))
expect(shell.current_screen == "guild_handoff" and _draft_service().load_company().company.active_contract.is_empty(), "Cancel returns to the same Guild with no Contract")
await _touch(shell.find_child("GuildContractBoard", true, false))
await _touch(shell.find_child("ContractDifficultyStandard", true, false))
expect(shell.contract_difficulty == "standard", "player can make a different legal choice after Cancel")
await _touch(shell.find_child("ContractConfirm", true, false))
expect(shell.current_screen == "market", "explicit confirm creates the Contract and reaches its Market")
var committed: Dictionary = _draft_service().load_company().company
expect(not committed.active_contract.is_empty() and committed.active_contract.difficulty == "standard", "confirmed difficulty is the only persisted choice")
expect(int(committed.active_contract.coin) == 52 and int(committed.active_contract.line_capacity) == 4, "Market handoff shows authoritative starting resources")
expect(committed.owned_recruits.size() == 2 and committed.active_contract.hire_receipts.is_empty(), "DEV-3B creates no Recruit purchase")
# The resource rail renders caption and value inside a hand-drawn badge
# (M58-C); the label carries the text now, with the compact LINE copy.
var company_badge := shell.find_child("MarketCompanyStatus", true, false) as Control
var company_label := company_badge.find_child("StatusLabel", true, false) as Label if company_badge != null else null
expect(company_label != null and company_label.text == "LINE 2/4", "visible status distinguishes Company from Line capacity")
expect(shell.find_child("MarketNoSelection", true, false) != null and shell.find_child("MarketOfferList", true, false).get_child_count() == 3, "Market opens with no recommended offer and all three Recruit rows")
var board_hash := String(committed.active_contract.market_board.board_hash)
await _touch(shell.find_child("MarketBackGuild", true, false))
expect(shell.current_screen == "guild_handoff", "Market Back returns to Guild")
var market_button := shell.find_child("GuildMarket", true, false) as Button
expect(market_button != null and not market_button.disabled and shell.find_child("GuildContractActive", true, false).disabled, "active Contract makes Market available and prevents a second Contract")
await _touch(market_button)
expect(shell.current_screen == "market", "active Contract opens the current Market")
# The reconstructed Market owns Back to Guild and Settings. Title is a
# Guild-owned utility boundary, so follow the approved player-order route
# instead of treating the read-only Market title label as a button.
await _touch(shell.find_child("MarketBackGuild", true, false))
expect(shell.current_screen == "guild_handoff", "Market Back returns to Guild")
await _touch(shell.find_child("GuildTitle", true, false))
expect(shell.current_screen == "title", "Guild Title command returns to Title without changing state")
await _touch(shell.find_child("TitleContinue", true, false))
expect(shell.current_screen == "market", "Continue resumes at the active Market destination")
shell.queue_free()
await _frames(3)
var restarted = await _shell(settings)
restarted._open_after_boot()
await _frames(4)
expect(restarted.current_screen == "market", "process restart resumes the committed Market")
var reloaded: Dictionary = _draft_service().load_company().company
expect(String(reloaded.active_contract.market_board.board_hash) == board_hash and reloaded.active_contract.hire_receipts.is_empty(), "restart preserves the exact board and zero purchases")
restarted.queue_free()
await _frames(3)
_cleanup()
if failures.is_empty():
print("ABG_DEV3_GUILD_OK guild_next=contract difficulty=select_cancel_confirm contract=once market_handoff=true company=2 line=4 coin=52 purchase=0 continue=market restart=true parsed_touch=%d" % parsed_touches)
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func _create_company() -> Dictionary:
var service := _draft_service()
var draft: Dictionary = service.begin_or_load(SEED)
var ids := [String(draft.draft.offers[0].offer_id), String(draft.draft.offers[2].offer_id)]
service.set_selection(ids)
var committed: Dictionary = service.commit_company()
expect(committed.ok, "fixture creates the persistent two-Recruit Company")
return committed.company if committed.ok else {}
func _shell(settings: Dictionary):
var shell = EntryShell.new()
shell.auto_start = false
shell.settings_store.path = SETTINGS_PATH
shell.initial_company_service.path = DRAFT_PATH
shell.initial_company_service.company_path = COMPANY_PATH
root.add_child(shell)
await _frames(3)
shell.settings = settings.duplicate(true)
return shell
func _draft_service() -> RefCounted:
var service := DraftService.new()
service.path = DRAFT_PATH
service.company_path = COMPANY_PATH
return service
func _touch(control: Control) -> void:
if control == null:
expect(false, "touch target exists")
return
var center := root.get_final_transform() * control.get_global_rect().get_center()
var press := InputEventScreenTouch.new()
press.position = center
press.pressed = true
press.index = 0
Input.parse_input_event(press)
await _frames(2)
var release := InputEventScreenTouch.new()
release.position = center
release.pressed = false
release.index = 0
Input.parse_input_event(release)
parsed_touches += 1
await _frames(5)
func _cleanup() -> void:
for base in [DRAFT_PATH, COMPANY_PATH, SETTINGS_PATH]:
for suffix in ["", ".bak", ".candidate", ".bak.candidate", ".corrupt"]:
var file_path := "%s%s" % [base, suffix]
if FileAccess.file_exists(file_path):
DirAccess.remove_absolute(file_path)
func _frames(count: int) -> void:
for frame in range(count):
await process_frame
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("DEV-3 Guild/Contract: %s" % message)