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)