//go:build fiestaseed && browser // The Fiesta seed exporter for ui. hack/seed/run.sh copies this file // into a scratch rastrillo worktree at the seed commit and runs TestFiestaSeed // there, beside the browser drives Fiesta ports, // so it can call their page builders. It writes, under FIESTA_SEED_OUT: // // - check/testdata/fixtures/sizing.html, the whole sizing fixture; // - check/testdata/fixtures/spelling-class.html, the whole class // fixture in class spelling; // - check/testdata/fixtures//[-].html, every // case of every page builder in the ported files, listed in // check/testdata/fixtures/index.json; // - check/testdata/legacy/{sidebar,console}.html, the pre-phone-index // layouts rendered the way their drives render them. // // Three kinds of fixture. A "document" is a whole page: what a builder // that returns an http.Handler served for one request, or what a // layout builder rendered, with /rastrillo.js read as /fiesta.js. A // "body" is what a builder handed sizingDoc. A "piece" is one rendered // partial a test glues into its own literal markup; the port keeps the // literal and reads the piece. Where a builder is a plain function it // is called, so its own wrapper markup is in the fixture and the port // retypes nothing; where a render sits inside a Test function the // exporter repeats that one call with rastrillo's data. // // The layout drives fill the chrome outside the // content slot with their own blocks: brand Harbour, nav-invoices, // #rail-locale, a blank brand, a whitespace view. No layout-* example // carries that chrome, so every one of their pages is seeded as a // document, with the defines the drive gives it, and the words rastrillo // gives it, exactly: these are test data the ported assertions match. // // With FIESTA_SEED_DRYRUN set it writes nothing. The browser tag is // needed because the builders live in browser-tagged files; nothing // here starts a browser. package ui import ( "encoding/json" "errors" "fmt" "html/template" "io/fs" "net/http" "net/http/httptest" "os" "path/filepath" "strings" "testing" "amadan.net/rastrillo/rastrillo" ) type fixtureEntry struct { File string `json:"file"` Builder string `json:"builder"` Case string `json:"case"` Path string `json:"path"` Kind string `json:"kind,omitempty"` Request string `json:"request,omitempty"` Example string `json:"example,omitempty"` Unseeded string `json:"unseeded,omitempty"` Note string `json:"note,omitempty"` } type fixtureWriter struct { t *testing.T out string dry bool index []fixtureEntry written int } // The drives' own words, kept exactly as rastrillo writes them, because the // ported assertions match them. const ( postsNav3 = `PostsDraftsSettings` postsNav2 = `PostsDrafts` viewIndex = `{{define "view"}}index{{end}}` upInvoices = `{{define "up"}}/#nav-invoices{{end}}` upOrders = `{{define "up"}}/#nav-orders{{end}}` upTeam = `{{define "up"}}/#nav-team{{end}}` topbarLinks = `

Posts

A link in the page

