package check import ( "regexp" "slices" "strings" "testing" "amadan.net/rastrillo/fiesta" ) // The rake line is retired (design doc §6-v2.2), and "retired" has to // mean one place. tokens.css is the single stylesheet every rastrillo // page links, so a pseudo-element declared there once, as content:none, // is a pseudo-element that draws nothing anywhere — provided nothing // else in the shipped CSS declares one too. // // That last clause is what this test is for. A theme is colour, type // and shape; a theme that grew a [rst-page-header]::after would put the // flourish back on one theme and nowhere else, which is both the // hardest version of this bug to see and the easiest one to write. // // The browser half of the claim — that content:none lays out no box, in // every theme, in both schemes, in ar as well as en — is // header_rule_browser_test.go's. This is the cheap half, and it runs in // the ordinary suite. func TestTheRakeLineIsDeclaredRetiredExactlyOnce(t *testing.T) { // The rule as it now stands, spelled in both markup vocabularies // because tokens.css pairs a class selector with every attribute one // until stage 3 of the migration. // // Read as rules, not text: a copy in a comment counted as the one // declaration, and a pattern that stopped at a comma missed a // pseudo-element after a selector list inside :is(). rules := leafRules(t, string(fiesta.TokensCSS())) const retired = ".rst-page-header::after, [rst-page-header]::after" got := 0 for _, r := range rules { if r.selector == retired && slices.Equal(r.decls, [][3]string{{"content", "none", ""}}) { got++ } } if got != 1 { t.Errorf("tokens.css declares the retired header pseudo-element %d times as\n%s { content: none; }\nwant exactly 1 — the whole point of retiring it in one file is that there is one place to look", got, retired) } // Nothing else may draw one. The reading is deliberately loose (any // selector naming a page header, any pseudo-element after it, : or // ::) so a flourish reintroduced under ::before, or on the titles, // is caught as readily as the exact rule that was removed. headerPseudo := regexp.MustCompile(`(\.rst-page-header|\[rst-page-header).*::?(after|before)`) for _, r := range rules { if r.selector == retired { continue } for _, sel := range splitSelectorList(r.selector) { if headerPseudo.MatchString(sel) { t.Errorf("tokens.css draws %q on the page header; the header's one decoration is its rule, and that lives on the theme axis as --rst-header-rule", sel) } } } for _, name := range fiesta.ThemeNames() { theme, ok := fiesta.ThemeCSS(name) if !ok { t.Fatalf("fiesta.ThemeCSS(%q) missing", name) } // A theme file has no selectors at all — it is one :root block // and two toggle rules — so any mention of the header at all is // the beginning of this bug. if strings.Contains(string(theme), "page-header") { t.Errorf("themes/%s.css mentions the page header. A theme sets --rst-header-rule and stops there: structure is tokens.css's, and a theme that styles a component has started a second stylesheet", name) } } }