package docsite import ( "html" "regexp" "strings" "testing" ) // spanText matches one tok-* span. var spanText = regexp.MustCompile(`([^<]*)`) // tokens returns the class and unescaped text of every tok-* span. func tokens(out string) map[string][]string { got := map[string][]string{} for _, m := range spanText.FindAllStringSubmatch(out, -1) { got[m[1]] = append(got[m[1]], html.UnescapeString(m[2])) } return got } func assertToken(t *testing.T, lang, out, class, text string) { t.Helper() for _, v := range tokens(out)[class] { if strings.TrimSpace(v) == text { return } } t.Errorf("%s: no %s span for %q in\n%s", lang, class, text, out) } // assertNoTags fails when output has markup other than tok-* spans. func assertNoTags(t *testing.T, lang, out string) { t.Helper() rest := spanText.ReplaceAllString(out, "") if strings.ContainsAny(rest, "<>") { t.Errorf("%s: unexpected markup in %q", lang, rest) } if strings.Contains(out, "style=") { t.Errorf("%s: inline style in %q", lang, out) } } func TestHighlightGo(t *testing.T) { src := "package main\n\n// Say says.\nfunc Say() (int, string) {\n\treturn 42, \"a\"\nport: 5432\nenabled: true\nplain: text value\n" for _, lang := range []string{"yaml", "yml"} { out := highlight(lang, src) assertToken(t, lang, out, "tok-com", "# comment") assertToken(t, lang, out, "tok-key", "name") assertToken(t, lang, out, "tok-str", `"quoted "`) assertToken(t, lang, out, "tok-num", "5432") assertToken(t, lang, out, "tok-num", "true") assertToken(t, lang, out, "tok-str", "text value") assertNoTags(t, lang, out) } out := highlight("json", `{"a": null, "b": 1}`) assertToken(t, "json", out, "tok-num", "null") assertToken(t, "json", out, "tok-num", "1") assertNoTags(t, "json", out) } func TestHighlightShell(t *testing.T) { src := "$ summer docs:build --out \"site\" # build\n# a comment line\n ./bin/acme migrate \\\n --force\n\nsummer\n" for _, lang := range []string{"sh", "shell", "bash", "console"} { out := highlight(lang, src) assertToken(t, lang, out, "tok-prompt", "$") assertToken(t, lang, out, "tok-kw", "summer") assertToken(t, lang, out, "tok-kw", "./bin/acme") assertToken(t, lang, out, "tok-com", "# a comment line") assertToken(t, lang, out, "tok-com", "# build") assertToken(t, lang, out, "tok-str", `"site"`) assertNoTags(t, lang, out) // The continuation line has no command word. for _, kw := range tokens(out)["tok-kw"] { if strings.Contains(kw, "--force") { t.Errorf("%s: continuation line highlighted as a command: %q", lang, kw) } } if !strings.HasPrefix(out, `$ summer `) { t.Errorf("%s: output starts %q", lang, out[:min(len(out), 80)]) } if !strings.Contains(out, "\n ./bin/acme") { t.Errorf("%s: indentation before a command was not kept:\n%s", lang, out) } if html.UnescapeString(spanText.ReplaceAllString(out, "$2")) != src { t.Errorf("%s: output does not round-trip to the source:\n%s", lang, out) } } // A line fragment without a trailing newline stays one line. if out := highlight("sh", "summer serve"); strings.Contains(out, "\n") { t.Errorf("sh fragment gained a newline: %q", out) } } func TestHighlightFallback(t *testing.T) { src := "a < b && c > \"d\" 'e'" want := html.EscapeString(src) for _, lang := range []string{"", "text", "txt", "plain", "plaintext", "no-such-language"} { if got := highlight(lang, src); got != want { t.Errorf("highlight(%q) = %q, want escaped plain text %q", lang, got, want) } } if lexerFor("go") == nil || lexerFor("text") != nil || lexerFor("no-such-language") != nil { t.Error("lexerFor") } var b strings.Builder writeSpan(&b, "tok-kw", "") writeSpan(&b, "", "") if b.String() != "<x>" { t.Errorf("writeSpan = %q", b.String()) } } func TestHighlightFenceMarkup(t *testing.T) { s, pages := renderFixture(t, map[string]string{ "docs/setup/start.md": page("Start", "setup", 10, "```\nno \n```\n\n```go title=x\nx := 1\n```\n"), }) out := string(renderHTML(t, s, pages["setup/start"]).html) for _, want := range []string{ `
", `
x := 1`,
	} {
		if !strings.Contains(out, want) {
			t.Errorf("missing %s in\n%s", want, out)
		}
	}
	if strings.Contains(out, "
") { t.Error("a fence without src= has a caption") } } func TestGoLang(t *testing.T) { for _, lang := range []string{"go", "Go", "GO", "golang", "Golang", "main.go"} { if !goLang(lang) { t.Errorf("goLang(%q) = false, want true", lang) } } for _, lang := range []string{"go-html-template", "go-text-template", "text", "yaml", "sh", ""} { if goLang(lang) { t.Errorf("goLang(%q) = true, want false", lang) } } }