v0.29.0 21b3150

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

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