messaging_test.go RawHistoryFolder

1package webkit
2
3import (
4	"context"
5	"encoding/json"
6	"sync"
7	"testing"
8
9	"github.com/bnema/dumber/internal/application/port"
10	"github.com/stretchr/testify/require"
11)
12
13type messageRouterContextKey struct{}
14
15func TestMessageRouterBaseContextConcurrentUpdate(t *testing.T) {
16	t.Parallel()
17
18	router := NewMessageRouter(context.Background())
19	var wg sync.WaitGroup
20	wg.Add(2)
21
22	go func() {
23		defer wg.Done()
24		for i := range 1000 {
25			router.SetBaseContext(context.WithValue(context.Background(), messageRouterContextKey{}, i))
26		}
27	}()
28	go func() {
29		defer wg.Done()
30		for range 1000 {
31			if router.baseContext() == nil {
32				t.Error("base context must never be nil")
33			}
34		}
35	}()
36
37	wg.Wait()
38}
39
40func TestHandleAllowlistedEditableFocusMessage_AllowsUntrustedPage(t *testing.T) {
41	router := NewMessageRouter(context.Background())
42	wv := &WebView{uri: "https://example.com", editableFocusBridgeToken: "secret-token"}
43
44	var states []bool
45	wv.SetCallbacks(&port.WebViewCallbacks{
46		OnEditableFocusChanged: func(editable bool) {
47			states = append(states, editable)
48		},
49	})
50
51	handled := router.handleAllowlistedBridgeMessage(wv, Message{
52		Type:    "editable_focus_changed",
53		Payload: json.RawMessage(`{"editable":true,"token":"secret-token"}`),
54	})
55
56	require.True(t, handled)
57	require.Equal(t, []bool{true}, states)
58}
59
60func TestHandleAllowlistedEditableFocusMessage_IsNoopAfterPoolReuse(t *testing.T) {
61	router := NewMessageRouter(context.Background())
62	wv := &WebView{uri: "https://example.com", editableFocusBridgeToken: "secret-token"}
63	wv.ResetForPoolReuse()
64
65	// Re-register after reset so a stale token is the only reason handling is a noop.
66	called := false
67	wv.SetCallbacks(&port.WebViewCallbacks{
68		OnEditableFocusChanged: func(bool) {
69			called = true
70		},
71	})
72
73	handled := router.handleAllowlistedBridgeMessage(wv, Message{
74		Type:    "editable_focus_changed",
75		Payload: json.RawMessage(`{"editable":true,"token":"secret-token"}`),
76	})
77
78	require.True(t, handled)
79	require.False(t, called)
80}
81
82func TestResetForPoolReuse_RotatesEditableFocusBridgeToken(t *testing.T) {
83	wv := &WebView{editableFocusBridgeToken: "secret-token"}
84
85	wv.ResetForPoolReuse()
86
87	require.NotEmpty(t, wv.EditableFocusBridgeToken())
88	require.NotEqual(t, "secret-token", wv.EditableFocusBridgeToken())
89}
90
91func TestIsTrustedBridgeURI(t *testing.T) {
92	t.Parallel()
93
94	trusted := []string{
95		"dumb://history",
96		"dumb://favorites",
97		"dumb://config",
98		"dumb://error",
99		"dumb://crash",
100		"dumb://history/path?cursor=1",
101	}
102	for _, raw := range trusted {
103		t.Run(raw, func(t *testing.T) {
104			t.Parallel()
105			require.True(t, isTrustedBridgeURI(raw))
106		})
107	}
108
109	untrusted := []string{
110		"",
111		"https://example.com",
112		"http://localhost",
113		"file:///tmp/history.html",
114		"dumb://evil/history",
115		"dumb://example.com",
116		"javascript:alert(1)",
117	}
118	for _, raw := range untrusted {
119		t.Run(raw, func(t *testing.T) {
120			t.Parallel()
121			require.False(t, isTrustedBridgeURI(raw))
122		})
123	}
124}

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