check-safety/colors_test.go
Alex Dunmow c4d01de82c feat: concise output + diff-scoped any check
Reporter is now a collector: checks declare a verdict (OK/Skip/Warn/Fail/
Fatal) plus findings, and a single central render() prints output. Default
output is silent on pass/skip — only FAIL/WARN/ERR checks print, followed by
one tally line, so a clean run is two lines. --verbose restores full per-check
output. All ~30 checks were converted to this API; orphaned guidance/label
helpers (printPerTargetOKLines, per-check *Help blocks, colors_format.go) were
removed.

The any-usage check (2e) now defaults to only the unstaged working-tree diff
(changed lines), via a new per-repo git-diff index in changedlines.go; --all-any
restores the full scan. Not-a-git-repo / no-diff warns on nothing.

Golden fixtures regenerated; integration tests updated to the new format; added
unit tests for the diff index.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 01:16:56 +08:00

134 lines
4.1 KiB
Go

package main
import (
"os"
"path/filepath"
"testing"
)
func TestCheckTemplColorsFlagsHardcodedColorsAndInlineStyles(t *testing.T) {
root := t.TempDir()
file := filepath.Join(root, "sample.templ")
content := `package sample
templ Example() {
<div class="text-blue-600 bg-[#fff]"></div>
<div class="border border-border" style="color: #fff; background: rgb(255, 0, 0)"></div>
<div style={ "background-color: hsl(12 100% 50%)" }></div>
}
`
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
t.Fatalf("write sample templ: %v", err)
}
violations := checkTemplColors(root)
if len(violations) < 5 {
t.Fatalf("checkTemplColors() returned %d violations, want at least 5: %#v", len(violations), violations)
}
rules := map[string]bool{}
for _, violation := range violations {
rules[violation.rule] = true
}
for _, rule := range []string{
"no-hardcoded-tw-color",
"no-hex-in-class",
"no-tw-arbitrary-hex",
"no-inline-hex-style",
"no-inline-rgb-style",
} {
if !rules[rule] {
t.Fatalf("missing rule %q in violations: %#v", rule, violations)
}
}
}
func TestCheckTemplColorsAllowsSemanticTokensAndCSSVariables(t *testing.T) {
root := t.TempDir()
file := filepath.Join(root, "sample.templ")
content := `package sample
templ Example() {
<div class="bg-success/10 text-success border-border"></div>
<div style="background: hsl(var(--background)); color: hsl(var(--foreground))"></div>
<div style={ "background: hsl(var(--primary)); color: hsl(var(--primary-foreground))" }></div>
}
`
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
t.Fatalf("write sample templ: %v", err)
}
violations := checkTemplColors(root)
if len(violations) != 0 {
t.Fatalf("checkTemplColors() returned %d violations, want 0: %#v", len(violations), violations)
}
}
func TestCheckNinjaTplColorsFlagsHardcodedColors(t *testing.T) {
root := t.TempDir()
tplDir := filepath.Join(root, "templates", "blocks")
if err := os.MkdirAll(tplDir, 0755); err != nil {
t.Fatalf("mkdir: %v", err)
}
file := filepath.Join(tplDir, "hero.ninjatpl")
content := `<section style="background:hsl(232,55%,4%);min-height:28rem;">
<h1 class="text-[hsl(210,18%,96%)] text-5xl">{{ headline }}</h1>
<p class="text-[rgba(220,232,248,0.75)]">{{ subtext }}</p>
<a style="background:hsl(168,82%,46%)">{{ primary_cta }}</a>
<div style="background:#0a0e24">hardcoded hex</div>
<span class="bg-[rgba(130,70,220,0.1)]">badge</span>
<span class="text-blue-500">named color</span>
</section>
`
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
t.Fatalf("write ninjatpl: %v", err)
}
violations := checkNinjaTplColors(root)
if len(violations) < 6 {
t.Fatalf("checkNinjaTplColors() returned %d violations, want at least 6: %#v", len(violations), violations)
}
rules := map[string]bool{}
for _, v := range violations {
rules[v.rule] = true
}
for _, rule := range []string{
"no-hardcoded-hsl",
"no-tw-arbitrary-color",
"no-inline-hex",
"no-hardcoded-tw-color",
} {
if !rules[rule] {
t.Fatalf("missing rule %q in violations: %#v", rule, violations)
}
}
}
func TestCheckNinjaTplColorsAllowsCSSVariablesAndNeutralOverlays(t *testing.T) {
root := t.TempDir()
tplDir := filepath.Join(root, "templates", "blocks")
if err := os.MkdirAll(tplDir, 0755); err != nil {
t.Fatalf("mkdir: %v", err)
}
file := filepath.Join(tplDir, "hero.ninjatpl")
content := `<section class="bg-background text-foreground">
<h1 class="text-primary text-5xl">{{ headline }}</h1>
<p class="text-muted-foreground">{{ subtext }}</p>
<a style="background:hsl(var(--primary));color:hsl(var(--primary-foreground))">{{ cta }}</a>
<div style="background:rgba(0,0,0,0.5)">dark overlay</div>
<div style="background:rgba(255,255,255,0.1)">light overlay</div>
<span class="bg-primary/10 text-accent">badge</span>
</section>
`
if err := os.WriteFile(file, []byte(content), 0644); err != nil {
t.Fatalf("write ninjatpl: %v", err)
}
violations := checkNinjaTplColors(root)
if len(violations) != 0 {
t.Fatalf("checkNinjaTplColors() returned %d violations, want 0: %#v", len(violations), violations)
}
}