package docsite import ( "bytes" "context" "io" "net/http" "net/http/httptest" "os" "path/filepath" "regexp" "strings" "sync" "testing" "time" ) // themeTree is a two-section fixture: the index (two H2 headings, a // callout), two setup pages and one API page with a Go fence. func themeTree(t *testing.T) string { t.Helper() return writeTree(t, map[string]string{ "docs/site.yaml": fixtureSite + "edit_url: \"https://forge.example/edit/{path}\"\n", "docs/index.md": page("Acme docs", "index", 0, "## Alpha\n\nText.\n\n### Detail\n\n> [!NOTE]\n> A note.\n\n> [!TIP]\n> A tip.\n\n> [!WARNING]\n> Careful.\n\n"+ "```sh\n$ summer docs:build\n```\n\n## Beta\n\nMore.\n"), "docs/setup/start.md": page("Start", "setup", 10, "## Only heading\n\nText.\n"), "docs/setup/second.md": page("Second", "setup", 20, "Text.\n"), "modules/alpha/alpha.go": "package alpha\n", "modules/alpha/README.md": "# alpha\n\nAlpha does one thing well.\n\n## Usage\n\n" + "```go\nfunc main() {\n\treturn \"x\" // done\n}\n```\n\n```yaml\nkey: true\n```\n", }) } func buildTheme(t *testing.T) string { t.Helper() out := filepath.Join(t.TempDir(), "site") if _, problems, err := Build(Options{Root: themeTree(t), Commands: fixtureCommands, Out: out}); err != nil || len(problems) > 0 { t.Fatalf("Build: %v %q", err, problemLines(problems)) } return out } func readOut(t *testing.T, out, name string) string { t.Helper() b, err := os.ReadFile(filepath.Join(out, name)) if err != nil { t.Fatal(err) } return string(b) } var ( scriptTag = regexp.MustCompile(`]*>`) anyTag = regexp.MustCompile(`<[a-zA-Z][^>]*>`) styleAttr = regexp.MustCompile(`\sstyle=`) handlerAttr = regexp.MustCompile(`\son[a-z]+=`) ) // assertCSPSafe fails on inline script bodies, inline styles and inline // event handlers (script-src 'self'; style-src 'self'). func assertCSPSafe(t *testing.T, name, html string) { t.Helper() for _, loc := range scriptTag.FindAllStringIndex(html, -1) { tag := html[loc[0]:loc[1]] if !strings.Contains(tag, ` src="`) || !strings.HasPrefix(html[loc[1]:], "") { t.Errorf("%s has an inline script: %s", name, tag) } } for _, tag := range anyTag.FindAllString(html, -1) { if styleAttr.MatchString(tag) || handlerAttr.MatchString(tag) { t.Errorf("%s has an inline style or handler: %s", name, tag) } } if strings.Contains(html, " element", name) } } func TestBuildSiteMarkers(t *testing.T) { out := buildTheme(t) index := readOut(t, out, "index.html") for _, marker := range []string{ ``, `