package fiesta
import (
"bytes"
"io/fs"
"slices"
"sort"
"strings"
"testing"
)
// The root package is the design system's files, embedded, behind a
// handful of accessors. These tests hold the accessors to the tree:
// every name a list returns is a file that is there, every file that
// is there is a name a list returns, and what an accessor hands back
// is the same bytes a caller would read from FS().
// readFS reads one path from FS(), failing the test when the tree
// is not there at all rather than letting a nil fs.FS panic the
// binary and hide every other test's verdict.
func readFS(t *testing.T, path string) []byte {
t.Helper()
tree := FS()
if tree == nil {
t.Fatalf("FS() is nil; the embedded tree is missing")
}
b, err := fs.ReadFile(tree, path)
if err != nil {
t.Fatalf("FS() has no %s: %v", path, err)
}
return b
}
// listFS names the files under one directory of FS(), base names
// only, sorted.
func listFS(t *testing.T, dir string) []string {
t.Helper()
tree := FS()
if tree == nil {
t.Fatalf("FS() is nil; the embedded tree is missing")
}
entries, err := fs.ReadDir(tree, dir)
if err != nil {
t.Fatalf("FS() has no directory %s: %v", dir, err)
}
var names []string
for _, e := range entries {
if !e.IsDir() {
names = append(names, e.Name())
}
}
sort.Strings(names)
return names
}
func TestTheStylesheetsAreEmbedded(t *testing.T) {
for _, tc := range []struct {
name string
got []byte
path string
}{
{"TokensCSS", TokensCSS(), "css/tokens.css"},
{"ShellCSS", ShellCSS(), "css/shell.css"},
} {
if len(tc.got) == 0 {
t.Errorf("%s() is empty", tc.name)
continue
}
if want := readFS(t, tc.path); !bytes.Equal(tc.got, want) {
t.Errorf("%s() differs from FS()'s %s", tc.name, tc.path)
}
}
}
func TestEveryThemeIsEmbedded(t *testing.T) {
want := []string{"day", "plain", "signal"}
if got := ThemeNames(); !slices.Equal(got, want) {
t.Fatalf("ThemeNames() = %q, want %q", got, want)
}
for _, name := range want {
css, ok := ThemeCSS(name)
if !ok {
t.Errorf("ThemeCSS(%q) reports no such theme", name)
continue
}
if !bytes.Contains(css, []byte(":root")) {
t.Errorf("ThemeCSS(%q) has no :root block", name)
}
if embedded := readFS(t, "css/themes/"+name+".css"); !bytes.Equal(css, embedded) {
t.Errorf("ThemeCSS(%q) differs from FS()'s css/themes/%s.css", name, name)
}
}
if _, ok := ThemeCSS("nope"); ok {
t.Errorf("ThemeCSS(%q) reports a theme that is not shipped", "nope")
}
var files []string
for _, f := range listFS(t, "css/themes") {
files = append(files, strings.TrimSuffix(f, ".css"))
}
if !slices.Equal(files, want) {
t.Errorf("css/themes/ holds %q, but ThemeNames() lists %q", files, want)
}
}
func TestEveryScriptIsEmbedded(t *testing.T) {
want := []string{"fiesta.js", "busy.js", "light.js", "anchor.js", "shell.js", "transition.js", "copy.js", "select.js", "datetime.js", "calendar.js"}
if got := ScriptNames(); !slices.Equal(got, want) {
t.Fatalf("ScriptNames() = %q, want %q", got, want)
}
for _, name := range want {
js, ok := Script(name)
if !ok {
t.Errorf("Script(%q) reports no such script", name)
continue
}
if len(js) == 0 {
t.Errorf("Script(%q) is empty", name)
}
if embedded := readFS(t, "js/"+name); !bytes.Equal(js, embedded) {
t.Errorf("Script(%q) differs from FS()'s js/%s", name, name)
}
}
if _, ok := Script("nope.js"); ok {
t.Errorf("Script(%q) reports a script that is not shipped", "nope.js")
}
files := listFS(t, "js")
sorted := slices.Clone(want)
sort.Strings(sorted)
if !slices.Equal(files, sorted) {
t.Errorf("js/ holds %q, but ScriptNames() lists %q", files, want)
}
}
func TestEveryLanguageIsEmbedded(t *testing.T) {
want := []string{"en", "ar", "bn", "es", "ga", "hi", "ja", "pt-PT", "ru", "vi", "yue", "zh-Hans"}
if got := Languages(); !slices.Equal(got, want) {
t.Fatalf("Languages() = %q, want %q", got, want)
}
for _, lang := range want {
b, ok := Strings(lang)
if !ok {
t.Errorf("Strings(%q) reports no such language", lang)
continue
}
if len(b) == 0 {
t.Errorf("Strings(%q) is empty", lang)
}
if embedded := readFS(t, "i18n/"+lang+".json"); !bytes.Equal(b, embedded) {
t.Errorf("Strings(%q) differs from FS()'s i18n/%s.json", lang, lang)
}
}
// pt is the test alias, never a file; a miss is false, not a panic.
for _, bad := range []string{"pt", "nope", "../LICENSE"} {
if _, ok := Strings(bad); ok {
t.Errorf("Strings(%q) reports a language that is not shipped", bad)
}
}
files := listFS(t, "i18n")
var sorted []string
for _, lang := range want {
sorted = append(sorted, lang+".json")
}
sort.Strings(sorted)
if !slices.Equal(files, sorted) {
t.Errorf("i18n/ holds %q, but Languages() lists %q", files, want)
}
}
func TestEveryIconIsOneLucideSVG(t *testing.T) {
want := []string{
"alert-triangle", "check", "check-circle", "chevron-down",
"help-circle", "info", "kebab", "menu", "plus", "search", "x", "x-circle",
}
got := IconSlugs()
if len(got) != 12 {
t.Errorf("IconSlugs() lists %d slugs, want 12: %q", len(got), got)
}
if !sort.StringsAreSorted(got) {
t.Errorf("IconSlugs() is not sorted: %q", got)
}
if !slices.Equal(got, want) {
t.Fatalf("IconSlugs() = %q, want %q", got, want)
}
const open = `: %q", slug, s)
}
if strings.Count(s, "