` ) func TestFiestaSeed(t *testing.T) { out := os.Getenv("FIESTA_SEED_OUT") dry := os.Getenv("FIESTA_SEED_DRYRUN") != "" if out == "" && !dry { t.Skip("FIESTA_SEED_OUT is unset; this test is the Fiesta seed exporter, run by hack/seed/run.sh") } w := &fixtureWriter{t: t, out: out, dry: dry} if !dry { w.clearFixtures() } layout := func(shell string) []byte { src, ok := Layout(shell) if !ok { t.Fatalf("no %s layout", shell) } return src } // The two whole fixtures. w.whole("sizing_fixture_test.go", "sizingFixture", "sizing.html", sizingFixture(t), "the whole fixture, every adversarial case included; the port wraps it in sizingDoc") w.whole("markup_v3_browser_test.go", "classFixture", "spelling-class.html", classSpelling(t, classFixture(t)), "classSpelling(t, classFixture(t)): the whole fixture in class spelling; markup.Respell derives the attribute twin") // The legacy layouts, rendered with shellLayoutPage's defaults. for _, name := range []string{"sidebar", "console"} { w.write("check/testdata/legacy/"+name+".html", []byte(fiestaScripts(shellLayoutPage(t, legacyLayout(t, name), "ltr")))) w.add(fixtureEntry{File: "index_browser_test.go", Builder: "legacyLayout", Case: name, Path: "../legacy/" + name + ".html", Kind: "document", Note: "shellLayoutPage(t, legacyLayout(t, " + name + "), \"ltr\") with no extra defines"}) } // ── browser_test.go's handlers, and scheme_browser_test.go ─────── h, _ := page(t, 40) w.document("browser_test.go", "page", "40", "/", h, "page(t, 40)") h, _ = datePage(t, "field-date", "due") w.document("browser_test.go", "datePage", "field-date", "/", h, `datePage(t, "field-date", "due")`) w.document("browser_test.go", "datePage", "field-date-nocal", "/?nocal", h, `datePage(t, "field-date", "due"), without calendar.js`) h, _ = datePage(t, "field-time", "at") w.document("browser_test.go", "datePage", "field-time", "/", h, `datePage(t, "field-time", "at")`) h, _ = groupPage(t) w.document("browser_test.go", "groupPage", "", "/", h, "") w.document("browser_test.go", "menuPage", "", "/", menuPage(t), "") w.document("browser_test.go", "menuPage", "old-spelling", "/old-spelling", menuPage(t), "the mid-upgrade page in class spelling") w.document("browser_test.go", "modalPage", "", "/", modalPage(t), `Styleguide()["modal"] in a bare page; the same markup is example modal/default`) w.document("browser_test.go", "fieldRowPage", "ltr", "/?dir=ltr", fieldRowPage(t), "") w.document("browser_test.go", "fieldRowPage", "rtl", "/?dir=rtl", fieldRowPage(t), "") h, _ = busyPage(t) w.document("browser_test.go", "busyPage", "", "/", h, "") w.document("browser_test.go", "bidiRowPage", "", "/", bidiRowPage(t), "") w.document("scheme_browser_test.go", "schemeApp", "", "/", schemeApp(t), "the column shell through ui.Pages with no scheme cookie, so System is current; its server-backed tests stay in rastrillo; only the three literal schemeFlipBody tests are ported") // ── The component drives ───────────────────────────────────────── w.piece("sizing_browser_test.go", "TestTheCalendarDocksAndItsDaysAreTaps", "field", render(t, "field-date", map[string]any{"Name": "due", "Label": "Due", "Value": "2026-08-28"}), "the field both pages hold") w.add(fixtureEntry{File: "sizing_browser_test.go", Builder: "TestDesktopDensityIsPinned", Case: "modal", Example: "modal/default", Note: `Styleguide()["modal"]`}) w.add(fixtureEntry{File: "sizing_browser_test.go", Builder: "TestEveryTapTargetIsAtLeast44Pixels", Case: "modal", Example: "modal/default", Note: `Styleguide()["modal"]`}) w.add(fixtureEntry{File: "sizing_browser_test.go", Builder: "TestTheFloorChangesNothingButSizeOnADesktop", Case: "", Path: "sizing.html", Kind: "body", Note: "sizingFixture(t) plus a literal tail the test keeps"}) w.body("rows_browser_test.go", "rowsFixture", "", rowsFixture(t), "") w.body("rows_browser_test.go", "narrowRowsPage", "", bodyOf(t, narrowRowsPage(t)["/"]), "the / page's body") w.body("blocks_browser_test.go", "segTabsPage", "", bodyOf(t, segTabsPage(t)["/"]), "segTabsPage(t)[\"/\"]: the tabs over the card and over the box, with their ids") w.piece("blocks_browser_test.go", "TestADetailListsLabelSitsOnItsValuesFirstLine", "dl", render(t, "detail-list", map[string]any{"Items": []any{ map[string]any{"Label": "Audience", "Value": "Members"}, map[string]any{"Label": "Published", "Value": "2 August 2026", "DateTime": "2026-08-02"}, map[string]any{"Label": "Reference", "Value": "post_01H9ZQ", "Mono": true}, map[string]any{"Label": "Summary", "Value": "Release notes for August: the new export, faster search, a fix for invoices that never arrived, and the rest of the month's small changes."}, }}), "") w.body("bulkbar_browser_test.go", "bulkBarPage", "", bodyOf(t, bulkBarPage(t, "ltr")), "bulkBarPage(t, \"ltr\"): every bulkBarCases entry in its
\">; the port flips dir on the document") dropdown := func() string { return render(t, "dropdown", map[string]any{"Label": "Newest", "Aria": "Sort: Newest", "Items": []any{ map[string]any{"Href": "/o?sort=new", "Label": "Newest", "Current": true}, map[string]any{"Href": "/o?sort=old", "Label": "Oldest"}, }}) } w.piece("dropdown_browser_test.go", "TestADropdownOpensUnderItsOwnSummary", "partial", dropdown(), "the partial; the sample is example dropdown/menu-idiom") w.piece("dropdown_browser_test.go", "TestADropdownWithoutAnchorsOrScriptsStaysOnScreen", "partial", dropdown(), "the partial; the sample is example dropdown/menu-idiom") w.piece("dropdown_browser_test.go", "TestAnOpenDropdownIsPlacedAgainWhenTheWindowChanges", "menu", dropdown(), "") w.body("pagination_browser_test.go", "paginationPage", "", bodyOf(t, paginationPage(t, "ltr")), "paginationPage(t, \"ltr\"): every paginationCases entry in its
\">; the port flips dir on the document") rowMenuPages, _ := rowMenuPage(t) w.body("rowmenu_browser_test.go", "rowMenuPage", "root", bodyOf(t, rowMenuPages["/"]), "the / page: #outside and the three-row list with #row-a (/act/a, /go/view-a, the hidden fields), #row-b and #row-c (the long POST label)") w.body("rowmenu_browser_test.go", "rowMenuPage", "bottom", bodyOf(t, rowMenuPages["/bottom"]), "the /bottom page: the same list under a 150vh spacer; /go/ is a literal landing page") w.piece("rowmenu_browser_test.go", "TestARowMenuOpensBesideItsOwnKebab", "menu", render(t, "row-menu", map[string]any{"Name": "Grace Hopper", "Items": []any{ map[string]any{"Label": "Edit", "Href": "/go/edit"}, map[string]any{"Label": "Archive", "Action": "/act/a"}, map[string]any{"Label": "Delete order…", "Href": "/go/delete", "Danger": true}, }}), "") w.body("mediagrid_browser_test.go", "mediaPage", "", bodyOf(t, mediaPage(t, "ltr")), "the ltr body; the port flips dir on the document; pictures at /images/tall.svg and /images/wide.svg") w.body("apitokens_browser_test.go", "tokensPageDoc", "", bodyOf(t, tokensPageDoc(t)), "") w.document("signin_browser_test.go", "signinPage", "", "/", signinPage(t), "the live cases link