package gallery import ( "html" "html/template" "io/fs" "regexp" "strings" "testing" "amadan.net/rastrillo/fiesta" "amadan.net/rastrillo/fiesta/example" "amadan.net/rastrillo/fiesta/internal/codeview" ) // The preview widget as the render writes it: Desktop, Mobile and HTML // behind three radios, one widget per state or one per grouped // example, the HTML tab without the wrapper and with the line naming // it, the row documents, the Copy blocks, the frame titles and the // shells' open buttons. The browser drives in view_browser_test.go and // its siblings take over where the engine has to answer. // examplePage is one example's page in one directory, failed loudly. func examplePage(t *testing.T, files map[string][]byte, theme, locale, name string) string { t.Helper() page := string(files[theme+"/"+locale+"/"+name+".html"]) if page == "" { t.Fatalf("no %s/%s/%s.html in the rendered tree", theme, locale, name) } return page } // widgetOpen is a widget's opening tag, matched exactly: `
`) // panelOf is a widget's HTML panel, or "" when it has none. func panelOf(widget string) string { return regexp.MustCompile(`(?s)
.*?
`).FindString(widget) } // sourceBlock finds every source block on a page: the code a reader // copies, highlighted. var sourceBlock = regexp.MustCompile(`(?s)
]*>(.*?)
`) var anyElement = regexp.MustCompile(`<[^>]*>`) // sourceTexts is every source block in a fragment as a reader sees and // copies it: the highlighting removed and the entities decoded. func sourceTexts(fragment string) []string { var out []string for _, m := range sourceBlock.FindAllStringSubmatch(fragment, -1) { out = append(out, html.UnescapeString(anyElement.ReplaceAllString(m[1], ""))) } return out } // TestEveryExampleIsFramedDesktopMobileAndHTML: on every example page // of day/en, one widget per state, or one per grouped example; three // radios in each, one name per widget and the name unique on the page, // none checked; the HTML panel on every widget, one block per state; // ds-view--page on the page-frame examples and on no other; and the // rules that make the tabs switch anything, in view.css. func TestEveryExampleIsFramedDesktopMobileAndHTML(t *testing.T) { files := render(t, "") s := newTestSite(t, fiesta.FS(), "") theme := fiesta.ThemeNames()[0] total := 0 var pageClass []string for _, e := range s.m.Examples { page := examplePage(t, files, theme, "en", e.Name) widgets := widgetsOf(page) want := len(e.States) if grouped[e.Name] { want = 1 } if len(widgets) != want { t.Errorf("%s: %d preview widgets, want %d", e.Name, len(widgets), want) } if strings.Contains(page, "ds-view--page") { pageClass = append(pageClass, e.Name) } if n := strings.Count(page, "ds-view--page"); n != 0 && n != len(widgets) { t.Errorf("%s: %d of %d widgets take the page class; a page's widgets are all one class", e.Name, n, len(widgets)) } total += len(widgets) groups := map[string]bool{} for i, w := range widgets { radios := radioInput.FindAllStringSubmatch(w, -1) if len(radios) != 3 { t.Errorf("%s widget %d has %d tabs, want 3: Desktop, Mobile and HTML", e.Name, i, len(radios)) continue } for _, r := range radios { if r[1] != radios[0][1] { t.Errorf("%s widget %d: tabs in one widget carry two names (%q, %q), so they are not one group", e.Name, i, radios[0][1], r[1]) } if r[2] != "" { t.Errorf("%s widget %d: a tab starts checked; none may, or the stylesheet cannot tell a reader's choice from the markup's default", e.Name, i) } } if groups[radios[0][1]] { t.Errorf("%s widget %d: the radio name %q is already used by another widget on this page", e.Name, i, radios[0][1]) } groups[radios[0][1]] = true var order string for _, m := range tabClass.FindAllStringSubmatch(w, -1) { order += m[1] } if order != "dmh" { t.Errorf("%s widget %d: tabs %q, want Desktop, Mobile, HTML", e.Name, i, order) } if n := strings.Count(w, `