Add Wails stub bridge coverage
Some checks are pending
Security Scan / security (push) Waiting to run
Test / test (push) Waiting to run

This commit is contained in:
Snider 2026-04-17 18:58:23 +01:00
parent 115064490a
commit 345afb16bb
19 changed files with 1326 additions and 17 deletions

View file

@ -1,3 +1,11 @@
module github.com/wailsapp/wails/v3
go 1.26.0
require github.com/stretchr/testify v1.11.1
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

10
stubs/wails/go.sum Normal file
View file

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View file

@ -187,6 +187,7 @@ type WebviewWindowOptions struct {
// WebviewWindow is a lightweight, in-memory window implementation.
type WebviewWindow struct {
mu sync.RWMutex
id uint
opts WebviewWindowOptions
title string
url string
@ -204,9 +205,10 @@ type WebviewWindow struct {
eventHandlers map[events.WindowEventType][]func(*WindowEvent)
}
func newWebviewWindow(options WebviewWindowOptions) *WebviewWindow {
func newWebviewWindow(id uint, options WebviewWindowOptions) *WebviewWindow {
window := &WebviewWindow{
opts: options,
id: id,
title: options.Title,
url: options.URL,
html: options.HTML,
@ -358,7 +360,7 @@ func (w *WebviewWindow) OnWindowEvent(eventType events.WindowEventType, callback
// ID returns a stable numeric identifier for this window.
//
// id := w.ID()
func (w *WebviewWindow) ID() uint { return 0 }
func (w *WebviewWindow) ID() uint { return w.id }
// ClientID returns the client identifier (empty for native windows).
//
@ -751,12 +753,14 @@ func (w *WebviewWindow) selectAll()
// WindowManager manages in-memory windows.
type WindowManager struct {
mu sync.RWMutex
nextID uint
windows []*WebviewWindow
}
func (wm *WindowManager) NewWithOptions(options WebviewWindowOptions) *WebviewWindow {
window := newWebviewWindow(options)
wm.mu.Lock()
wm.nextID++
window := newWebviewWindow(wm.nextID, options)
wm.windows = append(wm.windows, window)
wm.mu.Unlock()
return window

View file

@ -0,0 +1,135 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type handlerFunc func()
func (f handlerFunc) ServeHTTP(_ ResponseWriter, _ *Request) {
f()
}
type fakeFS struct{}
func (fakeFS) Open(name string) (interface{ Read([]byte) (int, error) }, error) {
_ = name
return nil, nil
}
func TestApplicationOptions_ChainMiddleware_Good(t *testing.T) {
calls := make([]string, 0, 3)
base := handlerFunc(func() {
calls = append(calls, "handler")
})
first := func(next Handler) Handler {
return handlerFunc(func() {
calls = append(calls, "first")
next.ServeHTTP(nil, nil)
})
}
second := func(next Handler) Handler {
return handlerFunc(func() {
calls = append(calls, "second")
next.ServeHTTP(nil, nil)
})
}
ChainMiddleware(first, second)(base).ServeHTTP(nil, nil)
assert.Equal(t, []string{"first", "second", "handler"}, calls)
}
func TestApplicationOptions_ChainMiddleware_Bad(t *testing.T) {
calls := 0
base := handlerFunc(func() {
calls++
})
ChainMiddleware()(base).ServeHTTP(nil, nil)
assert.Equal(t, 1, calls)
}
func TestApplicationOptions_ChainMiddleware_Ugly(t *testing.T) {
calls := 0
base := handlerFunc(func() {
calls++
})
shortCircuit := func(next Handler) Handler {
_ = next
return handlerFunc(func() {})
}
ChainMiddleware(shortCircuit)(base).ServeHTTP(nil, nil)
assert.Zero(t, calls)
}
func TestApplicationOptions_NewRGB_Good(t *testing.T) {
got := NewRGB(0x11, 0x22, 0x33)
assert.Equal(t, RGBA{Red: 0x11, Green: 0x22, Blue: 0x33, Alpha: 255}, got)
}
func TestApplicationOptions_NewRGB_Bad(t *testing.T) {
got := NewRGB(0, 0, 0)
assert.Equal(t, RGBA{Alpha: 255}, got)
}
func TestApplicationOptions_NewRGB_Ugly(t *testing.T) {
got := NewRGB(255, 255, 255)
assert.Equal(t, RGBA{Red: 255, Green: 255, Blue: 255, Alpha: 255}, got)
}
func TestApplicationOptions_NewRGBPtr_Good(t *testing.T) {
got := NewRGBPtr(0x11, 0x22, 0x33)
require.NotNil(t, got)
assert.Equal(t, uint32(0x00332211), *got)
}
func TestApplicationOptions_NewRGBPtr_Bad(t *testing.T) {
got := NewRGBPtr(0, 0, 0)
require.NotNil(t, got)
assert.Zero(t, *got)
}
func TestApplicationOptions_NewRGBPtr_Ugly(t *testing.T) {
got := NewRGBPtr(255, 255, 255)
require.NotNil(t, got)
assert.Equal(t, uint32(0x00ffffff), *got)
}
func TestApplicationOptions_AssetFileServerFS_Good(t *testing.T) {
assert.Nil(t, AssetFileServerFS(nil))
}
func TestApplicationOptions_AssetFileServerFS_Bad(t *testing.T) {
assert.Nil(t, AssetFileServerFS(fakeFS{}))
}
func TestApplicationOptions_AssetFileServerFS_Ugly(t *testing.T) {
assert.Nil(t, BundledAssetFileServer(fakeFS{}))
}
func TestApplicationOptions_BundledAssetFileServer_Good(t *testing.T) {
assert.Nil(t, BundledAssetFileServer(nil))
}
func TestApplicationOptions_BundledAssetFileServer_Bad(t *testing.T) {
assert.Nil(t, BundledAssetFileServer(fakeFS{}))
}
func TestApplicationOptions_BundledAssetFileServer_Ugly(t *testing.T) {
assert.Nil(t, BundledAssetFileServer(fakeFS{}))
}

View file

@ -0,0 +1,251 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var _ Window = (*WebviewWindow)(nil)
var _ Window = (*BrowserWindow)(nil)
func TestApplication_NewRGBA_Good(t *testing.T) {
got := NewRGBA(1, 2, 3, 4)
assert.Equal(t, RGBA{Red: 1, Green: 2, Blue: 3, Alpha: 4}, got)
}
func TestApplication_NewRGBA_Bad(t *testing.T) {
got := NewRGBA(0, 0, 0, 0)
assert.Equal(t, RGBA{}, got)
}
func TestApplication_NewRGBA_Ugly(t *testing.T) {
got := NewRGBA(255, 255, 255, 255)
assert.Equal(t, RGBA{Red: 255, Green: 255, Blue: 255, Alpha: 255}, got)
}
func TestApplication_MenuRole_String_Good(t *testing.T) {
cases := []struct {
role MenuRole
want string
}{
{AppMenu, "app"},
{FileMenu, "file"},
{EditMenu, "edit"},
{ViewMenu, "view"},
{WindowMenu, "window"},
{HelpMenu, "help"},
}
for _, tc := range cases {
t.Run(tc.want, func(t *testing.T) {
assert.Equal(t, tc.want, tc.role.String())
})
}
}
func TestApplication_MenuRole_String_Bad(t *testing.T) {
assert.Equal(t, "unknown", MenuRole(-1).String())
}
func TestApplication_MenuRole_String_Ugly(t *testing.T) {
assert.Equal(t, "unknown", MenuRole(999).String())
}
func TestApplication_Menu_Good(t *testing.T) {
menu := NewMenu()
menuItem := menu.Add("Open")
menu.AddSeparator()
submenu := menu.AddSubmenu("More")
menu.AddRole(FileMenu)
require.NotNil(t, menuItem)
assert.Equal(t, "Open", menuItem.Label)
require.NotNil(t, submenu)
assert.Len(t, menu.Items, 4)
assert.Equal(t, "Open", menu.Items[0].Label)
assert.Equal(t, "---", menu.Items[1].Label)
assert.Equal(t, "More", menu.Items[2].Label)
assert.Equal(t, "file", menu.Items[3].Label)
}
func TestApplication_Menu_Bad(t *testing.T) {
menu := NewMenu()
assert.Empty(t, menu.Items)
}
func TestApplication_Menu_Ugly(t *testing.T) {
menu := NewMenu()
submenu := menu.AddSubmenu("Nested")
submenu.Add("Child")
assert.Len(t, menu.Items, 1)
assert.Len(t, submenu.Items, 1)
assert.Equal(t, "Nested", menu.Items[0].Label)
}
func TestApplication_SystemTray_Good(t *testing.T) {
tray := (&SystemTrayManager{}).New()
menu := NewMenu()
window := (&WindowManager{}).NewWithOptions(WebviewWindowOptions{Name: "tray"})
tray.SetIcon([]byte{1, 2, 3})
tray.SetTemplateIcon([]byte{4, 5, 6})
tray.SetTooltip("tooltip")
tray.SetLabel("label")
tray.SetMenu(menu)
tray.AttachWindow(window)
assert.Equal(t, []byte{1, 2, 3}, tray.icon)
assert.Equal(t, []byte{4, 5, 6}, tray.templateIcon)
assert.Equal(t, "tooltip", tray.tooltip)
assert.Equal(t, "label", tray.label)
assert.Same(t, menu, tray.menu)
assert.Same(t, window, tray.attachedWindow)
}
func TestApplication_SystemTray_Bad(t *testing.T) {
tray := (&SystemTrayManager{}).New()
assert.Empty(t, tray.icon)
assert.Empty(t, tray.templateIcon)
assert.Empty(t, tray.tooltip)
assert.Empty(t, tray.label)
assert.Nil(t, tray.menu)
assert.Nil(t, tray.attachedWindow)
}
func TestApplication_SystemTray_Ugly(t *testing.T) {
tray := (&SystemTrayManager{}).New()
icon := []byte{9, 8, 7}
tray.SetIcon(icon)
icon[0] = 0
assert.Equal(t, []byte{9, 8, 7}, tray.icon)
}
func TestApplication_WindowEventContext_Good(t *testing.T) {
ctx := &WindowEventContext{
droppedFiles: []string{"a", "b"},
dropDetails: &DropTargetDetails{ElementID: "drop"},
}
assert.Equal(t, []string{"a", "b"}, ctx.DroppedFiles())
require.NotNil(t, ctx.DropTargetDetails())
assert.Equal(t, "drop", ctx.DropTargetDetails().ElementID)
}
func TestApplication_WindowEventContext_Bad(t *testing.T) {
ctx := &WindowEventContext{}
assert.Empty(t, ctx.DroppedFiles())
assert.Nil(t, ctx.DropTargetDetails())
}
func TestApplication_WindowEventContext_Ugly(t *testing.T) {
ctx := &WindowEventContext{droppedFiles: []string{"x"}}
files := ctx.DroppedFiles()
files[0] = "mutated"
assert.Equal(t, []string{"x"}, ctx.DroppedFiles())
}
func TestApplication_WindowEvent_Good(t *testing.T) {
event := &WindowEvent{}
require.NotNil(t, event.Context())
require.Same(t, event.Context(), event.Context())
}
func TestApplication_WindowEvent_Bad(t *testing.T) {
event := &WindowEvent{ctx: &WindowEventContext{}}
assert.Same(t, event.ctx, event.Context())
}
func TestApplication_WindowEvent_Ugly(t *testing.T) {
event := &WindowEvent{}
event.Context().droppedFiles = []string{"file"}
assert.Equal(t, []string{"file"}, event.Context().DroppedFiles())
}
func TestApplication_WebviewWindow_Good(t *testing.T) {
manager := &WindowManager{}
window := manager.NewWithOptions(WebviewWindowOptions{
Name: "main",
Title: "Main",
URL: "https://example.com",
HTML: "<h1>Hello</h1>",
X: 10,
Y: 20,
Width: 800,
Height: 600,
Hidden: false,
AlwaysOnTop: true,
})
assert.Equal(t, uint(1), window.ID())
assert.Equal(t, "main", window.Name())
assert.Equal(t, "Main", window.Title())
assert.Equal(t, 10, window.Bounds().X)
assert.Equal(t, 20, window.Bounds().Y)
assert.Equal(t, 800, window.Width())
assert.Equal(t, 600, window.Height())
assert.True(t, window.IsVisible())
assert.False(t, window.IsFullscreen())
assert.False(t, window.IsMaximised())
assert.True(t, window.GetOpacity() > 0)
assert.Equal(t, 1.0, window.GetZoom())
assert.Same(t, window, window.SetAlwaysOnTop(false))
assert.Same(t, window, window.SetOpacity(0.5))
assert.Equal(t, 0.5, window.GetOpacity())
assert.Same(t, window, window.SetURL("https://example.org"))
assert.Same(t, window, window.SetHTML("<p>Hi</p>"))
assert.Same(t, window, window.SetSize(1024, 768))
assert.Equal(t, 1024, window.Width())
assert.Equal(t, 768, window.Height())
}
func TestApplication_WebviewWindow_Bad(t *testing.T) {
manager := &WindowManager{}
window := manager.NewWithOptions(WebviewWindowOptions{Hidden: true})
assert.False(t, window.IsVisible())
assert.Nil(t, manager.Get("missing"))
assert.Nil(t, manager.GetByID(99))
}
func TestApplication_WebviewWindow_Ugly(t *testing.T) {
manager := &WindowManager{}
first := manager.NewWithOptions(WebviewWindowOptions{Name: "first"})
second := manager.NewWithOptions(WebviewWindowOptions{Name: "second"})
assert.Same(t, first, manager.Get("first"))
assert.Same(t, second, manager.GetByID(2))
assert.Len(t, manager.GetAll(), 2)
}
func TestApplication_App_Good(t *testing.T) {
app := &App{}
assert.NotNil(t, app.NewMenu())
}
func TestApplication_App_Bad(t *testing.T) {
var app App
assert.Zero(t, app.Logger)
assert.Zero(t, app.Window)
assert.Zero(t, app.Menu)
}
func TestApplication_App_Ugly(t *testing.T) {
app := &App{}
app.Quit()
}

View file

@ -0,0 +1,61 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBrowserManager_OpenURL_Good(t *testing.T) {
manager := &BrowserManager{}
err := manager.OpenURL("https://example.com")
assert.NoError(t, err)
assert.Equal(t, "https://example.com", manager.LastURL)
}
func TestBrowserManager_OpenURL_Bad(t *testing.T) {
manager := &BrowserManager{}
err := manager.OpenURL("")
assert.NoError(t, err)
assert.Empty(t, manager.LastURL)
}
func TestBrowserManager_OpenURL_Ugly(t *testing.T) {
manager := &BrowserManager{}
err := manager.OpenURL("file:///tmp/%00")
assert.NoError(t, err)
assert.Equal(t, "file:///tmp/%00", manager.LastURL)
}
func TestBrowserManager_OpenFile_Good(t *testing.T) {
manager := &BrowserManager{}
err := manager.OpenFile("/tmp/report.txt")
assert.NoError(t, err)
assert.Equal(t, "/tmp/report.txt", manager.LastFile)
}
func TestBrowserManager_OpenFile_Bad(t *testing.T) {
manager := &BrowserManager{}
err := manager.OpenFile("")
assert.NoError(t, err)
assert.Empty(t, manager.LastFile)
}
func TestBrowserManager_OpenFile_Ugly(t *testing.T) {
manager := &BrowserManager{}
err := manager.OpenFile("/tmp/\x00report.txt")
assert.NoError(t, err)
assert.Equal(t, "/tmp/\x00report.txt", manager.LastFile)
}

View file

@ -0,0 +1,61 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBrowserWindow_NewBrowserWindow_Good(t *testing.T) {
window := NewBrowserWindow(7, "client-abc")
require.NotNil(t, window)
assert.Equal(t, uint(7), window.ID())
assert.Equal(t, "browser-7", window.Name())
assert.Equal(t, "client-abc", window.ClientID())
assert.True(t, window.IsVisible())
assert.False(t, window.IsFullscreen())
assert.False(t, window.IsMaximised())
assert.False(t, window.IsMinimised())
assert.False(t, window.IsFocused())
assert.False(t, window.IsIgnoreMouseEvents())
assert.False(t, window.Resizable())
assert.Equal(t, 1.0, window.GetZoom())
assert.Nil(t, window.NativeWindow())
assert.True(t, window.shouldUnconditionallyClose())
}
func TestBrowserWindow_NewBrowserWindow_Bad(t *testing.T) {
window := NewBrowserWindow(0, "")
assert.Equal(t, uint(0), window.ID())
assert.Equal(t, "browser-0", window.Name())
assert.Empty(t, window.ClientID())
assert.True(t, window.IsVisible())
}
func TestBrowserWindow_NewBrowserWindow_Ugly(t *testing.T) {
window := NewBrowserWindow(99, "client")
assert.Same(t, window, window.Show())
assert.Same(t, window, window.Hide())
assert.Same(t, window, window.Fullscreen())
assert.Same(t, window, window.Maximise())
assert.Same(t, window, window.Minimise())
assert.Same(t, window, window.SetAlwaysOnTop(true))
assert.Same(t, window, window.SetBackgroundColour(NewRGBA(1, 2, 3, 4)))
assert.Same(t, window, window.SetFrameless(true))
assert.Same(t, window, window.SetHTML("<b>hi</b>"))
assert.Same(t, window, window.SetMinSize(10, 20))
assert.Same(t, window, window.SetMaxSize(30, 40))
assert.Same(t, window, window.SetRelativePosition(1, 2))
assert.Same(t, window, window.SetResizable(true))
assert.Same(t, window, window.SetIgnoreMouseEvents(true))
assert.Same(t, window, window.SetSize(100, 200))
assert.Same(t, window, window.SetTitle("Title"))
assert.Same(t, window, window.SetURL("https://example.com"))
assert.Same(t, window, window.SetZoom(1.5))
assert.Same(t, window, window.ZoomReset())
assert.NoError(t, window.Print())
}

View file

@ -0,0 +1,73 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestClipboard_SetText_Good(t *testing.T) {
clipboard := &Clipboard{}
ok := clipboard.SetText("hello")
assert.True(t, ok)
text, present := clipboard.Text()
assert.True(t, present)
assert.Equal(t, "hello", text)
}
func TestClipboard_SetText_Bad(t *testing.T) {
clipboard := &Clipboard{}
ok := clipboard.SetText("")
assert.True(t, ok)
text, present := clipboard.Text()
assert.True(t, present)
assert.Empty(t, text)
}
func TestClipboard_SetText_Ugly(t *testing.T) {
clipboard := &Clipboard{}
ok := clipboard.SetText("line1\nline2")
assert.True(t, ok)
text, present := clipboard.Text()
require.True(t, present)
assert.Equal(t, "line1\nline2", text)
}
func TestClipboardManager_Text_Good(t *testing.T) {
manager := &ClipboardManager{}
ok := manager.SetText("copied")
assert.True(t, ok)
text, present := manager.Text()
assert.True(t, present)
assert.Equal(t, "copied", text)
}
func TestClipboardManager_Text_Bad(t *testing.T) {
manager := &ClipboardManager{}
text, present := manager.Text()
assert.False(t, present)
assert.Empty(t, text)
}
func TestClipboardManager_Text_Ugly(t *testing.T) {
manager := &ClipboardManager{}
raw := "zero\x00byte"
ok := manager.SetText(raw)
assert.True(t, ok)
text, present := manager.Text()
assert.True(t, present)
assert.Equal(t, raw, text)
}

View file

@ -0,0 +1,72 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestContextMenuManager_Add_Good(t *testing.T) {
manager := &ContextMenuManager{}
menu := manager.New()
menu.Add("Open")
manager.Add("files", menu)
got, ok := manager.Get("files")
require.True(t, ok)
require.Same(t, menu, got)
assert.Len(t, manager.GetAll(), 1)
}
func TestContextMenuManager_Add_Bad(t *testing.T) {
manager := &ContextMenuManager{}
manager.Add("empty", nil)
got, ok := manager.Get("empty")
assert.True(t, ok)
assert.Nil(t, got)
}
func TestContextMenuManager_Add_Ugly(t *testing.T) {
manager := &ContextMenuManager{}
first := manager.New()
second := manager.New()
manager.Add("dup", first)
manager.Add("dup", second)
got, ok := manager.Get("dup")
require.True(t, ok)
require.Same(t, second, got)
assert.Len(t, manager.GetAll(), 1)
}
func TestContextMenuManager_Remove_Good(t *testing.T) {
manager := &ContextMenuManager{}
menu := manager.New()
manager.Add("files", menu)
manager.Remove("files")
_, ok := manager.Get("files")
assert.False(t, ok)
assert.Empty(t, manager.GetAll())
}
func TestContextMenuManager_Remove_Bad(t *testing.T) {
manager := &ContextMenuManager{}
manager.Remove("missing")
assert.Empty(t, manager.GetAll())
}
func TestContextMenuManager_Remove_Ugly(t *testing.T) {
manager := &ContextMenuManager{}
manager.Remove("")
assert.Empty(t, manager.GetAll())
}

View file

@ -262,11 +262,11 @@ type MessageButton struct {
// dialog.SetTitle("Done").SetMessage("File saved successfully.")
// _ = dialog.Show()
type MessageDialog struct {
mu sync.RWMutex
dialogType DialogType
title string
message string
buttons []MessageButton
mu sync.RWMutex
dialogType DialogType
title string
message string
buttons []MessageButton
clickedButton string
}

View file

@ -0,0 +1,165 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDialog_OpenFileDialogStruct_Good(t *testing.T) {
dialog := newOpenFileDialog()
dialog.SetOptions(&OpenFileDialogOptions{
Title: "Pick",
Directory: "/tmp",
Filters: []FileFilter{{DisplayName: "Text", Pattern: "*.txt"}},
AllowsMultipleSelection: true,
CanChooseDirectories: true,
CanChooseFiles: false,
ShowHiddenFiles: true,
})
dialog.SetSelectedFiles([]string{"/tmp/a.txt", "/tmp/b.txt"})
got, err := dialog.PromptForSingleSelection()
require.NoError(t, err)
assert.Equal(t, "/tmp/a.txt", got)
paths, err := dialog.PromptForMultipleSelection()
require.NoError(t, err)
assert.Equal(t, []string{"/tmp/a.txt", "/tmp/b.txt"}, paths)
}
func TestDialog_OpenFileDialogStruct_Bad(t *testing.T) {
dialog := newOpenFileDialog()
dialog.SetOptions(nil)
got, err := dialog.PromptForSingleSelection()
require.NoError(t, err)
assert.Empty(t, got)
assert.True(t, dialog.canChooseFiles)
}
func TestDialog_OpenFileDialogStruct_Ugly(t *testing.T) {
dialog := newOpenFileDialog()
selected := []string{"/tmp/input.txt"}
dialog.SetSelectedFiles(selected)
selected[0] = "/tmp/mutated.txt"
paths, err := dialog.PromptForMultipleSelection()
require.NoError(t, err)
assert.Equal(t, []string{"/tmp/input.txt"}, paths)
}
func TestDialog_SaveFileDialogStruct_Good(t *testing.T) {
dialog := newSaveFileDialog()
dialog.SetOptions(&SaveFileDialogOptions{
Title: "Export",
Directory: "/tmp",
Filename: "report.csv",
Filters: []FileFilter{{DisplayName: "CSV", Pattern: "*.csv"}},
ShowHiddenFiles: true,
})
dialog.SetSelectedPath("/tmp/report.csv")
got, err := dialog.PromptForSingleSelection()
require.NoError(t, err)
assert.Equal(t, "/tmp/report.csv", got)
}
func TestDialog_SaveFileDialogStruct_Bad(t *testing.T) {
dialog := newSaveFileDialog()
dialog.SetOptions(nil)
got, err := dialog.PromptForSingleSelection()
require.NoError(t, err)
assert.Empty(t, got)
}
func TestDialog_SaveFileDialogStruct_Ugly(t *testing.T) {
dialog := newSaveFileDialog()
selected := "/tmp/a.csv"
dialog.SetSelectedPath(selected)
selected = "/tmp/b.csv"
got, err := dialog.PromptForSingleSelection()
require.NoError(t, err)
assert.Equal(t, "/tmp/a.csv", got)
}
func TestDialog_MessageDialog_Good(t *testing.T) {
dialog := newMessageDialog(QuestionDialogType)
dialog.SetTitle("Confirm").
SetMessage("Proceed?").
AddButton("Yes").
AddButton("No").
SetDefaultButton("Yes").
SetCancelButton("No")
dialog.SetButtonClickedForStub("Yes")
got, err := dialog.Show()
require.NoError(t, err)
assert.Equal(t, "Yes", got)
assert.Equal(t, QuestionDialogType, dialog.dialogType)
assert.Equal(t, "Confirm", dialog.title)
assert.Equal(t, "Proceed?", dialog.message)
require.Len(t, dialog.buttons, 2)
assert.True(t, dialog.buttons[0].IsDefault)
assert.True(t, dialog.buttons[1].IsCancel)
}
func TestDialog_MessageDialog_Bad(t *testing.T) {
dialog := newMessageDialog(InfoDialogType)
got, err := dialog.Show()
require.NoError(t, err)
assert.Empty(t, got)
assert.Equal(t, InfoDialogType, dialog.dialogType)
}
func TestDialog_MessageDialog_Ugly(t *testing.T) {
dialog := newMessageDialog(ErrorDialogType)
dialog.AddButton("Retry").AddButton("Retry")
dialog.SetDefaultButton("Retry")
dialog.SetCancelButton("Retry")
require.Len(t, dialog.buttons, 2)
assert.True(t, dialog.buttons[0].IsDefault)
assert.True(t, dialog.buttons[0].IsCancel)
assert.True(t, dialog.buttons[1].IsDefault)
assert.True(t, dialog.buttons[1].IsCancel)
}
func TestDialog_DialogManager_Good(t *testing.T) {
manager := &DialogManager{}
assert.Equal(t, InfoDialogType, manager.Info().dialogType)
assert.Equal(t, QuestionDialogType, manager.Question().dialogType)
assert.Equal(t, WarningDialogType, manager.Warning().dialogType)
assert.Equal(t, ErrorDialogType, manager.Error().dialogType)
assert.NotNil(t, manager.OpenFile())
assert.NotNil(t, manager.SaveFile())
}
func TestDialog_DialogManager_Bad(t *testing.T) {
manager := &DialogManager{}
assert.NotNil(t, manager.OpenFileWithOptions(nil))
assert.NotNil(t, manager.SaveFileWithOptions(nil))
}
func TestDialog_DialogManager_Ugly(t *testing.T) {
manager := &DialogManager{}
open := manager.OpenFileWithOptions(&OpenFileDialogOptions{AllowsMultipleSelection: true})
save := manager.SaveFileWithOptions(&SaveFileDialogOptions{Filename: "out.csv"})
assert.True(t, open.multipleAllowed)
assert.Equal(t, "out.csv", save.filename)
}

View file

@ -21,12 +21,12 @@ type EnvironmentInfo struct {
// manager.SetDarkMode(true)
// dark := manager.IsDarkMode() // true
type EnvironmentManager struct {
mu sync.RWMutex
darkMode bool
accentColour string
operatingSystem string
architecture string
debugMode bool
mu sync.RWMutex
darkMode bool
accentColour string
operatingSystem string
architecture string
debugMode bool
}
// SetDarkMode sets the dark mode state used by IsDarkMode.

View file

@ -0,0 +1,92 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestEnvironmentManager_IsDarkMode_Good(t *testing.T) {
manager := &EnvironmentManager{}
manager.SetDarkMode(true)
assert.True(t, manager.IsDarkMode())
}
func TestEnvironmentManager_IsDarkMode_Bad(t *testing.T) {
manager := &EnvironmentManager{}
assert.False(t, manager.IsDarkMode())
}
func TestEnvironmentManager_IsDarkMode_Ugly(t *testing.T) {
manager := &EnvironmentManager{}
manager.SetDarkMode(true)
manager.SetDarkMode(false)
assert.False(t, manager.IsDarkMode())
}
func TestEnvironmentManager_GetAccentColor_Good(t *testing.T) {
manager := &EnvironmentManager{}
manager.SetAccentColour("rgb(1,2,3)")
assert.Equal(t, "rgb(1,2,3)", manager.GetAccentColor())
}
func TestEnvironmentManager_GetAccentColor_Bad(t *testing.T) {
manager := &EnvironmentManager{}
assert.Equal(t, "rgb(0,122,255)", manager.GetAccentColor())
}
func TestEnvironmentManager_GetAccentColor_Ugly(t *testing.T) {
manager := &EnvironmentManager{}
manager.SetAccentColour("")
assert.Equal(t, "rgb(0,122,255)", manager.GetAccentColor())
}
func TestEnvironmentManager_Info_Good(t *testing.T) {
manager := &EnvironmentManager{}
manager.SetDarkMode(true)
manager.SetAccentColour("rgb(1,2,3)")
manager.operatingSystem = "linux"
manager.architecture = "amd64"
manager.debugMode = true
got := manager.Info()
assert.Equal(t, "linux", got.OS)
assert.Equal(t, "amd64", got.Arch)
assert.True(t, got.Debug)
assert.True(t, got.IsDarkMode)
assert.Equal(t, "rgb(1,2,3)", got.AccentColour)
}
func TestEnvironmentManager_Info_Bad(t *testing.T) {
manager := &EnvironmentManager{}
got := manager.Info()
assert.Empty(t, got.OS)
assert.Empty(t, got.Arch)
assert.False(t, got.Debug)
assert.False(t, got.IsDarkMode)
assert.Empty(t, got.AccentColour)
}
func TestEnvironmentManager_Info_Ugly(t *testing.T) {
manager := &EnvironmentManager{}
manager.operatingSystem = "plan9"
manager.architecture = "riscv64"
manager.debugMode = true
manager.SetDarkMode(true)
got := manager.Info()
assert.Equal(t, "plan9", got.OS)
assert.Equal(t, "riscv64", got.Arch)
assert.True(t, got.Debug)
assert.True(t, got.IsDarkMode)
}

View file

@ -90,9 +90,9 @@ type applicationEventListener struct {
// cancel := manager.On("data:ready", func(e *CustomEvent) { process(e.Data) })
// defer cancel()
type EventManager struct {
mu sync.RWMutex
customListeners map[string][]*customEventListener
appListeners map[uint][]*applicationEventListener
mu sync.RWMutex
customListeners map[string][]*customEventListener
appListeners map[uint][]*applicationEventListener
}
func newEventManager() *EventManager {

View file

@ -0,0 +1,137 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/wailsapp/wails/v3/pkg/events"
)
func TestEvents_CustomEvent_Good(t *testing.T) {
event := &CustomEvent{Name: "ready", Data: "payload", Sender: "ui"}
assert.False(t, event.IsCancelled())
event.Cancel()
assert.True(t, event.IsCancelled())
}
func TestEvents_CustomEvent_Bad(t *testing.T) {
event := &CustomEvent{}
assert.Empty(t, event.Name)
assert.Nil(t, event.Data)
assert.False(t, event.IsCancelled())
}
func TestEvents_CustomEvent_Ugly(t *testing.T) {
event := &CustomEvent{Name: "event", Data: []any{"a", 1}, Sender: "sender"}
event.Cancel()
event.Cancel()
assert.True(t, event.IsCancelled())
assert.Equal(t, []any{"a", 1}, event.Data)
}
func TestEvents_ApplicationEvent_Good(t *testing.T) {
event := &ApplicationEvent{Id: 7, ctx: newApplicationEventContext()}
assert.False(t, event.IsCancelled())
require.NotNil(t, event.Context())
event.Cancel()
assert.True(t, event.IsCancelled())
}
func TestEvents_ApplicationEvent_Bad(t *testing.T) {
event := &ApplicationEvent{}
assert.Nil(t, event.Context())
assert.False(t, event.IsCancelled())
}
func TestEvents_ApplicationEvent_Ugly(t *testing.T) {
event := &ApplicationEvent{Id: 99}
event.Cancel()
event.Cancel()
assert.True(t, event.IsCancelled())
}
func TestEvents_EventManager_Emit_Good(t *testing.T) {
manager := newEventManager()
calls := 0
manager.On("ready", func(event *CustomEvent) {
calls++
assert.Equal(t, "ready", event.Name)
assert.Equal(t, "payload", event.Data)
event.Cancel()
})
manager.On("ready", func(*CustomEvent) {
calls++
})
cancelled := manager.Emit("ready", "payload")
assert.True(t, cancelled)
assert.Equal(t, 1, calls)
}
func TestEvents_EventManager_Emit_Bad(t *testing.T) {
manager := &EventManager{}
assert.False(t, manager.Emit("missing"))
}
func TestEvents_EventManager_Emit_Ugly(t *testing.T) {
manager := newEventManager()
calls := 0
manager.OnMultiple("tick", func(event *CustomEvent) {
calls++
assert.Equal(t, []any{1, 2}, event.Data)
}, 1)
assert.False(t, manager.Emit("tick", 1, 2))
assert.False(t, manager.Emit("tick", 1, 2))
assert.Equal(t, 1, calls)
}
func TestEvents_EventManager_OnApplicationEvent_Good(t *testing.T) {
manager := newEventManager()
eventType := events.ApplicationEventType(42)
calls := 0
cancel := manager.OnApplicationEvent(eventType, func(event *ApplicationEvent) {
calls++
require.NotNil(t, event)
event.Cancel()
})
require.Len(t, manager.appListeners[uint(eventType)], 1)
cancel()
assert.Empty(t, manager.appListeners[uint(eventType)])
assert.Equal(t, 0, calls)
}
func TestEvents_EventManager_OnApplicationEvent_Bad(t *testing.T) {
manager := &EventManager{}
eventType := events.ApplicationEventType(7)
cancel := manager.OnApplicationEvent(eventType, func(*ApplicationEvent) {})
require.NotNil(t, cancel)
cancel()
assert.Empty(t, manager.appListeners[uint(eventType)])
}
func TestEvents_EventManager_OnApplicationEvent_Ugly(t *testing.T) {
manager := newEventManager()
eventType := events.ApplicationEventType(1)
manager.OnApplicationEvent(eventType, func(*ApplicationEvent) {})
assert.Len(t, manager.appListeners[uint(eventType)], 1)
manager.Off("does-not-exist")
assert.Len(t, manager.appListeners[uint(eventType)], 1)
}

View file

@ -0,0 +1,71 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestKeyBindingManager_Add_Good(t *testing.T) {
manager := &KeyBindingManager{}
calls := 0
manager.Add("CmdOrCtrl+K", func(window Window) {
calls++
assert.Nil(t, window)
})
handled := manager.Process("CmdOrCtrl+K", nil)
assert.True(t, handled)
assert.Equal(t, 1, calls)
assert.Len(t, manager.GetAll(), 1)
}
func TestKeyBindingManager_Add_Bad(t *testing.T) {
manager := &KeyBindingManager{}
handled := manager.Process("missing", nil)
assert.False(t, handled)
assert.Empty(t, manager.GetAll())
}
func TestKeyBindingManager_Add_Ugly(t *testing.T) {
manager := &KeyBindingManager{}
calls := 0
manager.Add("CmdOrCtrl+K", func(Window) { calls++ })
manager.Add("CmdOrCtrl+K", func(Window) { calls += 10 })
handled := manager.Process("CmdOrCtrl+K", nil)
assert.True(t, handled)
assert.Equal(t, 10, calls)
}
func TestKeyBindingManager_Remove_Good(t *testing.T) {
manager := &KeyBindingManager{}
manager.Add("CmdOrCtrl+K", func(Window) {})
manager.Remove("CmdOrCtrl+K")
assert.False(t, manager.Process("CmdOrCtrl+K", nil))
assert.Empty(t, manager.GetAll())
}
func TestKeyBindingManager_Remove_Bad(t *testing.T) {
manager := &KeyBindingManager{}
manager.Remove("missing")
assert.Empty(t, manager.GetAll())
}
func TestKeyBindingManager_Remove_Ugly(t *testing.T) {
manager := &KeyBindingManager{}
manager.Remove("")
assert.Empty(t, manager.GetAll())
}

View file

@ -0,0 +1,50 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMenuItem_NewRole_Good(t *testing.T) {
cases := []struct {
role Role
want string
}{
{AppMenu, "App"},
{FileMenu, "File"},
{EditMenu, "Edit"},
{HelpMenu, "Help"},
{Quit, "Quit"},
}
for _, tc := range cases {
t.Run(tc.want, func(t *testing.T) {
got := NewRole(tc.role)
assert.Equal(t, tc.want, got.Label)
assert.True(t, got.Enabled)
})
}
}
func TestMenuItem_NewRole_Bad(t *testing.T) {
got := NewRole(Role(-1))
assert.Empty(t, got.Label)
assert.True(t, got.Enabled)
assert.Equal(t, "unknown", MenuRole(999).String())
}
func TestMenuItem_NewRole_Ugly(t *testing.T) {
item := NewMenuItem("Open")
item.SetAccelerator("CmdOrCtrl+O")
item.SetTooltip("Open a file")
item.SetChecked(true)
item.SetEnabled(false)
assert.Equal(t, "Open", item.Label)
assert.Equal(t, "CmdOrCtrl+O", item.GetAccelerator())
assert.Equal(t, "Open a file", item.Tooltip)
assert.True(t, item.Checked)
assert.False(t, item.Enabled)
}

View file

@ -0,0 +1,67 @@
package application
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestScreen_Rect_Good(t *testing.T) {
rect := Rect{X: 10, Y: 20, Width: 300, Height: 200}
assert.Equal(t, Point{X: 10, Y: 20}, rect.Origin())
assert.Equal(t, Point{X: 310, Y: 220}, rect.Corner())
assert.False(t, rect.IsEmpty())
assert.True(t, rect.Contains(Point{X: 10, Y: 20}))
assert.False(t, rect.Contains(Point{X: 310, Y: 220}))
assert.Equal(t, Size{Width: 300, Height: 200}, rect.RectSize())
}
func TestScreen_Rect_Bad(t *testing.T) {
rect := Rect{}
assert.True(t, rect.IsEmpty())
assert.False(t, rect.Contains(Point{}))
}
func TestScreen_Rect_Ugly(t *testing.T) {
rect := Rect{X: -10, Y: -5, Width: 10, Height: 5}
assert.False(t, rect.IsEmpty())
assert.True(t, rect.Contains(Point{X: -10, Y: -5}))
}
func TestScreenManager_SetScreens_Good(t *testing.T) {
manager := &ScreenManager{}
primary := &Screen{ID: "1", IsPrimary: true}
secondary := &Screen{ID: "2"}
manager.SetScreens([]*Screen{primary, secondary})
require.Same(t, primary, manager.GetPrimary())
require.Same(t, primary, manager.GetCurrent())
assert.Equal(t, []*Screen{primary, secondary}, manager.GetAll())
}
func TestScreenManager_SetScreens_Bad(t *testing.T) {
manager := &ScreenManager{}
manager.SetScreens(nil)
assert.Nil(t, manager.GetPrimary())
assert.Nil(t, manager.GetCurrent())
assert.Empty(t, manager.GetAll())
}
func TestScreenManager_SetScreens_Ugly(t *testing.T) {
manager := &ScreenManager{}
primary := &Screen{ID: "1", IsPrimary: true}
current := &Screen{ID: "current"}
manager.SetCurrent(current)
manager.SetScreens([]*Screen{primary})
require.Same(t, primary, manager.GetPrimary())
require.Same(t, current, manager.GetCurrent())
}

View file

@ -0,0 +1,52 @@
package application
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
type namedService struct{}
func (namedService) ServiceName() string { return "named" }
type plainService struct{}
func TestServices_NewService_Good(t *testing.T) {
instance := &plainService{}
service := NewService(instance)
assert.Same(t, instance, service.Instance())
assert.Equal(t, DefaultServiceOptions, service.Options())
assert.Equal(t, "application.plainService", getServiceName(service))
}
func TestServices_NewService_Bad(t *testing.T) {
instance := &namedService{}
service := NewServiceWithOptions(instance, ServiceOptions{Name: "explicit"})
assert.Same(t, instance, service.Instance())
assert.Equal(t, "explicit", getServiceName(service))
}
func TestServices_NewService_Ugly(t *testing.T) {
instance := &namedService{}
service := NewService(instance)
assert.Equal(t, "named", getServiceName(service))
}
func TestServices_ServiceInterfaces_Good(t *testing.T) {
var _ ServiceName = namedService{}
var _ ServiceStartup = (*startupService)(nil)
var _ ServiceShutdown = (*shutdownService)(nil)
}
type startupService struct{}
func (*startupService) ServiceStartup(context.Context, ServiceOptions) error { return nil }
type shutdownService struct{}
func (*shutdownService) ServiceShutdown() error { return nil }