oficina / docs Public

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

Plain git — no account needed to clone.

Download

Download this file

1package docs
2
3import (
4 "io/fs"
5 "strings"
6 "testing"
7 "unicode"
8)
9
10// TestOurTemplatesUseTheAttributeGrammar holds the markup constraint
11// at the source, not the rendered page: every HTML file this app
12// authors under views/ (embedded as viewsFS) must write the bare
13// rst-* attribute grammar (<div rst-box>), never class="rst-*".
14//
15// This is deliberately a package-internal test, not a docs_test one,
16// because it needs viewsFS itself — walking our own template sources
17// rather than a rendered response is the whole point: rastrillo's own
18// partials and shell emit class="rst-*", and that is their output,
19// not this app's markup, so it must never appear in this walk in the
20// first place, rather than being screened out after the fact.
21//
22// This replaces what used to be a rendered-page assertion
23// ("no class=\"rst-\" anywhere in the response body"), which could
24// not tell the two apart: rastrillo's column shell hard-codes
25// class="rst-skip" and class="rst-page" outside any block this app
26// can override, so that blanket check failed on every page this app
27// serves regardless of what its own templates wrote — see git history
28// on this file for the reverted fix (rewriting rastrillo's shell
29// bytes at parse time) and why it was wrong: a string substitution
30// over a dependency's markup fails silently the day rastrillo changes
31// those two spots, which it has already said it will before 1.0.
32// Checking our own sources instead needs no such rewrite, is correct
33// (rastrillo's vendored output is not ours to hold to our own
34// convention), and stays stable across a rastrillo upgrade that
35// changes its shell's own spelling, because it does not depend on
36// rastrillo's internal template text at all.
37func TestOurTemplatesUseTheAttributeGrammar(t *testing.T) {
38 err := fs.WalkDir(viewsFS, ".", func(path string, d fs.DirEntry, err error) error {
39 if err != nil {
40 return err
41 }
42 if d.IsDir() {
43 return nil
44 }
45 b, err := fs.ReadFile(viewsFS, path)
46 if err != nil {
47 return err
48 }
49 if strings.Contains(string(b), `class="rst-`) {
50 t.Errorf("%s: class vocabulary found — this app writes the attribute grammar (Global Constraints)", path)
51 }
52 return nil
53 })
54 if err != nil {
55 t.Fatalf("walk viewsFS: %v", err)
56 }
57}
58
59// The catalog is written in US English, per Paul's ruling on 2026-09-03.
60// It reached this rule by shipping the counterexample: the alignment
61// buttons read "Align centre" while the command, the CSS class and the
62// keyword `text-align` itself all say `center`, so the label disagreed
63// with every other name for the same thing — and with the person who
64// asked for the feature.
65//
66// The real answer is a locale on the profile offering both spellings
67// (discussion 31), which this test does not pre-empt: when that lands,
68// en-GB is a second table and this guard moves to the en-US one. Until
69// then there is exactly one table, and nothing else was checking which
70// English it was written in.
71//
72// It is deliberately a word list rather than a dictionary. A general
73// speller is a dependency and a source of false positives on product
74// nouns; the pairs below are the ones a UK-based writer actually
75// reaches for, and a miss costs one wrong word that the next addition
76// to the list catches. Each entry is a British form whose US form
77// differs — never a word that is merely uncommon.
78func TestTheCatalogIsWrittenInUSEnglish(t *testing.T) {
79 // British form -> the US form it should have been. The value is
80 // never used to rewrite anything; it is there so a failure tells
81 // the reader what to write instead of only that something is
82 // wrong. Derived forms are listed rather than stemmed, because a
83 // suffix rule that turns "premise" into a finding is worse than
84 // a list that misses "colourless" until someone writes it.
85 british := map[string]string{
86 "centre": "center",
87 "centred": "centered",
88 "centres": "centers",
89 "colour": "color",
90 "colours": "colors",
91 "coloured": "colored",
92 "behaviour": "behavior",
93 "behaviours": "behaviors",
94 "licence": "license",
95 "defence": "defense",
96 "grey": "gray",
97 "catalogue": "catalog",
98 "dialogue": "dialog",
99 "favourite": "favorite",
100 "favourites": "favorites",
101 "cancelled": "canceled",
102 "cancelling": "canceling",
103 "customise": "customize",
104 "customised": "customized",
105 "organise": "organize",
106 "organised": "organized",
107 "organising": "organizing",
108 "recognise": "recognize",
109 "recognised": "recognized",
110 "initialise": "initialize",
111 "analyse": "analyze",
112 "analysed": "analyzed",
113 "apologise": "apologize",
114 "programme": "program",
115 "whilst": "while",
116 "towards": "toward",
117 }
118
119 // Words, lowercased, with anything that is not a letter treated as
120 // a separator: the catalog's values carry apostrophes, ellipses and
121 // ordinary punctuation, and "couldn't" has to split into "couldn"
122 // and "t" rather than leaving a word glued to a mark and invisible
123 // to an exact-match lookup.
124 words := func(s string) []string {
125 return strings.FieldsFunc(strings.ToLower(s), func(r rune) bool {
126 return !unicode.IsLetter(r)
127 })
128 }
129
130 for key, value := range Catalog {
131 for _, w := range words(value) {
132 if us, ok := british[w]; ok {
133 t.Errorf("%s: %q is British English — write %q (this catalog is en-US; see discussion 31)", key, w, us)
134 }
135 }
136 }
137}
138