oficina / docs Public

Clone
git clone https://amadan.net/oficina/docs

Plain git — no account needed to clone.

Download

Download this file

1package docs
2
3import (
4 "context"
5 "net/http"
6 "net/http/httptest"
7 "strings"
8 "testing"
9)
10
11// Paged and infinite.
12//
13// The mode is decided server-side from a cookie so the FIRST paint
14// already matches the person's preference — the whole reason it is a
15// cookie rather than localStorage, which the server cannot see. These
16// check the server's half; the measuring and the gaps themselves are
17// browser work (fixtures/pages.mjs) and cannot be tested here at all,
18// because Chromium will not start in this project's sandbox.
19
20// signedInGetter signs in ONCE and returns a function that fetches the
21// document with whatever docs-view cookie is asked for.
22//
23// Once, because app.TestRequest signs in on every call and POST /signin
24// is rate limited — six calls in a loop came back
25// "/signin?err=rate" and the test failed for a reason that had nothing
26// to do with what it was checking. The session cookie from the first
27// request is carried onto the rest by hand.
28func signedInGetter(t *testing.T, app *App, address, sqid string) func(cookie string) string {
29 t.Helper()
30 base := app.TestRequest(t, address, http.MethodGet, "/d/"+sqid, nil)
31 session := base.Cookies()
32
33 return func(cookie string) string {
34 req := httptest.NewRequest(http.MethodGet, "/d/"+sqid, nil)
35 for _, c := range session {
36 req.AddCookie(c)
37 }
38 if cookie != "" {
39 req.AddCookie(&http.Cookie{Name: ViewCookie, Value: cookie})
40 }
41 return serve(app, req).Body.String()
42 }
43}
44
45func TestTheViewModeComesFromTheCookie(t *testing.T) {
46 app, err := NewForTest(t)
47 if err != nil {
48 t.Fatalf("NewForTest: %v", err)
49 }
50 owner := app.TestPerson(t, "pages@example.com")
51 doc, err := app.Store().Create(context.Background(), owner, "Pages")
52 if err != nil {
53 t.Fatalf("Create: %v", err)
54 }
55
56 cases := []struct {
57 cookie string
58 want string
59 why string
60 }{
61 {"", ViewInfinite, "no cookie is infinite — Paul's chosen default"},
62 {ViewPaged, ViewPaged, "the cookie is honoured"},
63 {ViewInfinite, ViewInfinite, "and honoured the other way"},
64 // The cookie is written by a script and is therefore a value a
65 // person can set to anything at all, and it lands in an HTML
66 // attribute. Anything that is not exactly "paged" is infinite —
67 // the same closed-comparison discipline fragment.rs's
68 // ALIGNMENTS uses, and for the same reason.
69 {"PAGED", ViewInfinite, "case matters; the comparison is exact"},
70 {"paged; DROP", ViewInfinite, "a value that is not a mode is not a mode"},
71 {`"><script>`, ViewInfinite, "and never reaches the attribute"},
72 }
73 get := signedInGetter(t, app, "pages@example.com", doc.Sqid)
74 for _, c := range cases {
75 body := get(c.cookie)
76 if !strings.Contains(body, `data-view="`+c.want+`"`) {
77 t.Errorf("cookie %q: the canvas is not data-view=%q — %s", c.cookie, c.want, c.why)
78 }
79 if c.cookie != "" && c.cookie != ViewPaged && c.cookie != ViewInfinite {
80 if strings.Contains(body, c.cookie) {
81 t.Errorf("cookie %q reached the page verbatim", c.cookie)
82 }
83 }
84 }
85}
86
87// The View menu says which mode you are in before any script runs,
88// because the server already knows.
89//
90// aria-pressed on a <button>, not aria-checked: these menus are
91// disclosures whose items are ordinary buttons, and aria-checked
92// belongs to menuitemcheckbox/menuitemradio. Using the attribute that
93// does not apply to the element is worse than using none.
94func TestTheViewMenuReportsTheCurrentMode(t *testing.T) {
95 app, err := NewForTest(t)
96 if err != nil {
97 t.Fatalf("NewForTest: %v", err)
98 }
99 owner := app.TestPerson(t, "viewmenu@example.com")
100 doc, err := app.Store().Create(context.Background(), owner, "Pages")
101 if err != nil {
102 t.Fatalf("Create: %v", err)
103 }
104
105 get := signedInGetter(t, app, "viewmenu@example.com", doc.Sqid)
106 paged := get(ViewPaged)
107 if !strings.Contains(paged, `data-docs-command="view_paged" aria-pressed="true"`) {
108 t.Error("in paged mode the Paged item is not pressed")
109 }
110 if !strings.Contains(paged, `data-docs-command="view_infinite" aria-pressed="false"`) {
111 t.Error("in paged mode the Infinite item is not un-pressed")
112 }
113
114 infinite := get("")
115 if !strings.Contains(infinite, `data-docs-command="view_infinite" aria-pressed="true"`) {
116 t.Error("by default the Infinite item is not pressed")
117 }
118 if !strings.Contains(infinite, `data-docs-command="view_paged" aria-pressed="false"`) {
119 t.Error("by default the Paged item is not un-pressed")
120 }
121
122 // Exactly one of the PAIR, in either mode. A group where both or
123 // neither is pressed reads as broken and is the failure a
124 // server-rendered one-of group is most likely to have.
125 //
126 // Counted over the two view commands only, not over the whole page:
127 // the first version counted every aria-pressed="true" in the body
128 // and found two, because the formatting bar renders the same
129 // attribute on its own toggles. It was measuring the page and
130 // reporting on the menu.
131 for name, body := range map[string]string{"paged": paged, "infinite": infinite} {
132 pressed := 0
133 for _, cmd := range []string{"view_paged", "view_infinite"} {
134 if strings.Contains(body, `data-docs-command="`+cmd+`" aria-pressed="true"`) {
135 pressed++
136 }
137 }
138 if pressed != 1 {
139 t.Errorf("%s: %d of the two view-mode items are pressed, expected exactly 1", name, pressed)
140 }
141 }
142}
143
144// Both View items are checkable: the pair shows the current mode with
145// estilo's check column, and exactly one is pressed.
146func TestTheViewModeItemsAreCheckable(t *testing.T) {
147 v := newMenuViewForTest()
148
149 found, pressed := 0, 0
150 for _, m := range v.Menus {
151 for _, it := range m.Items {
152 if it.Command != "view_paged" && it.Command != "view_infinite" {
153 continue
154 }
155 found++
156 if !it.Checkable {
157 t.Errorf("%q has no check state", it.Command)
158 }
159 if it.Pressed {
160 pressed++
161 }
162 }
163 }
164 if found != 2 || pressed != 1 {
165 t.Fatalf("found %d view-mode items with %d pressed, expected 2 and 1", found, pressed)
166 }
167}
168