internal/web/web_test.go

7756246a087737fe2633ac6e794424e139120737
gitbay/internal/web/web_test.go history · blame · raw

118 lines · 4408 bytes

  1package web
  2
  3import (
  4	"io/fs"
  5	"regexp"
  6	"strings"
  7	"testing"
  8)
  9
 10// Every page template parses with the layout at start-up, so a broken
 11// template fails the process rather than the first visit to its page.
 12// Package init has already done the work; this asserts it covered every
 13// file (#116).
 14func TestEveryPageTemplateParses(t *testing.T) {
 15	names := Pages()
 16	if len(names) < 30 {
 17		t.Fatalf("only %d page templates parsed: %v", len(names), names)
 18	}
 19	for _, want := range []string{"tree.html", "blob.html", "mr.html", "issue.html", "dashboard.html", "registered.html"} {
 20		if _, ok := pages[want]; !ok {
 21			t.Errorf("%s not parsed", want)
 22		}
 23	}
 24}
 25
 26// A th with no rule of its own falls back to the browser's centred default
 27// (#151). The classes that do left-align one are read out of the stylesheet
 28// rather than listed here, so a new rule does not need a test change.
 29func TestHeaderRowsAreLeftAligned(t *testing.T) {
 30	aligned := map[string]bool{}
 31	for _, rule := range strings.Split(string(StyleCSS), "}") {
 32		sel, decls, ok := strings.Cut(rule, "{")
 33		if !ok || !strings.Contains(decls, "text-align: left") || !strings.Contains(sel, "th") {
 34			continue
 35		}
 36		for _, class := range regexp.MustCompile(`\.([a-z-]+)`).FindAllStringSubmatch(sel, -1) {
 37			aligned[class[1]] = true
 38		}
 39	}
 40	if len(aligned) == 0 {
 41		t.Fatal("no left-aligning th rule found in the stylesheet")
 42	}
 43
 44	classes := regexp.MustCompile(`class="([^"]*)"`)
 45	entries, err := fs.ReadDir(templateFS, "templates")
 46	if err != nil {
 47		t.Fatal(err)
 48	}
 49	for _, e := range entries {
 50		b, err := templateFS.ReadFile("templates/" + e.Name())
 51		if err != nil {
 52			t.Fatal(err)
 53		}
 54		// The table and row tags a header sits under, plus the header row
 55		// itself: any one of them may carry the class.
 56		for _, head := range strings.Split(string(b), "<thead>")[1:] {
 57			open, _, _ := strings.Cut(head, "</tr>")
 58			before := strings.Split(string(b), "<thead>"+head)[0]
 59			if i := strings.LastIndex(before, "<table"); i >= 0 {
 60				open += before[i:]
 61			}
 62			hit := false
 63			for _, m := range classes.FindAllStringSubmatch(open, -1) {
 64				for _, c := range strings.Fields(m[1]) {
 65					hit = hit || aligned[c]
 66				}
 67			}
 68			if !hit {
 69				t.Errorf("%s: header row is not left-aligned by any rule: %.60s", e.Name(), open)
 70			}
 71		}
 72	}
 73}
 74
 75// when names the zone rather than leaving an absolute time ambiguous, and
 76// passes an unparseable value through unchanged (#182).
 77func TestWhenNamesTheZone(t *testing.T) {
 78	got := funcs["when"].(func(string) string)("2026-09-12T02:18:07.123Z")
 79	if got != "2026-09-12 02:18 UTC" {
 80		t.Fatalf("when: %q", got)
 81	}
 82	if got := funcs["when"].(func(string) string)("not a time"); got != "not a time" {
 83		t.Fatalf("passthrough: %q", got)
 84	}
 85}
 86
 87// TestMainWidthClass renders every page against an empty struct and
 88// checks main carries exactly one width class, and that the pages the
 89// spec calls wide or bounded say so.
 90func TestMainWidthClass(t *testing.T) {
 91	wide := map[string]bool{"tree.html": true, "blob.html": true, "blame.html": true, "log.html": true, "commit.html": true, "compare.html": true, "builds.html": true, "build.html": true, "search.html": true, "globalsearch.html": true}
 92	bounded := map[string]bool{"landing.html": true, "login.html": true, "register.html": true, "registered.html": true, "new.html": true, "issuenew.html": true, "mrnew.html": true, "settings.html": true, "account.html": true, "admin.html": true, "edit.html": true, "snippetnew.html": true, "privacy.html": true, "404.html": true}
 93	for _, name := range Pages() {
 94		src, err := TemplateSource(name)
 95		if err != nil {
 96			t.Fatal(err)
 97		}
 98		has := strings.Contains(src, `{{define "width"}}`)
 99		switch {
100		case wide[name] && !strings.Contains(src, `{{define "width"}}wide{{end}}`):
101			t.Errorf("%s: want width wide", name)
102		case bounded[name] && !strings.Contains(src, `{{define "width"}}bounded{{end}}`):
103			t.Errorf("%s: want width bounded", name)
104		case !wide[name] && !bounded[name] && has:
105			t.Errorf("%s: defines a width but the spec calls it reading", name)
106		}
107	}
108	layout, err := TemplateSource("layout.html")
109	if err != nil {
110		t.Fatal(err)
111	}
112	if !strings.Contains(layout, `<main id="content" class="content {{template "width" .}}">`) {
113		t.Error("layout.html: main does not carry the width block")
114	}
115	if !strings.Contains(layout, `{{define "width"}}reading{{end}}`) {
116		t.Error("layout.html: no default width")
117	}
118}