Complete UI state and error surfaces
This commit is contained in:
+110
@@ -2,6 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -12,6 +13,7 @@ import (
|
||||
"git.julianfamily.org/keepassgo/clipboard"
|
||||
"git.julianfamily.org/keepassgo/session"
|
||||
"git.julianfamily.org/keepassgo/vault"
|
||||
"git.julianfamily.org/keepassgo/webdav"
|
||||
)
|
||||
|
||||
func TestUIFiltersUsingVaultModelPathsAndSearch(t *testing.T) {
|
||||
@@ -781,3 +783,111 @@ func TestUIActionErrorsAndStatusMessagesAreCapturedForDisplay(t *testing.T) {
|
||||
t.Fatal("statusMessage = empty, want visible success status")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUILockSurfacePromptsForMasterKeyMaterial(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithSession("desktop", &session.Manager{})
|
||||
u.masterPassword.SetText("correct horse battery staple")
|
||||
|
||||
if err := u.createVaultAction(); err != nil {
|
||||
t.Fatalf("createVaultAction() error = %v", err)
|
||||
}
|
||||
if err := u.lockAction(); err != nil {
|
||||
t.Fatalf("lockAction() error = %v", err)
|
||||
}
|
||||
|
||||
got := u.sessionSurface()
|
||||
if !got.Locked {
|
||||
t.Fatal("sessionSurface().Locked = false, want true")
|
||||
}
|
||||
if got.Title != "Vault locked" {
|
||||
t.Fatalf("sessionSurface().Title = %q, want %q", got.Title, "Vault locked")
|
||||
}
|
||||
if got.Message != "Enter a master password, choose a key file, or provide both to unlock the vault." {
|
||||
t.Fatalf("sessionSurface().Message = %q, want unlock prompt", got.Message)
|
||||
}
|
||||
|
||||
if msg := u.listEmptyMessage(); msg != "Unlock the vault to browse entries and groups." {
|
||||
t.Fatalf("listEmptyMessage() = %q, want locked list prompt", msg)
|
||||
}
|
||||
if msg := u.detailPlaceholderMessage(); msg != "Unlock the vault to inspect entries, attachments, and history." {
|
||||
t.Fatalf("detailPlaceholderMessage() = %q, want locked detail prompt", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIEmptyStatesExplainCurrentSectionAndSearch(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{})
|
||||
|
||||
if msg := u.listEmptyMessage(); msg != "Create or open a vault, then add an entry to get started." {
|
||||
t.Fatalf("listEmptyMessage() = %q, want empty entries guidance", msg)
|
||||
}
|
||||
|
||||
u.search.SetText("dynadot")
|
||||
u.filter()
|
||||
if msg := u.listEmptyMessage(); msg != `No entries match "dynadot". Clear or refine the search.` {
|
||||
t.Fatalf("search listEmptyMessage() = %q, want search guidance", msg)
|
||||
}
|
||||
|
||||
u.search.SetText("")
|
||||
u.showTemplatesSection()
|
||||
if msg := u.listEmptyMessage(); msg != "No templates yet. Save a reusable entry as a template." {
|
||||
t.Fatalf("template listEmptyMessage() = %q, want template empty guidance", msg)
|
||||
}
|
||||
|
||||
u.showRecycleBinSection()
|
||||
if msg := u.listEmptyMessage(); msg != "Recycle Bin is empty." {
|
||||
t.Fatalf("recycle listEmptyMessage() = %q, want recycle empty guidance", msg)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIBannerSurfacePrefersLoadingThenErrorThenStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{})
|
||||
u.loadingMessage = "Opening vault..."
|
||||
if got := u.bannerSurface(); got.Kind != bannerLoading || got.Message != "Opening vault..." {
|
||||
t.Fatalf("bannerSurface() with loading = %#v, want loading banner", got)
|
||||
}
|
||||
|
||||
u.loadingMessage = ""
|
||||
u.errorMessage = "save failed"
|
||||
if got := u.bannerSurface(); got.Kind != bannerError || got.Message != "save failed" {
|
||||
t.Fatalf("bannerSurface() with error = %#v, want error banner", got)
|
||||
}
|
||||
|
||||
u.errorMessage = ""
|
||||
u.statusMessage = "save complete"
|
||||
if got := u.bannerSurface(); got.Kind != bannerStatus || got.Message != "save complete" {
|
||||
t.Fatalf("bannerSurface() with status = %#v, want status banner", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIRunActionNormalizesRemoteSaveConflictsForDisplay(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{})
|
||||
u.runAction("save vault", func() error {
|
||||
return errors.New("save remote vaults/main.kdbx: " + webdav.ErrConflict.Error())
|
||||
})
|
||||
|
||||
if got := u.errorMessage; got != "Save conflict: the remote vault changed. Reopen it and retry the save." {
|
||||
t.Fatalf("errorMessage = %q, want normalized save conflict guidance", got)
|
||||
}
|
||||
if got := u.statusMessage; got != "" {
|
||||
t.Fatalf("statusMessage = %q, want empty on conflict", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIUsesKeePassGOProductCopy(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if productName != "KeePassGO" {
|
||||
t.Fatalf("productName = %q, want %q", productName, "KeePassGO")
|
||||
}
|
||||
if desktopSubtitle != "KeePass-compatible password management for desktop-first workflows" {
|
||||
t.Fatalf("desktopSubtitle = %q, want updated product subtitle", desktopSubtitle)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user