1package ui
2
3import (
4 "context"
5 "testing"
6
7 "github.com/bnema/dumber/internal/application/port/mocks"
8 "github.com/bnema/dumber/internal/application/usecase"
9 "github.com/bnema/dumber/internal/domain/entity"
10 "github.com/bnema/dumber/internal/infrastructure/config"
11 "github.com/bnema/dumber/internal/shared/syncdispatch"
12 "github.com/bnema/dumber/internal/ui/component"
13 "github.com/bnema/dumber/internal/ui/coordinator"
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
16)
17
18func TestExternalLinkSplitDirectionPlacements(t *testing.T) {
19 tests := map[entity.ExternalLinkPlacement]usecase.SplitDirection{
20 "": usecase.SplitRight,
21 entity.ExternalLinkPlacementLeft: usecase.SplitLeft,
22 entity.ExternalLinkPlacementRight: usecase.SplitRight,
23 entity.ExternalLinkPlacementTop: usecase.SplitUp,
24 entity.ExternalLinkPlacementBottom: usecase.SplitDown,
25 }
26 for placement, want := range tests {
27 t.Run(string(placement), func(t *testing.T) {
28 assert.Equal(t, want, externalLinkSplitDirection(placement))
29 })
30 }
31}
32
33func TestApp_OpenExternalURLTabbedTargetsLastFocusedWindowAndPreservesURL(t *testing.T) {
34 ctx := context.Background()
35 const originalURL = "https://example.com/path?token=secret#fragment"
36 active := entity.NewTab("tab-1", "ws-1", entity.NewPane("pane-1"))
37 tabs := entity.NewTabList()
38 tabs.Add(active)
39 focused := &browserWindow{id: "focused", tabs: tabs}
40
41 ids := []string{"tab-2", "ws-2", "pane-2"}
42 index := 0
43 app := &App{
44 runtimeConfig: runtimeConfigStateForTest(&config.Config{Workspace: entity.WorkspaceConfig{ExternalLinks: entity.ExternalLinksConfig{Behavior: entity.ExternalLinkBehaviorTabbed}}}),
45 tabsUC: usecase.NewManageTabsUseCase(func() string { id := ids[index]; index++; return id }, nil),
46 browserWindows: map[string]*browserWindow{
47 focused.id: focused,
48 },
49 browserWindowOrder: []string{focused.id},
50 lastFocusedWindowID: focused.id,
51 workspaceViews: map[entity.TabID]*component.WorkspaceView{},
52 windowForTab: map[entity.TabID]*browserWindow{},
53 }
54 app.tabCoord = coordinator.NewTabCoordinator(ctx, coordinator.TabCoordinatorConfig{TabsUC: app.tabsUC})
55 app.tabCoord.SetOnTabCreated(func(_ context.Context, _ coordinator.TabTarget, tab *entity.Tab) {
56 app.workspaceViews[tab.ID] = &component.WorkspaceView{}
57 app.setBrowserWindowForTab(tab.ID, focused)
58 })
59
60 require.NoError(t, app.OpenExternalURL(ctx, originalURL))
61 require.Len(t, tabs.Tabs, 2)
62 created := tabs.ActiveTab()
63 require.NotNil(t, created)
64 require.NotNil(t, created.Workspace)
65 assert.Equal(t, originalURL, created.Workspace.ActivePane().Pane.URI)
66 assert.Same(t, focused, app.browserWindowForTab(created.ID))
67}
68
69func TestApp_OpenExternalURLSnapshotsRuntimeConfigOnce(t *testing.T) {
70 ctx := context.Background()
71 provider := mocks.NewMockRuntimeConfigProvider(t)
72 provider.EXPECT().Current().Return(entity.RuntimeConfigSnapshot{UI: entity.RuntimeUIConfig{Workspace: entity.WorkspaceConfig{ExternalLinks: entity.ExternalLinksConfig{Behavior: entity.ExternalLinkBehaviorWindowed}}}}).Once()
73 var factoryCalls int
74 var dispatchCalls int
75 app := &App{
76 runtimeConfig: newRuntimeConfigState(provider),
77 dispatchOnMainThread: func(label string, fn func()) syncdispatch.SyncDispatchResult {
78 dispatchCalls++
79 fn()
80 return syncdispatch.SyncDispatchResult{Label: label, Status: syncdispatch.SyncDispatchInline}
81 },
82 browserWindowFactory: func(_ context.Context, _ string) (*browserWindow, error) {
83 factoryCalls++
84 return &browserWindow{id: "windowed", tabs: entity.NewTabList()}, nil
85 },
86 browserWindows: map[string]*browserWindow{},
87 workspaceViews: map[entity.TabID]*component.WorkspaceView{},
88 windowForTab: map[entity.TabID]*browserWindow{},
89 }
90
91 require.NoError(t, app.OpenExternalURL(ctx, "https://example.com"))
92 assert.Equal(t, 1, dispatchCalls)
93 assert.Equal(t, 1, factoryCalls)
94}
95
96func TestApp_OpenExternalURLFallsBackWhenLastFocusedWindowIsStale(t *testing.T) {
97 ctx := context.Background()
98 const originalURL = "https://example.com/stale?token=secret"
99 staleTab := entity.NewTab("stale-tab", "stale-workspace", entity.NewPane("stale-pane"))
100 staleTabs := entity.NewTabList()
101 staleTabs.Add(staleTab)
102 stale := &browserWindow{id: "stale", tabs: staleTabs}
103 var factoryURL string
104 app := &App{
105 runtimeConfig: runtimeConfigStateForTest(&config.Config{Workspace: entity.WorkspaceConfig{ExternalLinks: entity.ExternalLinksConfig{Behavior: entity.ExternalLinkBehaviorTabbed}}}),
106 browserWindows: map[string]*browserWindow{},
107 lastFocusedWindowID: stale.id,
108 windowForTab: map[entity.TabID]*browserWindow{staleTab.ID: stale},
109 workspaceViews: map[entity.TabID]*component.WorkspaceView{},
110 browserWindowFactory: func(_ context.Context, url string) (*browserWindow, error) {
111 factoryURL = url
112 return &browserWindow{id: "fallback", tabs: entity.NewTabList()}, nil
113 },
114 }
115
116 require.NoError(t, app.OpenExternalURL(ctx, originalURL))
117 assert.Equal(t, originalURL, factoryURL)
118}
119
120func TestApp_OpenExternalURLFallbackPreservesOriginalURL(t *testing.T) {
121 ctx := context.Background()
122 const originalURL = "https://example.com/path?token=secret#fragment"
123 var factoryURL string
124 app := &App{
125 runtimeConfig: runtimeConfigStateForTest(&config.Config{Workspace: entity.WorkspaceConfig{ExternalLinks: entity.ExternalLinksConfig{Behavior: entity.ExternalLinkBehaviorSplit, Placement: entity.ExternalLinkPlacementLeft}}}),
126 browserWindowFactory: func(_ context.Context, url string) (*browserWindow, error) {
127 factoryURL = url
128 return &browserWindow{id: "fallback", tabs: entity.NewTabList()}, nil
129 },
130 browserWindows: map[string]*browserWindow{},
131 workspaceViews: map[entity.TabID]*component.WorkspaceView{},
132 windowForTab: map[entity.TabID]*browserWindow{},
133 }
134
135 require.NoError(t, app.OpenExternalURL(ctx, originalURL))
136 assert.Equal(t, originalURL, factoryURL)
137}