oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1package slidestest
2
3import (
4 "amadan.net/oficina/estilo.git/busy"
5 "bytes"
6 "fmt"
7 "os"
8 "path/filepath"
9 "regexp"
10 "sort"
11 "testing"
12
13 "amadan.net/oficina/estilo.git/theme"
14 "amadan.net/rastrillo/rastrillo/ui"
15)
16
17// vendoredTheme is the theme this app was scaffolded with. Change it
18// here when you swap static/theme.css for another of ui.ThemeNames().
19const vendoredTheme = "day"
20
21// vendoredIsMine names the vendored files this app has edited
22// DELIBERATELY. Add one here when you mean to diverge: this test stops
23// pinning it, and rastrillo doctor leaves it alone too. The file was
24// yours from the moment the scaffold wrote it — this is only where you
25// record that the edit was on purpose, so the next reader (and the next
26// upgrade) can tell it from drift.
27var vendoredIsMine = map[string]bool{
28 // rastrillo's shim as estilo carries it: without the old busy rule,
29 // which would fight busy.js. Pinned to estilo by TestBusyFilesAreEstilos.
30 "rastrillo.js": true,
31 // estilo's busy.js, for the same reason: the busy rule as the suite
32 // carries it. ui's own copy moved on at rastrillo fb806a2 (pow.js
33 // arming) and estilo's has not, so the two can no longer both be
34 // pinned. Pinned to estilo by TestBusyFilesAreEstilos.
35 "busy.js": true,
36 // Calma with Slides' accent, from estilo (estilo AGENTS.md, decisions
37 // table, row 8) rather than rastrillo's day. Pinned to estilo instead,
38 // by TestThemeIsEstilosForSlides below.
39 "theme.css": true,
40}
41
42// TestThemeIsEstilosForSlides pins static/theme.css to the estilo this app
43// is built against. After an estilo upgrade, re-write it with
44// `go run ./scripts/writeestilo` — never by hand.
45func TestThemeIsEstilosForSlides(t *testing.T) {
46 want, ok := theme.ForApp("slides")
47 if !ok {
48 t.Fatal("estilo's theme ring has no slides")
49 }
50 got, err := os.ReadFile(filepath.Join("..", "slides", "static", "theme.css"))
51 if err != nil {
52 t.Fatal(err)
53 }
54 if !bytes.Equal(got, want) {
55 t.Error("static/theme.css differs from estilo's theme for slides; run go run ./scripts/writeestilo")
56 }
57}
58
59// The scaffold delivered these files once; they are app-owned from
60// then on. This test pins each vendored copy byte-identical to the
61// library it came from: a reviewer runs the suite instead of reading
62// ~56KB of assets as app diff, and a framework upgrade that forgets
63// to re-copy is caught instead of drifting silently.
64//
65// The list is ui.VendoredAssets — the same function rastrillo new
66// wrote these files from and rastrillo doctor compares them against —
67// so a file added to the library's vendored set is pinned here without
68// this file changing.
69func TestVendoredAssetsMatchTheLibrary(t *testing.T) {
70 assets, ok := ui.VendoredAssets(vendoredTheme)
71 if !ok {
72 t.Fatalf("unknown theme %q", vendoredTheme)
73 }
74 for name, lib := range assets {
75 if vendoredIsMine[name] {
76 continue
77 }
78 vendored, err := os.ReadFile(filepath.Join("..", "slides", "static", name))
79 if err != nil {
80 t.Errorf("read vendored %s: %v", name, err)
81 continue
82 }
83 if !bytes.Equal(vendored, lib) {
84 t.Errorf("static/%s differs from the library copy; re-copy it with rastrillo doctor --fix (or add it to vendoredIsMine if the edit was deliberate)", name)
85 }
86 }
87}
88
89// TestEveryRastrilloTokenSlidesUsesExists makes a misspelled design token a
90// test failure instead of a browser falling back silently. The vocabulary is
91// the committed tokens.css; usages include both authored templates and CSS.
92func TestEveryRastrilloTokenSlidesUsesExists(t *testing.T) {
93 declared := map[string]bool{}
94 definitions := append(append([]byte{}, ui.TokensCSS()...), theme.CSS()...)
95 for _, match := range regexp.MustCompile(`--(rst-[a-z0-9-]+)\s*:`).FindAllSubmatch(definitions, -1) {
96 declared[string(match[1])] = true
97 }
98 if len(declared) == 0 {
99 t.Fatal("tokens.css declares no --rst-* tokens")
100 }
101 files, err := filepath.Glob(filepath.Join("..", "slides", "templates", "*.html"))
102 if err != nil {
103 t.Fatal(err)
104 }
105 css, err := filepath.Glob(filepath.Join("..", "slides", "static", "*.css"))
106 if err != nil {
107 t.Fatal(err)
108 }
109 for _, file := range css {
110 // tokens.css is rastrillo's vendored component implementation;
111 // TestVendoredAssetsMatchTheLibrary pins it. This gate is for
112 // Slides-authored CSS and inline template styles.
113 if filepath.Base(file) != "tokens.css" {
114 files = append(files, file)
115 }
116 }
117 used := map[string]bool{}
118 pattern := regexp.MustCompile(`var\(--(rst-[a-z0-9-]+)`)
119 comments := regexp.MustCompile(`(?s)/\*.*?\*/`)
120 for _, file := range files {
121 body, err := os.ReadFile(file)
122 if err != nil {
123 t.Fatal(err)
124 }
125 for _, match := range pattern.FindAllSubmatch(comments.ReplaceAll(body, nil), -1) {
126 used[string(match[1])] = true
127 }
128 }
129 var missing []string
130 for token := range used {
131 if !declared[token] {
132 missing = append(missing, token)
133 }
134 }
135 sort.Strings(missing)
136 for _, token := range missing {
137 t.Error(fmt.Sprintf("Slides uses --%s, but tokens.css does not declare it", token))
138 }
139}
140
141// rastrillo's busy rule (Paul, 2026-09-29): a submit button's label gives
142// way to a spinner for at least 650ms, the button disables, and Back
143// resets it. static/ carries it as estilo does — the shim without the old
144// rule, busy.js and busy.css — pinned here to the estilo this app builds
145// against. After an estilo upgrade: go run ./scripts/writeestilo.
146func TestBusyFilesAreEstilos(t *testing.T) {
147 for name, want := range map[string][]byte{
148 "rastrillo.js": busy.ShimJS(),
149 "busy.js": busy.JS(),
150 "busy.css": busy.CSS(),
151 } {
152 got, err := os.ReadFile(filepath.Join("..", "slides", "static", name))
153 if err != nil {
154 t.Fatal(err)
155 }
156 if !bytes.Equal(got, want) {
157 t.Errorf("static/%s differs from estilo's; run go run ./scripts/writeestilo", name)
158 }
159 }
160}
161