oficina / sheets Public

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

Plain git — no account needed to clone.

Download

Download this file

1package sheets
2
3import (
4 "fmt"
5 "math"
6 "net/http"
7 "net/http/httptest"
8 "strings"
9 "testing"
10
11 "amadan.net/oficina/sheets/internal/sheets/store"
12)
13
14// The pair spec §11.1 names: Excel's own light-yellow
15// conditional-formatting preset, #9C6500 ink on #FFEB9C fill, which the
16// spec records as 4.12:1 against an independent source. If our
17// implementation of a published standard disagrees with a figure
18// somebody else computed, ours is what is wrong.
19func TestExcelsOwnPresetIsBelowTheFloor(t *testing.T) {
20 got, ok := contrastRatio("FFFFEB9C", "FF9C6500")
21 if !ok {
22 t.Fatal("contrastRatio could not read the pair")
23 }
24 if math.Abs(got-4.12) > 0.02 {
25 t.Fatalf("contrast(#FFEB9C, #9C6500) = %.3f, want 4.12 — "+
26 "the figure spec §11.1 records against an independent source", got)
27 }
28 if got >= aaFloor {
29 t.Fatalf("%.2f is not below the %.1f floor; this pair is the whole reason §11.1 exists", got, aaFloor)
30 }
31}
32
33// Anchors from the standard itself, so the implementation is checked
34// against WCAG rather than against its own output.
35func TestContrastRatioAnchors(t *testing.T) {
36 for _, tc := range []struct {
37 a, b string
38 want float64
39 }{
40 {"FFFFFFFF", "FF000000", 21}, // white on black is the maximum
41 {"FF000000", "FFFFFFFF", 21}, // and the order must not matter
42 {"FF808080", "FF808080", 1}, // a colour against itself
43 {"FFFFFFFF", "FF767676", 4.54}, // the classic "just passes" grey
44 } {
45 got, ok := contrastRatio(tc.a, tc.b)
46 if !ok {
47 t.Fatalf("contrastRatio(%s, %s) failed to parse", tc.a, tc.b)
48 }
49 if math.Abs(got-tc.want) > 0.02 {
50 t.Errorf("contrast(%s, %s) = %.3f, want %.2f", tc.a, tc.b, got, tc.want)
51 }
52 }
53}
54
55func TestContrastRatioRejectsRubbish(t *testing.T) {
56 for _, bad := range []string{"", "fff", "zzzzzz", "FFFFFFFFFF"} {
57 if _, ok := contrastRatio(bad, "FF000000"); ok {
58 t.Errorf("contrastRatio accepted %q", bad)
59 }
60 }
61 // Six-char RGB is accepted as well as eight-char ARGB, because
62 // excelize hands back both shapes depending on the file.
63 if _, ok := contrastRatio("FFEB9C", "9C6500"); !ok {
64 t.Error("contrastRatio rejected plain 6-char RGB")
65 }
66}
67
68// A fill on its own cannot fail. Our own ink comes from the theme, and
69// that pairing is one this app controls; only a pair that both arrived
70// in somebody's file can fail here.
71func TestLowContrastNeedsBothHalves(t *testing.T) {
72 cases := []struct {
73 name string
74 f *store.Format
75 want bool
76 }{
77 {"nil format", nil, false},
78 {"no colours at all", &store.Format{}, false},
79 {"fill only", &store.Format{FillHex: "FFFFEB9C"}, false},
80 {"ink only", &store.Format{InkHex: "FF9C6500"}, false},
81 {"Excel's failing preset", &store.Format{FillHex: "FFFFEB9C", InkHex: "FF9C6500"}, true},
82 {"black on white", &store.Format{FillHex: "FFFFFFFF", InkHex: "FF000000"}, false},
83 {"unreadable", &store.Format{FillHex: "FF0000FF", InkHex: "FF000000"}, true},
84 }
85 for _, tc := range cases {
86 if got := lowContrast(tc.f); got != tc.want {
87 t.Errorf("%s: lowContrast = %v, want %v", tc.name, got, tc.want)
88 }
89 }
90}
91
92// The obligation is surfacing, not overriding: a person must be told,
93// and the cell must be left alone.
94func TestAFailingPairIsSurfacedOnBothViews(t *testing.T) {
95 app, cookies := signIn(t)
96 s, cells := newSheet(t, app, cookies, "Imported")
97
98 // Put a value in A1, then give it the format an import would have
99 // retained from Excel's own preset.
100 postCell(t, app, cookies, cells, `{"r":0,"c":0,"raw":"warned"}`)
101
102 h, err := app.openTabFor(s)
103 if err != nil {
104 t.Fatalf("open tab: %v", err)
105 }
106 rowID, _ := h.EnsureRow(0)
107 colID, _ := h.EnsureCol(0)
108 if err := h.SetFormat(rowID, colID, store.Format{
109 FillHex: "FFFFEB9C", InkHex: "FF9C6500",
110 }); err != nil {
111 t.Fatalf("SetFormat: %v", err)
112 }
113 h.Release()
114
115 for _, path := range []string{
116 fmt.Sprintf("/sheets/%d", s.ID),
117 fmt.Sprintf("/sheets/%d/table", s.ID),
118 } {
119 body := do(t, app, as(httptest.NewRequest(http.MethodGet, path, nil), cookies)).Body.String()
120 if !strings.Contains(body, "below the contrast floor") {
121 t.Errorf("%s does not surface the failing pair", path)
122 }
123 if !strings.Contains(body, "<code>A1</code>") {
124 t.Errorf("%s does not name the cell", path)
125 }
126 }
127}
128
129// Surfacing must not fire on a sheet that has no imported colours at
130// all, or it becomes a banner everybody learns to ignore.
131func TestNoNoticeWithoutAFailingPair(t *testing.T) {
132 app, cookies := signIn(t)
133 s, cells := newSheet(t, app, cookies, "Plain")
134 postCell(t, app, cookies, cells, `{"r":0,"c":0,"raw":"7"}`)
135
136 body := do(t, app, as(httptest.NewRequest(http.MethodGet,
137 fmt.Sprintf("/sheets/%d", s.ID), nil), cookies)).Body.String()
138 if strings.Contains(body, "below the contrast floor") {
139 t.Fatal("a sheet with no imported colours is warned about anyway")
140 }
141}
142
143// Spec §11.1: rendered as authored. The warning must not change what
144// the cell holds or how it is painted.
145func TestSurfacingDoesNotChangeTheCell(t *testing.T) {
146 app, cookies := signIn(t)
147 s, cells := newSheet(t, app, cookies, "Imported")
148 postCell(t, app, cookies, cells, `{"r":0,"c":0,"raw":"warned"}`)
149
150 h, err := app.openTabFor(s)
151 if err != nil {
152 t.Fatalf("open tab: %v", err)
153 }
154 rowID, _ := h.EnsureRow(0)
155 colID, _ := h.EnsureCol(0)
156 if err := h.SetFormat(rowID, colID, store.Format{FillHex: "FFFFEB9C", InkHex: "FF9C6500"}); err != nil {
157 t.Fatalf("SetFormat: %v", err)
158 }
159 h.Release()
160
161 do(t, app, as(httptest.NewRequest(http.MethodGet, fmt.Sprintf("/sheets/%d", s.ID), nil), cookies))
162
163 h2, err := app.openTabFor(s)
164 if err != nil {
165 t.Fatalf("reopen: %v", err)
166 }
167 defer h2.Release()
168 f := h2.Format(rowID, colID)
169 if f == nil || f.FillHex != "FFFFEB9C" || f.InkHex != "FF9C6500" {
170 t.Fatalf("the colours changed after being surfaced: %+v", f)
171 }
172}
173