Files
aetherbound-guild/runtime/tests/p9_9/layout_accessibility_test.gd
T

196 lines
8.5 KiB
GDScript

extends SceneTree
const Controller = preload("res://p9_9/runtime_controller.gd")
const RunService = preload("res://p9_9/run_service.gd")
const BattleService = preload("res://p9_9/battle_service.gd")
const Fixture = preload("res://tests/p9_9/runtime_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_P9_9_LAYOUT_CAPTURE_ROOT")
if not capture_root.is_empty():
DirAccess.make_dir_recursive_absolute(capture_root)
var unclaimed := Fixture.closed_p98()
var draft := draft_state("CH-RG01-THREAT-01")
var market_one := market_one_state("CH-RG01-CATALOG-01", false)
var full_market := market_one_state("CH-RG01-CATALOG-01", true)
var catalog_two := market_state_after(1, "CH-RG01-CATALOG-01")
var threat_two := market_state_after(1, "CH-RG01-THREAT-01")
var recovery := recovery_state()
var market_four := market_state_after(3, "CH-RG01-THREAT-01")
await fixture("01-charter-zh", unclaimed, Vector2i(844, 390), "zh_CN", 1.0, false, false)
await fixture("02-draft-en-130", draft, Vector2i(760, 360), "en", 1.3, false, false)
await fixture("03-market1-zh", market_one, Vector2i(844, 390), "zh_CN", 1.0, false, false)
await fixture("04-market1-full-large", full_market, Vector2i(1280, 720), "en", 1.0, false, false)
await fixture("05-catalog-market2", catalog_two, Vector2i(844, 390), "zh_CN", 1.0, true, false)
await fixture("06-threat-market2-130", threat_two, Vector2i(844, 390), "en", 1.3, false, false, "P99-R2-Q1")
await battle_fixture("07-battle-low-power", full_market, Vector2i(844, 390), "zh_CN", 1.0, false, true)
await fixture("08-recovery-reduced", recovery, Vector2i(844, 390), "en", 1.0, true, false)
await fixture("09-market4-large", market_four, Vector2i(1280, 720), "zh_CN", 1.0, false, false)
await fixture("10-portrait-guidance", unclaimed, Vector2i(390, 844), "zh_CN", 1.0, false, false, "", true)
if is_instance_valid(experience):
experience.queue_free()
for _index in 45:
await process_frame
if failures.is_empty():
print("ABG_P9_9_LAYOUT_ACCESSIBILITY_OK fixtures=10 states=charter,draft,market1,formation,catalog,threat,battle,recovery,market4,portrait viewports=760x360,844x390,1280x720,390x844 locales=zh_CN,en text_130=true reduced_motion=true muted=true low_power=true controls=48 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, text_scale: float, reduced: bool, low_power: bool, focus_route := "", portrait := false) -> void:
await mount(snapshot, viewport, locale, text_scale, reduced, low_power)
if not String(focus_route).is_empty():
experience.focused_route_id = String(focus_route)
experience._queue_render()
await settle_frames()
verify_current(label, viewport, portrait)
await capture(label)
func battle_fixture(label: String, snapshot: Dictionary, viewport: Vector2i, locale: String, text_scale: float, reduced: bool, low_power: bool) -> void:
await mount(snapshot, viewport, locale, text_scale, reduced, low_power)
experience.focused_route_id = "P99-R1-Q1"
experience.tap_route("P99-R1-Q1")
experience.input_guard_until_frame = -1
experience.tap_route("P99-R1-Q1")
experience.battle_auto_advance = false
for _index in 24:
experience.advance_battle_ticks(1)
await settle_frames()
verify_current(label, viewport, false)
expect(experience.battle_active and experience.find_child("P97BattleActor_P99-D1", true, false) != null, "battle fixture shows the moving Company")
await capture(label)
func mount(snapshot: Dictionary, viewport: Vector2i, locale: String, text_scale: float, reduced: bool, low_power: bool) -> void:
if is_instance_valid(experience):
experience.queue_free()
await settle_frames()
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 = text_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 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 panels" % label)
var header_subtitle := experience.find_child("P99HeaderSubtitle", true, false) as Label
if header_subtitle != null:
expect(bounds.encloses(header_subtitle.get_global_rect()), "%s header subtitle stays in viewport" % label)
expect(header_subtitle.get_visible_line_count() == header_subtitle.get_line_count(), "%s header subtitle has no hidden lines" % label)
for node in experience.find_children("*", "Button", true, false):
var button := node as Button
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 in viewport" % [label, button.name])
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" % label)
func draft_state(claim_id: String) -> Dictionary:
var service = RunService.new(Fixture.claimed_profile(claim_id))
return service.state.snapshot()
func market_one_state(claim_id: String, full: bool) -> Dictionary:
var service: Variant = prepared_company(claim_id, full)
return service.state.snapshot()
func market_state_after(clears: int, claim_id: String) -> Dictionary:
var service: Variant = prepared_company(claim_id, true)
for round_index in range(1, clears + 1):
play(service, "P99-R%d-Q0" % round_index, "layout-%s-%d" % [claim_id, round_index])
return service.state.snapshot()
func recovery_state() -> Dictionary:
var service = RunService.new(Fixture.claimed_profile("CH-RG01-THREAT-01"))
for index in range(2):
service.set_provisional("layout-risk-select-%d" % index, ["P99-D2", "P99-D4"][index], true)
service.confirm_run("layout-risk-create")
service.hire("layout-risk-hire-0", "P99-M1-H3")
service.hire("layout-risk-hire-1", "P99-M1-H4")
play(service, "P99-R1-Q2", "layout-risk-r1")
var result: Dictionary = play(service, "P99-R2-Q2", "layout-risk-r2")
expect(result.outcome == "defeat" and service.state.recovery_required, "recovery fixture reaches natural defeat")
return service.state.snapshot()
func prepared_company(claim_id: String, full: bool):
var service = RunService.new(Fixture.claimed_profile(claim_id))
service.set_provisional("layout-select-0", "P99-D1", true)
service.set_provisional("layout-select-1", "P99-D3", true)
service.confirm_run("layout-create")
if full:
service.hire("layout-hire-0", "P99-M1-H1")
service.hire("layout-hire-1", "P99-M1-H2")
return service
func play(service, route_id: String, prefix: String) -> Dictionary:
service.commit_route("%s-route" % prefix, route_id)
var battle = BattleService.new(service.state.selected_charter_claim_id)
battle.start_from_market(service.state)
var guard := 0
while not battle.state.terminal and guard < 1800:
battle.advance_ticks(1)
guard += 1
var snapshot: Dictionary = battle.state.snapshot()
service.settle_battle("%s-settle" % prefix, snapshot)
return {"outcome": battle.state.outcome, "snapshot": snapshot}
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() -> void:
await process_frame
await process_frame
await process_frame
await process_frame
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("P9.9 layout: %s" % message)