package gallery import ( "strings" "testing" "amadan.net/rastrillo/fiesta" ) // The integration gate: what holds only with the four streams in // together, the shell's rail over Getting started's rows, the URL gate // over the demo, the modal and the shells in the rail, and the widget // count with the Overview's demo in it. Each is a preservation of a // stream's own gate, extended to what another stream added, and each // was already passing when written; the comment on each names the // mutation that turns it red. // TestTheSidebarLinksGettingStartedExactlyOnce extends // TestTheSidebarLinksEverythingOnThePageExactlyOnce to Getting started, // whose rows are another stream's: in every theme and locale the rail's // fragments into getting-started.html are the page's anchored rows, in // order, one per file the release ships and the page lists. Mutation: // assetNav dropping its first item reports the rail one row short on // every Getting started page. func TestTheSidebarLinksGettingStartedExactlyOnce(t *testing.T) { files := render(t, "") s := newTestSite(t, fiesta.FS(), "") file := s.fileOf("getting-started") read := 0 for _, dir := range dirs() { theme, locale, _ := strings.Cut(strings.TrimSuffix(dir, "/"), "/") name := dir + file page := kindPage(t, files, theme, locale, file) rail := railOf(t, name, page) self := s.href(theme, locale, file) var anchors, fragments []string for _, m := range anchorMarker.FindAllStringSubmatch(page, -1) { anchors = append(anchors, m[1]) } for _, m := range navHref.FindAllStringSubmatch(rail, -1) { if strings.HasPrefix(m[1], self+"#") { fragments = append(fragments, strings.TrimPrefix(m[1], self+"#")) } } view, err := s.buildAssets(s.newPage(theme, locale, "getting-started", file, "")) if err != nil { t.Fatal(err) } if len(view.List) == 0 { t.Fatalf("%s lists no file", name) } if len(fragments) != len(view.List) || strings.Join(fragments, " ") != strings.Join(anchors, " ") { t.Errorf("%s: the rail links %d rows %v, the page anchors %v and lists %d files", name, len(fragments), fragments, anchors, len(view.List)) } read++ } if read != len(dirs()) { t.Fatalf("read %d Getting started pages, want %d", read, len(dirs())) } } // TestEveryDemoInTheRailResolves extends TestEveryGalleryURLResolves // to the Demos the rail and the phone index carry, which three streams // wrote: at a mount, on every page of every directory, the rail's Demos // are the five shells, the modal route and the demo application, each // a file the render wrote under that directory, and the Overview's // phone index lists the same seven; and every link the demo's // documents and the modal route mark as the gallery's resolves too. // Mutation: demoHref writing "demo.htm" reports the demo application // as a file the render did not write, here and in the URL gate. func TestEveryDemoInTheRailResolves(t *testing.T) { const mount = "/x" files := render(t, mount) s := newTestSite(t, fiesta.FS(), mount) checked := 0 for _, dir := range dirs() { theme, locale, _ := strings.Cut(strings.TrimSuffix(dir, "/"), "/") want := demoHrefs(t, s, theme, locale) for _, pk := range s.pageKinds() { name := dir + pk.File page := kindPage(t, files, theme, locale, pk.File) groups := railSections(railOf(t, name, page)) if len(groups) == 0 { t.Fatalf("%s: no rail", name) } demos := groups[len(groups)-1] got := map[string]bool{} for _, m := range navHref.FindAllStringSubmatch(demos.Body, -1) { got[m[1]] = true path := strings.TrimPrefix(m[1], mount+"/") if _, ok := files[path]; !ok || !strings.HasPrefix(path, dir) { t.Errorf("%s: the Demos link %q, which is not a file of %s", name, m[1], dir) } checked++ } if !sameKeys(got, want) { t.Errorf("%s: the Demos are %v, want %v", name, keysOf(got), keysOf(want)) } if pk.Kind == "overview" { m := indexNav.FindStringSubmatch(page) if m == nil { t.Fatalf("%s: no phone index", name) } rows := map[string]bool{} for _, l := range navHref.FindAllStringSubmatch(m[2], -1) { if want[l[1]] { rows[l[1]] = true } } if !sameKeys(rows, want) { t.Errorf("%s: the phone index lists the Demos %v, want %v", name, keysOf(rows), keysOf(want)) } } } for _, extra := range s.extraDocumentNames() { for _, u := range galleryURLs(t, files[dir+extra]) { path, _, _ := strings.Cut(strings.TrimPrefix(u, mount+"/"), "#") if _, ok := files[path]; !ok || !strings.HasPrefix(u, mount+"/") { t.Errorf("%s%s links %q, which the render did not write", dir, extra, u) } checked++ } } } if checked == 0 { t.Fatal("no link was checked") } } // TestEveryWidgetIsFramedAsItsKindSays extends // TestEveryExampleIsFramedDesktopMobileAndHTML to the Overview's demo, // another stream's widget in the first stream's markup: on day/en the // example pages hold one widget per state or per grouped example, each // with Desktop, Mobile and HTML and its panel, and in every directory // the Overview holds exactly one more, the demo, at the page width with // Desktop and Mobile and no HTML; every widget's radios carry a name of // their own on the page. Mutation: the demo's widget given a Source // (an HTML panel) reports the Overview's widget as an example's. func TestEveryWidgetIsFramedAsItsKindSays(t *testing.T) { files := render(t, "") s := newTestSite(t, fiesta.FS(), "") theme := fiesta.ThemeNames()[0] examples, overviews := 0, 0 for _, e := range s.m.Examples { for _, w := range widgetsOf(examplePage(t, files, theme, "en", e.Name)) { examples++ var order string for _, m := range tabClass.FindAllStringSubmatch(w, -1) { order += m[1] } if order != "dmh" || panelOf(w) == "" { t.Errorf("%s: a widget with tabs %q and panel %v, want Desktop, Mobile, HTML and its panel", e.Name, order, panelOf(w) != "") } } } for _, dir := range dirs() { name := dir + "index.html" page := string(files[name]) widgets := widgetsOf(page) if len(widgets) != 1 { t.Errorf("%s: %d widgets, want the demo alone", name, len(widgets)) continue } overviews++ w := widgets[0] var order string for _, m := range tabClass.FindAllStringSubmatch(w, -1) { order += m[1] } if order != "dm" || panelOf(w) != "" || !strings.Contains(page, `