Skip to content
quarry Artifact [c7080308e9]
Login

Artifact [c7080308e9]

Artifact c7080308e95131830ddc42d4b2f52dcea2ee809d8a100f3675776646049e9a61:


package web

import (
	"bytes"
	"html/template"
	"regexp"
	"strings"
	"testing"
)

// A fleet row's actions are several forms. A form is block-level, so
// without display:contents they stack and every row becomes three
// lines tall — which is what the fleet page looked like.
func TestFleetRowActionsShareOneRow(t *testing.T) {
	if !strings.Contains(styleSource, ".row-acts { display: flex") {
		t.Error("the actions cell is not a flex row")
	}
	if !strings.Contains(styleSource, ".row-acts form { display: contents; }") {
		t.Error("the forms inside it still generate their own boxes, so the buttons will stack")
	}
	if !strings.Contains(fleetAdminSource, `<div class="row-acts">`) {
		t.Error("the fleet row's actions are not marked up as one")
	}
	// The page must stack, not scroll, on a phone.
	if !strings.Contains(styleSource, "ul.rows > li.row { grid-template-columns: minmax(0, 1fr); }") {
		t.Error("rows do not collapse to one column at narrow widths")
	}
}

// The footer is fixed, so every page must reserve its height plus a
// visible gap. Clearing it by a hair still reads as content running into
// it, and the reserve and the bar must come from one value or they drift.
func TestBodyClearsTheFixedFooter(t *testing.T) {
	if !strings.Contains(styleSource, "position: fixed; bottom: 0") {
		t.Skip("footer is no longer fixed")
	}
	// The safe-area inset is part of the reserve AND the footer's own
	// padding: on an iPhone with Safari's chrome collapsed, the home
	// indicator overlays a bottom:0 bar, and the render time sat under
	// it. Elsewhere env() is 0 and both terms are inert.
	if !strings.Contains(styleSource, "padding-bottom: calc(var(--footer-h) + 2rem + env(safe-area-inset-bottom))") {
		t.Error("body does not reserve the footer's height plus the safe-area inset")
	}
	if !strings.Contains(styleSource, "padding: 0 0 env(safe-area-inset-bottom) 0") {
		t.Error("the footer does not pad above the home indicator")
	}
	if !strings.Contains(styleSource, "viewport-fit=cover") {
		t.Error("without viewport-fit=cover the safe-area env() is always zero")
	}
	if !strings.Contains(styleSource, "min-height: var(--footer-h)") {
		t.Error("the footer does not use the height the page reserved for it")
	}
}

// The Popover API is click-driven; using it as a tooltip gave a click
// target that ALSO carried a native title, so two tooltips competed and
// the cursor turned into a question mark. There is no standard hover
// tooltip primitive (popover="hint" is Chrome-only), so column headers
// carry a real visible label instead — readable by touch and by a
// screen reader, neither of which can hover.
func TestColumnHeadersDoNotUsePopoverAsATooltip(t *testing.T) {
	if strings.Contains(styleSource, "popovertarget=\"tip-") {
		t.Error("a column header still opens a popover as a tooltip")
	}
	if strings.Contains(styleSource, "cursor: help") {
		t.Error("the question-mark cursor is back")
	}
	if !strings.Contains(styleSource, ".collabel") {
		t.Error("column headers have no visible label to fall back from")
	}
	// The icon alone must still have an accessible name at every width.
	if !strings.Contains(styleSource, `.colhead { display: inline-flex`) {
		t.Error("the header label and icon are not one unit")
	}
}

// At the wide breakpoint the header labels appear and the tooltips
// that carried their words retire; below it the sr-only label names
// each column for a screen reader and the tooltip for a pointer. The
// legend that used to restate all four above the header is gone — it
// read as a duplicate of the header row itself.
func TestIconColumnsAreExplainedAtEveryWidth(t *testing.T) {
	const labelAt = ".collabel { position: static; width: auto; height: auto; overflow: visible;"
	if !strings.Contains(styleSource, labelAt) {
		t.Error("header labels do not appear at the wide breakpoint")
	}
	if !strings.Contains(styleSource, `.colhead[data-tip]::after { display: none; }`) {
		t.Error("tooltips do not retire where the visible label already says the word")
	}
	if strings.Contains(styleSource, "collegend") {
		t.Error("the legend is back; it duplicates the header row beneath it")
	}
}

