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

129 lines
4.0 KiB
GDScript

extends SceneTree
const MainScene = preload("res://main.tscn")
const LANDSCAPE_FIXTURES := [
{"size": Vector2i(1280, 720), "locale": "zh_CN", "scale": 1.0},
{"size": Vector2i(844, 390), "locale": "en", "scale": 1.0},
{"size": Vector2i(844, 390), "locale": "zh_CN", "scale": 1.3},
]
const PAGES := ["title", "yard", "battle", "victory"]
var failures: Array[String] = []
var fixture_count := 0
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
for fixture in LANDSCAPE_FIXTURES:
for page_name in PAGES:
await verify_landscape(fixture, page_name)
await verify_portrait()
if failures.is_empty():
print("ABG_P9_0R1_LAYOUT_OK fixtures=%d landscape=12 portrait=1 pages=4 locales=2 text_130=true min_touch=44" % fixture_count)
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func verify_landscape(fixture: Dictionary, page_name: 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.locale = fixture.locale
game.text_scale = fixture.scale
game.front_choice = "Hearthguard"
game.rear_choice = "Farstring"
game.boot_verified = true
match page_name:
"title": game.page = game.AppPage.TITLE
"yard": game.page = game.AppPage.YARD
"battle":
game.page = game.AppPage.BATTLE
game.battle_playing = false
game.battle_phase = 2
game.front_hp = 82
"victory":
game.page = game.AppPage.VICTORY
game.enemy_hp = 0
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), page_name]
verify_interactive_bounds(game, Rect2(Vector2.ZERO, Vector2(fixture.size)), label)
expect(find_control(game, "RotateNotice") == null, "%s unexpectedly shows portrait notice" % label)
if page_name != "title":
expect(find_control(game, "HomeButton") != null, "%s missing Home" % label)
expect(find_control(game, "RestartButton") != null, "%s missing Restart" % label)
game.queue_free()
viewport.queue_free()
await drain_cleanup()
func verify_portrait() -> void:
var viewport := SubViewport.new()
viewport.size = Vector2i(390, 844)
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.page = game.AppPage.YARD
game.render()
await settle()
fixture_count += 1
expect(find_control(game, "RotateNotice") != null, "portrait missing rotate guidance")
verify_interactive_bounds(game, Rect2(Vector2.ZERO, Vector2(390, 844)), "390x844/portrait")
game.queue_free()
viewport.queue_free()
await drain_cleanup()
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()
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 settle() -> void:
for frame in range(8):
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.0R1 layout: %s" % message)