v0.24.0 a261e52

content_reveal_test.go RawHistoryFolder

1package coordinator
2
3import (
4	"context"
5	"sync"
6	"testing"
7
8	"github.com/stretchr/testify/assert"
9
10	"github.com/bnema/dumber/internal/domain/entity"
11	"github.com/bnema/dumber/internal/infrastructure/webkit"
12)
13
14// TestPendingReveal tests the pending reveal state management.
15// These are internal package tests that verify the state transitions
16// without needing to mock WebKit dependencies.
17
18func TestMarkPendingReveal_SetsState(t *testing.T) {
19	c := &ContentCoordinator{
20		pendingReveal: make(map[entity.PaneID]bool),
21	}
22	paneID := entity.PaneID("pane-1")
23
24	c.markPendingReveal(paneID)
25
26	c.revealMu.Lock()
27	defer c.revealMu.Unlock()
28	assert.True(t, c.pendingReveal[paneID])
29}
30
31func TestClearPendingReveal_RemovesState(t *testing.T) {
32	c := &ContentCoordinator{
33		pendingReveal: make(map[entity.PaneID]bool),
34	}
35	paneID := entity.PaneID("pane-1")
36
37	// First mark as pending
38	c.markPendingReveal(paneID)
39
40	// Then clear
41	c.clearPendingReveal(paneID)
42
43	c.revealMu.Lock()
44	defer c.revealMu.Unlock()
45	assert.False(t, c.pendingReveal[paneID])
46}
47
48func TestRevealIfPending_NotPending_DoesNothing(t *testing.T) {
49	callbackCalled := false
50	c := &ContentCoordinator{
51		pendingReveal: make(map[entity.PaneID]bool),
52		webViews:      make(map[entity.PaneID]*webkit.WebView),
53		onWebViewShown: func(paneID entity.PaneID) {
54			callbackCalled = true
55		},
56	}
57	paneID := entity.PaneID("pane-1")
58
59	// Call reveal without marking pending first
60	c.revealIfPending(context.Background(), paneID, "http://example.com", "test")
61
62	assert.False(t, callbackCalled, "callback should not be called when not pending")
63}
64
65func TestRevealIfPending_ClearsStateOnReveal(t *testing.T) {
66	c := &ContentCoordinator{
67		pendingReveal: make(map[entity.PaneID]bool),
68		webViews:      make(map[entity.PaneID]*webkit.WebView),
69	}
70	paneID := entity.PaneID("pane-1")
71
72	// Mark as pending
73	c.markPendingReveal(paneID)
74
75	// Reveal (will return early since webview is nil, but should still clear state)
76	c.revealIfPending(context.Background(), paneID, "http://example.com", "test")
77
78	c.revealMu.Lock()
79	defer c.revealMu.Unlock()
80	assert.False(t, c.pendingReveal[paneID], "pending state should be cleared after reveal attempt")
81}
82
83func TestRevealIfPending_OnlyRevealsOnce(t *testing.T) {
84	revealCount := 0
85	c := &ContentCoordinator{
86		pendingReveal: make(map[entity.PaneID]bool),
87		webViews:      make(map[entity.PaneID]*webkit.WebView),
88		onWebViewShown: func(paneID entity.PaneID) {
89			revealCount++
90		},
91	}
92	paneID := entity.PaneID("pane-1")
93
94	// Mark as pending
95	c.markPendingReveal(paneID)
96
97	// Call reveal multiple times
98	c.revealIfPending(context.Background(), paneID, "", "first")
99	c.revealIfPending(context.Background(), paneID, "", "second")
100	c.revealIfPending(context.Background(), paneID, "", "third")
101
102	// Callback should not be called since webview is nil, but state should be cleared
103	assert.Equal(t, 0, revealCount, "callback not called when webview is nil")
104
105	// Verify state is cleared
106	c.revealMu.Lock()
107	defer c.revealMu.Unlock()
108	assert.False(t, c.pendingReveal[paneID])
109}
110
111func TestPendingReveal_ConcurrentAccess(t *testing.T) {
112	c := &ContentCoordinator{
113		pendingReveal: make(map[entity.PaneID]bool),
114		webViews:      make(map[entity.PaneID]*webkit.WebView),
115	}
116
117	var wg sync.WaitGroup
118	paneIDs := []entity.PaneID{"pane-1", "pane-2", "pane-3", "pane-4", "pane-5"}
119
120	// Concurrently mark and clear panes
121	for i := 0; i < 100; i++ {
122		for _, paneID := range paneIDs {
123			wg.Add(3)
124
125			go func(id entity.PaneID) {
126				defer wg.Done()
127				c.markPendingReveal(id)
128			}(paneID)
129
130			go func(id entity.PaneID) {
131				defer wg.Done()
132				c.clearPendingReveal(id)
133			}(paneID)
134
135			go func(id entity.PaneID) {
136				defer wg.Done()
137				c.revealIfPending(context.Background(), id, "", "concurrent")
138			}(paneID)
139		}
140	}
141
142	wg.Wait()
143	// Test passes if no race conditions occur
144}
145
146func TestMarkPendingReveal_MultiplePanes(t *testing.T) {
147	c := &ContentCoordinator{
148		pendingReveal: make(map[entity.PaneID]bool),
149	}
150
151	pane1 := entity.PaneID("pane-1")
152	pane2 := entity.PaneID("pane-2")
153
154	c.markPendingReveal(pane1)
155	c.markPendingReveal(pane2)
156
157	c.revealMu.Lock()
158	defer c.revealMu.Unlock()
159	assert.True(t, c.pendingReveal[pane1])
160	assert.True(t, c.pendingReveal[pane2])
161}
162
163func TestClearPendingReveal_OnlyAffectsTargetPane(t *testing.T) {
164	c := &ContentCoordinator{
165		pendingReveal: make(map[entity.PaneID]bool),
166	}
167
168	pane1 := entity.PaneID("pane-1")
169	pane2 := entity.PaneID("pane-2")
170
171	c.markPendingReveal(pane1)
172	c.markPendingReveal(pane2)
173	c.clearPendingReveal(pane1)
174
175	c.revealMu.Lock()
176	defer c.revealMu.Unlock()
177	assert.False(t, c.pendingReveal[pane1], "pane1 should be cleared")
178	assert.True(t, c.pendingReveal[pane2], "pane2 should still be pending")
179}

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 8

Tags 42

↑↓ moveenter openesc close