extends SceneTree const Controller = preload("res://p10_2/runtime_controller.gd") const BattleService = preload("res://p10_2/mid_contract_battle_service.gd") const ResolutionService = preload("res://p10_2/mid_contract_resolution_service.gd") const Fixture = preload("res://tests/p10_2/mid_contract_fixture.gd") var failures: Array[String] = [] var experience var capture_root := "" var captures := 0 func _initialize() -> void: call_deferred("run_test") func run_test() -> void: capture_root = OS.get_environment("ABG_P10_2_LAYOUT_CAPTURE_ROOT") if not capture_root.is_empty(): DirAccess.make_dir_recursive_absolute(capture_root) await fixture("01-market2-small-zh", Fixture.new().migrated_snapshot(), Vector2i(760, 360), "zh_CN", 1.0, false, false, Controller.P102_SCREEN_MARKET) await fixture("02-market3-reference-en130", Fixture.new().market_service_at(3).state.snapshot(), Vector2i(844, 390), "en", 1.3, false, false, Controller.P102_SCREEN_MARKET) await fixture("03-plan-large-zh", Fixture.new().prepared_service(3, 2).state.snapshot(), Vector2i(1280, 720), "zh_CN", 1.0, false, false, Controller.P102_SCREEN_PLAN) await battle_fixture("04-battle-reference-en", Vector2i(844, 390), "en", 1.0, false, false, 28) await result_fixture("05-result-small-zh130", Vector2i(760, 360), "zh_CN", 1.3) await fixture("06-recovery-reference-zh", recovery_snapshot(), Vector2i(844, 390), "zh_CN", 1.0, false, false, Controller.P102_SCREEN_RECOVERY) await fixture("07-market5-large-reduced", Fixture.new().market_service_at(5).state.snapshot(), Vector2i(1280, 720), "zh_CN", 1.0, true, false, Controller.P102_SCREEN_MARKET_5) await settings_fixture("08-settings-reference-en130", Fixture.new().migrated_snapshot(), Vector2i(844, 390), "en", 1.3) await fixture("09-market-low-power", Fixture.new().market_service_at(4).state.snapshot(), Vector2i(844, 390), "zh_CN", 1.0, false, true, Controller.P102_SCREEN_MARKET) await fixture("10-portrait-guidance", Fixture.new().migrated_snapshot(), Vector2i(390, 844), "zh_CN", 1.0, false, false, Controller.P102_SCREEN_MARKET, true) if is_instance_valid(experience): experience.queue_free() await settle_frames(45) if failures.is_empty(): print("ABG_P10_2_LAYOUT_ACCESSIBILITY_OK fixtures=10 states=market2,market3,plan,battle,result,recovery,market5,settings,low_power,rotate viewports=760x360,844x390,1280x720,390x844 locales=zh_CN,en text_130=true reduced_motion=true muted=true low_power=true controls=48 panels=0 cards=0 captures=%d" % captures) quit(0) else: for failure in failures: push_error(failure) quit(1) func fixture(label: String, snapshot: Dictionary, viewport: Vector2i, locale: String, scale: float, reduced: bool, low_power: bool, expected_screen: String, portrait := false) -> void: await mount(snapshot, viewport, locale, scale, reduced, low_power) expect(experience.screen == expected_screen, "%s restores %s; got %s" % [label, expected_screen, experience.screen]) verify_current(label, viewport, portrait) await capture(label) func battle_fixture(label: String, viewport: Vector2i, locale: String, scale: float, reduced: bool, low_power: bool, ticks: int) -> void: await mount(Fixture.new().prepared_service(4, 1).state.snapshot(), viewport, locale, scale, reduced, low_power) experience.choose_region_plan("guard_center") experience.advance_region_battle_ticks(ticks) await settle_frames() expect(experience.screen == Controller.P102_SCREEN_BATTLE and not experience.region_battle_complete, "%s shows live battle" % label) expect(experience.find_children("P101BattleActor_*", "TextureRect", true, false).size() >= 2, "%s shows the Company line" % label) expect(not experience.find_children("P101BattleEnemy_*", "TextureRect", true, false).is_empty(), "%s shows entered enemies" % label) verify_current(label, viewport, false) await capture(label) func result_fixture(label: String, viewport: Vector2i, locale: String, scale: float) -> void: await mount(Fixture.new().prepared_service(4, 1).state.snapshot(), viewport, locale, scale, false, false) experience.choose_region_plan("guard_center") experience.advance_region_battle_ticks(999) await settle_frames() expect(experience.region_battle_complete and experience.find_child("P101BattleResultBand", true, false) != null, "%s shows terminal result" % label) verify_current(label, viewport, false) await capture(label) func settings_fixture(label: String, snapshot: Dictionary, viewport: Vector2i, locale: String, scale: float) -> void: await mount(snapshot, viewport, locale, scale, true, false) experience.toggle_settings() await settle_frames() expect(experience.find_child("P100SettingsBand", true, false) != null, "%s exposes inherited settings" % label) verify_current(label, viewport, false) await capture(label) func recovery_snapshot() -> Dictionary: var service = Fixture.new().prepared_service(4, 2) var battle: Dictionary = BattleService.new(service.state.snapshot()).run_to_terminal("hold_line") var resolver = ResolutionService.new(service.state.snapshot()) var settled: Dictionary = resolver.settle("p102-layout-recovery", resolver.state.revision, battle.snapshot) return Dictionary(settled.snapshot).duplicate(true) if settled.ok else {} func mount(snapshot: Dictionary, viewport: Vector2i, locale: String, scale: float, reduced: bool, low_power: bool) -> void: if is_instance_valid(experience): experience.queue_free() await settle_frames(12) root.size = viewport root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND root.content_scale_size = viewport experience = Controller.new() experience.persistence_enabled = false experience.battle_auto_advance = false experience.locale = locale experience.text_scale = scale experience.reduced_motion = reduced experience.muted = true experience.low_power = low_power experience.size = Vector2(viewport) experience.configure_entry(snapshot) root.add_child(experience) await settle_frames() func verify_current(label: String, viewport: Vector2i, portrait: bool) -> void: var bounds := Rect2(Vector2.ZERO, Vector2(viewport)) if portrait: expect(experience.find_child("P97RotateNotice", true, false) != null, "%s shows landscape guidance" % label) return expect(experience.find_child("P97RotateNotice", true, false) == null, "%s renders the landscape world" % label) expect(float(experience.get_meta("visible_world_ratio", 0.0)) >= 0.80, "%s keeps world-first composition" % label) expect(experience.find_children("*", "Panel", true, false).is_empty(), "%s adds no app Panel surfaces" % label) for node in experience.find_children("*", "Button", true, false): var button := node as Button if not button.visible or button.is_queued_for_deletion(): continue expect(button.size.x >= 48 and button.size.y >= 48, "%s target %s is at least 48px" % [label, button.name]) expect(bounds.encloses(button.get_global_rect()), "%s target %s stays inside %s" % [label, button.name, viewport]) for node in experience.find_children("*", "Control", true, false): var lowered := String(node.name).to_lower() expect("card" not in lowered and "modal" not in lowered and "dialog" not in lowered, "%s has no card/modal surface: %s" % [label, node.name]) func capture(label: String) -> void: if capture_root.is_empty(): return await RenderingServer.frame_post_draw var error := root.get_texture().get_image().save_png(capture_root.path_join(label + ".png")) expect(error == OK, "%s capture writes" % label) if error == OK: captures += 1 await process_frame func settle_frames(count := 5) -> void: for _index in count: await process_frame func expect(condition: bool, message: String) -> void: if not condition: failures.append("P10.2 layout: %s" % message)