package slidestest import ( "bytes" "encoding/xml" "errors" "io" "net/http" "net/http/httptest" "regexp" "strings" "testing" ) // slideIDOn finds the slide the editor page opened on — the id every // thumbnail route in this file then asks for. var slideIDOn = regexp.MustCompile(`data-slide="([a-z2-7]{12})"`) func currentSlideID(t *testing.T, a *testApp, deckPath string, cookies []*http.Cookie) string { t.Helper() page := mustGet(t, a, deckPath+"/edit", http.StatusOK, cookies).Body.String() m := slideIDOn.FindStringSubmatch(page) if m == nil { t.Fatalf("no current slide id on the editor page:\n%s", page) } return m[1] } // wellFormed insists the body parses as XML and is rooted at an . // Every assertion in this file about a thumbnail leans on it: a picture // that will not parse is not a picture, and a browser shows nothing at // all for one rather than saying why. func wellFormed(t *testing.T, body []byte) { t.Helper() dec := xml.NewDecoder(bytes.NewReader(body)) root := "" for { tok, err := dec.Token() if errors.Is(err, io.EOF) { break } if err != nil { t.Fatalf("the thumbnail is not well-formed XML: %v\n%s", err, body) } if s, ok := tok.(xml.StartElement); ok && root == "" { root = s.Name.Local } } if root != "svg" { t.Fatalf("the thumbnail is rooted at <%s>, want :\n%s", root, body) } } // thumbHeaders is the header set every surface's thumbnail carries. func checkThumbHeaders(t *testing.T, rec *httptest.ResponseRecorder, where string) { t.Helper() for _, c := range []struct{ header, want string }{ {"Content-Type", "image/svg+xml; charset=utf-8"}, {"X-Content-Type-Options", "nosniff"}, {"Cache-Control", "private, max-age=0, must-revalidate"}, {"Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; img-src 'self' data:"}, } { if got := rec.Header().Get(c.header); got != c.want { t.Errorf("%s: %s = %q, want %q", where, c.header, got, c.want) } } if rec.Header().Get("ETag") == "" { t.Errorf("%s: no ETag, so every keystroke refetches every thumbnail", where) } } // TestSlideThumbnailIsAnSVGOfTheSlide: the signed-in route answers a // picture, headed as one, with the slide's own label as its accessible // name. func TestSlideThumbnailIsAnSVGOfTheSlide(t *testing.T) { a := newApp(t) cookies := signedIn(t, a, allowedAddress) path := sampleDeck(t, a, cookies) id := currentSlideID(t, a, path, cookies) rec := mustGet(t, a, path+"/slides/"+id+".svg", http.StatusOK, cookies) checkThumbHeaders(t, rec, "the signed-in route") body := rec.Body.Bytes() wellFormed(t, body) if !bytes.Contains(body, []byte(`role="img"`)) { t.Errorf("the thumbnail is not announced as a picture:\n%s", body) } if !bytes.Contains(body, []byte(`viewBox="0 0 1920 1080"`)) { t.Errorf("the thumbnail is not the 16:9 frame:\n%s", body) } if !bytes.Contains(body, []byte("Welcome to Slides")) { t.Errorf("the thumbnail is not named after the slide:\n%s", body) } // The words of the slide are IN the picture, as text and not as a // bitmap: a thumbnail is searchable, selectable and readable by a // screen reader that opens it, and it is the same file the export // is made of. if !bytes.Contains(body, []byte("