188 lines
5.4 KiB
Go
188 lines
5.4 KiB
Go
package help
|
|
|
|
import (
|
|
"io/fs"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestEveryPageHasAFile stellt sicher, dass die Liste in help.go und die
|
|
// Dateien in pages/ zusammenpassen — in beide Richtungen. Eine Seite ohne
|
|
// Datei gäbe einen Fehler beim Aufruf, eine Datei ohne Listeneintrag wäre
|
|
// unerreichbar und würde unbemerkt veralten.
|
|
func TestEveryPageHasAFile(t *testing.T) {
|
|
listed := map[string]bool{}
|
|
for _, s := range All() {
|
|
listed[s.Slug] = true
|
|
if _, err := s.Markdown(); err != nil {
|
|
t.Errorf("Seite %q: %v", s.Slug, err)
|
|
}
|
|
}
|
|
|
|
files, err := fs.Glob(pagesFS, "pages/*.md")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, d := range files {
|
|
slug := strings.TrimSuffix(strings.TrimPrefix(d, "pages/"), ".md")
|
|
if !listed[slug] {
|
|
t.Errorf("Datei %s steht nicht in der Seitenliste", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInternalLinksLeadToPages prüft jeden Link der Form (/hilfe/…) in
|
|
// allen Seiten. Wird eine Seite umbenannt, fällt ein vergessener Verweis hier
|
|
// auf statt erst als 404 beim Lesen.
|
|
func TestInternalLinksLeadToPages(t *testing.T) {
|
|
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)\)`)
|
|
|
|
for _, s := range All() {
|
|
md, err := s.Markdown()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, hits := range link.FindAllStringSubmatch(md, -1) {
|
|
if _, ok := Find(hits[1]); !ok {
|
|
t.Errorf("Seite %q verlinkt auf unbekannte Hilfeseite %q", s.Slug, hits[1])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFindUnknownPage(t *testing.T) {
|
|
if _, ok := Find("gibt-es-nicht"); ok {
|
|
t.Error("Finde meldet eine Seite, die es nicht gibt")
|
|
}
|
|
}
|
|
|
|
func TestRenderTablesAndNoRawHTML(t *testing.T) {
|
|
html, err := Render("| a | b |\n|---|---|\n| 1 | 2 |\n\n<script>alert(1)</script>")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(html, "<table>") {
|
|
t.Error("Tabellen werden nicht als Tabelle dargestellt")
|
|
}
|
|
if strings.Contains(html, "<script>") {
|
|
t.Error("Roh-HTML wird durchgelassen")
|
|
}
|
|
}
|
|
|
|
// TestAllReturnsCopy stellt sicher, dass ein Aufrufer die Navigation nicht
|
|
// für alle anderen verändern kann.
|
|
func TestAllReturnsCopy(t *testing.T) {
|
|
a := All()
|
|
a[0].Title = "verändert"
|
|
if All()[0].Title == "verändert" {
|
|
t.Error("Alle gibt die interne Liste statt einer Kopie heraus")
|
|
}
|
|
}
|
|
|
|
// Die drei Lesergruppen, für die Tests.
|
|
var (
|
|
projectReader = Reader{}
|
|
workspaceAdmin = Reader{WorkspaceAdmin: true}
|
|
platformAdmin = Reader{PlatformAdmin: true}
|
|
)
|
|
|
|
func TestVisiblePerAudience(t *testing.T) {
|
|
contains := func(pages []Page, slug string) bool {
|
|
for _, s := range pages {
|
|
if s.Slug == slug {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
for _, f := range []struct {
|
|
name string
|
|
reader Reader
|
|
workspacePage, platformPage bool
|
|
}{
|
|
{"Projektzugang", projectReader, false, false},
|
|
{"Workspace-Admin", workspaceAdmin, true, false},
|
|
{"Plattform-Admin", platformAdmin, true, true},
|
|
} {
|
|
visible := Visible(f.reader)
|
|
if contains(visible, "workspace-admin") != f.workspacePage {
|
|
t.Errorf("%s: Seite für Workspace-Admins sichtbar = %v", f.name, !f.workspacePage)
|
|
}
|
|
if contains(visible, "plattform-admin") != f.platformPage {
|
|
t.Errorf("%s: Seite für Plattform-Admins sichtbar = %v", f.name, !f.platformPage)
|
|
}
|
|
if !contains(visible, "start") {
|
|
t.Errorf("%s sieht die Startseite nicht", f.name)
|
|
}
|
|
}
|
|
if len(Visible(platformAdmin)) != len(All()) {
|
|
t.Error("der Plattform-Admin sieht nicht alle Seiten")
|
|
}
|
|
}
|
|
|
|
// TestPagesLinkOnlyVisible: Ein Link auf eine Seite, die der Leser
|
|
// nicht sehen darf, liefe für ihn ins Leere (404). Geprüft für jede
|
|
// Lesergruppe: Was sie sieht, verlinkt nur auf das, was sie auch sieht.
|
|
func TestPagesLinkOnlyVisible(t *testing.T) {
|
|
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)[#)]`)
|
|
|
|
for _, reader := range []Reader{projectReader, workspaceAdmin} {
|
|
for _, s := range Visible(reader) {
|
|
md, err := s.Markdown()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, hits := range link.FindAllStringSubmatch(md, -1) {
|
|
if target, ok := Find(hits[1]); ok && !target.CanRead(reader) {
|
|
t.Errorf("Seite %q verlinkt auf %q, die dieser Leser nicht sieht (%+v)", s.Slug, target.Slug, reader)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestLinkAnchorsExist: Jeder Link auf einen Abschnitt einer Hilfeseite —
|
|
// (/hilfe/seite#abschnitt) oder innerhalb der Seite (#abschnitt) — zeigt auf
|
|
// eine Überschrift, die es dort gibt. Sonst landet man oben auf der Seite
|
|
// statt beim gemeinten Abschnitt, und niemand merkt es.
|
|
//
|
|
// Die Hilfe-Links der Templates prüft TestHelpLinksPointToExistingSections
|
|
// in internal/web.
|
|
func TestLinkAnchorsExist(t *testing.T) {
|
|
idPattern := regexp.MustCompile(`<h[1-6] id="([^"]+)"`)
|
|
anchor := map[string]map[string]bool{}
|
|
for _, s := range All() {
|
|
md, err := s.Markdown()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
html, err := Render(md)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
anchor[s.Slug] = map[string]bool{}
|
|
for _, m := range idPattern.FindAllStringSubmatch(html, -1) {
|
|
anchor[s.Slug][m[1]] = true
|
|
}
|
|
}
|
|
|
|
link := regexp.MustCompile(`\]\((?:/hilfe/([a-z0-9-]+))?#([a-z0-9-]+)\)`)
|
|
for _, s := range All() {
|
|
md, _ := s.Markdown()
|
|
for _, m := range link.FindAllStringSubmatch(md, -1) {
|
|
target := m[1]
|
|
if target == "" {
|
|
target = s.Slug
|
|
}
|
|
if _, exists := anchor[target]; !exists {
|
|
t.Errorf("%s: Link auf unbekannte Seite %q", s.Slug, target)
|
|
continue
|
|
}
|
|
if !anchor[target][m[2]] {
|
|
t.Errorf("%s: Abschnitt #%s gibt es auf %q nicht", s.Slug, m[2], target)
|
|
}
|
|
}
|
|
}
|
|
}
|