// The footer is one line, so something must give on a narrow screen —
// but the render time is the reason that bar earns its space. It is
// therefore the part that never shrinks, and the forge name and version
// collapse ahead of it. Truncating the whole left half dropped the
// timing FIRST, which is exactly backwards.
func TestFooterKeepsTheRenderTime(t *testing.T) {
	if !strings.Contains(styleSource, ".f-time { flex: none; }") {
		t.Error("the render time can shrink; it must not")
	}
	if strings.Contains(styleSource, ".foot-l { min-width: 0; overflow: hidden; text-overflow: ellipsis;") {
		t.Error("the whole left half truncates again, which cuts the timing off the end")
	}
	// The lower-priority parts must retire before it, at declared and
	// distinct widths. Matched by the breakpoint and the part rather
	// than by the whole rule: what matters is WHICH width sheds WHAT,
	// so adding another part to a step must not read as a regression.
	step := func(width string) string {
		at := strings.Index(styleSource, "@media (max-width: "+width+") {")
		if at < 0 {
			return ""
		}
		end := strings.Index(styleSource[at:], "}")
		if end < 0 {
			return ""
		}
		return styleSource[at : at+end]
	}
	wide, narrow := step("34rem"), step("26rem")
	if wide == "" || narrow == "" {
		t.Fatal("the collapse order is not declared")
	}
	for _, part := range []string{".f-brand", ".f-ops"} {
		if !strings.Contains(wide, part) {
			t.Errorf("%s does not retire at the wide step", part)
		}
	}
	if !strings.Contains(narrow, ".f-ver") {
		t.Error(".f-ver does not retire at the narrow step")
	}
	// The TOTAL is what the footer is for. Only the per-store breakdown
	// may be shed; a rule hiding .f-time itself would take the whole
	// reason the bar earns its line.
	if strings.Contains(styleSource, ".f-time { display: none") ||
		strings.Contains(styleSource, ".f-time{display:none") {
		t.Error("the render time itself is hidden; only .f-ops may retire")
	}
}

// Datastar morphs #fleet and #tl from the server on every change. A
// widget holding its own state inside one of those regions is destroyed
// mid-interaction by a repaint — an open menu vanishes, a half-typed
// filter resets. This is the failure Morpheus was built to work around
// in JS-state component kits; quarry avoids it structurally by keeping
// every popover in page chrome, OUTSIDE the morphed region.
//
// That is currently true by luck of layout rather than by rule, which
// is why it is asserted: the natural next feature is a per-row menu,
// and it would break in a way nobody reproduces by hand.
var tmplComment = regexp.MustCompile(`(?s)\{\{-? */\*.*?\*/ *-?\}\}`)

func TestMorphedRegionsHoldNoStatefulWidgets(t *testing.T) {
	region := func(src, define string) string {
		i := strings.Index(src, `{{define "`+define+`"}}`)
		if i < 0 {
			t.Fatalf("no %s template", define)
		}
		rest := src[i:]
		if j := strings.Index(rest[10:], "{{define "); j >= 0 {
			rest = rest[:j+10]
		}
		return rest
	}
	for _, tc := range []struct{ name, src, define string }{
		{"explore", exploreSource, "explore-body"},
		{"timeline", timelineSource, "tl-body"},
	} {
		// Template comments are stripped: the prose beside these rules
		// discusses popovers, and a substring search finds the word in
		// the explanation rather than in any markup.
		body := tmplComment.ReplaceAllString(region(tc.src, tc.define), "")
		for _, bad := range []string{"popover", "data-tip"} {
			if strings.Contains(body, bad) {
				t.Errorf("%s is morphed over SSE but contains %q; a repaint would destroy it mid-interaction",
					tc.define, bad)
			}
		}
	}
}

// The fleet templates read mirror structs whose fields exist only to be
// named in the template — nothing else touches them, so the compiler
// cannot notice when a template gains a field the struct lacks. That
// drift is invisible until an operator loads the page and gets a
// template error: {{.Health.Gaps}} against a struct without Gaps took
// the whole /fleet surface down while every test stayed green.
//
// Executing the templates against the real structs is the check the
// compiler cannot do.
func TestFleetTemplatesExecuteAgainstTheirStructs(t *testing.T) {
	var row fleetHealthRow
	row.Name, row.Code = "alpha", "alpha"
	row.Health.Gaps, row.Health.Failures = 1, 2
	row.Health.LastErr = "boom"
	row.Health.Stalled, row.Health.RetryAt = true, "2026-08-19T00:00:00Z"

	for _, tc := range []struct {
		name string
		page *template.Template
		def  string
		data pageData
	}{
		{"fleet admin", fleetAdminPage, "content",
			pageData{FleetHealth: []fleetHealthRow{row}, Unhealthy: 1,
				ErrorTotal: 3, ServerErrors: []errlogRow{{
					Message: "boom", Surface: "web", Path: "/x",
					Count: 3, Last: "2026-08-19T10:00:00Z"}}}},
		{"explore", explorePage, "explore-body",
			pageData{Fleet: []repoInfo{{Name: "alpha"}}}},
	} {
		t.Run(tc.name, func(t *testing.T) {
			var buf bytes.Buffer
			if err := tc.page.ExecuteTemplate(&buf, tc.def, tc.data); err != nil {
				t.Fatalf("template no longer matches its structs: %v", err)
			}
		})
	}
}

