refactor(ui): add runtime config snapshot helpers
- commit
- 3a44e8a40c385aea8f103b87e09d05e636c2fcc2
- author
- brice · · 2026-06-23 14:39 UTC
- committer
- brice · · 2026-06-23 20:01 UTC
- parent
- 82561bc
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
7 files · +315 -16
M .mockery.yaml +3 -0
| @@ -75,6 +75,9 @@ packages: | |||
| 75 | 75 | NativeWidgetProvider: {} | |
| 76 | 76 | Notification: {} | |
| 77 | 77 | RenderingEnvManager: {} | |
| 78 | + | RuntimeConfigProvider: | |
| 79 | + | config: | |
| 80 | + | filename: runtime_config_provider.go | |
| 78 | 81 | RuntimeVersionProbe: {} | |
| 79 | 82 | ScriptRefresher: {} | |
| 80 | 83 | SnapshotService: {} | |
| @@ -75,6 +75,9 @@ packages: | |||||
| 75 | NativeWidgetProvider: {} | 75 | NativeWidgetProvider: {} | ||
| 76 | Notification: {} | 76 | Notification: {} | ||
| 77 | RenderingEnvManager: {} | 77 | RenderingEnvManager: {} | ||
| 78 | + | RuntimeConfigProvider: | |||
| 79 | + | config: | |||
| 80 | + | filename: runtime_config_provider.go | |||
| 78 | RuntimeVersionProbe: {} | 81 | RuntimeVersionProbe: {} | ||
| 79 | ScriptRefresher: {} | 82 | ScriptRefresher: {} | ||
| 80 | SnapshotService: {} | 83 | SnapshotService: {} | ||
M cmd/dumber/main.go +4 -2
| @@ -865,10 +865,12 @@ func buildUIDependencies( | |||
| 865 | 865 | ||
| 866 | 866 | focusProvider := textinput.NewFocusProvider() | |
| 867 | 867 | browserLauncher := desktop.NewBrowserLauncher(browserLaunchRelay) | |
| 868 | + | runtimeConfig := bootstrap.NewRuntimeConfigProvider(cfg, config.GetManager()) | |
| 868 | 869 | ||
| 869 | 870 | uiDeps := &ui.Dependencies{ | |
| 870 | - | Ctx: ctx, | |
| 871 | - | Config: cfg, | |
| 871 | + | Ctx: ctx, | |
| 872 | + | Config: cfg, | |
| 873 | + | RuntimeConfig: runtimeConfig, | |
| 872 | 874 | EngineSettingsPayload: func() port.EngineSettingsPayload { | |
| 873 | 875 | return bootstrap.EngineSettingsPayloadFromConfig(cfg) | |
| 874 | 876 | }, | |
| @@ -865,10 +865,12 @@ func buildUIDependencies( | |||||
| 865 | 865 | ||||
| 866 | focusProvider := textinput.NewFocusProvider() | 866 | focusProvider := textinput.NewFocusProvider() | ||
| 867 | browserLauncher := desktop.NewBrowserLauncher(browserLaunchRelay) | 867 | browserLauncher := desktop.NewBrowserLauncher(browserLaunchRelay) | ||
| 868 | + | runtimeConfig := bootstrap.NewRuntimeConfigProvider(cfg, config.GetManager()) | |||
| 868 | 869 | ||||
| 869 | uiDeps := &ui.Dependencies{ | 870 | uiDeps := &ui.Dependencies{ | ||
| 870 | - | Ctx: ctx, | 871 | + | Ctx: ctx, |
| 871 | - | Config: cfg, | 872 | + | Config: cfg, |
| 873 | + | RuntimeConfig: runtimeConfig, | |||
| 872 | EngineSettingsPayload: func() port.EngineSettingsPayload { | 874 | EngineSettingsPayload: func() port.EngineSettingsPayload { | ||
| 873 | return bootstrap.EngineSettingsPayloadFromConfig(cfg) | 875 | return bootstrap.EngineSettingsPayloadFromConfig(cfg) | ||
| 874 | }, | 876 | }, | ||
A internal/application/port/mocks/runtime_config_provider.go +165 -0
| @@ -0,0 +1,165 @@ | |||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |
| 2 | + | // github.com/vektra/mockery | |
| 3 | + | // template: testify | |
| 4 | + | ||
| 5 | + | package mocks | |
| 6 | + | ||
| 7 | + | import ( | |
| 8 | + | "github.com/bnema/dumber/internal/application/port" | |
| 9 | + | mock "github.com/stretchr/testify/mock" | |
| 10 | + | ) | |
| 11 | + | ||
| 12 | + | // NewMockRuntimeConfigProvider creates a new instance of MockRuntimeConfigProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |
| 13 | + | // The first argument is typically a *testing.T value. | |
| 14 | + | func NewMockRuntimeConfigProvider(t interface { | |
| 15 | + | mock.TestingT | |
| 16 | + | Cleanup(func()) | |
| 17 | + | }) *MockRuntimeConfigProvider { | |
| 18 | + | mock := &MockRuntimeConfigProvider{} | |
| 19 | + | mock.Mock.Test(t) | |
| 20 | + | ||
| 21 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |
| 22 | + | ||
| 23 | + | return mock | |
| 24 | + | } | |
| 25 | + | ||
| 26 | + | // MockRuntimeConfigProvider is an autogenerated mock type for the RuntimeConfigProvider type | |
| 27 | + | type MockRuntimeConfigProvider struct { | |
| 28 | + | mock.Mock | |
| 29 | + | } | |
| 30 | + | ||
| 31 | + | type MockRuntimeConfigProvider_Expecter struct { | |
| 32 | + | mock *mock.Mock | |
| 33 | + | } | |
| 34 | + | ||
| 35 | + | func (_m *MockRuntimeConfigProvider) EXPECT() *MockRuntimeConfigProvider_Expecter { | |
| 36 | + | return &MockRuntimeConfigProvider_Expecter{mock: &_m.Mock} | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // Current provides a mock function for the type MockRuntimeConfigProvider | |
| 40 | + | func (_mock *MockRuntimeConfigProvider) Current() port.RuntimeConfigSnapshot { | |
| 41 | + | ret := _mock.Called() | |
| 42 | + | ||
| 43 | + | if len(ret) == 0 { | |
| 44 | + | panic("no return value specified for Current") | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | var r0 port.RuntimeConfigSnapshot | |
| 48 | + | if returnFunc, ok := ret.Get(0).(func() port.RuntimeConfigSnapshot); ok { | |
| 49 | + | r0 = returnFunc() | |
| 50 | + | } else { | |
| 51 | + | r0 = ret.Get(0).(port.RuntimeConfigSnapshot) | |
| 52 | + | } | |
| 53 | + | return r0 | |
| 54 | + | } | |
| 55 | + | ||
| 56 | + | // MockRuntimeConfigProvider_Current_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Current' | |
| 57 | + | type MockRuntimeConfigProvider_Current_Call struct { | |
| 58 | + | *mock.Call | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | // Current is a helper method to define mock.On call | |
| 62 | + | func (_e *MockRuntimeConfigProvider_Expecter) Current() *MockRuntimeConfigProvider_Current_Call { | |
| 63 | + | return &MockRuntimeConfigProvider_Current_Call{Call: _e.mock.On("Current")} | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | func (_c *MockRuntimeConfigProvider_Current_Call) Run(run func()) *MockRuntimeConfigProvider_Current_Call { | |
| 67 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 68 | + | run() | |
| 69 | + | }) | |
| 70 | + | return _c | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | func (_c *MockRuntimeConfigProvider_Current_Call) Return(runtimeConfigSnapshot port.RuntimeConfigSnapshot) *MockRuntimeConfigProvider_Current_Call { | |
| 74 | + | _c.Call.Return(runtimeConfigSnapshot) | |
| 75 | + | return _c | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | func (_c *MockRuntimeConfigProvider_Current_Call) RunAndReturn(run func() port.RuntimeConfigSnapshot) *MockRuntimeConfigProvider_Current_Call { | |
| 79 | + | _c.Call.Return(run) | |
| 80 | + | return _c | |
| 81 | + | } | |
| 82 | + | ||
| 83 | + | // OnChange provides a mock function for the type MockRuntimeConfigProvider | |
| 84 | + | func (_mock *MockRuntimeConfigProvider) OnChange(fn func(port.RuntimeConfigSnapshot)) { | |
| 85 | + | _mock.Called(fn) | |
| 86 | + | return | |
| 87 | + | } | |
| 88 | + | ||
| 89 | + | // MockRuntimeConfigProvider_OnChange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnChange' | |
| 90 | + | type MockRuntimeConfigProvider_OnChange_Call struct { | |
| 91 | + | *mock.Call | |
| 92 | + | } | |
| 93 | + | ||
| 94 | + | // OnChange is a helper method to define mock.On call | |
| 95 | + | // - fn func(port.RuntimeConfigSnapshot) | |
| 96 | + | func (_e *MockRuntimeConfigProvider_Expecter) OnChange(fn any) *MockRuntimeConfigProvider_OnChange_Call { | |
| 97 | + | return &MockRuntimeConfigProvider_OnChange_Call{Call: _e.mock.On("OnChange", fn)} | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | func (_c *MockRuntimeConfigProvider_OnChange_Call) Run(run func(fn func(port.RuntimeConfigSnapshot))) *MockRuntimeConfigProvider_OnChange_Call { | |
| 101 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 102 | + | var arg0 func(port.RuntimeConfigSnapshot) | |
| 103 | + | if args[0] != nil { | |
| 104 | + | arg0 = args[0].(func(port.RuntimeConfigSnapshot)) | |
| 105 | + | } | |
| 106 | + | run( | |
| 107 | + | arg0, | |
| 108 | + | ) | |
| 109 | + | }) | |
| 110 | + | return _c | |
| 111 | + | } | |
| 112 | + | ||
| 113 | + | func (_c *MockRuntimeConfigProvider_OnChange_Call) Return() *MockRuntimeConfigProvider_OnChange_Call { | |
| 114 | + | _c.Call.Return() | |
| 115 | + | return _c | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | func (_c *MockRuntimeConfigProvider_OnChange_Call) RunAndReturn(run func(fn func(port.RuntimeConfigSnapshot))) *MockRuntimeConfigProvider_OnChange_Call { | |
| 119 | + | _c.Run(run) | |
| 120 | + | return _c | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | // Watch provides a mock function for the type MockRuntimeConfigProvider | |
| 124 | + | func (_mock *MockRuntimeConfigProvider) Watch() error { | |
| 125 | + | ret := _mock.Called() | |
| 126 | + | ||
| 127 | + | if len(ret) == 0 { | |
| 128 | + | panic("no return value specified for Watch") | |
| 129 | + | } | |
| 130 | + | ||
| 131 | + | var r0 error | |
| 132 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |
| 133 | + | r0 = returnFunc() | |
| 134 | + | } else { | |
| 135 | + | r0 = ret.Error(0) | |
| 136 | + | } | |
| 137 | + | return r0 | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | // MockRuntimeConfigProvider_Watch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Watch' | |
| 141 | + | type MockRuntimeConfigProvider_Watch_Call struct { | |
| 142 | + | *mock.Call | |
| 143 | + | } | |
| 144 | + | ||
| 145 | + | // Watch is a helper method to define mock.On call | |
| 146 | + | func (_e *MockRuntimeConfigProvider_Expecter) Watch() *MockRuntimeConfigProvider_Watch_Call { | |
| 147 | + | return &MockRuntimeConfigProvider_Watch_Call{Call: _e.mock.On("Watch")} | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | func (_c *MockRuntimeConfigProvider_Watch_Call) Run(run func()) *MockRuntimeConfigProvider_Watch_Call { | |
| 151 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 152 | + | run() | |
| 153 | + | }) | |
| 154 | + | return _c | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | func (_c *MockRuntimeConfigProvider_Watch_Call) Return(err error) *MockRuntimeConfigProvider_Watch_Call { | |
| 158 | + | _c.Call.Return(err) | |
| 159 | + | return _c | |
| 160 | + | } | |
| 161 | + | ||
| 162 | + | func (_c *MockRuntimeConfigProvider_Watch_Call) RunAndReturn(run func() error) *MockRuntimeConfigProvider_Watch_Call { | |
| 163 | + | _c.Call.Return(run) | |
| 164 | + | return _c | |
| 165 | + | } | |
| @@ -0,0 +1,165 @@ | |||||
| 1 | + | // Code generated by mockery; DO NOT EDIT. | |||
| 2 | + | // github.com/vektra/mockery | |||
| 3 | + | // template: testify | |||
| 4 | + | ||||
| 5 | + | package mocks | |||
| 6 | + | ||||
| 7 | + | import ( | |||
| 8 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 9 | + | mock "github.com/stretchr/testify/mock" | |||
| 10 | + | ) | |||
| 11 | + | ||||
| 12 | + | // NewMockRuntimeConfigProvider creates a new instance of MockRuntimeConfigProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. | |||
| 13 | + | // The first argument is typically a *testing.T value. | |||
| 14 | + | func NewMockRuntimeConfigProvider(t interface { | |||
| 15 | + | mock.TestingT | |||
| 16 | + | Cleanup(func()) | |||
| 17 | + | }) *MockRuntimeConfigProvider { | |||
| 18 | + | mock := &MockRuntimeConfigProvider{} | |||
| 19 | + | mock.Mock.Test(t) | |||
| 20 | + | ||||
| 21 | + | t.Cleanup(func() { mock.AssertExpectations(t) }) | |||
| 22 | + | ||||
| 23 | + | return mock | |||
| 24 | + | } | |||
| 25 | + | ||||
| 26 | + | // MockRuntimeConfigProvider is an autogenerated mock type for the RuntimeConfigProvider type | |||
| 27 | + | type MockRuntimeConfigProvider struct { | |||
| 28 | + | mock.Mock | |||
| 29 | + | } | |||
| 30 | + | ||||
| 31 | + | type MockRuntimeConfigProvider_Expecter struct { | |||
| 32 | + | mock *mock.Mock | |||
| 33 | + | } | |||
| 34 | + | ||||
| 35 | + | func (_m *MockRuntimeConfigProvider) EXPECT() *MockRuntimeConfigProvider_Expecter { | |||
| 36 | + | return &MockRuntimeConfigProvider_Expecter{mock: &_m.Mock} | |||
| 37 | + | } | |||
| 38 | + | ||||
| 39 | + | // Current provides a mock function for the type MockRuntimeConfigProvider | |||
| 40 | + | func (_mock *MockRuntimeConfigProvider) Current() port.RuntimeConfigSnapshot { | |||
| 41 | + | ret := _mock.Called() | |||
| 42 | + | ||||
| 43 | + | if len(ret) == 0 { | |||
| 44 | + | panic("no return value specified for Current") | |||
| 45 | + | } | |||
| 46 | + | ||||
| 47 | + | var r0 port.RuntimeConfigSnapshot | |||
| 48 | + | if returnFunc, ok := ret.Get(0).(func() port.RuntimeConfigSnapshot); ok { | |||
| 49 | + | r0 = returnFunc() | |||
| 50 | + | } else { | |||
| 51 | + | r0 = ret.Get(0).(port.RuntimeConfigSnapshot) | |||
| 52 | + | } | |||
| 53 | + | return r0 | |||
| 54 | + | } | |||
| 55 | + | ||||
| 56 | + | // MockRuntimeConfigProvider_Current_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Current' | |||
| 57 | + | type MockRuntimeConfigProvider_Current_Call struct { | |||
| 58 | + | *mock.Call | |||
| 59 | + | } | |||
| 60 | + | ||||
| 61 | + | // Current is a helper method to define mock.On call | |||
| 62 | + | func (_e *MockRuntimeConfigProvider_Expecter) Current() *MockRuntimeConfigProvider_Current_Call { | |||
| 63 | + | return &MockRuntimeConfigProvider_Current_Call{Call: _e.mock.On("Current")} | |||
| 64 | + | } | |||
| 65 | + | ||||
| 66 | + | func (_c *MockRuntimeConfigProvider_Current_Call) Run(run func()) *MockRuntimeConfigProvider_Current_Call { | |||
| 67 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 68 | + | run() | |||
| 69 | + | }) | |||
| 70 | + | return _c | |||
| 71 | + | } | |||
| 72 | + | ||||
| 73 | + | func (_c *MockRuntimeConfigProvider_Current_Call) Return(runtimeConfigSnapshot port.RuntimeConfigSnapshot) *MockRuntimeConfigProvider_Current_Call { | |||
| 74 | + | _c.Call.Return(runtimeConfigSnapshot) | |||
| 75 | + | return _c | |||
| 76 | + | } | |||
| 77 | + | ||||
| 78 | + | func (_c *MockRuntimeConfigProvider_Current_Call) RunAndReturn(run func() port.RuntimeConfigSnapshot) *MockRuntimeConfigProvider_Current_Call { | |||
| 79 | + | _c.Call.Return(run) | |||
| 80 | + | return _c | |||
| 81 | + | } | |||
| 82 | + | ||||
| 83 | + | // OnChange provides a mock function for the type MockRuntimeConfigProvider | |||
| 84 | + | func (_mock *MockRuntimeConfigProvider) OnChange(fn func(port.RuntimeConfigSnapshot)) { | |||
| 85 | + | _mock.Called(fn) | |||
| 86 | + | return | |||
| 87 | + | } | |||
| 88 | + | ||||
| 89 | + | // MockRuntimeConfigProvider_OnChange_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'OnChange' | |||
| 90 | + | type MockRuntimeConfigProvider_OnChange_Call struct { | |||
| 91 | + | *mock.Call | |||
| 92 | + | } | |||
| 93 | + | ||||
| 94 | + | // OnChange is a helper method to define mock.On call | |||
| 95 | + | // - fn func(port.RuntimeConfigSnapshot) | |||
| 96 | + | func (_e *MockRuntimeConfigProvider_Expecter) OnChange(fn any) *MockRuntimeConfigProvider_OnChange_Call { | |||
| 97 | + | return &MockRuntimeConfigProvider_OnChange_Call{Call: _e.mock.On("OnChange", fn)} | |||
| 98 | + | } | |||
| 99 | + | ||||
| 100 | + | func (_c *MockRuntimeConfigProvider_OnChange_Call) Run(run func(fn func(port.RuntimeConfigSnapshot))) *MockRuntimeConfigProvider_OnChange_Call { | |||
| 101 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 102 | + | var arg0 func(port.RuntimeConfigSnapshot) | |||
| 103 | + | if args[0] != nil { | |||
| 104 | + | arg0 = args[0].(func(port.RuntimeConfigSnapshot)) | |||
| 105 | + | } | |||
| 106 | + | run( | |||
| 107 | + | arg0, | |||
| 108 | + | ) | |||
| 109 | + | }) | |||
| 110 | + | return _c | |||
| 111 | + | } | |||
| 112 | + | ||||
| 113 | + | func (_c *MockRuntimeConfigProvider_OnChange_Call) Return() *MockRuntimeConfigProvider_OnChange_Call { | |||
| 114 | + | _c.Call.Return() | |||
| 115 | + | return _c | |||
| 116 | + | } | |||
| 117 | + | ||||
| 118 | + | func (_c *MockRuntimeConfigProvider_OnChange_Call) RunAndReturn(run func(fn func(port.RuntimeConfigSnapshot))) *MockRuntimeConfigProvider_OnChange_Call { | |||
| 119 | + | _c.Run(run) | |||
| 120 | + | return _c | |||
| 121 | + | } | |||
| 122 | + | ||||
| 123 | + | // Watch provides a mock function for the type MockRuntimeConfigProvider | |||
| 124 | + | func (_mock *MockRuntimeConfigProvider) Watch() error { | |||
| 125 | + | ret := _mock.Called() | |||
| 126 | + | ||||
| 127 | + | if len(ret) == 0 { | |||
| 128 | + | panic("no return value specified for Watch") | |||
| 129 | + | } | |||
| 130 | + | ||||
| 131 | + | var r0 error | |||
| 132 | + | if returnFunc, ok := ret.Get(0).(func() error); ok { | |||
| 133 | + | r0 = returnFunc() | |||
| 134 | + | } else { | |||
| 135 | + | r0 = ret.Error(0) | |||
| 136 | + | } | |||
| 137 | + | return r0 | |||
| 138 | + | } | |||
| 139 | + | ||||
| 140 | + | // MockRuntimeConfigProvider_Watch_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Watch' | |||
| 141 | + | type MockRuntimeConfigProvider_Watch_Call struct { | |||
| 142 | + | *mock.Call | |||
| 143 | + | } | |||
| 144 | + | ||||
| 145 | + | // Watch is a helper method to define mock.On call | |||
| 146 | + | func (_e *MockRuntimeConfigProvider_Expecter) Watch() *MockRuntimeConfigProvider_Watch_Call { | |||
| 147 | + | return &MockRuntimeConfigProvider_Watch_Call{Call: _e.mock.On("Watch")} | |||
| 148 | + | } | |||
| 149 | + | ||||
| 150 | + | func (_c *MockRuntimeConfigProvider_Watch_Call) Run(run func()) *MockRuntimeConfigProvider_Watch_Call { | |||
| 151 | + | _c.Call.Run(func(args mock.Arguments) { | |||
| 152 | + | run() | |||
| 153 | + | }) | |||
| 154 | + | return _c | |||
| 155 | + | } | |||
| 156 | + | ||||
| 157 | + | func (_c *MockRuntimeConfigProvider_Watch_Call) Return(err error) *MockRuntimeConfigProvider_Watch_Call { | |||
| 158 | + | _c.Call.Return(err) | |||
| 159 | + | return _c | |||
| 160 | + | } | |||
| 161 | + | ||||
| 162 | + | func (_c *MockRuntimeConfigProvider_Watch_Call) RunAndReturn(run func() error) *MockRuntimeConfigProvider_Watch_Call { | |||
| 163 | + | _c.Call.Return(run) | |||
| 164 | + | return _c | |||
| 165 | + | } | |||
M internal/ui/app.go +53 -8
| @@ -78,6 +78,10 @@ type App struct { | |||
| 78 | 78 | gtkApp *gtk.Application | |
| 79 | 79 | mainWindow *window.MainWindow | |
| 80 | 80 | ||
| 81 | + | runtimeConfig port.RuntimeConfigSnapshot | |
| 82 | + | runtimeConfigLoaded bool | |
| 83 | + | popupBrowsingContexts entity.BrowsingContextConfig | |
| 84 | + | ||
| 81 | 85 | browserWindows map[string]*browserWindow | |
| 82 | 86 | browserWindowOrder []string // registration order of window IDs | |
| 83 | 87 | lastFocusedWindowID string | |
| @@ -181,16 +185,20 @@ func New(deps *Dependencies) (*App, error) { | |||
| 181 | 185 | return nil, err | |
| 182 | 186 | } | |
| 183 | 187 | ctx, cancel := context.WithCancelCause(deps.Ctx) | |
| 188 | + | initialRuntimeConfig := deps.RuntimeConfig.Current() | |
| 184 | 189 | ||
| 185 | 190 | app := &App{ | |
| 186 | - | deps: deps, | |
| 187 | - | tabs: entity.NewTabList(), | |
| 188 | - | tabsUC: deps.TabsUC, | |
| 189 | - | panesUC: deps.PanesUC, | |
| 190 | - | workspaceViews: make(map[entity.TabID]*component.WorkspaceView), | |
| 191 | - | windowForTab: make(map[entity.TabID]*browserWindow), | |
| 192 | - | floatingSessions: make(map[floatingSessionKey]*floatingWorkspaceSession), | |
| 193 | - | browserWindows: make(map[string]*browserWindow), | |
| 191 | + | deps: deps, | |
| 192 | + | runtimeConfig: initialRuntimeConfig, | |
| 193 | + | runtimeConfigLoaded: true, | |
| 194 | + | popupBrowsingContexts: initialRuntimeConfig.UI.Workspace.BrowsingContexts, | |
| 195 | + | tabs: entity.NewTabList(), | |
| 196 | + | tabsUC: deps.TabsUC, | |
| 197 | + | panesUC: deps.PanesUC, | |
| 198 | + | workspaceViews: make(map[entity.TabID]*component.WorkspaceView), | |
| 199 | + | windowForTab: make(map[entity.TabID]*browserWindow), | |
| 200 | + | floatingSessions: make(map[floatingSessionKey]*floatingWorkspaceSession), | |
| 201 | + | browserWindows: make(map[string]*browserWindow), | |
| 194 | 202 | dispatchOnMainThread: func(label string, fn func()) syncdispatch.SyncDispatchResult { | |
| 195 | 203 | if fn != nil { | |
| 196 | 204 | fn() | |
| @@ -255,6 +263,43 @@ func New(deps *Dependencies) (*App, error) { | |||
| 255 | 263 | return app, nil | |
| 256 | 264 | } | |
| 257 | 265 | ||
| 266 | + | func (a *App) runtimeConfigSnapshot() port.RuntimeConfigSnapshot { | |
| 267 | + | if a == nil { | |
| 268 | + | return port.RuntimeConfigSnapshot{} | |
| 269 | + | } | |
| 270 | + | if a.runtimeConfigLoaded { | |
| 271 | + | return a.runtimeConfig | |
| 272 | + | } | |
| 273 | + | if a.deps != nil && a.deps.RuntimeConfig != nil { | |
| 274 | + | return a.deps.RuntimeConfig.Current() | |
| 275 | + | } | |
| 276 | + | return port.RuntimeConfigSnapshot{} | |
| 277 | + | } | |
| 278 | + | ||
| 279 | + | func (a *App) updateRuntimeConfig(snapshot port.RuntimeConfigSnapshot) { | |
| 280 | + | if a != nil { | |
| 281 | + | a.runtimeConfig = snapshot | |
| 282 | + | a.runtimeConfigLoaded = true | |
| 283 | + | a.updatePopupRuntimeConfig(snapshot.UI.Workspace.BrowsingContexts) | |
| 284 | + | } | |
| 285 | + | } | |
| 286 | + | ||
| 287 | + | func (a *App) updatePopupRuntimeConfig(cfg entity.BrowsingContextConfig) { | |
| 288 | + | if a != nil { | |
| 289 | + | a.popupBrowsingContexts = cfg | |
| 290 | + | } | |
| 291 | + | } | |
| 292 | + | ||
| 293 | + | func (a *App) popupBrowsingContextConfig() *entity.BrowsingContextConfig { | |
| 294 | + | if a == nil { | |
| 295 | + | return nil | |
| 296 | + | } | |
| 297 | + | if !a.runtimeConfigLoaded { | |
| 298 | + | a.updatePopupRuntimeConfig(a.runtimeConfigSnapshot().UI.Workspace.BrowsingContexts) | |
| 299 | + | } | |
| 300 | + | return &a.popupBrowsingContexts | |
| 301 | + | } | |
| 302 | + | ||
| 258 | 303 | // Run starts the GTK application and blocks until it exits. | |
| 259 | 304 | // Returns the exit code. | |
| 260 | 305 | func (a *App) Run(ctx context.Context, args []string) int { | |
| @@ -78,6 +78,10 @@ type App struct { | |||||
| 78 | gtkApp *gtk.Application | 78 | gtkApp *gtk.Application | ||
| 79 | mainWindow *window.MainWindow | 79 | mainWindow *window.MainWindow | ||
| 80 | 80 | ||||
| 81 | + | runtimeConfig port.RuntimeConfigSnapshot | |||
| 82 | + | runtimeConfigLoaded bool | |||
| 83 | + | popupBrowsingContexts entity.BrowsingContextConfig | |||
| 84 | + | ||||
| 81 | browserWindows map[string]*browserWindow | 85 | browserWindows map[string]*browserWindow | ||
| 82 | browserWindowOrder []string // registration order of window IDs | 86 | browserWindowOrder []string // registration order of window IDs | ||
| 83 | lastFocusedWindowID string | 87 | lastFocusedWindowID string | ||
| @@ -181,16 +185,20 @@ func New(deps *Dependencies) (*App, error) { | |||||
| 181 | return nil, err | 185 | return nil, err | ||
| 182 | } | 186 | } | ||
| 183 | ctx, cancel := context.WithCancelCause(deps.Ctx) | 187 | ctx, cancel := context.WithCancelCause(deps.Ctx) | ||
| 188 | + | initialRuntimeConfig := deps.RuntimeConfig.Current() | |||
| 184 | 189 | ||||
| 185 | app := &App{ | 190 | app := &App{ | ||
| 186 | - | deps: deps, | 191 | + | deps: deps, |
| 187 | - | tabs: entity.NewTabList(), | 192 | + | runtimeConfig: initialRuntimeConfig, |
| 188 | - | tabsUC: deps.TabsUC, | 193 | + | runtimeConfigLoaded: true, |
| 189 | - | panesUC: deps.PanesUC, | 194 | + | popupBrowsingContexts: initialRuntimeConfig.UI.Workspace.BrowsingContexts, |
| 190 | - | workspaceViews: make(map[entity.TabID]*component.WorkspaceView), | 195 | + | tabs: entity.NewTabList(), |
| 191 | - | windowForTab: make(map[entity.TabID]*browserWindow), | 196 | + | tabsUC: deps.TabsUC, |
| 192 | - | floatingSessions: make(map[floatingSessionKey]*floatingWorkspaceSession), | 197 | + | panesUC: deps.PanesUC, |
| 193 | - | browserWindows: make(map[string]*browserWindow), | 198 | + | workspaceViews: make(map[entity.TabID]*component.WorkspaceView), |
| 199 | + | windowForTab: make(map[entity.TabID]*browserWindow), | |||
| 200 | + | floatingSessions: make(map[floatingSessionKey]*floatingWorkspaceSession), | |||
| 201 | + | browserWindows: make(map[string]*browserWindow), | |||
| 194 | dispatchOnMainThread: func(label string, fn func()) syncdispatch.SyncDispatchResult { | 202 | dispatchOnMainThread: func(label string, fn func()) syncdispatch.SyncDispatchResult { | ||
| 195 | if fn != nil { | 203 | if fn != nil { | ||
| 196 | fn() | 204 | fn() | ||
| @@ -255,6 +263,43 @@ func New(deps *Dependencies) (*App, error) { | |||||
| 255 | return app, nil | 263 | return app, nil | ||
| 256 | } | 264 | } | ||
| 257 | 265 | ||||
| 266 | + | func (a *App) runtimeConfigSnapshot() port.RuntimeConfigSnapshot { | |||
| 267 | + | if a == nil { | |||
| 268 | + | return port.RuntimeConfigSnapshot{} | |||
| 269 | + | } | |||
| 270 | + | if a.runtimeConfigLoaded { | |||
| 271 | + | return a.runtimeConfig | |||
| 272 | + | } | |||
| 273 | + | if a.deps != nil && a.deps.RuntimeConfig != nil { | |||
| 274 | + | return a.deps.RuntimeConfig.Current() | |||
| 275 | + | } | |||
| 276 | + | return port.RuntimeConfigSnapshot{} | |||
| 277 | + | } | |||
| 278 | + | ||||
| 279 | + | func (a *App) updateRuntimeConfig(snapshot port.RuntimeConfigSnapshot) { | |||
| 280 | + | if a != nil { | |||
| 281 | + | a.runtimeConfig = snapshot | |||
| 282 | + | a.runtimeConfigLoaded = true | |||
| 283 | + | a.updatePopupRuntimeConfig(snapshot.UI.Workspace.BrowsingContexts) | |||
| 284 | + | } | |||
| 285 | + | } | |||
| 286 | + | ||||
| 287 | + | func (a *App) updatePopupRuntimeConfig(cfg entity.BrowsingContextConfig) { | |||
| 288 | + | if a != nil { | |||
| 289 | + | a.popupBrowsingContexts = cfg | |||
| 290 | + | } | |||
| 291 | + | } | |||
| 292 | + | ||||
| 293 | + | func (a *App) popupBrowsingContextConfig() *entity.BrowsingContextConfig { | |||
| 294 | + | if a == nil { | |||
| 295 | + | return nil | |||
| 296 | + | } | |||
| 297 | + | if !a.runtimeConfigLoaded { | |||
| 298 | + | a.updatePopupRuntimeConfig(a.runtimeConfigSnapshot().UI.Workspace.BrowsingContexts) | |||
| 299 | + | } | |||
| 300 | + | return &a.popupBrowsingContexts | |||
| 301 | + | } | |||
| 302 | + | ||||
| 258 | // Run starts the GTK application and blocks until it exits. | 303 | // Run starts the GTK application and blocks until it exits. | ||
| 259 | // Returns the exit code. | 304 | // Returns the exit code. | ||
| 260 | func (a *App) Run(ctx context.Context, args []string) int { | 305 | func (a *App) Run(ctx context.Context, args []string) int { | ||
M internal/ui/app_clipboard_test.go +18 -6
| @@ -23,11 +23,17 @@ func TestNew_DoesNotWireClipboardOrchestratorWhenClipboardMissing(t *testing.T) | |||
| 23 | 23 | }). | |
| 24 | 24 | Return(nil). | |
| 25 | 25 | Once() | |
| 26 | + | runtimeConfig := portmocks.NewMockRuntimeConfigProvider(t) | |
| 27 | + | runtimeConfig.EXPECT(). | |
| 28 | + | Current(). | |
| 29 | + | Return(port.RuntimeConfigSnapshot{}). | |
| 30 | + | Once() | |
| 26 | 31 | ||
| 27 | 32 | deps := &Dependencies{ | |
| 28 | - | Ctx: ctx, | |
| 29 | - | Config: &config.Config{}, | |
| 30 | - | Engine: engine, | |
| 33 | + | Ctx: ctx, | |
| 34 | + | Config: &config.Config{}, | |
| 35 | + | RuntimeConfig: runtimeConfig, | |
| 36 | + | Engine: engine, | |
| 31 | 37 | HandlerDeps: port.HandlerDeps{ | |
| 32 | 38 | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | |
| 33 | 39 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | |
| @@ -52,11 +58,17 @@ func TestNew_WiresClipboardOrchestratorWhenClipboardPresent(t *testing.T) { | |||
| 52 | 58 | }). | |
| 53 | 59 | Return(nil). | |
| 54 | 60 | Once() | |
| 61 | + | runtimeConfig := portmocks.NewMockRuntimeConfigProvider(t) | |
| 62 | + | runtimeConfig.EXPECT(). | |
| 63 | + | Current(). | |
| 64 | + | Return(port.RuntimeConfigSnapshot{}). | |
| 65 | + | Once() | |
| 55 | 66 | ||
| 56 | 67 | deps := &Dependencies{ | |
| 57 | - | Ctx: ctx, | |
| 58 | - | Config: &config.Config{}, | |
| 59 | - | Engine: engine, | |
| 68 | + | Ctx: ctx, | |
| 69 | + | Config: &config.Config{}, | |
| 70 | + | RuntimeConfig: runtimeConfig, | |
| 71 | + | Engine: engine, | |
| 60 | 72 | HandlerDeps: port.HandlerDeps{ | |
| 61 | 73 | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | |
| 62 | 74 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | |
| @@ -23,11 +23,17 @@ func TestNew_DoesNotWireClipboardOrchestratorWhenClipboardMissing(t *testing.T) | |||||
| 23 | }). | 23 | }). | ||
| 24 | Return(nil). | 24 | Return(nil). | ||
| 25 | Once() | 25 | Once() | ||
| 26 | + | runtimeConfig := portmocks.NewMockRuntimeConfigProvider(t) | |||
| 27 | + | runtimeConfig.EXPECT(). | |||
| 28 | + | Current(). | |||
| 29 | + | Return(port.RuntimeConfigSnapshot{}). | |||
| 30 | + | Once() | |||
| 26 | 31 | ||||
| 27 | deps := &Dependencies{ | 32 | deps := &Dependencies{ | ||
| 28 | - | Ctx: ctx, | 33 | + | Ctx: ctx, |
| 29 | - | Config: &config.Config{}, | 34 | + | Config: &config.Config{}, |
| 30 | - | Engine: engine, | 35 | + | RuntimeConfig: runtimeConfig, |
| 36 | + | Engine: engine, | |||
| 31 | HandlerDeps: port.HandlerDeps{ | 37 | HandlerDeps: port.HandlerDeps{ | ||
| 32 | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | 38 | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | ||
| 33 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | 39 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | ||
| @@ -52,11 +58,17 @@ func TestNew_WiresClipboardOrchestratorWhenClipboardPresent(t *testing.T) { | |||||
| 52 | }). | 58 | }). | ||
| 53 | Return(nil). | 59 | Return(nil). | ||
| 54 | Once() | 60 | Once() | ||
| 61 | + | runtimeConfig := portmocks.NewMockRuntimeConfigProvider(t) | |||
| 62 | + | runtimeConfig.EXPECT(). | |||
| 63 | + | Current(). | |||
| 64 | + | Return(port.RuntimeConfigSnapshot{}). | |||
| 65 | + | Once() | |||
| 55 | 66 | ||||
| 56 | deps := &Dependencies{ | 67 | deps := &Dependencies{ | ||
| 57 | - | Ctx: ctx, | 68 | + | Ctx: ctx, |
| 58 | - | Config: &config.Config{}, | 69 | + | Config: &config.Config{}, |
| 59 | - | Engine: engine, | 70 | + | RuntimeConfig: runtimeConfig, |
| 71 | + | Engine: engine, | |||
| 60 | HandlerDeps: port.HandlerDeps{ | 72 | HandlerDeps: port.HandlerDeps{ | ||
| 61 | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | 73 | SaveConfig: func(context.Context, dto.WebUIConfig) error { return nil }, | ||
| 62 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | 74 | SaveOmniboxInitialBehavior: func(context.Context, entity.OmniboxInitialBehavior) error { return nil }, | ||
M internal/ui/deps.go +4 -0
| @@ -20,6 +20,7 @@ type Dependencies struct { | |||
| 20 | 20 | // Core context and configuration | |
| 21 | 21 | Ctx context.Context | |
| 22 | 22 | Config *config.Config // TODO: replace with port interface in a later milestone | |
| 23 | + | RuntimeConfig port.RuntimeConfigProvider | |
| 23 | 24 | EngineSettingsPayload func() port.EngineSettingsPayload | |
| 24 | 25 | InitialURL string // URL to open on startup (optional) | |
| 25 | 26 | RestoreSessionID string // Session ID to restore on startup (optional) | |
| @@ -123,6 +124,9 @@ func (d *Dependencies) Validate() error { | |||
| 123 | 124 | if d.Config == nil { | |
| 124 | 125 | return ErrMissingDependency("Config") | |
| 125 | 126 | } | |
| 127 | + | if d.RuntimeConfig == nil { | |
| 128 | + | return ErrMissingDependency("RuntimeConfig") | |
| 129 | + | } | |
| 126 | 130 | if d.Engine == nil { | |
| 127 | 131 | return ErrMissingDependency("Engine") | |
| 128 | 132 | } | |
| @@ -20,6 +20,7 @@ type Dependencies struct { | |||||
| 20 | // Core context and configuration | 20 | // Core context and configuration | ||
| 21 | Ctx context.Context | 21 | Ctx context.Context | ||
| 22 | Config *config.Config // TODO: replace with port interface in a later milestone | 22 | Config *config.Config // TODO: replace with port interface in a later milestone | ||
| 23 | + | RuntimeConfig port.RuntimeConfigProvider | |||
| 23 | EngineSettingsPayload func() port.EngineSettingsPayload | 24 | EngineSettingsPayload func() port.EngineSettingsPayload | ||
| 24 | InitialURL string // URL to open on startup (optional) | 25 | InitialURL string // URL to open on startup (optional) | ||
| 25 | RestoreSessionID string // Session ID to restore on startup (optional) | 26 | RestoreSessionID string // Session ID to restore on startup (optional) | ||
| @@ -123,6 +124,9 @@ func (d *Dependencies) Validate() error { | |||||
| 123 | if d.Config == nil { | 124 | if d.Config == nil { | ||
| 124 | return ErrMissingDependency("Config") | 125 | return ErrMissingDependency("Config") | ||
| 125 | } | 126 | } | ||
| 127 | + | if d.RuntimeConfig == nil { | |||
| 128 | + | return ErrMissingDependency("RuntimeConfig") | |||
| 129 | + | } | |||
| 126 | if d.Engine == nil { | 130 | if d.Engine == nil { | ||
| 127 | return ErrMissingDependency("Engine") | 131 | return ErrMissingDependency("Engine") | ||
| 128 | } | 132 | } | ||
A internal/ui/runtime_config_test.go +68 -0
| @@ -0,0 +1,68 @@ | |||
| 1 | + | package ui | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "testing" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |
| 7 | + | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |
| 8 | + | "github.com/bnema/dumber/internal/domain/entity" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func TestAppRuntimeConfigReturnsProviderSnapshot(t *testing.T) { | |
| 12 | + | provider := portmocks.NewMockRuntimeConfigProvider(t) | |
| 13 | + | provider.EXPECT(). | |
| 14 | + | Current(). | |
| 15 | + | Return(port.RuntimeConfigSnapshot{ | |
| 16 | + | UI: port.RuntimeUIConfig{DefaultUIScale: 1.6}, | |
| 17 | + | }). | |
| 18 | + | Once() | |
| 19 | + | app := &App{deps: &Dependencies{RuntimeConfig: provider}} | |
| 20 | + | ||
| 21 | + | got := app.runtimeConfigSnapshot() | |
| 22 | + | ||
| 23 | + | if got.UI.DefaultUIScale != 1.6 { | |
| 24 | + | t.Fatalf("DefaultUIScale=%v, want 1.6", got.UI.DefaultUIScale) | |
| 25 | + | } | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | func TestAppUpdateRuntimeConfigStoresLatestSnapshot(t *testing.T) { | |
| 29 | + | app := &App{} | |
| 30 | + | app.updateRuntimeConfig(port.RuntimeConfigSnapshot{ | |
| 31 | + | UI: port.RuntimeUIConfig{SidebarWidth: 340}, | |
| 32 | + | }) | |
| 33 | + | ||
| 34 | + | got := app.runtimeConfigSnapshot() | |
| 35 | + | if got.UI.SidebarWidth != 340 { | |
| 36 | + | t.Fatalf("SidebarWidth=%d, want 340", got.UI.SidebarWidth) | |
| 37 | + | } | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | func TestAppUpdateRuntimeConfigRefreshesStablePopupConfigPointer(t *testing.T) { | |
| 41 | + | app := &App{} | |
| 42 | + | app.updateRuntimeConfig(port.RuntimeConfigSnapshot{ | |
| 43 | + | UI: port.RuntimeUIConfig{ | |
| 44 | + | Workspace: entity.WorkspaceConfig{ | |
| 45 | + | BrowsingContexts: entity.BrowsingContextConfig{OpenInNewPane: true}, | |
| 46 | + | }, | |
| 47 | + | }, | |
| 48 | + | }) | |
| 49 | + | ptr := app.popupBrowsingContextConfig() | |
| 50 | + | ||
| 51 | + | app.updateRuntimeConfig(port.RuntimeConfigSnapshot{ | |
| 52 | + | UI: port.RuntimeUIConfig{ | |
| 53 | + | Workspace: entity.WorkspaceConfig{ | |
| 54 | + | BrowsingContexts: entity.BrowsingContextConfig{OpenInNewPane: false, OAuthAutoClose: true}, | |
| 55 | + | }, | |
| 56 | + | }, | |
| 57 | + | }) | |
| 58 | + | ||
| 59 | + | if ptr != app.popupBrowsingContextConfig() { | |
| 60 | + | t.Fatal("popup config pointer must remain stable for content coordinator") | |
| 61 | + | } | |
| 62 | + | if ptr.OpenInNewPane { | |
| 63 | + | t.Fatal("popup config pointer did not observe updated OpenInNewPane") | |
| 64 | + | } | |
| 65 | + | if !ptr.OAuthAutoClose { | |
| 66 | + | t.Fatal("popup config pointer did not observe updated OAuthAutoClose") | |
| 67 | + | } | |
| 68 | + | } | |
| @@ -0,0 +1,68 @@ | |||||
| 1 | + | package ui | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "testing" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/dumber/internal/application/port" | |||
| 7 | + | portmocks "github.com/bnema/dumber/internal/application/port/mocks" | |||
| 8 | + | "github.com/bnema/dumber/internal/domain/entity" | |||
| 9 | + | ) | |||
| 10 | + | ||||
| 11 | + | func TestAppRuntimeConfigReturnsProviderSnapshot(t *testing.T) { | |||
| 12 | + | provider := portmocks.NewMockRuntimeConfigProvider(t) | |||
| 13 | + | provider.EXPECT(). | |||
| 14 | + | Current(). | |||
| 15 | + | Return(port.RuntimeConfigSnapshot{ | |||
| 16 | + | UI: port.RuntimeUIConfig{DefaultUIScale: 1.6}, | |||
| 17 | + | }). | |||
| 18 | + | Once() | |||
| 19 | + | app := &App{deps: &Dependencies{RuntimeConfig: provider}} | |||
| 20 | + | ||||
| 21 | + | got := app.runtimeConfigSnapshot() | |||
| 22 | + | ||||
| 23 | + | if got.UI.DefaultUIScale != 1.6 { | |||
| 24 | + | t.Fatalf("DefaultUIScale=%v, want 1.6", got.UI.DefaultUIScale) | |||
| 25 | + | } | |||
| 26 | + | } | |||
| 27 | + | ||||
| 28 | + | func TestAppUpdateRuntimeConfigStoresLatestSnapshot(t *testing.T) { | |||
| 29 | + | app := &App{} | |||
| 30 | + | app.updateRuntimeConfig(port.RuntimeConfigSnapshot{ | |||
| 31 | + | UI: port.RuntimeUIConfig{SidebarWidth: 340}, | |||
| 32 | + | }) | |||
| 33 | + | ||||
| 34 | + | got := app.runtimeConfigSnapshot() | |||
| 35 | + | if got.UI.SidebarWidth != 340 { | |||
| 36 | + | t.Fatalf("SidebarWidth=%d, want 340", got.UI.SidebarWidth) | |||
| 37 | + | } | |||
| 38 | + | } | |||
| 39 | + | ||||
| 40 | + | func TestAppUpdateRuntimeConfigRefreshesStablePopupConfigPointer(t *testing.T) { | |||
| 41 | + | app := &App{} | |||
| 42 | + | app.updateRuntimeConfig(port.RuntimeConfigSnapshot{ | |||
| 43 | + | UI: port.RuntimeUIConfig{ | |||
| 44 | + | Workspace: entity.WorkspaceConfig{ | |||
| 45 | + | BrowsingContexts: entity.BrowsingContextConfig{OpenInNewPane: true}, | |||
| 46 | + | }, | |||
| 47 | + | }, | |||
| 48 | + | }) | |||
| 49 | + | ptr := app.popupBrowsingContextConfig() | |||
| 50 | + | ||||
| 51 | + | app.updateRuntimeConfig(port.RuntimeConfigSnapshot{ | |||
| 52 | + | UI: port.RuntimeUIConfig{ | |||
| 53 | + | Workspace: entity.WorkspaceConfig{ | |||
| 54 | + | BrowsingContexts: entity.BrowsingContextConfig{OpenInNewPane: false, OAuthAutoClose: true}, | |||
| 55 | + | }, | |||
| 56 | + | }, | |||
| 57 | + | }) | |||
| 58 | + | ||||
| 59 | + | if ptr != app.popupBrowsingContextConfig() { | |||
| 60 | + | t.Fatal("popup config pointer must remain stable for content coordinator") | |||
| 61 | + | } | |||
| 62 | + | if ptr.OpenInNewPane { | |||
| 63 | + | t.Fatal("popup config pointer did not observe updated OpenInNewPane") | |||
| 64 | + | } | |||
| 65 | + | if !ptr.OAuthAutoClose { | |||
| 66 | + | t.Fatal("popup config pointer did not observe updated OAuthAutoClose") | |||
| 67 | + | } | |||
| 68 | + | } | |||