188 lines
5.4 KiB
Go
188 lines
5.4 KiB
Go
package hilfe
|
|
|
|
import (
|
|
"io/fs"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestJedeSeiteHatEineDatei stellt sicher, dass die Liste in hilfe.go und die
|
|
// Dateien in seiten/ 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 TestJedeSeiteHatEineDatei(t *testing.T) {
|
|
gelistet := map[string]bool{}
|
|
for _, s := range Alle() {
|
|
gelistet[s.Slug] = true
|
|
if _, err := s.Markdown(); err != nil {
|
|
t.Errorf("Seite %q: %v", s.Slug, err)
|
|
}
|
|
}
|
|
|
|
dateien, err := fs.Glob(seitenFS, "seiten/*.md")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, d := range dateien {
|
|
slug := strings.TrimSuffix(strings.TrimPrefix(d, "seiten/"), ".md")
|
|
if !gelistet[slug] {
|
|
t.Errorf("Datei %s steht nicht in der Seitenliste", d)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestInterneLinksFuehrenZuSeiten 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 TestInterneLinksFuehrenZuSeiten(t *testing.T) {
|
|
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)\)`)
|
|
|
|
for _, s := range Alle() {
|
|
md, err := s.Markdown()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, treffer := range link.FindAllStringSubmatch(md, -1) {
|
|
if _, ok := Finde(treffer[1]); !ok {
|
|
t.Errorf("Seite %q verlinkt auf unbekannte Hilfeseite %q", s.Slug, treffer[1])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFindeUnbekannteSeite(t *testing.T) {
|
|
if _, ok := Finde("gibt-es-nicht"); ok {
|
|
t.Error("Finde meldet eine Seite, die es nicht gibt")
|
|
}
|
|
}
|
|
|
|
func TestRenderTabellenUndKeinRohHTML(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")
|
|
}
|
|
}
|
|
|
|
// TestAlleGibtKopie stellt sicher, dass ein Aufrufer die Navigation nicht
|
|
// für alle anderen verändern kann.
|
|
func TestAlleGibtKopie(t *testing.T) {
|
|
a := Alle()
|
|
a[0].Titel = "verändert"
|
|
if Alle()[0].Titel == "verändert" {
|
|
t.Error("Alle gibt die interne Liste statt einer Kopie heraus")
|
|
}
|
|
}
|
|
|
|
// Die drei Lesergruppen, für die Tests.
|
|
var (
|
|
projektLeser = Leser{}
|
|
workspaceAdmin = Leser{WorkspaceAdmin: true}
|
|
plattformAdmin = Leser{PlattformAdmin: true}
|
|
)
|
|
|
|
func TestSichtbarJeLesergruppe(t *testing.T) {
|
|
enthaelt := func(seiten []Seite, slug string) bool {
|
|
for _, s := range seiten {
|
|
if s.Slug == slug {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
for _, f := range []struct {
|
|
name string
|
|
leser Leser
|
|
workspaceSeite, plattSei bool
|
|
}{
|
|
{"Projektzugang", projektLeser, false, false},
|
|
{"Workspace-Admin", workspaceAdmin, true, false},
|
|
{"Plattform-Admin", plattformAdmin, true, true},
|
|
} {
|
|
sichtbar := Sichtbar(f.leser)
|
|
if enthaelt(sichtbar, "workspace-admin") != f.workspaceSeite {
|
|
t.Errorf("%s: Seite für Workspace-Admins sichtbar = %v", f.name, !f.workspaceSeite)
|
|
}
|
|
if enthaelt(sichtbar, "plattform-admin") != f.plattSei {
|
|
t.Errorf("%s: Seite für Plattform-Admins sichtbar = %v", f.name, !f.plattSei)
|
|
}
|
|
if !enthaelt(sichtbar, "start") {
|
|
t.Errorf("%s sieht die Startseite nicht", f.name)
|
|
}
|
|
}
|
|
if len(Sichtbar(plattformAdmin)) != len(Alle()) {
|
|
t.Error("der Plattform-Admin sieht nicht alle Seiten")
|
|
}
|
|
}
|
|
|
|
// TestSeitenVerlinkenNurSichtbares: 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 TestSeitenVerlinkenNurSichtbares(t *testing.T) {
|
|
link := regexp.MustCompile(`\(/hilfe/([a-z0-9-]+)[#)]`)
|
|
|
|
for _, leser := range []Leser{projektLeser, workspaceAdmin} {
|
|
for _, s := range Sichtbar(leser) {
|
|
md, err := s.Markdown()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, treffer := range link.FindAllStringSubmatch(md, -1) {
|
|
if ziel, ok := Finde(treffer[1]); ok && !ziel.DarfLesen(leser) {
|
|
t.Errorf("Seite %q verlinkt auf %q, die dieser Leser nicht sieht (%+v)", s.Slug, ziel.Slug, leser)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestAnkerInLinksGibtEs: 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 TestHilfelinksZeigenAufVorhandeneAbschnitte
|
|
// in internal/web.
|
|
func TestAnkerInLinksGibtEs(t *testing.T) {
|
|
idMuster := regexp.MustCompile(`<h[1-6] id="([^"]+)"`)
|
|
anker := map[string]map[string]bool{}
|
|
for _, s := range Alle() {
|
|
md, err := s.Markdown()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
html, err := Render(md)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
anker[s.Slug] = map[string]bool{}
|
|
for _, m := range idMuster.FindAllStringSubmatch(html, -1) {
|
|
anker[s.Slug][m[1]] = true
|
|
}
|
|
}
|
|
|
|
link := regexp.MustCompile(`\]\((?:/hilfe/([a-z0-9-]+))?#([a-z0-9-]+)\)`)
|
|
for _, s := range Alle() {
|
|
md, _ := s.Markdown()
|
|
for _, m := range link.FindAllStringSubmatch(md, -1) {
|
|
ziel := m[1]
|
|
if ziel == "" {
|
|
ziel = s.Slug
|
|
}
|
|
if _, gibt := anker[ziel]; !gibt {
|
|
t.Errorf("%s: Link auf unbekannte Seite %q", s.Slug, ziel)
|
|
continue
|
|
}
|
|
if !anker[ziel][m[2]] {
|
|
t.Errorf("%s: Abschnitt #%s gibt es auf %q nicht", s.Slug, m[2], ziel)
|
|
}
|
|
}
|
|
}
|
|
}
|