1package css
2
3import "testing"
4
5func TestDocumentedGrammar(t *testing.T) {
6 selectors := []struct {
7 selector string
8 match bool
9 }{
10 {"button", true}, {"BUTTON", true}, {".ok", true}, {"#x", true},
11 {"button.ok#x:hover", true}, {"app button", true}, {"app > button", true},
12 {"button, row", true}, {"button:active", false}, {"button:focus", false},
13 {"button:focus-visible", false}, {"button:disabled", false}, {"button:checked", false},
14 {"app > row > button", false}, {"row button", false},
15 }
16 for _, tt := range selectors {
17 t.Run(tt.selector, func(t *testing.T) {
18 en := Compile(Sheet{}, Parse(tt.selector+" {color:red}"))
19 p := en.Compute(&Element{Type: "app"}, nil)
20 c := en.Compute(&Element{Type: "button", ID: "x", Classes: []string{"ok"}, State: Hover}, p).Style.Color == namedColors["red"]
21 if c != tt.match {
22 t.Fatalf("match=%v expected %v", c, tt.match)
23 }
24 })
25 }
26 bad := []string{"*", "[type=button]", "button+row", "button~row", "button:not(.x)", "button::before", "button:future", "button, :future"}
27 for _, s := range bad {
28 t.Run("invalid-"+s, func(t *testing.T) {
29 parsed := Parse(s + "{color:red} button{color:blue}")
30 if len(parsed.Rules) != 1 || len(parsed.Diagnostics) == 0 || Compile(Sheet{}, parsed).Compute(&Element{Type: "button"}, nil).Style.Color != namedColors["blue"] {
31 t.Fatalf("did not drop whole bad rule: %+v", parsed)
32 }
33 })
34 }
35 t.Run("unclosed EOF", func(t *testing.T) {
36 parsed := Parse(`button{color:red`)
37 if len(parsed.Rules) != 0 || len(parsed.Diagnostics) == 0 {
38 t.Fatal(parsed)
39 }
40 })
41 cases := []struct {
42 src string
43 want string
44 }{
45 {`button{bad: rgb(1;2;3); color:red}`, "red"},
46 {`button{bad: [1;2;3]; color:blue}`, "blue"},
47 {`button{bad:{one;two}; color:green}`, "green"},
48 {`button{no-colon; color:red}`, "red"},
49 }
50 for _, tt := range cases {
51 t.Run(tt.src, func(t *testing.T) {
52 p := Parse(tt.src)
53 if len(p.Rules) != 1 {
54 t.Fatal(p)
55 }
56 want := namedColors[tt.want]
57 if c := Compile(Sheet{}, p).Compute(&Element{Type: "button"}, nil).Style.Color; c != want {
58 t.Fatalf("got %+v, want %+v; parse %+v", c, want, p)
59 }
60 })
61 }
62}
63
64func TestDocumentedValueGrammar(t *testing.T) {
65 colors := []struct{ valid, invalid string }{
66 {"red", "not-a-color"}, {"transparent", "rgb(nan,0,0)"}, {"currentColor", "#xx0"},
67 {"#abc", "#12"}, {"#abcd", "#12345"}, {"#112233", "#1234567"}, {"#11223344", "#123456789"},
68 {"rgb(255,0,0)", "rgb(1,2)"}, {"rgba(255 0 0 / 50%)", "rgba(1,2,3)"},
69 {"hsl(0 100% 50%)", "hsl(0 50 50%)"}, {"hsla(0,100%,50%,.5)", "hsla(0,100%,50%)"},
70 }
71 for _, tt := range colors {
72 t.Run(tt.valid, func(t *testing.T) {
73 if _, ok := parseColor(tt.valid, Color{A: 1}); !ok {
74 t.Error("valid color rejected")
75 }
76 if _, ok := parseColor(tt.invalid, Color{A: 1}); ok {
77 t.Errorf("invalid color accepted %s", tt.invalid)
78 }
79 })
80 }
81 lengths := []struct{ valid, invalid string }{
82 {"2px", "2pt"}, {"2%", "2vw"}, {"2em", "2ex"}, {"2rem", "2vh"}, {"0", "2"},
83 }
84 for _, tt := range lengths {
85 t.Run(tt.valid, func(t *testing.T) {
86 if _, ok := length(tt.valid, false, false); !ok {
87 t.Fatal("valid length rejected")
88 }
89 if _, ok := length(tt.invalid, false, false); ok {
90 t.Fatalf("invalid length accepted %s", tt.invalid)
91 }
92 })
93 }
94 if _, ok := length("auto", false, true); !ok {
95 t.Fatal("auto rejected where allowed")
96 }
97 if _, ok := length("auto", false, false); ok {
98 t.Fatal("auto accepted where disallowed")
99 }
100 if _, ok := length("-1px", true, false); !ok {
101 t.Fatal("signed length rejected")
102 }
103 if _, ok := length("-1px", false, false); ok {
104 t.Fatal("negative length accepted")
105 }
106}