Commit 4228201736

42282017365bc1516f890d87eb5933bb5388905c

parent: b7251fa5a1

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-17 06:21 UTC

web: class coverage test ignores template actions

Template actions ({{...}}) are stripped before the class="..." regex
runs, so a nested-quote conditional no longer truncates the attribute
match. Fails today, listing classes with no rule in style.css:
  activity (owner.html)
  authorlink (layout.html)
  badge- (layout.html, log.html, tree.html)
  check- (build.html, builds.html, commit.html, mr.html)
  chip- (globalsearch.html, mr.html, mrs.html)
  chroma (layout.html)
  compareform (refs.html)
  feedline (dashboard.html)
  l (owner.html)
  lang- (tree.html)
  msmain (milestones.html, orgmilestones.html)
  signupform (register.html)

The dash/prefix entries (badge-, check-, chip-, lang-, l) are the
literal part left behind where a class is composed at render time,
e.g. class="chip-{{.State}}" -> "chip-"; style.css has rules for the
concrete values (chip-open, chip-done, ...) but none for the bare
prefix, so these five will always show up here.

Ref #218
internal/web/classes_test.go +7 −8
@@ -9,10 +9,10 @@ import (
99)
1010
1111// TestEveryTemplateClassHasARule fails on a class a template uses that
12// no selector in style.css mentions. Class tokens containing template
13// actions ({{...}}) are composed at render time and skipped; their
14// prefixes (chip-, badge-, check-, lang-) are covered by the rules for
15// the concrete values.
12// no selector in style.css mentions. Template actions are removed before
13// matching, so a class composed at render time (chip-{{.State}})
14// contributes only its literal part; the rules for the concrete values
15// cover the rest.
1616func TestEveryTemplateClassHasARule(t *testing.T) {
1717 css := string(StyleCSS)
1818 // Every ".name" that appears in a selector position: outside braces.
@@ -62,6 +62,7 @@ func TestEveryTemplateClassHasARule(t *testing.T) {
6262 }
6363
6464 attrRe := regexp.MustCompile(`class="([^"]*)"`)
65 actionRe := regexp.MustCompile(`(?s)\{\{.*?\}\}`)
6566 missing := map[string][]string{}
6667 entries, err := fs.ReadDir(templateFS, "templates")
6768 if err != nil {
@@ -72,11 +73,9 @@ func TestEveryTemplateClassHasARule(t *testing.T) {
7273 if err != nil {
7374 t.Fatal(err)
7475 }
75 for _, m := range attrRe.FindAllStringSubmatch(string(src), -1) {
76 clean := actionRe.ReplaceAllString(string(src), " ")
77 for _, m := range attrRe.FindAllStringSubmatch(clean, -1) {
7678 for _, c := range strings.Fields(m[1]) {
77 if strings.Contains(c, "{{") || strings.Contains(c, "}}") {
78 continue
79 }
8079 if !styled[c] {
8180 missing[c] = append(missing[c], e.Name())
8281 }