rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1package check
2
3import (
4 "regexp"
5 "strings"
6 "testing"
7
8 "amadan.net/rastrillo/fiesta"
9)
10
11// The column's footer row: the column shell ends with it, and it holds
12// the language and colour-scheme menus that used to stand above the
13// page's title. A menu there pushed the title down and read as the
14// page's first action; at the foot of the column it is out of the way
15// and still one tap from every page.
16
17// summaryFace matches the four selectors, one per state, of the rules
18// that give a dropdown's summary its face, in the attribute spelling:
19// the :not() list of the dropdowns that stay flat, then the summary
20// and its state.
21var summaryFace = regexp.MustCompile(`^\[rst-dropdown\]:not\((.*)\) > summary:not\(\[rst-btn\]\)(|:active|:hover|:hover:active)$`)
22
23// summaryFaceClass is summaryFace in the class spelling.
24var summaryFaceClass = regexp.MustCompile(`^\.rst-dropdown:not\((.*)\) > summary:not\(\.rst-btn\)(|:active|:hover|:hover:active)$`)
25
26// TestTheColumnShellEndsWithItsFooterRow: the column example opens its
27// page with the slot, so the page header is the first thing in it, and
28// closes it with the footer row holding the language menu and then the
29// scheme menu; no menu is left above the slot. The stylesheet keeps
30// the row's menus flat in every state of the summary face, hides a row
31// that holds only template whitespace, and makes the row, not each
32// menu, the box a panel hangs from where nothing can anchor it.
33func TestTheColumnShellEndsWithItsFooterRow(t *testing.T) {
34 doc := defaultMarkup(t, "layout-column")
35 const open, slot, slotEnd = `<main rst-page id="main">`, "<!-- fiesta:slot -->", "<!-- /fiesta:slot -->"
36 i, j, k := strings.Index(doc, open), strings.Index(doc, slot), strings.Index(doc, slotEnd)
37 if i < 0 || j < i || k < j {
38 t.Fatalf("layout-column has no %s followed by its slot; the checks below read nothing", open)
39 }
40 if before := doc[i+len(open) : j]; strings.TrimSpace(before) != "" {
41 t.Errorf("layout-column: between %s and the slot there is %.80q; the page header must be the first thing in the page", open, before)
42 }
43 if strings.Contains(doc[i:j], "<details") {
44 t.Error("layout-column: a <details> sits between <main> and the slot")
45 }
46 rest := doc[k+len(slotEnd):]
47 for _, want := range []string{
48 `<footer rst-page-foot>`,
49 `<details rst-dropdown rst-locale name="rst-menus">`, `</details>`,
50 `<details rst-dropdown rst-scheme name="rst-menus">`, `</details>`,
51 `</footer>`, `</main>`,
52 } {
53 rest = strings.TrimLeft(rest, " \t\n")
54 if strings.HasPrefix(want, "</details>") {
55 // The menu's own markup comes before its closing tag.
56 end := strings.Index(rest, want)
57 if end < 0 || strings.Contains(rest[:end], "<details") {
58 t.Fatalf("layout-column: after the slot, a menu is not closed before the next one opens")
59 }
60 rest = rest[end+len(want):]
61 continue
62 }
63 if !strings.HasPrefix(rest, want) {
64 t.Fatalf("layout-column: after the slot comes %.60q, want %s next: the footer row, the language menu, the scheme menu, then </footer> and </main>", rest, want)
65 }
66 rest = rest[len(want):]
67 }
68
69 decls, _ := parseDeclarations(t, "tokens.css", string(fiesta.TokensCSS()))
70 faces := map[string]bool{}
71 has := map[string]bool{}
72 for _, d := range decls {
73 has[d.Selector+" { "+d.Property+": "+d.Value+" }"] = true
74 for _, spelling := range []struct {
75 re *regexp.Regexp
76 flat string
77 }{{summaryFace, "[rst-page-foot] [rst-dropdown]"}, {summaryFaceClass, ".rst-page__foot .rst-dropdown"}} {
78 m := spelling.re.FindStringSubmatch(d.Selector)
79 if m == nil || d.Property != "box-shadow" {
80 continue
81 }
82 state := m[2]
83 if state == "" {
84 state = "at rest"
85 }
86 key := "rule " + state + " does not list " + spelling.flat
87 if _, seen := faces[key]; !seen {
88 faces[key] = false
89 }
90 for _, arg := range splitSelectorList(m[1]) {
91 if arg == spelling.flat {
92 faces[key] = true
93 }
94 }
95 }
96 }
97 if len(faces) != 8 {
98 t.Fatalf("read %d summary-face rules by spelling and state, want 8 (four states, two spellings): %v", len(faces), faces)
99 }
100 for _, key := range sortedKeys(faces) {
101 if !faces[key] {
102 t.Errorf("the summary face's %s in its :not(); a footer menu would wear a face in that state", key)
103 }
104 }
105 for _, want := range []string{
106 "[rst-page-foot]:not(:has(> *)) { display: none }",
107 ".rst-page__foot:not(:has(> *)) { display: none }",
108 "[rst-page-foot] [rst-dropdown] { position: static }",
109 ".rst-page__foot .rst-dropdown { position: static }",
110 } {
111 if !has[want] {
112 t.Errorf("tokens.css does not carry %s", want)
113 }
114 }
115}
116