Files
aetherbound-guild/runtime/tests/p9_1_layout_test.gd
T

148 lines
4.6 KiB
GDScript

extends SceneTree
const MainScene = preload("res://main.tscn")
const FIXTURES := [
{"size": Vector2i(1280, 720), "locale": "zh_CN", "scale": 1.0},
{"size": Vector2i(844, 390), "locale": "en", "scale": 1.0},
{"size": Vector2i(390, 844), "locale": "zh_CN", "scale": 1.3},
]
const STATES := ["contract", "offers", "market", "equipment", "order", "ready"]
var failures: Array[String] = []
var fixture_count := 0
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
for fixture in FIXTURES:
for state in STATES:
await verify_fixture(fixture, state)
if failures.is_empty():
print("ABG_P9_1_LAYOUT_OK fixtures=%d viewports=3 states=6 locales=2 text_130=true" % fixture_count)
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_fixture(fixture: Dictionary, state: String) -> void:
var viewport := SubViewport.new()
viewport.size = fixture.size
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var game := MainScene.instantiate()
game.auto_boot = false
viewport.add_child(game)
await settle()
game.boot_verified = true
game.page = game.AppPage.RUN
game.locale = fixture.locale
game.text_scale = fixture.scale
advance_to(game, state)
game.render()
await settle()
fixture_count += 1
var label := "%sx%s/%s/%s/%s" % [fixture.size.x, fixture.size.y, fixture.locale, str(fixture.scale), state]
var bounds := Rect2(Vector2.ZERO, Vector2(fixture.size))
for node_name in ["TopBar", "CurrentDecision", "Footer", "PrimaryAction"]:
var control := find_control(game, node_name)
expect(control != null, "%s missing %s" % [label, node_name])
if control != null:
expect(contains_rect(bounds, control.get_global_rect()), "%s %s outside viewport: %s" % [label, node_name, control.get_global_rect()])
var top := find_control(game, "TopBar")
var decision := find_control(game, "CurrentDecision")
var footer := find_control(game, "Footer")
if top != null and decision != null and footer != null:
expect(not top.get_global_rect().intersects(decision.get_global_rect()), "%s top overlaps decision" % label)
expect(not footer.get_global_rect().intersects(decision.get_global_rect()), "%s footer overlaps decision" % label)
verify_interactive_bounds(game, bounds, label)
game.queue_free()
viewport.queue_free()
await drain_cleanup()
func advance_to(game: Control, target: String) -> void:
if target == "contract":
return
game.run.select_contract()
game.run.select_difficulty("standard")
game.run.open_offers()
game.run.inspect_offer("OFF-03")
game.run.inspect_offer("OFF-04")
game.run.toggle_offer("OFF-03")
game.run.toggle_offer("OFF-04")
if target == "offers":
return
game.run.review_opening()
game.run.commit_opening()
game.run.open_market()
if target == "market":
return
game.run.hire("MKT-R1-01")
game.run.hire("MKT-R1-02")
if target == "equipment":
return
game.run.purchase_equipment()
game.run.equip_member("OFF-03")
if target == "order":
return
game.run.choose_order_unit("OFF-03")
game.run.choose_order_unit("MKT-R1-01")
game.run.commit_order()
func verify_interactive_bounds(node: Node, bounds: Rect2, label: String) -> void:
if node is BaseButton and node.is_visible_in_tree():
var rect: Rect2 = node.get_global_rect()
if inside_scroll(node):
expect(rect.position.x >= -0.5 and rect.end.x <= bounds.end.x + 0.5, "%s button %s has horizontal overflow: %s" % [label, node.name, rect])
else:
expect(contains_rect(bounds, rect), "%s button %s outside viewport: %s" % [label, node.name, rect])
expect(rect.size.x >= 44.0 and rect.size.y >= 44.0, "%s button %s below 44pt: %s" % [label, node.name, rect.size])
for child in node.get_children():
verify_interactive_bounds(child, bounds, label)
func find_control(node: Node, node_name: String) -> Control:
if node.name == node_name and node is Control and not node.is_queued_for_deletion():
return node
for child in node.get_children():
var found := find_control(child, node_name)
if found != null:
return found
return null
func contains_rect(outer: Rect2, inner: Rect2) -> bool:
return inner.position.x >= -0.5 and inner.position.y >= -0.5 and inner.end.x <= outer.end.x + 0.5 and inner.end.y <= outer.end.y + 0.5
func inside_scroll(node: Node) -> bool:
var current := node.get_parent()
while current != null:
if current is ScrollContainer:
return true
current = current.get_parent()
return false
func settle() -> void:
for frame in range(4):
await process_frame
func drain_cleanup() -> void:
for frame in range(30):
await process_frame
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("P9.1 layout: %s" % message)