scopedvr-map-editor/tests/test_gui_smoke.gd
Hermes godot-vr 87e7a96fd5 Fix GUI-Start-Fehler (Karte t_d3b93793): Terrain-Preset-Crash + command_pressed-Spam.
1. main.gd _build_right_panel: terrain_size_btn_medium.button_pressed=true
   feuerte toggled SOFORT synchron — der Handler griff auf
   terrain_radius_slider zu, der erst spaeter erzeugt wird → SCRIPT ERROR
   in _on_terrain_size_preset bei jedem GUI-Start. Fix:
   set_pressed_no_signal(true) (Slider startet unten passend mit 12 m) +
   Null-Check als Schutznetz im Handler.

2. main.gd _unhandled_key_input: event.command_pressed existiert an
   InputEventKey nicht (Godot-3-API-Relikt) → SCRIPT ERROR bei JEDER Taste,
   Shortcuts tot. Fix: is_command_or_control_pressed() (Ctrl auf
   Linux/Windows, Cmd auf macOS).

3. Neuer Regressionstest tests/test_gui_smoke.gd: instanziert die ECHTE
   Main-UI headless (deckt Runtime-UI-Pfade ab, die die logik-only-Suite
   nie sah): Panel-Bau ueberlebt _ready, Presets initial/klickbar
   (Slider+Label), Strg+Z/Strg+Y wirken end-to-end, leere History =
   freundliche Meldung. Suite: 334 + 20 Checks gruen.
2026-10-15 21:14:05 +00:00

147 lines
5.5 KiB
GDScript

