82 lines
2.6 KiB
Go
82 lines
2.6 KiB
Go
package renderkit
|
|
|
|
import (
|
|
stdhtml "html"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
var tagRe = regexp.MustCompile(`<[^>]*>`)
|
|
|
|
// visibleText strips the span markup and unescapes, i.e. what the user
|
|
// sees and what the copy button copies.
|
|
func visibleText(s string) string { return stdhtml.UnescapeString(tagRe.ReplaceAllString(s, "")) }
|
|
|
|
func TestHighlightPreservesText(t *testing.T) {
|
|
samples := map[string]string{
|
|
"go": "package main\n\n// hi\nfunc main() {\n\tx := \"a<b>\" + `raw\nline`\n\tif x != nil { return 42 }\n}\n",
|
|
"py": "def f(x):\n \"\"\"doc\"\"\"\n return x # c\n",
|
|
"bash": "echo \"$HOME\" ${A} | grep 'x' # note\nfoo#bar $#\n",
|
|
"json": "{\"a\": [1, 2.5, true, null], \"b\": \"x\"}",
|
|
"sql": "SELECT count(*) FROM t WHERE a = 'x' -- c\n",
|
|
"rust": "fn f<'a>(x: &'a str) -> u32 { 1 }\n",
|
|
"diff": "@@ -1 +1 @@\n-old\n+new\n ctx\n",
|
|
}
|
|
for lang, src := range samples {
|
|
out := highlight(lang, stdhtml.EscapeString(src))
|
|
if got := visibleText(out); got != src {
|
|
t.Errorf("%s: text changed\nwant %q\ngot %q", lang, src, got)
|
|
}
|
|
if !strings.Contains(out, "<span class=\"tok-") {
|
|
t.Errorf("%s: nothing was highlighted: %s", lang, out)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHighlightTokens(t *testing.T) {
|
|
out := highlight("go", stdhtml.EscapeString("func main() { return \"a<b>\" } // done"))
|
|
for _, want := range []string{
|
|
`<span class="tok-kw">func</span>`,
|
|
`<span class="tok-fn">main</span>`,
|
|
`<span class="tok-kw">return</span>`,
|
|
`<span class="tok-str">"a<b>"</span>`,
|
|
`<span class="tok-cmt">// done</span>`,
|
|
} {
|
|
if !strings.Contains(out, want) {
|
|
t.Errorf("missing %s in %s", want, out)
|
|
}
|
|
}
|
|
if !strings.Contains(highlight("json", `"k": 1`), `tok-key`) {
|
|
t.Error("json key not detected")
|
|
}
|
|
if !strings.Contains(highlight("sql", "select 1"), `<span class="tok-kw">select</span>`) {
|
|
t.Error("sql keywords should be case-insensitive")
|
|
}
|
|
}
|
|
|
|
func TestHighlightNeverInjectsMarkup(t *testing.T) {
|
|
evil := "x := \"</span><script>alert(1)</script>\" // <img src=x onerror=1>"
|
|
out := highlight("go", stdhtml.EscapeString(evil))
|
|
if strings.Contains(out, "<script") || strings.Contains(out, "<img") {
|
|
t.Fatalf("markup leaked: %s", out)
|
|
}
|
|
}
|
|
|
|
func TestHighlightUnknownLanguageUnchanged(t *testing.T) {
|
|
in := "a < b"
|
|
if got := highlight("brainfuck", in); got != in {
|
|
t.Fatalf("got %q", got)
|
|
}
|
|
if got := highlight("", in); got != in {
|
|
t.Fatalf("got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestMarkdownHighlightsFencedCode(t *testing.T) {
|
|
out := Markdown("```go\nfunc main() {}\n```")
|
|
if !strings.Contains(out, `<span class="tok-kw">func</span>`) || !strings.Contains(out, "copy-btn") {
|
|
t.Fatalf("not highlighted: %s", out)
|
|
}
|
|
}
|