package guards import ( "os" "path/filepath" "regexp" "sort" "strings" "testing" "amadan.net/oficina/estilo.git/base" "amadan.net/oficina/estilo.git/chrome" estilomenu "amadan.net/oficina/estilo.git/menu" "amadan.net/oficina/estilo.git/theme" ) // ourStylesheets are the CSS files this app writes. The vendored ones // are not checked: they define their own custom properties and know // nothing about rastrillo's. var ourStylesheets = []string{ "internal/sheets/static/sheets.css", } // tokenSources are the files that DEFINE rastrillo's tokens: the // structural ones copied by `rastrillo doctor` and the shared palette. var tokenSources = []string{ "internal/sheets/static/tokens.css", "Estilo theme module", } var ( tokenUse = regexp.MustCompile(`var\(\s*(--rst-[a-zA-Z0-9-]+)`) tokenDefine = regexp.MustCompile(`(--rst-[a-zA-Z0-9-]+)\s*:`) ) // TestEveryTokenWeUseExists is the check that would have caught five // invented token names shipping to a canary. // // CSS resolves an undefined custom property to its fallback without a // warning anywhere — no console message, no parse error, nothing in a // Go test. So `var(--rst-border, 1px solid #ccc)` renders a plausible // page that is simply not themed, and the only way to notice is to look // at it in both schemes and know what you expected. That is not a gate. // // The five were --rst-border, --rst-text-mut, --rst-warning, // --rst-font-mono and --rst-shell-bar-h. The real names are // --rst-line, --rst-text-muted, --rst-tone-warning-fg and --rst-font; // the last had no equivalent at all and the layout was measuring // something the framework never published. func TestEveryTokenWeUseExists(t *testing.T) { root := repoRoot(t) defined := map[string]bool{} for _, rel := range tokenSources { var body []byte var err error if rel == "Estilo theme module" { body = theme.CSS() } else { body, err = os.ReadFile(filepath.Join(root, rel)) } if err != nil { t.Fatalf("guards: read %s: %v — the token sources are what `rastrillo doctor` copies; "+ "if one moved, this test needs updating, not deleting", rel, err) } for _, m := range tokenDefine.FindAllStringSubmatch(string(body), -1) { defined[m[1]] = true } } if len(defined) == 0 { t.Fatal("guards: found no --rst-* definitions at all; this test is not checking anything") } var used int for _, rel := range ourStylesheets { body, err := os.ReadFile(filepath.Join(root, rel)) if err != nil { t.Fatalf("guards: read %s: %v", rel, err) } var missing []string for _, m := range tokenUse.FindAllStringSubmatch(string(body), -1) { used++ if !defined[m[1]] { missing = append(missing, m[1]) } } sort.Strings(missing) for _, name := range dedupe(missing) { t.Errorf("%s: var(%s) is not defined in %s — CSS resolves an unknown "+ "custom property to its fallback in silence, so this renders a page "+ "that is simply not themed", rel, name, strings.Join(tokenSources, " or ")) } } if used == 0 { t.Fatal("guards: our stylesheets reference no tokens at all; either they stopped " + "using the design system or this test is looking in the wrong place") } } func dedupe(in []string) []string { var out []string for i, s := range in { if i == 0 || s != in[i-1] { out = append(out, s) } } return out } // TestEveryEstiloPropertyWeUseExists is the same check for the estilo // custom properties sheets.css reads (--estilo-float-radius and the // rest), against the three stylesheets that define them. The same // failure as a misspelled --rst- token: CSS takes the fallback in // silence, so a float drawn with an estilo property nobody defines is // simply not the suite's float. func TestEveryEstiloPropertyWeUseExists(t *testing.T) { defined := map[string]bool{} def := regexp.MustCompile(`(--estilo-[a-zA-Z0-9-]+)\s*:`) for _, css := range [][]byte{base.CSS(), estilomenu.CSS(), chrome.CSS()} { for _, m := range def.FindAllStringSubmatch(string(css), -1) { defined[m[1]] = true } } if len(defined) == 0 { t.Fatal("guards: estilo defines no --estilo-* properties; this test is reading the wrong stylesheets") } use := regexp.MustCompile(`var\(\s*(--estilo-[a-zA-Z0-9-]+)`) for _, rel := range ourStylesheets { body := readRepoFile(t, repoRoot(t), rel) for _, m := range use.FindAllStringSubmatch(body, -1) { if !defined[m[1]] { t.Errorf("%s: var(%s) is not defined by estilo's base, menu or chrome stylesheet", rel, m[1]) } } } }