1426 lines
37 KiB
Go
1426 lines
37 KiB
Go
package display
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
core "dappco.re/go/core"
|
|
"forge.lthn.ai/core/gui/pkg/menu"
|
|
"forge.lthn.ai/core/gui/pkg/systray"
|
|
"forge.lthn.ai/core/gui/pkg/window"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// --- Test helpers ---
|
|
|
|
// newTestDisplayService creates a display service registered with Core for IPC testing.
|
|
func newTestDisplayService(t *testing.T) (*Service, *core.Core) {
|
|
t.Helper()
|
|
c := core.New(
|
|
core.WithService(Register(nil)),
|
|
core.WithServiceLock(),
|
|
)
|
|
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
return svc, c
|
|
}
|
|
|
|
// newTestConclave creates a full 4-service conclave for integration testing.
|
|
func newTestConclave(t *testing.T) *core.Core {
|
|
t.Helper()
|
|
c := core.New(
|
|
core.WithService(Register(nil)),
|
|
core.WithService(window.Register(window.NewMockPlatform())),
|
|
core.WithService(systray.Register(systray.NewMockPlatform())),
|
|
core.WithService(menu.Register(menu.NewMockPlatform())),
|
|
core.WithServiceLock(),
|
|
)
|
|
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
|
return c
|
|
}
|
|
|
|
func taskRun(c *core.Core, name string, task any) core.Result {
|
|
return c.Action(name).Run(context.Background(), core.NewOptions(
|
|
core.Option{Key: "task", Value: task},
|
|
))
|
|
}
|
|
|
|
// --- Tests ---
|
|
|
|
func TestNew_Good(t *testing.T) {
|
|
service, err := New()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, service)
|
|
}
|
|
|
|
func TestNew_Good_IndependentInstances(t *testing.T) {
|
|
service1, err1 := New()
|
|
service2, err2 := New()
|
|
assert.NoError(t, err1)
|
|
assert.NoError(t, err2)
|
|
assert.NotSame(t, service1, service2)
|
|
}
|
|
|
|
func TestRegister_Good(t *testing.T) {
|
|
factory := Register(nil) // nil wailsApp for testing
|
|
assert.NotNil(t, factory)
|
|
|
|
c := core.New(
|
|
core.WithService(factory),
|
|
core.WithServiceLock(),
|
|
)
|
|
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
|
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
assert.NotNil(t, svc)
|
|
}
|
|
|
|
func TestConfigQuery_Good(t *testing.T) {
|
|
svc, c := newTestDisplayService(t)
|
|
|
|
// Set window config
|
|
svc.configData["window"] = map[string]any{
|
|
"default_width": 1024,
|
|
}
|
|
|
|
r := c.QUERY(window.QueryConfig{})
|
|
require.True(t, r.OK)
|
|
cfg := r.Value.(map[string]any)
|
|
assert.Equal(t, 1024, cfg["default_width"])
|
|
}
|
|
|
|
func TestConfigQuery_Bad(t *testing.T) {
|
|
// No display service — window config query returns handled=false
|
|
c := core.New(core.WithServiceLock())
|
|
r := c.QUERY(window.QueryConfig{})
|
|
assert.False(t, r.OK)
|
|
}
|
|
|
|
func TestConfigTask_Good(t *testing.T) {
|
|
_, c := newTestDisplayService(t)
|
|
|
|
newCfg := map[string]any{"default_width": 800}
|
|
r := taskRun(c, "display.saveWindowConfig", window.TaskSaveConfig{Config: newCfg})
|
|
require.True(t, r.OK)
|
|
|
|
// Verify config was saved
|
|
r2 := c.QUERY(window.QueryConfig{})
|
|
cfg := r2.Value.(map[string]any)
|
|
assert.Equal(t, 800, cfg["default_width"])
|
|
}
|
|
|
|
func TestStorageTask_Bad(t *testing.T) {
|
|
_, c := newTestDisplayService(t)
|
|
|
|
r := c.Action("display.storage.set").Run(context.Background(), core.NewOptions(
|
|
core.Option{Key: "origin", Value: "core://settings"},
|
|
core.Option{Key: "bucket", Value: "localStorage"},
|
|
core.Option{Key: "key", Value: strings.Repeat("k", maxStorageKeyBytes+1)},
|
|
core.Option{Key: "value", Value: "dark"},
|
|
))
|
|
|
|
require.False(t, r.OK)
|
|
assert.Contains(t, r.Value.(error).Error(), "invalid storage entry")
|
|
}
|
|
|
|
func TestResolveScheme_StoreRoute_Good(t *testing.T) {
|
|
svc, _ := newTestDisplayService(t)
|
|
|
|
result := svc.ResolveScheme(context.Background(), "core://store?q=alpha")
|
|
require.True(t, result.OK)
|
|
|
|
payload, ok := result.Value.(map[string]any)
|
|
require.True(t, ok)
|
|
assert.Equal(t, "text/html", payload["content_type"])
|
|
|
|
body, ok := payload["body"].(string)
|
|
require.True(t, ok)
|
|
assert.Contains(t, body, "core://store")
|
|
assert.Contains(t, body, "storage scopes")
|
|
assert.Contains(t, body, "Search the in-memory storage scopes")
|
|
}
|
|
|
|
// --- Conclave integration tests ---
|
|
|
|
func TestServiceConclave_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
|
|
// Open a window via IPC
|
|
r := taskRun(c, "window.open", window.TaskOpenWindow{
|
|
Window: &window.Window{Name: "main"},
|
|
})
|
|
require.True(t, r.OK)
|
|
info := r.Value.(window.WindowInfo)
|
|
assert.Equal(t, "main", info.Name)
|
|
|
|
// Query window config from display
|
|
r2 := c.QUERY(window.QueryConfig{})
|
|
require.True(t, r2.OK)
|
|
assert.NotNil(t, r2.Value)
|
|
|
|
// Set app menu via IPC
|
|
r3 := taskRun(c, "menu.setAppMenu", menu.TaskSetAppMenu{Items: []menu.MenuItem{
|
|
{Label: "File"},
|
|
}})
|
|
require.True(t, r3.OK)
|
|
|
|
// Query app menu via IPC
|
|
r4 := c.QUERY(menu.QueryGetAppMenu{})
|
|
assert.True(t, r4.OK)
|
|
items := r4.Value.([]menu.MenuItem)
|
|
assert.Len(t, items, 1)
|
|
}
|
|
|
|
func TestServiceConclave_Bad(t *testing.T) {
|
|
// Sub-service starts without display — config QUERY returns handled=false
|
|
c := core.New(
|
|
core.WithService(window.Register(window.NewMockPlatform())),
|
|
core.WithServiceLock(),
|
|
)
|
|
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
|
|
|
r := c.QUERY(window.QueryConfig{})
|
|
assert.False(t, r.OK, "no display service means no config handler")
|
|
}
|
|
|
|
// --- IPC delegation tests (full conclave) ---
|
|
|
|
func TestOpenWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
t.Run("creates window with default options", func(t *testing.T) {
|
|
err := svc.OpenWindow()
|
|
assert.NoError(t, err)
|
|
|
|
// Verify via IPC query
|
|
infos := svc.ListWindowInfos()
|
|
assert.GreaterOrEqual(t, len(infos), 1)
|
|
})
|
|
|
|
t.Run("creates window with custom options", func(t *testing.T) {
|
|
err := svc.OpenWindow(
|
|
window.WithName("custom-window"),
|
|
window.WithTitle("Custom Title"),
|
|
window.WithSize(640, 480),
|
|
window.WithURL("/custom"),
|
|
)
|
|
assert.NoError(t, err)
|
|
|
|
r := c.QUERY(window.QueryWindowByName{Name: "custom-window"})
|
|
info := r.Value.(*window.WindowInfo)
|
|
assert.Equal(t, "custom-window", info.Name)
|
|
})
|
|
}
|
|
|
|
func TestGetWindowInfo_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
_ = svc.OpenWindow(
|
|
window.WithName("test-win"),
|
|
window.WithSize(800, 600),
|
|
)
|
|
|
|
// Modify position via IPC
|
|
taskRun(c, "window.setPosition", window.TaskSetPosition{Name: "test-win", X: 100, Y: 200})
|
|
|
|
info, err := svc.GetWindowInfo("test-win")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "test-win", info.Name)
|
|
assert.Equal(t, 100, info.X)
|
|
assert.Equal(t, 200, info.Y)
|
|
assert.Equal(t, 800, info.Width)
|
|
assert.Equal(t, 600, info.Height)
|
|
}
|
|
|
|
func TestGetWindowInfo_Bad(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
info, err := svc.GetWindowInfo("nonexistent")
|
|
// QueryWindowByName returns nil for nonexistent — handled=true, result=nil
|
|
assert.NoError(t, err)
|
|
assert.Nil(t, info)
|
|
}
|
|
|
|
func TestGetWindowInfo_BadType(t *testing.T) {
|
|
svc, c := newTestDisplayService(t)
|
|
c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result {
|
|
switch q.(type) {
|
|
case window.QueryWindowByName:
|
|
return core.Result{Value: "unexpected", OK: true}
|
|
default:
|
|
return core.Result{}
|
|
}
|
|
})
|
|
|
|
info, err := svc.GetWindowInfo("broken")
|
|
|
|
require.Error(t, err)
|
|
assert.Nil(t, info)
|
|
}
|
|
|
|
func TestListWindowInfos_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
_ = svc.OpenWindow(window.WithName("win-1"))
|
|
_ = svc.OpenWindow(window.WithName("win-2"))
|
|
|
|
infos := svc.ListWindowInfos()
|
|
assert.Len(t, infos, 2)
|
|
}
|
|
|
|
func TestSetWindowPosition_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("pos-win"))
|
|
|
|
err := svc.SetWindowPosition("pos-win", 300, 400)
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("pos-win")
|
|
assert.Equal(t, 300, info.X)
|
|
assert.Equal(t, 400, info.Y)
|
|
}
|
|
|
|
func TestSetWindowPosition_Bad(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
err := svc.SetWindowPosition("nonexistent", 0, 0)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSetWindowPosition_ActionFailure(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
c.Action("window.setPosition", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{OK: false}
|
|
})
|
|
|
|
err := svc.SetWindowPosition("pos-win", 300, 400)
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "window.setPosition")
|
|
}
|
|
|
|
func TestSetWindowSize_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("size-win"))
|
|
|
|
err := svc.SetWindowSize("size-win", 1024, 768)
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("size-win")
|
|
assert.Equal(t, 1024, info.Width)
|
|
assert.Equal(t, 768, info.Height)
|
|
}
|
|
|
|
func TestSetWindowBounds_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("bounds-win"))
|
|
|
|
err := svc.SetWindowBounds("bounds-win", 10, 20, 640, 480)
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("bounds-win")
|
|
assert.Equal(t, 10, info.X)
|
|
assert.Equal(t, 20, info.Y)
|
|
assert.Equal(t, 640, info.Width)
|
|
assert.Equal(t, 480, info.Height)
|
|
}
|
|
|
|
func TestSetWindowBounds_Bad(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
err := svc.SetWindowBounds("missing", 1, 2, 3, 4)
|
|
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSetWindowBounds_Ugly(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("bounds-win"))
|
|
|
|
err := svc.SetWindowBounds("bounds-win", -10, -20, 0, 1)
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("bounds-win")
|
|
assert.Equal(t, -10, info.X)
|
|
assert.Equal(t, -20, info.Y)
|
|
assert.Equal(t, 0, info.Width)
|
|
assert.Equal(t, 1, info.Height)
|
|
}
|
|
|
|
func TestMaximizeWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("max-win"))
|
|
|
|
err := svc.MaximizeWindow("max-win")
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("max-win")
|
|
assert.True(t, info.Maximized)
|
|
}
|
|
|
|
func TestRestoreWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("restore-win"))
|
|
_ = svc.MaximizeWindow("restore-win")
|
|
|
|
err := svc.RestoreWindow("restore-win")
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("restore-win")
|
|
assert.False(t, info.Maximized)
|
|
}
|
|
|
|
func TestFocusWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("focus-win"))
|
|
|
|
err := svc.FocusWindow("focus-win")
|
|
assert.NoError(t, err)
|
|
|
|
info, _ := svc.GetWindowInfo("focus-win")
|
|
assert.True(t, info.Focused)
|
|
}
|
|
|
|
func TestCloseWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("close-win"))
|
|
|
|
err := svc.CloseWindow("close-win")
|
|
assert.NoError(t, err)
|
|
|
|
// Window should be removed
|
|
info, _ := svc.GetWindowInfo("close-win")
|
|
assert.Nil(t, info)
|
|
}
|
|
|
|
func TestSetWindowVisibility_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("vis-win"))
|
|
|
|
err := svc.SetWindowVisibility("vis-win", false)
|
|
assert.NoError(t, err)
|
|
|
|
err = svc.SetWindowVisibility("vis-win", true)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestSetWindowAlwaysOnTop_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("ontop-win"))
|
|
|
|
err := svc.SetWindowAlwaysOnTop("ontop-win", true)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestSetWindowTitle_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("title-win"))
|
|
|
|
err := svc.SetWindowTitle("title-win", "New Title")
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestSetWindowFullscreen_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
windowSvc := core.MustServiceFor[*window.Service](c, "window")
|
|
_ = svc.OpenWindow(window.WithName("full-win"))
|
|
|
|
err := svc.SetWindowFullscreen("full-win", true)
|
|
|
|
require.NoError(t, err)
|
|
pw, ok := windowSvc.Manager().Get("full-win")
|
|
require.True(t, ok)
|
|
assert.True(t, pw.IsFullscreen())
|
|
}
|
|
|
|
func TestSetWindowFullscreen_Bad(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
err := svc.SetWindowFullscreen("missing", true)
|
|
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestLayoutBesideEditor_ActionFailure(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
c.Action("window.layoutBesideEditor", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{OK: false}
|
|
})
|
|
|
|
result, err := svc.LayoutBesideEditor("preview", "code", "right", 0.62)
|
|
|
|
require.Error(t, err)
|
|
assert.Zero(t, result)
|
|
assert.Contains(t, err.Error(), "window.layoutBesideEditor")
|
|
}
|
|
|
|
func TestSetWindowFullscreen_Ugly(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
windowSvc := core.MustServiceFor[*window.Service](c, "window")
|
|
_ = svc.OpenWindow(window.WithName("full-win"))
|
|
|
|
require.NoError(t, svc.SetWindowFullscreen("full-win", true))
|
|
err := svc.SetWindowFullscreen("full-win", false)
|
|
|
|
require.NoError(t, err)
|
|
pw, ok := windowSvc.Manager().Get("full-win")
|
|
require.True(t, ok)
|
|
assert.False(t, pw.IsFullscreen())
|
|
}
|
|
|
|
func TestGetWindowTitle_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("title-win"), window.WithTitle("Inspector"))
|
|
|
|
title, err := svc.GetWindowTitle("title-win")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Inspector", title)
|
|
}
|
|
|
|
func TestGetWindowTitle_Bad(t *testing.T) {
|
|
svc, _ := newTestDisplayService(t)
|
|
|
|
title, err := svc.GetWindowTitle("missing")
|
|
|
|
require.Error(t, err)
|
|
assert.Empty(t, title)
|
|
}
|
|
|
|
func TestGetWindowTitle_Ugly(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("title-win"), window.WithTitle("Line 1 <Line 2>\nTabbed"))
|
|
|
|
title, err := svc.GetWindowTitle("title-win")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Line 1 <Line 2>\nTabbed", title)
|
|
}
|
|
|
|
func TestMinimizeWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
windowSvc := core.MustServiceFor[*window.Service](c, "window")
|
|
_ = svc.OpenWindow(window.WithName("min-win"))
|
|
|
|
err := svc.MinimizeWindow("min-win")
|
|
|
|
require.NoError(t, err)
|
|
pw, ok := windowSvc.Manager().Get("min-win")
|
|
require.True(t, ok)
|
|
assert.True(t, pw.IsMinimised())
|
|
}
|
|
|
|
func TestMinimizeWindow_Bad(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
err := svc.MinimizeWindow("missing")
|
|
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMinimizeWindow_Ugly(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
windowSvc := core.MustServiceFor[*window.Service](c, "window")
|
|
_ = svc.OpenWindow(window.WithName("min-win"))
|
|
|
|
require.NoError(t, svc.MinimizeWindow("min-win"))
|
|
err := svc.MinimizeWindow("min-win")
|
|
|
|
require.NoError(t, err)
|
|
pw, ok := windowSvc.Manager().Get("min-win")
|
|
require.True(t, ok)
|
|
assert.True(t, pw.IsMinimised())
|
|
}
|
|
|
|
func TestHandleWSMessage_SetWindowOpacity_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("opacity-win"))
|
|
|
|
r := svc.handleWSMessage(WSMessage{
|
|
Action: "window:set-opacity",
|
|
Data: map[string]any{
|
|
"name": "opacity-win",
|
|
"opacity": 0.35,
|
|
},
|
|
})
|
|
require.True(t, r.OK)
|
|
|
|
info, err := svc.GetWindowInfo("opacity-win")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, info)
|
|
assert.InDelta(t, 0.35, info.Opacity, 0.0001)
|
|
}
|
|
|
|
func TestDisplay_requireStringField_Good(t *testing.T) {
|
|
value, err := requireStringField(map[string]any{"window": "main"}, "window")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "main", value)
|
|
}
|
|
|
|
func TestDisplay_requireStringField_Bad(t *testing.T) {
|
|
value, err := requireStringField(map[string]any{"window": ""}, "window")
|
|
|
|
require.Error(t, err)
|
|
assert.Empty(t, value)
|
|
}
|
|
|
|
func TestDisplay_requireStringField_Ugly(t *testing.T) {
|
|
value, err := requireStringField(map[string]any{"window": 42}, "window")
|
|
|
|
require.Error(t, err)
|
|
assert.Empty(t, value)
|
|
}
|
|
|
|
func TestDisplay_optionsFromMap_Good(t *testing.T) {
|
|
opts := optionsFromMap(map[string]any{"alpha": "one", "beta": 2})
|
|
|
|
require.Equal(t, 2, opts.Len())
|
|
got := map[string]any{}
|
|
for _, opt := range opts.Items() {
|
|
got[opt.Key] = opt.Value
|
|
}
|
|
assert.True(t, reflect.DeepEqual(map[string]any{"alpha": "one", "beta": 2}, got))
|
|
}
|
|
|
|
func TestDisplay_optionsFromMap_Bad(t *testing.T) {
|
|
opts := optionsFromMap(nil)
|
|
|
|
require.NotNil(t, opts)
|
|
assert.Equal(t, 0, opts.Len())
|
|
}
|
|
|
|
func TestDisplay_optionsFromMap_Ugly(t *testing.T) {
|
|
opts := wsOptions(map[string]any{"nested": map[string]any{"value": "x"}})
|
|
|
|
require.Equal(t, 1, opts.Len())
|
|
item := opts.Items()[0]
|
|
assert.Equal(t, "nested", item.Key)
|
|
assert.Equal(t, map[string]any{"value": "x"}, item.Value)
|
|
}
|
|
|
|
func TestDisplay_handleWSMessage_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("opacity-win"))
|
|
|
|
result := svc.handleWSMessage(WSMessage{
|
|
Action: "window:set-opacity",
|
|
Data: map[string]any{
|
|
"name": "opacity-win",
|
|
"opacity": 0.55,
|
|
},
|
|
})
|
|
require.True(t, result.OK)
|
|
|
|
info, err := svc.GetWindowInfo("opacity-win")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, info)
|
|
assert.InDelta(t, 0.55, info.Opacity, 0.0001)
|
|
}
|
|
|
|
func TestDisplay_handleWSMessage_Bad(t *testing.T) {
|
|
svc, _ := newTestDisplayService(t)
|
|
result := svc.handleWSMessage(WSMessage{Action: "unknown:action"})
|
|
|
|
require.False(t, result.OK)
|
|
assert.Contains(t, result.Value.(error).Error(), "unknown websocket action")
|
|
}
|
|
|
|
func TestDisplay_handleWSMessage_Ugly(t *testing.T) {
|
|
svc, _ := newTestDisplayService(t)
|
|
result := svc.handleWSMessage(WSMessage{
|
|
Action: "window:set-opacity",
|
|
Data: map[string]any{
|
|
"name": "main",
|
|
},
|
|
})
|
|
|
|
require.False(t, result.OK)
|
|
assert.Contains(t, result.Value.(error).Error(), "missing required field \"opacity\"")
|
|
}
|
|
|
|
func TestDisplay_handleWSMessage_LayoutCommands_Good(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
action string
|
|
msg WSMessage
|
|
check func(*testing.T, core.Options)
|
|
}{
|
|
{
|
|
name: "LayoutBesideEditor",
|
|
action: "window.layoutBesideEditor",
|
|
msg: WSMessage{
|
|
Action: "layout:beside-editor",
|
|
Data: map[string]any{
|
|
"name": "preview",
|
|
"editor": "code",
|
|
"side": "right",
|
|
"ratio": 0.62,
|
|
},
|
|
},
|
|
check: func(t *testing.T, opts core.Options) {
|
|
t.Helper()
|
|
task := opts.Get("task").Value.(window.TaskLayoutBesideEditor)
|
|
assert.Equal(t, "preview", task.Name)
|
|
assert.Equal(t, "code", task.Editor)
|
|
assert.Equal(t, "right", task.Side)
|
|
assert.InDelta(t, 0.62, task.Ratio, 0.0001)
|
|
},
|
|
},
|
|
{
|
|
name: "LayoutSuggest",
|
|
action: "window.layoutSuggest",
|
|
msg: WSMessage{
|
|
Action: "layout:suggest",
|
|
Data: map[string]any{
|
|
"screen_id": "screen-1",
|
|
"window_count": 3,
|
|
},
|
|
},
|
|
check: func(t *testing.T, opts core.Options) {
|
|
t.Helper()
|
|
task := opts.Get("task").Value.(window.TaskLayoutSuggest)
|
|
assert.Equal(t, "screen-1", task.ScreenID)
|
|
assert.Equal(t, 3, task.WindowCount)
|
|
},
|
|
},
|
|
{
|
|
name: "FindScreenSpace",
|
|
action: "window.findSpace",
|
|
msg: WSMessage{
|
|
Action: "screen:find-space",
|
|
Data: map[string]any{
|
|
"screen_id": "screen-1",
|
|
"width": 800,
|
|
"height": 600,
|
|
"padding": 24,
|
|
},
|
|
},
|
|
check: func(t *testing.T, opts core.Options) {
|
|
t.Helper()
|
|
task := opts.Get("task").Value.(window.TaskScreenFindSpace)
|
|
assert.Equal(t, "screen-1", task.ScreenID)
|
|
assert.Equal(t, 800, task.Width)
|
|
assert.Equal(t, 600, task.Height)
|
|
assert.Equal(t, 24, task.Padding)
|
|
},
|
|
},
|
|
{
|
|
name: "ArrangeWindowPair",
|
|
action: "window.arrangePair",
|
|
msg: WSMessage{
|
|
Action: "window:arrange-pair",
|
|
Data: map[string]any{
|
|
"primary": "editor",
|
|
"secondary": "preview",
|
|
"screen_id": "screen-1",
|
|
"ratio": 0.55,
|
|
},
|
|
},
|
|
check: func(t *testing.T, opts core.Options) {
|
|
t.Helper()
|
|
task := opts.Get("task").Value.(window.TaskWindowArrangePair)
|
|
assert.Equal(t, "editor", task.Primary)
|
|
assert.Equal(t, "preview", task.Secondary)
|
|
assert.Equal(t, "screen-1", task.ScreenID)
|
|
assert.InDelta(t, 0.55, task.Ratio, 0.0001)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
called := false
|
|
c.Action(tc.action, func(_ context.Context, opts core.Options) core.Result {
|
|
called = true
|
|
tc.check(t, opts)
|
|
return core.Result{OK: true}
|
|
})
|
|
|
|
result := svc.handleWSMessage(tc.msg)
|
|
require.True(t, result.OK)
|
|
assert.True(t, called)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDisplay_handleWSMessage_LayoutCommands_Bad(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
action string
|
|
msg WSMessage
|
|
field string
|
|
}{
|
|
{
|
|
name: "LayoutBesideEditor",
|
|
action: "window.layoutBesideEditor",
|
|
msg: WSMessage{
|
|
Action: "layout:beside-editor",
|
|
Data: map[string]any{
|
|
"name": "preview",
|
|
"editor": "code",
|
|
"side": "right",
|
|
},
|
|
},
|
|
field: "ratio",
|
|
},
|
|
{
|
|
name: "LayoutSuggest",
|
|
action: "window.layoutSuggest",
|
|
msg: WSMessage{
|
|
Action: "layout:suggest",
|
|
Data: map[string]any{
|
|
"screen_id": "screen-1",
|
|
},
|
|
},
|
|
field: "window_count",
|
|
},
|
|
{
|
|
name: "FindScreenSpace",
|
|
action: "window.findSpace",
|
|
msg: WSMessage{
|
|
Action: "screen:find-space",
|
|
Data: map[string]any{
|
|
"screen_id": "screen-1",
|
|
"width": 800,
|
|
"height": 600,
|
|
},
|
|
},
|
|
field: "padding",
|
|
},
|
|
{
|
|
name: "ArrangeWindowPair",
|
|
action: "window.arrangePair",
|
|
msg: WSMessage{
|
|
Action: "window:arrange-pair",
|
|
Data: map[string]any{
|
|
"primary": "editor",
|
|
"secondary": "preview",
|
|
"screen_id": "screen-1",
|
|
},
|
|
},
|
|
field: "ratio",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
called := false
|
|
c.Action(tc.action, func(_ context.Context, _ core.Options) core.Result {
|
|
called = true
|
|
return core.Result{OK: true}
|
|
})
|
|
|
|
result := svc.handleWSMessage(tc.msg)
|
|
|
|
require.False(t, result.OK)
|
|
assert.False(t, called)
|
|
assert.Contains(t, result.Value.(error).Error(), "missing required field \""+tc.field+"\"")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDisplay_handleWSMessage_LayoutCommands_Ugly(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
action string
|
|
msg WSMessage
|
|
field string
|
|
}{
|
|
{
|
|
name: "LayoutBesideEditor",
|
|
action: "window.layoutBesideEditor",
|
|
msg: WSMessage{
|
|
Action: "layout:beside-editor",
|
|
Data: map[string]any{
|
|
"name": "preview",
|
|
"editor": "code",
|
|
"side": "right",
|
|
"ratio": "0.62",
|
|
},
|
|
},
|
|
field: "ratio",
|
|
},
|
|
{
|
|
name: "LayoutSuggest",
|
|
action: "window.layoutSuggest",
|
|
msg: WSMessage{
|
|
Action: "layout:suggest",
|
|
Data: map[string]any{
|
|
"screen_id": "screen-1",
|
|
"window_count": 2.5,
|
|
},
|
|
},
|
|
field: "window_count",
|
|
},
|
|
{
|
|
name: "FindScreenSpace",
|
|
action: "window.findSpace",
|
|
msg: WSMessage{
|
|
Action: "screen:find-space",
|
|
Data: map[string]any{
|
|
"screen_id": "screen-1",
|
|
"width": "800",
|
|
"height": 600,
|
|
"padding": 24,
|
|
},
|
|
},
|
|
field: "width",
|
|
},
|
|
{
|
|
name: "ArrangeWindowPair",
|
|
action: "window.arrangePair",
|
|
msg: WSMessage{
|
|
Action: "window:arrange-pair",
|
|
Data: map[string]any{
|
|
"primary": "editor",
|
|
"secondary": "preview",
|
|
"screen_id": "screen-1",
|
|
"ratio": true,
|
|
},
|
|
},
|
|
field: "ratio",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
called := false
|
|
c.Action(tc.action, func(_ context.Context, _ core.Options) core.Result {
|
|
called = true
|
|
return core.Result{OK: true}
|
|
})
|
|
|
|
result := svc.handleWSMessage(tc.msg)
|
|
|
|
require.False(t, result.OK)
|
|
assert.False(t, called)
|
|
assert.Contains(t, result.Value.(error).Error(), "invalid required field \""+tc.field+"\"")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDisplay_handleTrayAction_Good(t *testing.T) {
|
|
platform := window.NewMockPlatform()
|
|
c := core.New(
|
|
core.WithService(Register(nil)),
|
|
core.WithService(window.Register(platform)),
|
|
core.WithService(systray.Register(systray.NewMockPlatform())),
|
|
core.WithService(menu.Register(menu.NewMockPlatform())),
|
|
core.WithServiceLock(),
|
|
)
|
|
require.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("one"))
|
|
_ = svc.OpenWindow(window.WithName("two"))
|
|
|
|
svc.handleTrayAction("open-desktop")
|
|
require.Len(t, platform.Windows, 2)
|
|
assert.True(t, platform.Windows[0].IsFocused())
|
|
assert.True(t, platform.Windows[1].IsFocused())
|
|
|
|
svc.handleTrayAction("close-desktop")
|
|
assert.False(t, platform.Windows[0].IsVisible())
|
|
assert.False(t, platform.Windows[1].IsVisible())
|
|
}
|
|
|
|
func TestGetFocusedWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("win-a"))
|
|
_ = svc.OpenWindow(window.WithName("win-b"))
|
|
_ = svc.FocusWindow("win-b")
|
|
|
|
focused := svc.GetFocusedWindow()
|
|
assert.Equal(t, "win-b", focused)
|
|
}
|
|
|
|
func TestGetFocusedWindow_Good_NoneSelected(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
_ = svc.OpenWindow(window.WithName("win-a"))
|
|
|
|
focused := svc.GetFocusedWindow()
|
|
assert.Equal(t, "", focused)
|
|
}
|
|
|
|
func TestCreateWindow_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
info, err := svc.CreateWindow(CreateWindowOptions{
|
|
Name: "new-win",
|
|
Title: "New Window",
|
|
URL: "/new",
|
|
Width: 600,
|
|
Height: 400,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "new-win", info.Name)
|
|
}
|
|
|
|
func TestCreateWindow_Bad(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
_, err := svc.CreateWindow(CreateWindowOptions{})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "window name is required")
|
|
}
|
|
|
|
func TestResetWindowState_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
err := svc.ResetWindowState()
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestGetSavedWindowStates_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
states := svc.GetSavedWindowStates()
|
|
assert.NotNil(t, states)
|
|
}
|
|
|
|
func TestDisplay_PublicCollections_AreNilSafe(t *testing.T) {
|
|
svc, _ := newTestDisplayService(t)
|
|
|
|
infos := svc.ListWindowInfos()
|
|
layouts := svc.ListLayouts()
|
|
states := svc.GetSavedWindowStates()
|
|
|
|
require.NotNil(t, infos)
|
|
require.NotNil(t, layouts)
|
|
require.NotNil(t, states)
|
|
assert.Empty(t, infos)
|
|
assert.Empty(t, layouts)
|
|
assert.Empty(t, states)
|
|
}
|
|
|
|
func TestDisplay_WindowService_NilSafe(t *testing.T) {
|
|
svc := &Service{}
|
|
|
|
assert.NotPanics(t, func() {
|
|
svc.ResetWindowState()
|
|
})
|
|
|
|
assert.NotPanics(t, func() {
|
|
states := svc.GetSavedWindowStates()
|
|
require.NotNil(t, states)
|
|
assert.Empty(t, states)
|
|
})
|
|
}
|
|
|
|
func TestHandleIPCEvents_WindowOpened_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
|
|
// Open a window — this should trigger ActionWindowOpened
|
|
// which HandleIPCEvents should convert to a WS event
|
|
r := taskRun(c, "window.open", window.TaskOpenWindow{
|
|
Window: &window.Window{Name: "test"},
|
|
})
|
|
require.True(t, r.OK)
|
|
info := r.Value.(window.WindowInfo)
|
|
assert.Equal(t, "test", info.Name)
|
|
}
|
|
|
|
func TestHandleListWorkspaces_Good(t *testing.T) {
|
|
c := newTestConclave(t)
|
|
svc := core.MustServiceFor[*Service](c, "display")
|
|
|
|
// handleListWorkspaces should not panic when workspace service is not available
|
|
assert.NotPanics(t, func() {
|
|
svc.handleListWorkspaces()
|
|
})
|
|
}
|
|
|
|
func TestWSEventManager_Good(t *testing.T) {
|
|
em := NewWSEventManager()
|
|
defer em.Close()
|
|
|
|
assert.NotNil(t, em)
|
|
assert.Equal(t, 0, em.ConnectedClients())
|
|
}
|
|
|
|
func TestService_OnShutdown_ClosesEventManager(t *testing.T) {
|
|
em := NewWSEventManager()
|
|
svc := &Service{events: em}
|
|
|
|
server := httptest.NewServer(http.HandlerFunc(em.HandleWebSocket))
|
|
t.Cleanup(server.Close)
|
|
|
|
wsURL := "ws" + strings.TrimPrefix(server.URL, "http")
|
|
conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
|
|
require.NoError(t, err)
|
|
defer func() { _ = conn.Close() }()
|
|
defer em.Close()
|
|
|
|
require.True(t, svc.OnShutdown(context.Background()).OK)
|
|
assert.Nil(t, svc.events)
|
|
|
|
require.Eventually(t, func() bool {
|
|
return em.ConnectedClients() == 0
|
|
}, 2*time.Second, 20*time.Millisecond)
|
|
|
|
_ = conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
|
|
_, _, err = conn.ReadMessage()
|
|
require.Error(t, err)
|
|
}
|
|
|
|
// --- Config file loading tests ---
|
|
|
|
func TestLoadConfig_Good(t *testing.T) {
|
|
// Create temp config file
|
|
dir := t.TempDir()
|
|
cfgPath := core.JoinPath(dir, ".core", "gui", "config.yaml")
|
|
require.NoError(t, os.MkdirAll(core.PathDir(cfgPath), 0o755))
|
|
require.NoError(t, os.WriteFile(cfgPath, []byte(`
|
|
window:
|
|
default_width: 1280
|
|
default_height: 720
|
|
systray:
|
|
tooltip: "Test App"
|
|
menu:
|
|
show_dev_tools: false
|
|
`), 0o644))
|
|
|
|
s, _ := New()
|
|
s.loadConfigFrom(cfgPath)
|
|
|
|
// Verify configData was populated from file
|
|
assert.Equal(t, 1280, s.configData["window"]["default_width"])
|
|
assert.Equal(t, "Test App", s.configData["systray"]["tooltip"])
|
|
assert.Equal(t, false, s.configData["menu"]["show_dev_tools"])
|
|
}
|
|
|
|
func TestLoadConfig_Bad_MissingFile(t *testing.T) {
|
|
s, _ := New()
|
|
s.loadConfigFrom(core.JoinPath(t.TempDir(), "nonexistent.yaml"))
|
|
|
|
// Should not panic, configData stays at empty defaults
|
|
assert.Empty(t, s.configData["window"])
|
|
assert.Empty(t, s.configData["systray"])
|
|
assert.Empty(t, s.configData["menu"])
|
|
}
|
|
|
|
func TestHandleConfigTask_Persists_Good(t *testing.T) {
|
|
dir := t.TempDir()
|
|
cfgPath := core.JoinPath(dir, "config.yaml")
|
|
|
|
s, _ := New()
|
|
s.loadConfigFrom(cfgPath) // Creates empty config (file doesn't exist yet)
|
|
|
|
// Simulate a TaskSaveConfig through the handler
|
|
c := core.New(
|
|
core.WithService(func(c *core.Core) core.Result {
|
|
s.ServiceRuntime = core.NewServiceRuntime[Options](c, Options{})
|
|
return core.Result{Value: s, OK: true}
|
|
}),
|
|
core.WithServiceLock(),
|
|
)
|
|
c.ServiceStartup(context.Background(), nil)
|
|
|
|
r := taskRun(c, "display.saveWindowConfig", window.TaskSaveConfig{
|
|
Config: map[string]any{"default_width": 1920},
|
|
})
|
|
require.True(t, r.OK)
|
|
|
|
// Verify file was written
|
|
data, err := os.ReadFile(cfgPath)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(data), "default_width")
|
|
}
|
|
|
|
func TestDisplay_LayoutSuggest_Good(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
var gotTask window.TaskLayoutSuggest
|
|
c.Action("window.layoutSuggest", func(_ context.Context, opts core.Options) core.Result {
|
|
gotTask = opts.Get("task").Value.(window.TaskLayoutSuggest)
|
|
return core.Result{
|
|
Value: window.LayoutSuggestion{
|
|
Mode: "coding",
|
|
Reason: "two-pane split",
|
|
ScreenID: "screen-1",
|
|
Width: 1280,
|
|
Height: 720,
|
|
},
|
|
OK: true,
|
|
}
|
|
})
|
|
|
|
got, err := svc.LayoutSuggest("screen-1", 2)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "coding", got.Mode)
|
|
assert.Equal(t, "two-pane split", got.Reason)
|
|
assert.Equal(t, "screen-1", got.ScreenID)
|
|
assert.Equal(t, 1280, got.Width)
|
|
assert.Equal(t, 720, got.Height)
|
|
assert.Equal(t, "screen-1", gotTask.ScreenID)
|
|
assert.Equal(t, 2, gotTask.WindowCount)
|
|
}
|
|
|
|
func TestDisplay_LayoutSuggest_Bad(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.layoutSuggest", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: assert.AnError, OK: false}
|
|
})
|
|
|
|
got, err := svc.LayoutSuggest("", 0)
|
|
|
|
require.Error(t, err)
|
|
assert.Equal(t, window.LayoutSuggestion{}, got)
|
|
assert.Equal(t, assert.AnError, err)
|
|
}
|
|
|
|
func TestDisplay_LayoutSuggest_Ugly(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.layoutSuggest", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: "unexpected", OK: true}
|
|
})
|
|
|
|
got, err := svc.LayoutSuggest("screen-1", 1)
|
|
|
|
require.Error(t, err)
|
|
assert.Equal(t, window.LayoutSuggestion{}, got)
|
|
assert.Contains(t, err.Error(), "unexpected result type")
|
|
}
|
|
|
|
func TestDisplay_GetLayout_Good(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result {
|
|
switch typed := q.(type) {
|
|
case window.QueryLayoutGet:
|
|
assert.Equal(t, "development", typed.Name)
|
|
return core.Result{
|
|
Value: &window.Layout{
|
|
Name: "development",
|
|
Windows: map[string]window.WindowState{
|
|
"editor": {},
|
|
"terminal": {},
|
|
},
|
|
CreatedAt: 1,
|
|
UpdatedAt: 2,
|
|
},
|
|
OK: true,
|
|
}
|
|
default:
|
|
return core.Result{}
|
|
}
|
|
})
|
|
|
|
got := svc.GetLayout("development")
|
|
|
|
require.NotNil(t, got)
|
|
assert.Equal(t, "development", got.Name)
|
|
assert.Len(t, got.Windows, 2)
|
|
assert.Equal(t, int64(1), got.CreatedAt)
|
|
assert.Equal(t, int64(2), got.UpdatedAt)
|
|
}
|
|
|
|
func TestDisplay_GetLayout_Bad(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result {
|
|
switch q.(type) {
|
|
case window.QueryLayoutGet:
|
|
return core.Result{Value: nil, OK: true}
|
|
default:
|
|
return core.Result{}
|
|
}
|
|
})
|
|
|
|
got := svc.GetLayout("missing")
|
|
|
|
assert.Nil(t, got)
|
|
}
|
|
|
|
func TestDisplay_GetLayout_Ugly(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result {
|
|
switch q.(type) {
|
|
case window.QueryLayoutGet:
|
|
return core.Result{Value: "unexpected", OK: true}
|
|
default:
|
|
return core.Result{}
|
|
}
|
|
})
|
|
|
|
got := svc.GetLayout("broken")
|
|
|
|
assert.Nil(t, got)
|
|
}
|
|
|
|
func TestDisplay_SaveLayout_Good(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
var gotTask window.TaskSaveLayout
|
|
c.Action("window.saveLayout", func(_ context.Context, opts core.Options) core.Result {
|
|
gotTask = opts.Get("task").Value.(window.TaskSaveLayout)
|
|
return core.Result{OK: true}
|
|
})
|
|
|
|
err := svc.SaveLayout("development")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "development", gotTask.Name)
|
|
}
|
|
|
|
func TestDisplay_SaveLayout_Bad(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.saveLayout", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: assert.AnError, OK: false}
|
|
})
|
|
|
|
err := svc.SaveLayout("development")
|
|
|
|
require.Error(t, err)
|
|
assert.Equal(t, assert.AnError, err)
|
|
}
|
|
|
|
func TestDisplay_SaveLayout_Ugly(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.saveLayout", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: "unexpected", OK: false}
|
|
})
|
|
|
|
err := svc.SaveLayout("")
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "window.saveLayout")
|
|
}
|
|
|
|
func TestDisplay_RestoreLayout_Good(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
var gotTask window.TaskRestoreLayout
|
|
c.Action("window.restoreLayout", func(_ context.Context, opts core.Options) core.Result {
|
|
gotTask = opts.Get("task").Value.(window.TaskRestoreLayout)
|
|
return core.Result{OK: true}
|
|
})
|
|
|
|
err := svc.RestoreLayout("development")
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "development", gotTask.Name)
|
|
}
|
|
|
|
func TestDisplay_RestoreLayout_Bad(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.restoreLayout", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: assert.AnError, OK: false}
|
|
})
|
|
|
|
err := svc.RestoreLayout("development")
|
|
|
|
require.Error(t, err)
|
|
assert.Equal(t, assert.AnError, err)
|
|
}
|
|
|
|
func TestDisplay_RestoreLayout_Ugly(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.restoreLayout", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: "unexpected", OK: false}
|
|
})
|
|
|
|
err := svc.RestoreLayout("")
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "window.restoreLayout")
|
|
}
|
|
|
|
func TestDisplay_SetWindowBackgroundColour_Good(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
var gotTask window.TaskSetBackgroundColour
|
|
c.Action("window.setBackgroundColour", func(_ context.Context, opts core.Options) core.Result {
|
|
gotTask = opts.Get("task").Value.(window.TaskSetBackgroundColour)
|
|
return core.Result{OK: true}
|
|
})
|
|
|
|
err := svc.SetWindowBackgroundColour("main", 1, 2, 3, 4)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "main", gotTask.Name)
|
|
assert.Equal(t, uint8(1), gotTask.Red)
|
|
assert.Equal(t, uint8(2), gotTask.Green)
|
|
assert.Equal(t, uint8(3), gotTask.Blue)
|
|
assert.Equal(t, uint8(4), gotTask.Alpha)
|
|
}
|
|
|
|
func TestDisplay_SetWindowBackgroundColour_Bad(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.setBackgroundColour", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: assert.AnError, OK: false}
|
|
})
|
|
|
|
err := svc.SetWindowBackgroundColour("main", 1, 2, 3, 4)
|
|
|
|
require.Error(t, err)
|
|
assert.Equal(t, assert.AnError, err)
|
|
}
|
|
|
|
func TestDisplay_SetWindowBackgroundColour_Ugly(t *testing.T) {
|
|
svc, c := newTestDisplayAPIService(t)
|
|
|
|
c.Action("window.setBackgroundColour", func(_ context.Context, _ core.Options) core.Result {
|
|
return core.Result{Value: "unexpected", OK: false}
|
|
})
|
|
|
|
err := svc.SetWindowBackgroundColour("", 0, 0, 0, 0)
|
|
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "window.setBackgroundColour")
|
|
}
|