Files

75 lines
2.1 KiB
Go

package web
import (
"io/fs"
"regexp"
"strings"
"testing"
)
// The app must work with no internet (it can run on an isolated network), so
// the built frontend may not reference anything outside the embedded files.
// Skipped until `make web` has produced dist/index.html.
func dist(t *testing.T) fs.FS {
t.Helper()
sub, err := fs.Sub(FS, "dist")
if err != nil {
t.Fatal(err)
}
if _, err := fs.Stat(sub, "index.html"); err != nil {
t.Skip("web/dist not built; run `make web`")
}
return sub
}
var (
attrRe = regexp.MustCompile(`(?:src|href)="([^"]+)"`)
assetRe = regexp.MustCompile(`"(/[A-Za-z0-9_.-]+\.(?:svg|png|ico|jpe?g|webp|gif|woff2?|ttf|otf|css|js|json|webmanifest))"`)
// Hosts that would mean a runtime fetch of a font, script or stylesheet.
remoteRe = regexp.MustCompile(`fonts\.(?:googleapis|gstatic)\.com|cdn\.jsdelivr|unpkg\.com|cdnjs\.|use\.typekit|@import\s+url\(\s*['"]?https?:`)
)
func exists(t *testing.T, root fs.FS, ref string) {
t.Helper()
if _, err := fs.Stat(root, strings.TrimPrefix(ref, "/")); err != nil {
t.Errorf("%q is referenced but not in the embedded dist", ref)
}
}
func TestIndexOnlyReferencesEmbeddedFiles(t *testing.T) {
root := dist(t)
b, _ := fs.ReadFile(root, "index.html")
refs := attrRe.FindAllStringSubmatch(string(b), -1)
if len(refs) == 0 {
t.Fatal("index.html references no assets")
}
for _, m := range refs {
ref := m[1]
if !strings.HasPrefix(ref, "/") || strings.HasPrefix(ref, "//") {
t.Errorf("index.html reference %q is not a local absolute path", ref)
continue
}
exists(t, root, ref)
}
}
func TestBundlesHaveNoRemoteResourcesAndAssetsExist(t *testing.T) {
root := dist(t)
err := fs.WalkDir(root, ".", func(p string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() || !(strings.HasSuffix(p, ".js") || strings.HasSuffix(p, ".css") || strings.HasSuffix(p, ".html")) {
return err
}
b, _ := fs.ReadFile(root, p)
if m := remoteRe.Find(b); m != nil {
t.Errorf("%s loads a remote resource: %q", p, m)
}
for _, m := range assetRe.FindAllStringSubmatch(string(b), -1) {
exists(t, root, m[1])
}
return nil
})
if err != nil {
t.Fatal(err)
}
}