package main
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestCheckTemplColorsFlagsHardcodedColorsAndInlineStyles(t *testing.T) {
root := t.TempDir()
file := filepath.Join(root, "sample.templ")
content := `package sample
templ Example() {
}
`
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() {
}
`
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 := `
`
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 := `
{{ headline }}
{{ subtext }}
{{ cta }}
dark overlay
light overlay
badge
`
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)
}
}
func TestFormatColorViolationsGroupsByFile(t *testing.T) {
report := formatColorViolations(".ninjatpl files", []colorViolation{
{file: "b.ninjatpl", line: 12, rule: "no-hardcoded-rgb", snippet: "rgba(1,2,3,0.5)"},
{file: "a.ninjatpl", line: 9, rule: "no-hardcoded-hsl", snippet: "hsl(10,20%,30%)"},
{file: "a.ninjatpl", line: 9, rule: "no-tw-arbitrary-color", snippet: "text-[hsl(10,20%,30%)]"},
{file: "a.ninjatpl", line: 4, rule: "no-inline-hex", snippet: "#fff"},
})
expected := strings.Join([]string{
" FAIL: 4 hardcoded color(s) in .ninjatpl files:",
" a.ninjatpl",
" 4 [no-inline-hex] #fff",
" 9 [no-hardcoded-hsl] hsl(10,20%,30%)",
" 9 [no-tw-arbitrary-color] text-[hsl(10,20%,30%)]",
" b.ninjatpl",
" 12 [no-hardcoded-rgb] rgba(1,2,3,0.5)",
}, "\n")
if report != expected {
t.Fatalf("formatColorViolations() mismatch\nwant:\n%s\n\ngot:\n%s", expected, report)
}
}
func TestCollectColorHintsDeduplicatesAndSorts(t *testing.T) {
hints := collectColorHints(
[]colorViolation{
{snippet: "text-blue-500"},
{snippet: "bg-amber-100"},
},
[]colorViolation{
{snippet: "text-blue-500"},
{snippet: "text-[hsl(38,52%,54%)]"},
},
)
expected := []string{"bg-warning/10", "hsl(var(--token))", "text-info"}
if strings.Join(hints, "|") != strings.Join(expected, "|") {
t.Fatalf("collectColorHints() = %#v, want %#v", hints, expected)
}
}