v0.30.0 eb3068f

permission_test.go RawHistoryFolder

1package dialog
2
3import (
4	"context"
5	"testing"
6
7	"github.com/bnema/dumber/internal/application/port"
8	"github.com/bnema/dumber/internal/domain/entity"
9	dialogmocks "github.com/bnema/dumber/internal/ui/dialog/mocks"
10	"github.com/stretchr/testify/assert"
11	"github.com/stretchr/testify/mock"
12)
13
14type permissionPopupShowCall struct {
15	heading string
16	body    string
17}
18
19func TestPermissionDialog_QueuesRequestsWhilePopupVisible(t *testing.T) {
20	popup := dialogmocks.NewMockPermissionPopup(t)
21	var showCalls []permissionPopupShowCall
22	var callback func(allowed, persistent bool)
23	popup.EXPECT().
24		Show(mock.Anything, mock.Anything, mock.Anything, mock.Anything).
25		Run(func(_ context.Context, heading string, body string, cb func(allowed, persistent bool)) {
26			showCalls = append(showCalls, permissionPopupShowCall{
27				heading: heading,
28				body:    body,
29			})
30			callback = cb
31		}).
32		Twice()
33
34	respond := func(allowed, persistent bool) {
35		if callback == nil {
36			return
37		}
38		cb := callback
39		callback = nil
40		cb(allowed, persistent)
41	}
42
43	d := &PermissionDialog{popup: popup}
44
45	var firstResult port.PermissionDialogResult
46	firstCalled := false
47	d.ShowPermissionDialog(
48		context.Background(),
49		"https://example.com",
50		[]entity.PermissionType{entity.PermissionTypeMicrophone},
51		nil,
52		func(result port.PermissionDialogResult) {
53			firstCalled = true
54			firstResult = result
55		},
56	)
57
58	secondCalled := false
59	var secondResult port.PermissionDialogResult
60	d.ShowPermissionDialog(
61		context.Background(),
62		"https://example.com",
63		[]entity.PermissionType{entity.PermissionTypeCamera},
64		nil,
65		func(result port.PermissionDialogResult) {
66			secondCalled = true
67			secondResult = result
68		},
69	)
70
71	if assert.Len(t, showCalls, 1) {
72		assert.Equal(t, "Allow Microphone Access?", showCalls[0].heading)
73	}
74	assert.False(t, firstCalled)
75	assert.False(t, secondCalled)
76
77	respond(true, false)
78	assert.True(t, firstCalled)
79	assert.Equal(t, port.PermissionDialogResult{Allowed: true, Persistent: false}, firstResult)
80
81	if assert.Len(t, showCalls, 2) {
82		assert.Equal(t, "Allow Camera Access?", showCalls[1].heading)
83	}
84	assert.False(t, secondCalled)
85
86	respond(false, true)
87	assert.True(t, secondCalled)
88	assert.Equal(t, port.PermissionDialogResult{Allowed: false, Persistent: true}, secondResult)
89}
90
91func TestPermissionDialog_NoPopup_DeniesRequest(t *testing.T) {
92	d := &PermissionDialog{popup: nil}
93
94	called := false
95	result := port.PermissionDialogResult{}
96	d.ShowPermissionDialog(
97		context.Background(),
98		"https://example.com",
99		[]entity.PermissionType{entity.PermissionTypeMicrophone},
100		nil,
101		func(r port.PermissionDialogResult) {
102			called = true
103			result = r
104		},
105	)
106
107	assert.True(t, called)
108	assert.Equal(t, port.PermissionDialogResult{Allowed: false, Persistent: false}, result)
109}
110
111func TestPermissionDialog_BuildHeadingAndBody_DisplayCombinations(t *testing.T) {
112	d := &PermissionDialog{}
113	origin := "https://meet.example.com"
114
115	tests := []struct {
116		name           string
117		permTypes      []entity.PermissionType
118		expectHeading  string
119		expectedAction string
120	}{
121		{
122			name:           "display only",
123			permTypes:      []entity.PermissionType{entity.PermissionTypeDisplay},
124			expectHeading:  "Allow Screen Sharing?",
125			expectedAction: "share your screen",
126		},
127		{
128			name:           "microphone and display",
129			permTypes:      []entity.PermissionType{entity.PermissionTypeMicrophone, entity.PermissionTypeDisplay},
130			expectHeading:  "Allow Microphone and Screen Sharing?",
131			expectedAction: "access your microphone and share your screen",
132		},
133		{
134			name:           "camera and display",
135			permTypes:      []entity.PermissionType{entity.PermissionTypeCamera, entity.PermissionTypeDisplay},
136			expectHeading:  "Allow Camera and Screen Sharing?",
137			expectedAction: "access your camera and share your screen",
138		},
139		{
140			name:           "all three",
141			permTypes:      []entity.PermissionType{entity.PermissionTypeMicrophone, entity.PermissionTypeCamera, entity.PermissionTypeDisplay},
142			expectHeading:  "Allow Microphone, Camera, and Screen Sharing?",
143			expectedAction: "access your microphone and camera, and share your screen",
144		},
145		{
146			name:           "website data access only",
147			permTypes:      []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess},
148			expectHeading:  "Allow Third-Party Data Access?",
149			expectedAction: "access its stored data while you browse this site",
150		},
151	}
152
153	for _, tt := range tests {
154		t.Run(tt.name, func(t *testing.T) {
155			assert.Equal(t, tt.expectHeading, d.buildHeading(tt.permTypes))
156			assert.Equal(t, origin+" wants to "+tt.expectedAction+".", d.buildBody(origin, tt.permTypes, nil))
157		})
158	}
159
160	// Test website_data_access with populated metadata (Epiphany-style rich message)
161	t.Run("website data access with both domains", func(t *testing.T) {
162		meta := entity.PermissionMetadata{
163			entity.PermissionMetadataKeyRequestingDomain: "accounts.google.com",
164			entity.PermissionMetadataKeyCurrentDomain:    "shop.example.com",
165		}
166		body := d.buildBody(origin, []entity.PermissionType{entity.PermissionTypeWebsiteDataAccess}, meta)
167		assert.Equal(t, origin+" wants to allow accounts.google.com to access its data (including cookies) while you browse shop.example.com.", body)
168	})
169}

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