// The ticket board never widens the page: columns wrap past a floor,
// empty ones fold to strips, and the old sideways-scrolling grid is
// gone. A regression here is the page scrolling horizontally on a
// laptop, which is the thing the board was rebuilt to stop.
func TestBoardNeverScrollsSideways(t *testing.T) {
	if strings.Contains(styleSource, ".board { display: grid") || strings.Contains(styleSource, "grid-auto-columns: 82vw") {
		t.Error("the board is a one-row grid again; it must wrap")
	}
	if !strings.Contains(styleSource, ".board { display: flex; flex-wrap: wrap") {
		t.Error("the board does not wrap its columns")
	}
	if !strings.Contains(boardSource, `class="board-col{{if not .Expanded}} folded{{end}}"`) {
		t.Error("empty columns no longer fold to strips")
	}
	if !strings.Contains(styleSource, ".board-col { flex: 1 1 100%; }") {
		t.Error("columns do not stack on a phone")
	}
}

// On the board a column has ONE width so a card is the same object in
// every column. Leaving the reading column is now the READER's call —
// the wide toggle — rather than something the board does to the page
// the moment it is chosen, so what this pins is that the escape hatch
// exists and is keyed to the toggle's own state.
func TestBoardColumnsHaveOneWidth(t *testing.T) {
	if !strings.Contains(styleSource, ".board-col { flex: 0 0 22rem") {
		t.Error("board columns are fluid again; cards will change width as columns open")
	}
	// Keyed to the button, not to a class on main: main sits outside
	// the morph target, so a class there could never follow a view
	// switch that patches only the region below it.
	if !strings.Contains(styleSource, `main:has(.t-wide a[aria-pressed="true"]) { max-width: none; }`) {
		t.Error("no way to leave the reading column")
	}
	if !strings.Contains(boardSource, `<a class="fold" href="{{.ToggleURL}}"`) || !strings.Contains(boardSource, `<a class="unfold" href="{{.ToggleURL}}"`) {
		t.Error("a column must be foldable and unfoldable from its header")
	}
}

// "edit mirror" lands on a form that only the Mirrors tab renders.
// fleet-panel gates fleet-mirrors on FleetTab, and the tab defaults to
// "stores", so a link carrying only ?edit= re-renders the Stores tab
// with no #mirror-form in it: the anchor scrolls nowhere and the click
// looks like a dead control. The handler had already found the row and
// filled MirrorEdit — the data was right and the page was wrong, which
// is why this reads as "nothing happens" rather than as an error.
func TestEditMirrorLandsOnTheTabThatHasTheForm(t *testing.T) {
	mirror := mirrorRow{Repo: "alpha", URL: "https://example.org/x", Enabled: true,
		Every: 3600, State: "ok"}
	var health fleetHealthRow
	health.Name, health.Code = "alpha", "alpha"
	rows, _ := fleetRows([]fleetHealthRow{health}, []mirrorRow{mirror})

	render := func(tab string) string {
		var buf bytes.Buffer
		data := pageData{FleetTab: tab, FleetHealth: []fleetHealthRow{health},
			Mirrors: []mirrorRow{mirror}, FleetRows: rows, MirrorEdit: &mirror}
		if err := fleetAdminPage.ExecuteTemplate(&buf, "fleet-panel", data); err != nil {
			t.Fatalf("fleet-panel/%s: %v", tab, err)
		}
		return buf.String()
	}

	// The premise: the form is on one tab and the link is on the other.
	if strings.Contains(render("stores"), `id="mirror-form"`) {
		t.Error("the Stores tab now renders the mirror form; this test's premise is stale")
	}
	if !strings.Contains(render("mirrors"), `id="mirror-form"`) {
		t.Error("the Mirrors tab does not render the mirror form")
	}

	// So every link aiming at that anchor has to ask for that tab.
	for _, link := range regexp.MustCompile(`href="([^"]*#mirror-form)"`).
		FindAllStringSubmatch(fleetAdminSource, -1) {
		if !strings.Contains(link[1], "tab=mirrors") {
			t.Errorf("%s targets #mirror-form but does not select the Mirrors tab, "+
				"so it lands on Stores and appears to do nothing", link[1])
		}
	}
}