1package css
2
3import (
4 "reflect"
5 "testing"
6)
7
8// Every row in docs/css.md has at least one accepted and one rejected example.
9// A valid example must override the row's initial; an invalid example must not.
10func TestDocumentedPropertyRows(t *testing.T) {
11 rows := []struct{ name, valid, invalid string }{
12 {"color", "red", "rgb(nope)"}, {"opacity", ".5", "2"},
13 {"background-color", "#abc", "#xyq"}, {"background", "red", "garbage"},
14 {"border-width", "1px 2px 3px 4px", "1px 2px 3px 4px 5px"},
15 {"border-style", "solid none", "dotted"}, {"border-color", "red blue", "invalid"},
16 {"border", "2px solid red", "2px dashed red"},
17 {"border-radius", "1px 2px 3px 4px", "2px / 1px"},
18 {"outline", "1px solid red", "1px dotted"},
19 {"outline-offset", "-2px", "1%"},
20 {"box-shadow", "inset 1px 2px 3px -4px red, 0 0 blue", "1px 2px -3px red"},
21 {"margin", "auto -1px 2% 3rem", "1px 2px 3px 4px 5px"},
22 {"padding", "1px 2px 3px 4px", "-1px"},
23 {"box-sizing", "border-box", "padding-box"},
24 {"display", "flex", "grid"},
25 {"flex-direction", "column", "diagonal"},
26 {"flex-wrap", "wrap", "reverse"},
27 {"flex", "2 3 10px", "2 nope"},
28 {"gap", "1px 2px", "1px 2px 3px"},
29 {"justify-content", "space-evenly", "between"},
30 {"overflow", "hidden auto", "clip"},
31 {"font-family", `"A B", serif`, "serif,,monospace"},
32 {"font-size", "120%", "-1px"},
33 {"font-weight", "bold", "550"},
34 {"font-style", "italic", "solid"},
35 {"line-height", "1.5", "-1px"},
36 {"text-align", "center", "justify"},
37 {"letter-spacing", "-1px", "1%"},
38 {"cursor", "pointer", "grab"},
39 {"accent-color", "red", "notacolor"},
40 {"--accent", "red", "var(--missing)"},
41 }
42 for _, side := range sideNames {
43 rows = append(rows,
44 struct{ name, valid, invalid string }{"border-" + side + "-width", "2px", "-1px"},
45 struct{ name, valid, invalid string }{"border-" + side + "-style", "solid", "dotted"},
46 struct{ name, valid, invalid string }{"border-" + side + "-color", "red", "notacolor"},
47 struct{ name, valid, invalid string }{"border-" + side, "2px solid red", "2px dashed red"},
48 struct{ name, valid, invalid string }{"margin-" + side, "-1em", "banana"},
49 struct{ name, valid, invalid string }{"padding-" + side, "2px", "-1px"},
50 )
51 }
52 for _, corner := range cornerNames {
53 rows = append(rows, struct{ name, valid, invalid string }{"border-" + corner + "-radius", "2%", "-1px"})
54 }
55 for _, name := range []string{"width", "height", "min-width", "min-height", "max-width", "max-height", "flex-basis"} {
56 rows = append(rows, struct{ name, valid, invalid string }{name, "2rem", "-1px"})
57 }
58 for _, name := range []string{"flex-grow", "flex-shrink"} {
59 rows = append(rows, struct{ name, valid, invalid string }{name, "2.5", "-1"})
60 }
61 for _, name := range []string{"row-gap", "column-gap"} {
62 rows = append(rows, struct{ name, valid, invalid string }{name, "2px", "-2px"})
63 }
64 for _, name := range []string{"align-items", "align-self"} {
65 rows = append(rows, struct{ name, valid, invalid string }{name, "center", "baseline"})
66 }
67 for _, name := range []string{"overflow-x", "overflow-y"} {
68 rows = append(rows, struct{ name, valid, invalid string }{name, "scroll", "clip"})
69 }
70 for _, name := range []string{"outline-width"} {
71 rows = append(rows, struct{ name, valid, invalid string }{name, "1px", "-1px"})
72 }
73 for _, name := range []string{"outline-style"} {
74 rows = append(rows, struct{ name, valid, invalid string }{name, "solid", "dashed"})
75 }
76 for _, name := range []string{"outline-color"} {
77 rows = append(rows, struct{ name, valid, invalid string }{name, "red", "nonsense"})
78 }
79 for _, r := range rows {
80 t.Run(r.name, func(t *testing.T) {
81 if r.name == "--accent" {
82 en := Compile(Sheet{}, Parse(`button{--accent:red;color:var(--accent)}`))
83 if en.Compute(&Element{Type: "button"}, nil).Style.Color != namedColors["red"] {
84 t.Fatal("custom property not resolved")
85 }
86 bad := Compile(Sheet{}, Parse(`button{--accent:var(--missing);color:var(--accent)}`))
87 if bad.Compute(&Element{Type: "button"}, nil).Style.Color != (Color{A: 1}) {
88 t.Fatal("invalid custom property not handled")
89 }
90 return
91 }
92 initial := Compile(Sheet{}, Sheet{}).Compute(&Element{Type: "button"}, nil).Style
93 valid := Compile(Sheet{}, Parse("button{"+r.name+":"+r.valid+"}")).Compute(&Element{Type: "button"}, nil).Style
94 invalid := Compile(Sheet{}, Parse("button{"+r.name+":"+r.invalid+"}")).Compute(&Element{Type: "button"}, nil).Style
95 if reflect.DeepEqual(*valid, *initial) {
96 t.Errorf("valid value %q did not alter style", r.valid)
97 }
98 if !reflect.DeepEqual(*invalid, *initial) {
99 t.Errorf("invalid value %q changed style", r.invalid)
100 }
101 })
102 }
103}