test(navigation): cover URI callbacks after pool reuse of the same WebView
- commit
- 80d4e6aeb396a1dbe56fd3d53bcff896ebee71e8
- author
- brice · · 2026-10-15 07:35 UTC
- parent
- 13aa777
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +31 -0
M internal/ui/coordinator/content/navigation_security_test.go +31 -0
| @@ -6,6 +6,8 @@ import ( | |||
| 6 | 6 | "testing" | |
| 7 | 7 | ||
| 8 | 8 | "github.com/stretchr/testify/assert" | |
| 9 | + | "github.com/stretchr/testify/mock" | |
| 10 | + | "github.com/stretchr/testify/require" | |
| 9 | 11 | ||
| 10 | 12 | "github.com/bnema/dumber/internal/application/port" | |
| 11 | 13 | "github.com/bnema/dumber/internal/application/port/mocks" | |
| @@ -289,3 +291,32 @@ func TestHandleURIChanged_NonHTTPDocumentClearsOrigin(t *testing.T) { | |||
| 289 | 291 | assert.Equal(t, []string{""}, rec.origins, doc) | |
| 290 | 292 | } | |
| 291 | 293 | } | |
| 294 | + | ||
| 295 | + | func TestURIChangedCallback_IgnoredAfterSameWebViewIsReusedFromPool(t *testing.T) { | |
| 296 | + | ctx := context.Background() | |
| 297 | + | c := newMinimalCoordinator() | |
| 298 | + | rec := &externalLaunchRecorder{} | |
| 299 | + | rec.install(c) | |
| 300 | + | spaCalls := 0 | |
| 301 | + | c.onPaneURIUpdated = func(entity.PaneID, string) { spaCalls++ } | |
| 302 | + | ||
| 303 | + | generation := uint64(1) | |
| 304 | + | wv := mocks.NewMockWebView(t) | |
| 305 | + | wv.EXPECT().ID().Return(port.WebViewID(1)).Maybe() | |
| 306 | + | wv.EXPECT().Generation().RunAndReturn(func() uint64 { return generation }).Maybe() | |
| 307 | + | var callbacks *port.WebViewCallbacks | |
| 308 | + | wv.EXPECT().SetCallbacks(mock.Anything).Run(func(cb *port.WebViewCallbacks) { callbacks = cb }).Once() | |
| 309 | + | ||
| 310 | + | c.setWebViewLocked(testPaneID, wv) | |
| 311 | + | c.setupWebViewCallbacks(ctx, testPaneID, wv) | |
| 312 | + | require.NotNil(t, callbacks) | |
| 313 | + | ||
| 314 | + | // The same object is reused from the pool for a new binding. | |
| 315 | + | generation = 2 | |
| 316 | + | callbacks.OnURIChanged("https://site.example/") | |
| 317 | + | callbacks.OnURIChanged("claude://login") | |
| 318 | + | ||
| 319 | + | assert.Empty(t, rec.prompts) | |
| 320 | + | assert.Empty(t, c.externalPromptOrigins) | |
| 321 | + | assert.Zero(t, spaCalls) | |
| 322 | + | } | |
| @@ -6,6 +6,8 @@ import ( | |||||
| 6 | "testing" | 6 | "testing" | ||
| 7 | 7 | ||||
| 8 | "github.com/stretchr/testify/assert" | 8 | "github.com/stretchr/testify/assert" | ||
| 9 | + | "github.com/stretchr/testify/mock" | |||
| 10 | + | "github.com/stretchr/testify/require" | |||
| 9 | 11 | ||||
| 10 | "github.com/bnema/dumber/internal/application/port" | 12 | "github.com/bnema/dumber/internal/application/port" | ||
| 11 | "github.com/bnema/dumber/internal/application/port/mocks" | 13 | "github.com/bnema/dumber/internal/application/port/mocks" | ||
| @@ -289,3 +291,32 @@ func TestHandleURIChanged_NonHTTPDocumentClearsOrigin(t *testing.T) { | |||||
| 289 | assert.Equal(t, []string{""}, rec.origins, doc) | 291 | assert.Equal(t, []string{""}, rec.origins, doc) | ||
| 290 | } | 292 | } | ||
| 291 | } | 293 | } | ||
| 294 | + | ||||
| 295 | + | func TestURIChangedCallback_IgnoredAfterSameWebViewIsReusedFromPool(t *testing.T) { | |||
| 296 | + | ctx := context.Background() | |||
| 297 | + | c := newMinimalCoordinator() | |||
| 298 | + | rec := &externalLaunchRecorder{} | |||
| 299 | + | rec.install(c) | |||
| 300 | + | spaCalls := 0 | |||
| 301 | + | c.onPaneURIUpdated = func(entity.PaneID, string) { spaCalls++ } | |||
| 302 | + | ||||
| 303 | + | generation := uint64(1) | |||
| 304 | + | wv := mocks.NewMockWebView(t) | |||
| 305 | + | wv.EXPECT().ID().Return(port.WebViewID(1)).Maybe() | |||
| 306 | + | wv.EXPECT().Generation().RunAndReturn(func() uint64 { return generation }).Maybe() | |||
| 307 | + | var callbacks *port.WebViewCallbacks | |||
| 308 | + | wv.EXPECT().SetCallbacks(mock.Anything).Run(func(cb *port.WebViewCallbacks) { callbacks = cb }).Once() | |||
| 309 | + | ||||
| 310 | + | c.setWebViewLocked(testPaneID, wv) | |||
| 311 | + | c.setupWebViewCallbacks(ctx, testPaneID, wv) | |||
| 312 | + | require.NotNil(t, callbacks) | |||
| 313 | + | ||||
| 314 | + | // The same object is reused from the pool for a new binding. | |||
| 315 | + | generation = 2 | |||
| 316 | + | callbacks.OnURIChanged("https://site.example/") | |||
| 317 | + | callbacks.OnURIChanged("claude://login") | |||
| 318 | + | ||||
| 319 | + | assert.Empty(t, rec.prompts) | |||
| 320 | + | assert.Empty(t, c.externalPromptOrigins) | |||
| 321 | + | assert.Zero(t, spaCalls) | |||
| 322 | + | } | |||