v0.1.0 7deb448

theme_accent_test.go RawHistoryFolder

1package config
2
3import (
4	"context"
5	"errors"
6	"os"
7	"path/filepath"
8	"strings"
9	"testing"
10	"time"
11
12	"github.com/bnema/vev/internal/domain"
13	"github.com/stretchr/testify/require"
14)
15
16func TestParseThemeAccent(t *testing.T) {
17	t.Parallel()
18
19	tests := []struct {
20		name     string
21		input    string
22		want     domain.ThemeAccent
23		warnings []domain.Warning
24	}{
25		{
26			name:  "auto",
27			input: "theme.accent = auto\n",
28			want:  domain.ThemeAccent{Mode: domain.ThemeAccentAuto},
29		},
30		{
31			name:  "slot zero is valid but warns",
32			input: "theme.accent = 0\n",
33			want:  domain.ThemeAccent{Mode: domain.ThemeAccentSlot, Slot: 0},
34			warnings: []domain.Warning{
35				{Line: 1, Msg: "theme.accent slot 0 is conventionally neutral"},
36			},
37		},
38		{
39			name:  "slot fifteen is valid but warns",
40			input: "theme.accent = 15\n",
41			want:  domain.ThemeAccent{Mode: domain.ThemeAccentSlot, Slot: 15},
42			warnings: []domain.Warning{
43				{Line: 1, Msg: "theme.accent slot 15 is conventionally neutral"},
44			},
45		},
46		{
47			name:  "chromatic slot",
48			input: "theme.accent = 2\n",
49			want:  domain.ThemeAccent{Mode: domain.ThemeAccentSlot, Slot: 2},
50		},
51		{
52			name:  "negative slot falls back to auto",
53			input: "theme.accent = -1\n",
54			want:  domain.ThemeAccent{Mode: domain.ThemeAccentAuto},
55			warnings: []domain.Warning{
56				{Line: 1, Msg: "invalid theme.accent \"-1\""},
57			},
58		},
59		{
60			name:  "out of range slot falls back to auto",
61			input: "theme.accent = 16\n",
62			want:  domain.ThemeAccent{Mode: domain.ThemeAccentAuto},
63			warnings: []domain.Warning{
64				{Line: 1, Msg: "invalid theme.accent \"16\""},
65			},
66		},
67		{
68			name:  "non numeric value falls back to auto",
69			input: "theme.accent = teal\n",
70			want:  domain.ThemeAccent{Mode: domain.ThemeAccentAuto},
71			warnings: []domain.Warning{
72				{Line: 1, Msg: "invalid theme.accent \"teal\""},
73			},
74		},
75		{
76			name:  "non canonical slot falls back to auto",
77			input: "theme.accent = 02\n",
78			want:  domain.ThemeAccent{Mode: domain.ThemeAccentAuto},
79			warnings: []domain.Warning{
80				{Line: 1, Msg: "invalid theme.accent \"02\""},
81			},
82		},
83		{
84			name:  "case sensitive auto falls back to auto",
85			input: "theme.accent = AUTO\n",
86			want:  domain.ThemeAccent{Mode: domain.ThemeAccentAuto},
87			warnings: []domain.Warning{
88				{Line: 1, Msg: "invalid theme.accent \"AUTO\""},
89			},
90		},
91	}
92
93	for _, tt := range tests {
94		t.Run(tt.name, func(t *testing.T) {
95			t.Parallel()
96
97			cfg, warnings, err := Parse(strings.NewReader(tt.input))
98			require.NoError(t, err)
99			require.Equal(t, tt.want, cfg.ThemeAccent)
100			require.Equal(t, tt.warnings, warnings)
101		})
102	}
103}
104
105func TestDefaultsThemeAccent(t *testing.T) {
106	t.Parallel()
107
108	require.Equal(t, domain.ThemeAccent{Mode: domain.ThemeAccentAuto}, domain.Defaults().ThemeAccent)
109}
110
111func TestWatchThemeAccentInvalidReloadFallsBackToAuto(t *testing.T) {
112	t.Parallel()
113
114	path := filepath.Join(t.TempDir(), "config")
115	require.NoError(t, os.WriteFile(path, []byte("theme.accent = 2\n"), 0o600))
116	initial, warnings, err := Load(path)
117	require.NoError(t, err)
118	require.Empty(t, warnings)
119	require.Equal(t, domain.ThemeAccent{Mode: domain.ThemeAccentSlot, Slot: 2}, initial.ThemeAccent)
120
121	clk := newWatchClockMock(t)
122	ctx, cancel := context.WithCancel(context.Background())
123	defer cancel()
124	type change struct {
125		cfg      domain.Config
126		warnings []domain.Warning
127	}
128	changes := make(chan change, 1)
129	done := make(chan error, 1)
130	go func() {
131		done <- Watch(ctx, clk.clock, path, func(cfg domain.Config, warnings []domain.Warning) {
132			changes <- change{cfg: cfg, warnings: warnings}
133		})
134	}()
135
136	clk.waitForTimer()
137	require.NoError(t, os.WriteFile(path, []byte("theme.accent = invalid\n"), 0o600))
138	clk.fire()
139
140	select {
141	case got := <-changes:
142		require.Equal(t, domain.ThemeAccent{Mode: domain.ThemeAccentAuto}, got.cfg.ThemeAccent)
143		require.Equal(t, []domain.Warning{{Line: 1, Msg: "invalid theme.accent \"invalid\""}}, got.warnings)
144	case <-time.After(2 * time.Second):
145		t.Fatal("timed out waiting for config reload")
146	}
147
148	cancel()
149	select {
150	case err := <-done:
151		require.True(t, errors.Is(err, context.Canceled))
152	case <-time.After(2 * time.Second):
153		t.Fatal("timed out waiting for config watcher shutdown")
154	}
155}

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 12

Tags 11

↑↓ moveenter openesc close