Sign in

Search issue titles and bodies, change requests and comments. Private repositories you cannot read are not included.

vole
Clone

Read only, no credentials:

https://rodent.software/git/vole.git

Write over SSH with a registered key:

git clone ssh://git@0.0.0.0:2222/vole.git

A free and open-source forge that lives in your Git repo.

Blame · server/web/highlight_test.go

Line attribution on main · 169 line(s)

306f36fc
Keenan Nemetz
12d ago
1 package web
2
3 import (
1b8675b1
Keenan Nemetz
8d ago
4 "encoding/json"
306f36fc
Keenan Nemetz
12d ago
5 "os"
1b8675b1
Keenan Nemetz
8d ago
6 "path/filepath"
306f36fc
Keenan Nemetz
12d ago
7 "strings"
8 "testing"
9 )
10
11 func TestPrismLanguageNamesKnownFiles(t *testing.T) {
12 t.Parallel()
13 for name, want := range map[string]string{
14 "main.go": "go",
15 "internal/run.go": "go",
16 "web/app.tsx": "typescript",
17 "script.mjs": "javascript",
18 "style.scss": "scss",
19 "deploy/nginx.conf": "nginx",
20 "app.conf": "ini",
21 "Makefile": "makefile",
22 "cmake/CMakeLists.txt": "cmake",
23 "Dockerfile": "docker",
24 "Containerfile": "docker",
25 ".editorconfig": "editorconfig",
26 "README.MD": "markdown",
27 "terraform/main.tf": "hcl",
28 // A file the highlighter does not claim stays plain: a wrong grammar is
29 // worse than no grammar.
30 "go.sum": "go-module",
31 "app.py": "python",
32 "lib.rs": "rust",
33 "main.js": "javascript",
34 "component.tsx": "typescript",
35 "deploy.tf": "hcl",
36 "schema.graphql": "graphql",
37 "queries.sql": "sql",
38 "notes.rst": "rest",
39 "LICENSE": "",
40 "notes.txt": "",
41 "image.webp": "",
42 } {
43 if got := prismLanguage(name); got != want {
44 t.Errorf("prismLanguage(%q) = %q, want %q", name, got, want)
45 }
46 }
47 }
48
ac00929f
Keenan Nemetz
12d ago
49 // The worker installs its own message handler and posts structured objects. Prism
50 // installs one too when it finds a WorkerGlobalScope, and that one expects a JSON
51 // string, so leaving it on throws JSON.parse on every job before the worker's
52 // handler runs.
53 func TestPrismWorkerDisablesPrismOwnMessageHandler(t *testing.T) {
54 t.Parallel()
55 source, err := os.ReadFile("static/prism-worker.js")
56 if err != nil {
57 t.Fatal(err)
58 }
59 if !strings.Contains(string(source), "disableWorkerMessageHandler: true") {
60 t.Fatal("prism-worker.js lets Prism parse the worker's structured messages as JSON")
61 }
62 }
63
1b8675b1
Keenan Nemetz
8d ago
64 // A worker that goes back to importing every grammar up front still colours
65 // correctly, so nothing else fails: the page just pays 565 KB and a few hundred
66 // milliseconds again. This is the assertion that notices.
67 func TestPrismWorkerLoadsGrammarsOnDemand(t *testing.T) {
68 t.Parallel()
69 source, err := os.ReadFile("static/prism-worker.js")
70 if err != nil {
71 t.Fatal(err)
72 }
73 for _, want := range []string{
74 "importScripts('prism.min.js', 'prism-manifest.js')",
75 "'prism-languages/' + name + '.js'",
76 } {
77 if !strings.Contains(string(source), want) {
78 t.Errorf("prism-worker.js no longer contains %q", want)
79 }
80 }
81 }
82
83 // readPrismManifest parses static/prism-manifest.js. The generator writes one
84 // assignment whose value is a JSON object, so decoding stops at the end of that
85 // object and the trailing semicolon is never read.
86 func readPrismManifest(t *testing.T) map[string][]string {
87 t.Helper()
88 source, err := os.ReadFile("static/prism-manifest.js")
89 if err != nil {
90 t.Fatalf("the Prism manifest is missing: %v", err)
91 }
92 const assignment = "self.PRISM_LANGUAGES = "
93 at := strings.Index(string(source), assignment)
94 if at < 0 {
95 t.Fatal("static/prism-manifest.js does not assign self.PRISM_LANGUAGES")
96 }
97 var languages map[string][]string
98 rest := strings.NewReader(string(source[at+len(assignment):]))
99 if err := json.NewDecoder(rest).Decode(&languages); err != nil {
100 t.Fatalf("static/prism-manifest.js is not a JSON object: %v", err)
101 }
102 return languages
103 }
104
105 // TestPrismLanguagesAreVendored holds the server's table to what the worker can
106 // load. A name here that neither the core nor the manifest defines colours
107 // nothing, and nothing fails at build time; the blob just quietly stays plain.
108 //
109 // The core covers the languages every Prism page starts with — markup, css,
110 // clike, javascript — and their aliases, so those have no files to import unless
111 // a component modifies them.
306f36fc
Keenan Nemetz
12d ago
112 func TestPrismLanguagesAreVendored(t *testing.T) {
113 t.Parallel()
1b8675b1
Keenan Nemetz
8d ago
114 core, err := os.ReadFile("static/prism.min.js")
115 if err != nil {
116 t.Fatalf("vendored Prism is missing: %v", err)
117 }
118 inCore := func(lang string) bool {
119 // Prism registers a language as `languages.name=` or, for a name with
120 // punctuation in it, `languages["name"]=`.
121 return strings.Contains(string(core), "languages."+lang+"=") ||
122 strings.Contains(string(core), `languages["`+lang+`"]=`)
123 }
124 languages := readPrismManifest(t)
125 for name, lang := range prismLanguages {
126 if inCore(lang) {
127 continue
128 }
129 if _, ok := languages[lang]; !ok {
130 t.Errorf("%q is mapped to %q, which neither static/prism.min.js nor static/prism-manifest.js defines", name, lang)
306f36fc
Keenan Nemetz
12d ago
131 }
132 }
1b8675b1
Keenan Nemetz
8d ago
133 }
134
135 // TestPrismGrammarsMatchTheManifest keeps the manifest and the vendored files in
136 // step in both directions: a file it names but nobody wrote only fails in a
137 // browser, as an unhighlighted blob, and a file nobody names is left behind when
138 // Prism drops a language. scripts/vendor-prism.py --check says the same thing
139 // after a network round trip; this is the answer without one.
140 func TestPrismGrammarsMatchTheManifest(t *testing.T) {
141 t.Parallel()
142 languages := readPrismManifest(t)
143 named := map[string]bool{}
144 for lang, files := range languages {
145 if len(files) == 0 {
146 t.Errorf("%q is in the manifest with no files to import", lang)
147 }
148 for _, file := range files {
149 named[file] = true
150 if _, err := os.Stat(filepath.Join("static", "prism-languages", file+".js")); err != nil {
151 t.Errorf("%q imports %s, which is not vendored: %v", lang, file, err)
306f36fc
Keenan Nemetz
12d ago
152 }
153 }
154 }
1b8675b1
Keenan Nemetz
8d ago
155 entries, err := os.ReadDir(filepath.Join("static", "prism-languages"))
156 if err != nil {
157 t.Fatalf("the Prism grammars are missing: %v", err)
158 }
159 for _, entry := range entries {
160 name, ok := strings.CutSuffix(entry.Name(), ".js")
161 if !ok || entry.IsDir() {
162 t.Errorf("static/prism-languages/%s is not a vendored grammar", entry.Name())
163 continue
164 }
165 if !named[name] {
166 t.Errorf("static/prism-languages/%s is in no manifest entry", entry.Name())
306f36fc
Keenan Nemetz
12d ago
167 }
168 }
169 }