v0.6.1 aa12db3

grammar_test.go RawHistoryFolder

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}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 12

↑↓ moveenter openesc close