scopedvr-map-editor/tests/test_gui_smoke.gd
Hermes (godot-vr) 08a75ac430 Shortcuts 2/3 (t_55257792): R-Drehen + Pfeil-Raster + PgUp/Down + Strg+Bild↑ + Greifen + Terrain-Pinsel-Tasten.
- R = Auswahl 90° drehen (Szene um Auswahl-Mitte, Bauen-Bauteil um Fußpunkt, Gruppe komplett; Umschalt+R = -90°), Mausrad-Feindrehen 15° bleibt
- Pfeiltasten = 2-m-Raster-Verschiebung, Richtung = Kamera-Blickrichtung auf nächste Weltachse gerastet (MoveMath.nudge_delta, pure)
- Bild↑/↓ = ±0,25 m auf der GANZEN Auswahl (EIN Undo), Strg+Bild↑ = Fußpunkt aufs Terrain/Boden absetzen
- G = Greifen (Blender-Semantik): Auswahl hängt an der Maus, LMB setzt mit Raster-Snap, Esc rollt byte-genau zurück (ShortcutPolicy cancel_grab)
- Terrain-Pinsel: [ / ] = Preset-Zyklus Klein/Mittel/Groß (Slider/Label/Preset-Buttons wandern mit), 1-4 = Heben/Senken/Glätten/Malen (PAINT-Clamp-Fix, Palette mit Malen sichtbar); Ziffern NUR im Terrain-Werkzeug
- Strg halten = temporärer Snap-Zwang beim Bewegen/Greifen/Platzieren
- Tests: test_shortcuts.gd 157 Checks (R byte-genau, Pfeil-Raster, Lift-Stufen, Boden-Snap, Greifen+Esc, Pinsel-Zyklus, History-Eintrag pro Operation), test_gui_smoke.gd erweitert (38 Checks) und in run_tests.sh aufgenommen; F-Regression seit Karte 1 damit abgedeckt
- Suite 9/9 grün (696 Checks), Builder-Export 21/0, Parse-Check 11 Dateien grün
2026-10-16 17:53:10 +00:00

207 lines
9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()
_test_terrain_hotkeys()
_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")
# Karte 1 (t_c46d31c2) hat F belegt: F zentriert die Auswahl — ohne
# Auswahl gibt es den freundlichen Hinweis (kein Crash, kein Wirken).
main._unhandled_key_input(_press_key(KEY_F, false))
_check(str(main.status_label.text).contains("Nichts ausgewählt"),
"F ohne Auswahl: freundlicher Hinweis, läuft 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")
## Karte 2/3 (t_55257792): Terrain-Ziffern 1–4 + [ / ] am ECHTEN main —
## Buttons/Slider/Label müssen mitwandern, Ziffern außerhalb des Terrain-
## Werkzeugs dürfen NICHTS tun (Konfliktfreiheit).
func _test_terrain_hotkeys() -> void:
print("Testgruppe: Terrain-Pinsel per Tastatur (1–4, [ und ])")
main._set_tool_mode("terrain")
_check(str(main.get("tool_mode")) == "terrain", "Terrain-Modus aktiv")
# Ziffer 2 = Senken: Modus-Button + Pinsel-Modus wandern mit.
main._unhandled_key_input(_press_key(KEY_2, false))
_check(main.terrain_mode_lower.button_pressed, "Ziffer 2: Button „Senken“ aktiv")
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.LOWER,
"Ziffer 2: Pinsel-Modus = Senken")
# Ziffer 4 = Malen: Palette sichtbar, Modus wirklich PAINT (alter
# set_brush_mode-Clamp hätte das still zu Glätten degradiert).
main._unhandled_key_input(_press_key(KEY_4, false))
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.PAINT,
"Ziffer 4: Pinsel-Modus = Malen (Clamp-Fix)")
_check(main.terrain_palette.visible, "Ziffer 4: Material-Palette sichtbar")
# Ziffer 1 zurück: Palette verschwindet wieder.
main._unhandled_key_input(_press_key(KEY_1, false))
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.RAISE,
"Ziffer 1: Pinsel-Modus = Heben")
_check(not main.terrain_palette.visible, "Ziffer 1: Material-Palette weg")
# [ / ] : Preset-Zyklus Klein(4)/Mittel(12)/Groß(32), Slider + Label mit.
# Slider steht nach dem Klein-Klick oben noch auf 4 m — die Kette startet
# also von Klein: ] ] ] = 12 → 32 → wrap 4, [ = 32, [ [ = 12 → 4.
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
_check(is_equal_approx(float(main.terrain_radius_slider.value), 12.0),
"] ab 4 m: Slider auf 12 m (Mittel)")
_check(str(main.terrain_radius_label.text).contains("12"), "]: Label zeigt 12 m")
_check(main.terrain_size_btn_medium.button_pressed, "]: Preset-Button Mittel aktiv")
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
_check(is_equal_approx(float(main.terrain_radius_slider.value), 32.0),
"] ab 12 m: Slider auf 32 m (Groß)")
_check(str(main.terrain_radius_label.text).contains("32"), "]: Label zeigt 32 m")
_check(main.terrain_size_btn_large.button_pressed, "]: Preset-Button Groß aktiv")
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
_check(is_equal_approx(float(main.terrain_radius_slider.value), 4.0),
"] ab 32 m: Zyklus wrappt auf Klein (4 m)")
main._unhandled_key_input(_press_key(KEY_BRACKETLEFT, false))
_check(is_equal_approx(float(main.terrain_radius_slider.value), 32.0),
"[ ab 4 m: wrappt zurück auf Groß (32 m)")
main._unhandled_key_input(_press_key(KEY_BRACKETLEFT, false))
main._unhandled_key_input(_press_key(KEY_BRACKETLEFT, false))
_check(is_equal_approx(float(main.terrain_radius_slider.value), 4.0),
"[ zweimal ab 32 m: 12 → 4 (Klein)")
# Konfliktfreiheit: außerhalb des Terrain-Werkzeugs tun Ziffern/[ NICHTS.
main._set_tool_mode("objects")
var slider_vorher: float = main.terrain_radius_slider.value
main._unhandled_key_input(_press_key(KEY_2, false))
main._unhandled_key_input(_press_key(KEY_BRACKETRIGHT, false))
_check(is_equal_approx(float(main.terrain_radius_slider.value), slider_vorher),
"Ziffern/[ im Objekt-Modus: Pinsel bleibt unangetastet")
_check(int(main.terrain_tool.get("brush_mode")) == TerrainField.Mode.RAISE,
"Ziffern im Objekt-Modus: Pinsel-Modus bleibt unangetastet")