Files
2026-09-11 06:14:38 +02:00

192 lines
5.5 KiB
Go
Raw Permalink 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.
package webui
import (
"regexp"
"strings"
"testing"
)
func appJS(t *testing.T) string {
t.Helper()
b, err := embedded.ReadFile("assets/app.js")
if err != nil {
t.Fatalf("read embedded app.js: %v", err)
}
return string(b)
}
func TestRenderDispatcherFunctionsExist(t *testing.T) {
js := appJS(t)
start := strings.Index(js, "function render(){")
if start < 0 {
t.Fatal("render dispatcher not found")
}
end := strings.Index(js[start:], "}\n")
if end < 0 {
t.Fatal("render dispatcher end not found")
}
body := js[start : start+end+1]
calls := regexp.MustCompile(`\b(render[A-Z][A-Za-z0-9_]*)\(`).FindAllStringSubmatch(body, -1)
if len(calls) == 0 {
t.Fatal("render dispatcher contains no render function calls")
}
seen := map[string]bool{}
for _, call := range calls {
name := call[1]
if seen[name] {
continue
}
seen[name] = true
if !strings.Contains(js, "function "+name+"(") {
t.Errorf("render dispatcher calls %s but app.js does not define it", name)
}
}
}
func TestPlacementUIHelpersExist(t *testing.T) {
js := appJS(t)
for _, name := range []string{
"placementRule",
"placementRows",
"placementCell",
"placementSourceLabel",
"renderPlacement",
"placementCellHTML",
"placementWorkerCard",
"loadPlacement",
"openPlacementDialog",
"lines",
"installedForWorker",
"savePlacementRule",
"placementExactAction",
} {
if !strings.Contains(js, "function "+name+"(") {
t.Errorf("missing placement UI helper %s", name)
}
}
}
func appCSS(t *testing.T) string {
t.Helper()
b, err := embedded.ReadFile("assets/app.css")
if err != nil {
t.Fatalf("read embedded app.css: %v", err)
}
return string(b)
}
func indexHTML(t *testing.T) string {
t.Helper()
b, err := embedded.ReadFile("assets/index.html")
if err != nil {
t.Fatalf("read embedded index.html: %v", err)
}
return string(b)
}
func TestProfessionalLightThemeIsEmbedded(t *testing.T) {
css := appCSS(t)
for _, want := range []string{
"color-scheme:light",
"--bg:#eef2f6",
"--surface:#ffffff",
"--text:#253345",
"--font-mono:",
"Checkpoint 24 — SCMDB-inspired professional light control-plane theme",
".live-flow-stage,.cluster-map-stage",
} {
if !strings.Contains(css, want) {
t.Errorf("light theme marker %q missing from app.css", want)
}
}
if strings.Contains(indexHTML(t), `name="color-scheme" content="dark light"`) {
t.Fatal("index still advertises dark color scheme")
}
if !strings.Contains(indexHTML(t), `name="color-scheme" content="light"`) {
t.Fatal("index does not advertise light color scheme")
}
}
func TestCanvasThemeUsesLightNodeSurfaces(t *testing.T) {
js := appJS(t)
for _, want := range []string{
"rgba(255,255,255,.97)",
"rgba(37,51,69,.94)",
"const liveColor=(v,a=1,l=44)",
} {
if !strings.Contains(js, want) {
t.Errorf("light canvas marker %q missing from app.js", want)
}
}
for _, legacy := range []string{
"rgba(10,18,25,.96)",
"rgba(11,20,27,.94)",
} {
if strings.Contains(js, legacy) {
t.Errorf("legacy dark canvas color %q still present", legacy)
}
}
}
func TestPolicyDialogCloseControlsBypassFormValidation(t *testing.T) {
html := indexHTML(t)
if strings.Contains(html, `<form method="dialog" class="dialog-card" id="policy-form">`) {
t.Fatal("policy dialog still uses method=dialog; required fields can block cancel controls")
}
for _, want := range []string{
`<button class="icon-button" type="button" data-close-dialog="policy-dialog" aria-label="Dialog schließen">×</button>`,
`<button type="button" class="button" data-close-dialog="policy-dialog">Abbrechen</button>`,
`<button id="policy-submit" type="submit" class="button primary">Speichern</button>`,
} {
if !strings.Contains(html, want) {
t.Errorf("policy dialog control missing or unsafe: %s", want)
}
}
js := appJS(t)
if !strings.Contains(js, `document.getElementById(b.dataset.closeDialog)?.close()`) {
t.Fatal("generic data-close-dialog handler missing")
}
if strings.Contains(js, `if(e.submitter?.value==='cancel')return;e.preventDefault();`) {
t.Fatal("policy submit handler still depends on cancel submit semantics")
}
}
func TestDialogCloseTargetsExist(t *testing.T) {
html := indexHTML(t)
dialogIDs := map[string]bool{}
for _, m := range regexp.MustCompile(`<dialog\s+id="([^"]+)"`).FindAllStringSubmatch(html, -1) {
dialogIDs[m[1]] = true
}
for _, m := range regexp.MustCompile(`data-close-dialog="([^"]+)"`).FindAllStringSubmatch(html, -1) {
if !dialogIDs[m[1]] {
t.Errorf("data-close-dialog points to missing dialog %q", m[1])
}
}
}
func TestRequiredDialogsHaveValidationIndependentCloseButtons(t *testing.T) {
html := indexHTML(t)
for _, dialogID := range []string{"policy-dialog", "pull-dialog"} {
pattern := regexp.MustCompile(`(?s)<dialog\s+id="` + regexp.QuoteMeta(dialogID) + `".*?</dialog>`)
block := pattern.FindString(html)
if block == "" {
t.Fatalf("dialog %s not found", dialogID)
}
if !strings.Contains(block, `required`) {
t.Fatalf("test assumption failed: dialog %s has no required field", dialogID)
}
if !strings.Contains(block, `type="button"`) || !strings.Contains(block, `data-close-dialog="`+dialogID+`"`) {
t.Errorf("dialog %s lacks a validation-independent close control", dialogID)
}
}
}
func TestModelTableExposesDistinctContextWindows(t *testing.T) {
js := appJS(t)
for _, want := range []string{"Modell-Max", "Konfig.", "Geladen", "configured_context_length", "loaded_context_length"} {
if !strings.Contains(js, want) {
t.Errorf("model context UI marker %q missing", want)
}
}
}