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>
134 lines
4.1 KiB
Go
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)
|
|
}
|
|
}
|