package lexer import ( "strings" "testing" ) // The highlighter emits HTML, and its input is Go source — which is full of <, > and &. // Anything that reaches the page unescaped is markup injection into your own docs page: // a snippet containing `
` would render a div. func TestHighlightGoEscapes(t *testing.T) { got := HighlightGo(`s := "
" // a & b`) if strings.Contains(got, "` in the SOURCE reached the output as markup:\n%s", got) } if !strings.Contains(got, "<div") { t.Errorf("the angle bracket was not escaped:\n%s", got) } if !strings.Contains(got, "&") { t.Errorf("the ampersand was not escaped:\n%s", got) } } func TestHighlightGoClassifies(t *testing.T) { got := HighlightGo("func main() { x := 42 // note\n}") for _, want := range []struct{ what, class, text string }{ {"keyword", KeywordClass, "func"}, {"call", FuncClass, "main"}, {"number", NumberClass, "42"}, {"comment", CommentClass, "// note"}, } { if !strings.Contains(got, ``+want.text+``) { t.Errorf("%s %q was not highlighted:\n%s", want.what, want.text, got) } } } // The //gowasm: directives are the most important line in half these snippets. They are // comments, and must survive as such. func TestHighlightGoKeepsDirectives(t *testing.T) { got := HighlightGo("//gowasm:page / static layout=public\nfunc HomePage() {}") if !strings.Contains(got, `//gowasm:page / static layout=public`) { t.Errorf("the directive was not kept whole as a comment:\n%s", got) } } // A lexer that can hang or eat the rest of the file on malformed input would take the // whole page down with it. Unterminated literals stop; they do not run away. func TestHighlightGoSurvivesMalformedInput(t *testing.T) { for _, src := range []string{ `x := "unterminated`, "y := `unterminated raw", "/* unterminated block", `z := '`, "", } { got := HighlightGo(src) // The text must all still be there — mangling is not an acceptable failure mode // either. Compare on the visible characters, ignoring the spans. if plain := stripTags(got); plain != src { t.Errorf("input %q came out as %q", src, plain) } } } // Nothing is dropped: every byte of the source is still on the page, in order. func TestHighlightGoIsLossless(t *testing.T) { src := "package app\n\nimport \"strings\"\n\nfunc f(n int) string {\n\treturn strings.Repeat(\"x\", n) // pad\n}\n" if plain := stripTags(HighlightGo(src)); plain != src { t.Errorf("the highlighter changed the source.\n got: %q\nwant: %q", plain, src) } } // stripTags removes the spans and unescapes, recovering the original source. func stripTags(s string) string { var b strings.Builder for i := 0; i < len(s); { if s[i] == '<' { j := strings.IndexByte(s[i:], '>') if j < 0 { break } i += j + 1 continue } b.WriteByte(s[i]) i++ } out := b.String() // Reverse html.EscapeString, innermost last. out = strings.ReplaceAll(out, "<", "<") out = strings.ReplaceAll(out, ">", ">") out = strings.ReplaceAll(out, """, `"`) out = strings.ReplaceAll(out, "'", "'") out = strings.ReplaceAll(out, "&", "&") return out }