203 lines
7.8 KiB
GDScript
203 lines
7.8 KiB
GDScript
extends SceneTree
|
|
## Editor-Test (headless): Theme-Konsistenz — Karte t_2336c58e (U7 Feinschliff).
|
|
## Ausführen: godot --headless --path . -s res://tests/test_theme.gd
|
|
## Exit 0 = alles grün.
|
|
##
|
|
## Was hier geprüft wird:
|
|
## 1. Theme-Ressource: main nutzt theme/editor_theme.tres (EINE Farbquelle),
|
|
## Theme-Schrift = UIScale.font_size().
|
|
## 2. Theme-Inhalt: Hover/Fokus/Disabled (Button), Tab-Hover, LineEdit,
|
|
## Splitter-Anfassen (grabber_area), Scrollbars, Tooltip + die Farb-Token
|
|
## (accent, status_ok/error, muted, field, title, warning).
|
|
## 3. KEINE hartkodierten Farben im Code: Suche nach "Color(" in
|
|
## core/ui/panels/*.gd, core/ui/main.gd, core/ui/inspector_panel.gd.
|
|
## 4. Alle Knöpfe (Button/MenuButton/CheckBox) haben einen Tooltip.
|
|
## 5. Typografie-Raster: Überschrift > Body > Nebentext, alles über UIScale.
|
|
## 6. Fenstertitel zeigt ungespeicherte Änderungen an (U7 Kleinigkeit).
|
|
|
|
const UIScale := preload("res://core/ui/ui_scale.gd")
|
|
|
|
var failures := 0
|
|
var checks := 0
|
|
var main: Control
|
|
|
|
|
|
func _initialize() -> void:
|
|
_run_all.call_deferred()
|
|
|
|
|
|
func _check(cond: bool, label: String) -> void:
|
|
checks += 1
|
|
if cond:
|
|
print(" ok: %s" % label)
|
|
else:
|
|
failures += 1
|
|
print(" FAIL: %s" % label)
|
|
|
|
|
|
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 _run_all() -> void:
|
|
var scene: PackedScene = load("res://core/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_theme_set()
|
|
_test_theme_content()
|
|
_test_no_hardcoded_colors()
|
|
_test_tooltips()
|
|
_test_typography()
|
|
_test_window_title_dirty()
|
|
|
|
_finish()
|
|
|
|
|
|
## 1) main hängt genau die Theme-Datei aus dem Projekt an (eine Quelle).
|
|
func _test_theme_set() -> void:
|
|
print("Testgruppe: Theme-Ressource gesetzt")
|
|
_check(main.theme != null, "main.theme gesetzt")
|
|
if main.theme == null:
|
|
return
|
|
var theme_file: Theme = load("res://core/theme/editor_theme.tres")
|
|
_check(main.theme == theme_file,
|
|
"main nutzt theme/editor_theme.tres (eine Farbquelle)")
|
|
_check(int(main.theme.default_font_size) == UIScale.font_size(),
|
|
"Theme-Schrift = UIScale.font_size() (%d)" % UIScale.font_size())
|
|
|
|
|
|
## 2) Die U7-Theme-Bausteine sind alle da (Hover/Fokus/Disabled, Splitter,
|
|
## Scrollbars, Tooltip, Farb-Token).
|
|
func _test_theme_content() -> void:
|
|
print("Testgruppe: Theme-Inhalt (Hover/Fokus/Disabled/Splitter/Farben)")
|
|
var th: Theme = main.theme
|
|
_check(th.has_stylebox("disabled", "Button"), "Button: Disabled-Style (ausgegraut)")
|
|
_check(th.has_stylebox("hover", "Button"), "Button: Hover-Style")
|
|
_check(th.has_stylebox("pressed", "Button"), "Button: Aktiv-Style")
|
|
_check(th.has_stylebox("focus", "Button"), "Button: Fokus-Rahmen")
|
|
_check(th.has_color("font_disabled_color", "Button"),
|
|
"Button: Disabled-Schriftfarbe (sichtbar ausgegraut)")
|
|
_check(th.has_color("font_placeholder_color", "LineEdit"),
|
|
"LineEdit: Platzhalter-Farbe")
|
|
_check(th.has_stylebox("focus", "LineEdit"), "LineEdit: Fokus-Rahmen")
|
|
_check(th.has_stylebox("tab_hovered", "TabContainer"), "Tabs: Hover-Zustand")
|
|
_check(th.has_stylebox("grabber_area", "HSplitContainer"),
|
|
"Splitter: Anfassen-Fläche (HSplit)")
|
|
_check(th.has_stylebox("grabber_area", "VSplitContainer"),
|
|
"Splitter: Anfassen-Fläche (VSplit)")
|
|
_check(th.has_stylebox("grabber_area_highlight", "HSplitContainer"),
|
|
"Splitter: Anfassen-Hervorhebung (HSplit)")
|
|
_check(th.has_stylebox("grabber", "VScrollBar"), "ScrollBar: Griff (V)")
|
|
_check(th.has_stylebox("grabber", "HScrollBar"), "ScrollBar: Griff (H)")
|
|
_check(th.has_stylebox("panel", "TooltipPanel"), "Tooltip: Panel-Style")
|
|
_check(th.has_color("accent", "Editor"), "Farb-Token: accent")
|
|
_check(th.has_color("status_ok", "Editor"), "Farb-Token: status_ok")
|
|
_check(th.has_color("status_error", "Editor"), "Farb-Token: status_error")
|
|
_check(th.has_color("muted", "Editor"), "Farb-Token: muted")
|
|
_check(th.has_color("field", "Editor"), "Farb-Token: field")
|
|
_check(th.has_color("title", "Editor"), "Farb-Token: title")
|
|
_check(th.has_color("warning", "Editor"), "Farb-Token: warning")
|
|
|
|
|
|
## 3) Keine Farbwerte mehr im Code — alles kommt aus dem Theme.
|
|
## Scan: core/ui/panels/*.gd (verbindlich) + main.gd + inspector_panel.gd.
|
|
## Achtung: nur "Color(" (Konstruktor) zählt — theme_color()/-> Color: sind
|
|
## Schlüsselwörter, keine Farbwerte.
|
|
func _test_no_hardcoded_colors() -> void:
|
|
print("Testgruppe: keine hartkodierten Farben im Code")
|
|
var dateien: Array = []
|
|
var dir := DirAccess.open("res://core/ui/panels")
|
|
if dir != null:
|
|
dir.list_dir_begin()
|
|
var fname := dir.get_next()
|
|
while not fname.is_empty():
|
|
if fname.ends_with(".gd"):
|
|
dateien.append("res://core/ui/panels/" + fname)
|
|
fname = dir.get_next()
|
|
dateien.append("res://core/ui/main.gd")
|
|
dateien.append("res://core/ui/inspector_panel.gd")
|
|
_check(dateien.size() > 5, "Farb-Scan: %d Skripte gefunden" % dateien.size())
|
|
var farb_dateien := 0
|
|
for pfad in dateien:
|
|
var text := FileAccess.get_file_as_string(pfad)
|
|
if text.contains("Color("):
|
|
farb_dateien += 1
|
|
print(" hartkodierte Farbe in: %s" % pfad)
|
|
_check(farb_dateien == 0,
|
|
"kein 'Color(' in den Panel-Skripten/main.gd/Inspektor")
|
|
|
|
|
|
## 4) Jeder klickbare Knopf (Button/MenuButton/CheckBox/CheckButton) im
|
|
## gebauten UI trägt einen Tooltip — Werkzeugname/Tastenkürzel inklusive.
|
|
func _test_tooltips() -> void:
|
|
print("Testgruppe: alle Knöpfe haben Tooltips")
|
|
var ohne := 0
|
|
var mit := 0
|
|
var stack: Array = [main]
|
|
while not stack.is_empty():
|
|
var n: Node = stack.pop_back()
|
|
if n is Button or n is MenuButton:
|
|
var str_text := str((n as Control).tooltip_text)
|
|
if str_text.strip_edges().is_empty():
|
|
ohne += 1
|
|
var ort := str(n.get_parent().name)
|
|
var elter := n.get_parent()
|
|
while elter != null and elter != main:
|
|
elter = elter.get_parent()
|
|
if elter != null:
|
|
ort = str(elter.name) + "/" + ort
|
|
print(" ohne Tooltip: %s (%s) text='%s' pfad=%s" % [
|
|
n.name, n.get_class(),
|
|
str((n as Control).get("text")), ort])
|
|
else:
|
|
mit += 1
|
|
for c in n.get_children():
|
|
stack.append(c)
|
|
_check(mit > 20, "Knöpfe mit Tooltip gefunden (%d)" % mit)
|
|
_check(ohne == 0, "kein Knopf ohne Tooltip (%d ohne)" % ohne)
|
|
|
|
|
|
## 5) Genau drei Typografie-Ebenen über UIScale: Überschrift > Body > Nebentext.
|
|
func _test_typography() -> void:
|
|
print("Testgruppe: Typografie-Raster über UIScale")
|
|
_check(UIScale.heading_font_size() > UIScale.font_size(),
|
|
"Überschrift (%d) größer als Body (%d)" % [
|
|
UIScale.heading_font_size(), UIScale.font_size()])
|
|
_check(UIScale.font_size() > UIScale.small_font_size(),
|
|
"Body (%d) größer als Nebentext (%d)" % [
|
|
UIScale.font_size(), UIScale.small_font_size()])
|
|
# Stufen-Skalierung wandert mit (Groß-Stufe, ohne UI-Rebuild zu brauchen):
|
|
UIScale.set_step_index(2)
|
|
_check(UIScale.heading_font_size() == UIScale.px(UIScale.HEADING_FONT_SIZE),
|
|
"Überschrift skaliert mit der UI-Stufe (Groß)")
|
|
_check(UIScale.small_font_size() == UIScale.px(UIScale.SMALL_FONT_SIZE),
|
|
"Nebentext skaliert mit der UI-Stufe (Groß)")
|
|
UIScale.set_step_index(0) # zurück auf Klein (wie der Test-Start)
|
|
|
|
|
|
## 6) Fenstertitel nennt ungespeicherte Änderungen (U7-Kleinigkeit).
|
|
func _test_window_title_dirty() -> void:
|
|
print("Testgruppe: Fenstertitel bei ungespeicherten Änderungen")
|
|
main.dirty = true
|
|
main.call("_refresh_file_label")
|
|
var titel_dirty := str(root.title)
|
|
_check(titel_dirty.contains("ungespeichert"),
|
|
"Titel bei dirty nennt „ungespeichert“ (%s)" % titel_dirty)
|
|
main.dirty = false
|
|
main.call("_refresh_file_label")
|
|
var titel_sauber := str(root.title)
|
|
_check(not titel_sauber.contains("ungespeichert"),
|
|
"Titel ohne dirty bleibt sauber (%s)" % titel_sauber)
|