refactor(ax): simplify display and webview constructors
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
762806d316
commit
3098fc471a
6 changed files with 87 additions and 48 deletions
|
|
@ -5,7 +5,7 @@ Complete API reference for the Display service (`pkg/display`).
|
|||
## Service Creation
|
||||
|
||||
```go
|
||||
func NewService() (*Service, error)
|
||||
func NewService() *Service
|
||||
```
|
||||
|
||||
## Window Management
|
||||
|
|
|
|||
|
|
@ -42,25 +42,23 @@ type Service struct {
|
|||
events *WebSocketEventManager
|
||||
}
|
||||
|
||||
// NewService constructs the display service.
|
||||
func NewService() (*Service, error) {
|
||||
// Display services start with an empty config cache.
|
||||
// svc := display.NewService()
|
||||
func NewService() *Service {
|
||||
return &Service{
|
||||
configData: map[string]map[string]any{
|
||||
"window": {},
|
||||
"systray": {},
|
||||
"menu": {},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Register creates a factory closure that captures the Wails app.
|
||||
// Pass nil for testing without a Wails runtime.
|
||||
// Build a Core factory without an option chain.
|
||||
// factory := display.Register(nil)
|
||||
func Register(wailsApp *application.App) func(*core.Core) (any, error) {
|
||||
return func(c *core.Core) (any, error) {
|
||||
s, err := NewService()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := NewService()
|
||||
s.ServiceRuntime = core.NewServiceRuntime[Options](c, Options{})
|
||||
s.wailsApp = wailsApp
|
||||
return s, nil
|
||||
|
|
|
|||
|
|
@ -48,16 +48,13 @@ func newTestConclave(t *testing.T) *core.Core {
|
|||
|
||||
func TestNewService(t *testing.T) {
|
||||
t.Run("creates service successfully", func(t *testing.T) {
|
||||
service, err := NewService()
|
||||
assert.NoError(t, err)
|
||||
service := NewService()
|
||||
assert.NotNil(t, service, "NewService() should return a non-nil service instance")
|
||||
})
|
||||
|
||||
t.Run("returns independent instances", func(t *testing.T) {
|
||||
service1, err1 := NewService()
|
||||
service2, err2 := NewService()
|
||||
assert.NoError(t, err1)
|
||||
assert.NoError(t, err2)
|
||||
service1 := NewService()
|
||||
service2 := NewService()
|
||||
assert.NotSame(t, service1, service2, "NewService() should return different instances")
|
||||
})
|
||||
}
|
||||
|
|
@ -500,7 +497,7 @@ menu:
|
|||
show_dev_tools: false
|
||||
`), 0o644))
|
||||
|
||||
s, _ := NewService()
|
||||
s := NewService()
|
||||
s.loadConfigFrom(cfgPath)
|
||||
|
||||
// Verify configData was populated from file
|
||||
|
|
@ -510,7 +507,7 @@ menu:
|
|||
}
|
||||
|
||||
func TestLoadConfig_Bad_MissingFile(t *testing.T) {
|
||||
s, _ := NewService()
|
||||
s := NewService()
|
||||
s.loadConfigFrom(filepath.Join(t.TempDir(), "nonexistent.yaml"))
|
||||
|
||||
// Should not panic, configData stays at empty defaults
|
||||
|
|
@ -523,7 +520,7 @@ func TestHandleConfigTask_Persists_Good(t *testing.T) {
|
|||
dir := t.TempDir()
|
||||
cfgPath := filepath.Join(dir, "config.yaml")
|
||||
|
||||
s, _ := NewService()
|
||||
s := NewService()
|
||||
s.loadConfigFrom(cfgPath) // Creates empty config (file doesn't exist yet)
|
||||
|
||||
// Simulate a TaskSaveConfig through the handler
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ The backend is written in Go and uses the `forge.lthn.ai/core/gui/pkg/display` p
|
|||
The `Service` struct is the main entry point for the display logic.
|
||||
|
||||
- **Initialization:**
|
||||
- `NewService() (*Service, error)`: Creates a new instance of the service.
|
||||
- `NewService() *Service`: Creates a new instance of the service.
|
||||
- `Register(wailsApp *application.App) func(*core.Core) (any, error)`: Captures the Wails app and registers the service with Core.
|
||||
- `OnStartup(ctx context.Context) error`: Loads config and registers IPC handlers.
|
||||
|
||||
|
|
|
|||
|
|
@ -54,28 +54,35 @@ type Service struct {
|
|||
watcherSetup func(conn connector, windowName string) // called after connection creation
|
||||
}
|
||||
|
||||
// Register creates a factory closure with the given options.
|
||||
func Register(optionFns ...func(*Options)) func(*core.Core) (any, error) {
|
||||
o := Options{
|
||||
DebugURL: "http://localhost:9222",
|
||||
Timeout: 30 * time.Second,
|
||||
ConsoleLimit: 1000,
|
||||
}
|
||||
for _, fn := range optionFns {
|
||||
fn(&o)
|
||||
}
|
||||
// Build a Core factory from one declarative Options value.
|
||||
// factory := webview.Register(webview.Options{DebugURL: "http://localhost:9333"})
|
||||
func Register(options Options) func(*core.Core) (any, error) {
|
||||
options = defaultOptions(options)
|
||||
return func(c *core.Core) (any, error) {
|
||||
svc := &Service{
|
||||
ServiceRuntime: core.NewServiceRuntime[Options](c, o),
|
||||
options: o,
|
||||
ServiceRuntime: core.NewServiceRuntime[Options](c, options),
|
||||
options: options,
|
||||
connections: make(map[string]connector),
|
||||
newConn: defaultNewConn(o),
|
||||
newConn: defaultNewConn(options),
|
||||
}
|
||||
svc.watcherSetup = svc.defaultWatcherSetup
|
||||
return svc, nil
|
||||
}
|
||||
}
|
||||
|
||||
func defaultOptions(options Options) Options {
|
||||
if options.DebugURL == "" {
|
||||
options.DebugURL = "http://localhost:9222"
|
||||
}
|
||||
if options.Timeout <= 0 {
|
||||
options.Timeout = 30 * time.Second
|
||||
}
|
||||
if options.ConsoleLimit <= 0 {
|
||||
options.ConsoleLimit = 1000
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
// defaultNewConn creates real go-webview connections.
|
||||
func defaultNewConn(options Options) func(string, string) (connector, error) {
|
||||
return func(debugURL, windowName string) (connector, error) {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package webview
|
|||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"forge.lthn.ai/core/go/pkg/core"
|
||||
"forge.lthn.ai/core/gui/pkg/window"
|
||||
|
|
@ -37,21 +38,41 @@ type mockConnector struct {
|
|||
consoleClearCalled bool
|
||||
}
|
||||
|
||||
func (m *mockConnector) Navigate(url string) error { m.lastNavURL = url; return nil }
|
||||
func (m *mockConnector) Click(sel string) error { m.lastClickSel = sel; return nil }
|
||||
func (m *mockConnector) Type(sel, text string) error { m.lastTypeSel = sel; m.lastTypeText = text; return nil }
|
||||
func (m *mockConnector) Hover(sel string) error { m.lastHoverSel = sel; return nil }
|
||||
func (m *mockConnector) Select(sel, val string) error { m.lastSelectSel = sel; m.lastSelectVal = val; return nil }
|
||||
func (m *mockConnector) Check(sel string, c bool) error { m.lastCheckSel = sel; m.lastCheckVal = c; return nil }
|
||||
func (m *mockConnector) Evaluate(s string) (any, error) { return m.evalResult, nil }
|
||||
func (m *mockConnector) Screenshot() ([]byte, error) { return m.screenshot, nil }
|
||||
func (m *mockConnector) GetURL() (string, error) { return m.url, nil }
|
||||
func (m *mockConnector) GetTitle() (string, error) { return m.title, nil }
|
||||
func (m *mockConnector) Navigate(url string) error { m.lastNavURL = url; return nil }
|
||||
func (m *mockConnector) Click(sel string) error { m.lastClickSel = sel; return nil }
|
||||
func (m *mockConnector) Type(sel, text string) error {
|
||||
m.lastTypeSel = sel
|
||||
m.lastTypeText = text
|
||||
return nil
|
||||
}
|
||||
func (m *mockConnector) Hover(sel string) error { m.lastHoverSel = sel; return nil }
|
||||
func (m *mockConnector) Select(sel, val string) error {
|
||||
m.lastSelectSel = sel
|
||||
m.lastSelectVal = val
|
||||
return nil
|
||||
}
|
||||
func (m *mockConnector) Check(sel string, c bool) error {
|
||||
m.lastCheckSel = sel
|
||||
m.lastCheckVal = c
|
||||
return nil
|
||||
}
|
||||
func (m *mockConnector) Evaluate(s string) (any, error) { return m.evalResult, nil }
|
||||
func (m *mockConnector) Screenshot() ([]byte, error) { return m.screenshot, nil }
|
||||
func (m *mockConnector) GetURL() (string, error) { return m.url, nil }
|
||||
func (m *mockConnector) GetTitle() (string, error) { return m.title, nil }
|
||||
func (m *mockConnector) GetHTML(sel string) (string, error) { return m.html, nil }
|
||||
func (m *mockConnector) ClearConsole() { m.consoleClearCalled = true }
|
||||
func (m *mockConnector) Close() error { m.closed = true; return nil }
|
||||
func (m *mockConnector) SetViewport(w, h int) error { m.lastViewportW = w; m.lastViewportH = h; return nil }
|
||||
func (m *mockConnector) UploadFile(sel string, p []string) error { m.lastUploadSel = sel; m.lastUploadPaths = p; return nil }
|
||||
func (m *mockConnector) ClearConsole() { m.consoleClearCalled = true }
|
||||
func (m *mockConnector) Close() error { m.closed = true; return nil }
|
||||
func (m *mockConnector) SetViewport(w, h int) error {
|
||||
m.lastViewportW = w
|
||||
m.lastViewportH = h
|
||||
return nil
|
||||
}
|
||||
func (m *mockConnector) UploadFile(sel string, p []string) error {
|
||||
m.lastUploadSel = sel
|
||||
m.lastUploadPaths = p
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConnector) QuerySelector(sel string) (*ElementInfo, error) {
|
||||
if len(m.elements) > 0 {
|
||||
|
|
@ -68,7 +89,7 @@ func (m *mockConnector) GetConsole() []ConsoleMessage { return m.console }
|
|||
|
||||
func newTestService(t *testing.T, mock *mockConnector) (*Service, *core.Core) {
|
||||
t.Helper()
|
||||
factory := Register()
|
||||
factory := Register(Options{})
|
||||
c, err := core.New(core.WithService(factory), core.WithServiceLock())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.ServiceStartup(context.Background(), nil))
|
||||
|
|
@ -83,6 +104,22 @@ func TestRegister_Good(t *testing.T) {
|
|||
assert.NotNil(t, svc)
|
||||
}
|
||||
|
||||
func TestRegister_Good_UsesOptions(t *testing.T) {
|
||||
options := Options{
|
||||
DebugURL: "http://localhost:9333",
|
||||
Timeout: 45 * time.Second,
|
||||
ConsoleLimit: 12,
|
||||
}
|
||||
|
||||
factory := Register(options)
|
||||
c, err := core.New(core.WithService(factory), core.WithServiceLock())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, c.ServiceStartup(context.Background(), nil))
|
||||
|
||||
svc := core.MustServiceFor[*Service](c, "webview")
|
||||
assert.Equal(t, options, svc.options)
|
||||
}
|
||||
|
||||
func TestQueryURL_Good(t *testing.T) {
|
||||
_, c := newTestService(t, &mockConnector{url: "https://example.com"})
|
||||
result, handled, err := c.QUERY(QueryURL{Window: "main"})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue