Files
aetherbound-guild/runtime/tests/dev3/guild_layered_reconstruction_test.gd
T
2026-08-20 18:31:45 +08:00

144 lines
8.3 KiB
GDScript

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://hub001_layered_draft.json"
const COMPANY_PATH := "user://hub001_layered_company.json"
const SEED := "abcdef0123456789abcdef0123456789"
const SIZES := [Vector2i(760, 360), Vector2i(844, 390), Vector2i(1280, 720)]
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
_cleanup()
var company := _create_company()
var viewport := SubViewport.new()
viewport.size = SIZES[0]
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var shell := EntryShell.new()
shell.auto_start = false
shell.initial_company_service.path = DRAFT_PATH
shell.initial_company_service.company_path = COMPANY_PATH
viewport.add_child(shell)
await _frames(3)
var settings := SettingsStore.new().defaults()
settings.setup_completed = true
settings.locale = "zh_CN"
settings.text_scale = 1.3
shell.settings = settings
shell.show_guild_handoff(company)
await _frames(4)
for viewport_size in SIZES:
viewport.size = viewport_size
await _frames(3)
_verify_layered_page(shell, viewport_size)
shell.queue_free()
await _frames(3)
_cleanup()
if failures.is_empty():
print("ABG_HUB001_LAYERED_OK sizes=760x360,844x390,1280x720 composite=false layers=world,rails,actors,board fixed_caps=true center_stretch=true touch_min=48 text=engine")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func _verify_layered_page(shell: Control, viewport_size: Vector2i) -> void:
expect(shell.find_child("GuildMasterBComposite", true, false) == null, "%s removes the coordinate-master composite" % viewport_size)
for node_name in [
"GuildMasterLayeredWorld", "GuildTopRail", "GuildTopRailLeft",
"GuildTopRailCenter", "GuildTopRailRight", "GuildBottomRail",
"GuildBottomRailLeft", "GuildBottomRailCenter", "GuildBottomRailRight",
"GuildMasterKioskTexture", "GuildMemberPortraitTexture_0",
"GuildMemberPortraitTexture_1", "GuildKioskCopyArea",
"GuildProgressStatusIcon", "GuildCoinStatusIcon", "GuildAnchorCompanyStatusIcon",
]:
expect(shell.find_child(node_name, true, false) != null, "%s has layered node %s" % [viewport_size, node_name])
var top_left := shell.find_child("GuildTopRailLeft", true, false) as TextureRect
var top_center := shell.find_child("GuildTopRailCenter", true, false) as TextureRect
var top_right := shell.find_child("GuildTopRailRight", true, false) as TextureRect
var bottom_left := shell.find_child("GuildBottomRailLeft", true, false) as TextureRect
var bottom_center := shell.find_child("GuildBottomRailCenter", true, false) as TextureRect
var bottom_right := shell.find_child("GuildBottomRailRight", true, false) as TextureRect
if top_left != null and top_center != null and top_right != null and bottom_left != null and bottom_center != null and bottom_right != null:
expect(top_left.stretch_mode == TextureRect.STRETCH_KEEP_ASPECT_CENTERED and top_right.stretch_mode == TextureRect.STRETCH_KEEP_ASPECT_CENTERED, "%s top caps keep aspect" % viewport_size)
expect(bottom_left.stretch_mode == TextureRect.STRETCH_KEEP_ASPECT_CENTERED and bottom_right.stretch_mode == TextureRect.STRETCH_KEEP_ASPECT_CENTERED, "%s bottom caps keep aspect" % viewport_size)
expect(top_center.stretch_mode == TextureRect.STRETCH_SCALE and bottom_center.stretch_mode == TextureRect.STRETCH_SCALE, "%s only clean centers stretch" % viewport_size)
expect(top_center.anchor_left <= 0.056 and top_center.anchor_right >= 0.953 and is_equal_approx(top_center.anchor_right, top_right.anchor_left), "%s top center meets both native-width fixed caps" % viewport_size)
expect(bottom_center.anchor_right >= 0.90 and bottom_right.anchor_left >= 0.90, "%s bottom stretch center meets the fixed Compass endcap" % viewport_size)
for button_name in ["GuildTitle", "GuildRosterButton", "GuildEquipment", "GuildParty", "GuildRisk", "GuildSettings"]:
var button := shell.find_child(button_name, true, false) as Button
expect(button != null, "%s has %s" % [viewport_size, button_name])
if button != null:
var rect := button.get_global_rect()
expect(rect.size.x >= 47.9 and rect.size.y >= 47.9, "%s %s meets 48px touch minimum: %s" % [viewport_size, button_name, rect.size])
if button_name in ["GuildRosterButton", "GuildEquipment", "GuildParty", "GuildRisk"]:
expect(rect.size.x <= 76.1 and rect.size.y <= 76.1, "%s %s stays inside the 76px responsive cap: %s" % [viewport_size, button_name, rect.size])
var chrome := button.find_child("Chrome", true, false) as Control
if button_name == "GuildTitle":
expect(chrome == null and String(button.get_meta("art_owned_control", "")) == "GuildTopRailLeft", "%s GuildTitle reuses the approved crest instead of adding a fifth bottom medallion" % viewport_size)
else:
expect(chrome != null, "%s %s has an authored semantic icon" % [viewport_size, button_name])
if chrome != null:
var expected_variant := "guild_status_icon" if button_name == "GuildSettings" else "guild_nav_icon"
expect(String(chrome.get("variant")) == expected_variant, "%s %s uses %s instead of shared icon geometry" % [viewport_size, button_name, expected_variant])
var nav_buttons := ["GuildRosterButton", "GuildEquipment", "GuildParty", "GuildRisk"]
for index in range(nav_buttons.size() - 1):
var left_button := shell.find_child(nav_buttons[index], true, false) as Button
var right_button := shell.find_child(nav_buttons[index + 1], true, false) as Button
if left_button != null and right_button != null:
var spacing := right_button.get_global_rect().position.x - left_button.get_global_rect().end.x
expect(spacing >= 3.9 and spacing <= 8.1, "%s bottom nav spacing remains compact: %.2f" % [viewport_size, spacing])
for label_name in ["GuildAnchorTitle", "GuildProgressStatus", "GuildCoinStatus", "GuildAnchorCompanyStatus", "GuildKioskText", "GuildContractReason", "GuildMarketReason"]:
expect(shell.find_child(label_name, true, false) is Label, "%s engine-renders %s" % [viewport_size, label_name])
for status_name in ["GuildProgressStatus", "GuildCoinStatus", "GuildAnchorCompanyStatus"]:
expect(shell.find_child("%sPill" % status_name, true, false) is Panel, "%s %s uses an inlaid value pill" % [viewport_size, status_name])
expect(shell.find_child("%sFrame" % status_name, true, false) == null, "%s %s has no opaque outer resource card" % [viewport_size, status_name])
var title_plate := shell.find_child("GuildTitlePlate", true, false) as Panel
var design_canvas := shell.find_child("GuildMasterDesignCanvas", true, false) as Control
if title_plate != null and design_canvas != null:
expect(title_plate.get_global_rect().size.x <= design_canvas.size.x * 0.151, "%s Guild identity stays within the approved compact width" % viewport_size)
for command_label in ["GuildTitleCommandLabel", "GuildRosterCommandLabel", "GuildEquipmentCommandLabel", "GuildPartyCommandLabel", "GuildRouteStatusLabel"]:
var label := shell.find_child(command_label, true, false) as Label
expect(label != null and not label.visible, "%s %s stays tooltip-owned instead of overlaying the approved icon rail" % [viewport_size, command_label])
var title := shell.find_child("GuildAnchorTitle", true, false) as Label
if title != null:
var title_size := title.get_theme_font_size("font_size")
expect(title_size >= (19 if viewport_size.y >= 700 else 11), "%s Guild title follows canvas-aware typography: %d" % [viewport_size, title_size])
func _create_company() -> Dictionary:
var service := DraftService.new()
service.path = DRAFT_PATH
service.company_path = COMPANY_PATH
var draft: Dictionary = service.begin_or_load(SEED)
service.set_selection([String(draft.draft.offers[0].offer_id), String(draft.draft.offers[2].offer_id)])
return service.commit_company().company
func _cleanup() -> void:
for base in [DRAFT_PATH, COMPANY_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("HUB-001 layered reconstruction: %s" % message)