extends SceneTree
## Editor-Test (headless): GUI-Smoke — Karte t_d3b93793.
## Ausführen: godot --headless --path . --script res://tests/test_gui_smoke.gd
## Exit 0 = alles grün.
##
## ANDERS als die übrigen Tests instanziert dieser Test die ECHTE Main-UI
## (scenes/main.tscn → src/ui/main.gd) und deckt damit Runtime-UI-Pfade ab,
## die die logik-only-Suite nie sieht (GUI-Start-Befund 15.10.: Terrain-Preset-
## Crash beim Panel-Bau + `command_pressed`-Spam bei jeder Taste — beides
## headless unsichtbar, weil das UI nie instanziert wurde).
##
## Was hier geprüft wird:
## 1. Panel-Bau überlebt _ready (kein abgebrochener _build_right_panel).
## 2. Terrain-Größen-Presets sind initial korrekt belegt (Mittel = 12 m)
## und klickbar (Klick stellt Radius-Slider + Label um).
## 3. Keyboard-Shortcut-Pfad (_unhandled_key_input) läuft ohne
## Property-Fehler und setzt Strg+Z wirklich in Undo um.
var failures := 0
var checks := 0
var main: Control
func _initialize() -> void:
_run_all.call_deferred()
func _run_all() -> void:
var scene: PackedScene = load("res://scenes/main.tscn")
_check(scene != null, "scenes/main.tscn laedt")
if scene == null:
_finish()
return
main = scene.instantiate()
root.add_child(main)
# Zwei Frames: _ready + Layout laufen lassen (auch deferred UI-Arbeit).
await process_frame
await process_frame
_test_ui_built()
_test_terrain_presets()
_test_shortcut_undo()
_finish()
func _finish() -> void:
if failures == 0:
print("ALL OK: %d checks" % checks)
quit(0)
else:
print("FAILED: %d/%d checks" % [failures, checks])
quit(1)
func _check(cond: bool, label: String) -> void:
checks += 1
if cond:
print(" ok: %s" % label)
else:
failures += 1
print(" FAIL: %s" % label)
## Abnahme 1: Der komplette UI-Bau (_ready → _build_ui → _build_right_panel)
## läuft durch — genau dort crashte der Terrain-Preset-Handler (main.gd:659).
func _test_ui_built() -> void:
print("Testgruppe: UI-Bau überlebt _ready")
_check(main != null, "Main-Instanz existiert")
_check(main.terrain_size_btn_small != null, "Preset-Button Klein gebaut")
_check(main.terrain_size_btn_medium != null, "Preset-Button Mittel gebaut")
_check(main.terrain_size_btn_large != null, "Preset-Button Groß gebaut")
_check(main.terrain_radius_slider != null, "Radius-Slider gebaut")
_check(main.undo_btn != null and main.redo_btn != null,
"Undo/Redo-Buttons gebaut (Panel-Bau lief komplett durch)")
## Abnahme 2a: Initialzustand ohne Nebeneffekt-Callback (set_pressed_no_signal):
## Mittel aktiv, Slider passend 12 m.
func _test_terrain_presets() -> void:
print("Testgruppe: Terrain-Größen-Presets")
_check(main.terrain_size_btn_medium.button_pressed,
"Mittel ist initial aktiv")
_check(is_equal_approx(float(main.terrain_radius_slider.value), 12.0),
"Slider startet bei 12 m (passend zum aktiven Preset)")
# Klick auf Groß: button_pressed=true feuert toggled → Handler muss
# jetzt (mit existierendem Slider) Radius + Label setzen.
main.terrain_size_btn_large.button_pressed = true
_check(is_equal_approx(float(main.terrain_radius_slider.value), 32.0),
"Klick auf Groß setzt Slider auf 32 m")
_check(str(main.terrain_radius_label.text).contains("32"),
"Pinsel-Label zeigt 32 m")
_check(main.terrain_size_btn_medium.button_pressed == false,
"ButtonGroup: Mittel beim Klick auf Groß deaktiviert")
# Klick auf Klein (Position-Klick simuliert echten User-Klick).
main.terrain_size_btn_small.button_pressed = true
_check(is_equal_approx(float(main.terrain_radius_slider.value), 4.0),
"Klick auf Klein setzt Slider auf 4 m")
func _press_key(keycode: Key, ctrl: bool) -> InputEventKey:
var ev := InputEventKey.new()
ev.keycode = keycode
ev.physical_keycode = keycode
ev.pressed = true
ev.ctrl_pressed = ctrl
return ev
## Abnahme 2b + 3: Der Shortcut-Pfad muss ohne Property-Fehler durchlaufen
## (vorher crashte `event.command_pressed` bei JEDER Taste) und Strg+Z
## tatsächlich in ein Undo übersetzen.
func _test_shortcut_undo() -> void:
print("Testgruppe: Keyboard-Shortcuts (Strg+Z)")
# Echte History-Aktion in die laufende Editor-History legen.
var before: Dictionary = main.history.snapshot(["objects"])
(main.doc["objects"] as Array).append(
{"id": "test_objekt", "pos": [10.0, 0.0, 10.0], "scale": 1.0,
"rot_y_deg": 0.0})
main.history.commit("Test: Objekt platzieren", before,
main.history.snapshot(["objects"]))
_check((main.doc["objects"] as Array).size() == 1, "Test-Objekt platziert")
# Vorher-Zustand für die unbehandelte Taste merken.
var status_vorher := str(main.status_label.text)
main._unhandled_key_input(_press_key(KEY_F, false))
_check(str(main.status_label.text) == status_vorher,
"unbehandelte Taste (F) ändert nichts, läuft aber sauber durch")
main._unhandled_key_input(_press_key(KEY_Z, true))
_check((main.doc["objects"] as Array).is_empty(), "Strg+Z hat das Objekt entfernt (Undo wirkt)")
_check(str(main.status_label.text).contains("Rückgängig gemacht"),
"Statusleiste meldet das Undo")
main._unhandled_key_input(_press_key(KEY_Y, true))
_check((main.doc["objects"] as Array).size() == 1, "Strg+Y stellt wieder her (Redo wirkt)")
# Leere History + Strg+Z: freundliche Meldung statt Crash.
# Erst das Redo-Ergebnis wieder weg-undoen, DANN ist die History leer.
main._unhandled_key_input(_press_key(KEY_Z, true))
_check((main.doc["objects"] as Array).is_empty(), "Strg+Z nach Redo: History leer")
main._unhandled_key_input(_press_key(KEY_Z, true))
_check(str(main.status_label.text).contains("Nichts zum Rückgängigmachen"),
"Strg+Z bei leerer History: Statusmeldung, kein Absturz")