Add keyboard-first accessibility behavior
This commit is contained in:
+175
@@ -11,6 +11,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gioui.org/io/key"
|
||||
"gioui.org/unit"
|
||||
|
||||
"git.julianfamily.org/keepassgo/clipboard"
|
||||
"git.julianfamily.org/keepassgo/session"
|
||||
"git.julianfamily.org/keepassgo/vault"
|
||||
@@ -758,6 +761,178 @@ func TestUIKeyboardShortcutActionsDispatchExpectedCommands(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIKeyboardNavigationMovesAcrossBreadcrumbsListAndDetail(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{
|
||||
Entries: []vault.Entry{
|
||||
{
|
||||
ID: "bellagio",
|
||||
Title: "Bellagio",
|
||||
Username: "rustyryan",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
{
|
||||
ID: "vault-console",
|
||||
Title: "Vault Console",
|
||||
Username: "dannyocean",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
},
|
||||
})
|
||||
u.showEntriesSection()
|
||||
u.currentPath = []string{"Root", "Internet"}
|
||||
u.filter()
|
||||
|
||||
if got := u.keyboardFocus; got != focusSearch {
|
||||
t.Fatalf("keyboardFocus = %q, want %q", got, focusSearch)
|
||||
}
|
||||
|
||||
u.handleKeyPress(key.NameTab, 0)
|
||||
if got := u.keyboardFocus; got != breadcrumbFocusID(0) {
|
||||
t.Fatalf("keyboardFocus after Tab = %q, want %q", got, breadcrumbFocusID(0))
|
||||
}
|
||||
|
||||
u.handleKeyPress(key.NameTab, 0)
|
||||
if got := u.keyboardFocus; got != listFocusID(0) {
|
||||
t.Fatalf("keyboardFocus after second Tab = %q, want %q", got, listFocusID(0))
|
||||
}
|
||||
if got := u.state.SelectedEntryID; got != "bellagio" {
|
||||
t.Fatalf("SelectedEntryID after list focus = %q, want %q", got, "bellagio")
|
||||
}
|
||||
|
||||
u.handleKeyPress(key.NameDownArrow, 0)
|
||||
if got := u.keyboardFocus; got != listFocusID(1) {
|
||||
t.Fatalf("keyboardFocus after Down = %q, want %q", got, listFocusID(1))
|
||||
}
|
||||
if got := u.state.SelectedEntryID; got != "vault-console" {
|
||||
t.Fatalf("SelectedEntryID after Down = %q, want %q", got, "vault-console")
|
||||
}
|
||||
|
||||
u.handleKeyPress(key.NameTab, 0)
|
||||
if got := u.keyboardFocus; got != detailFocusID(detailFieldTitle) {
|
||||
t.Fatalf("keyboardFocus after detail Tab = %q, want %q", got, detailFocusID(detailFieldTitle))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIKeyboardNavigationActivatesBreadcrumbs(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{
|
||||
Entries: []vault.Entry{
|
||||
{
|
||||
ID: "vault-console",
|
||||
Title: "Vault Console",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
},
|
||||
})
|
||||
u.showEntriesSection()
|
||||
u.currentPath = []string{"Root", "Internet"}
|
||||
u.filter()
|
||||
u.keyboardFocus = breadcrumbFocusID(0)
|
||||
|
||||
u.handleKeyPress(key.NameRightArrow, 0)
|
||||
if got := u.keyboardFocus; got != breadcrumbFocusID(1) {
|
||||
t.Fatalf("keyboardFocus after Right = %q, want %q", got, breadcrumbFocusID(1))
|
||||
}
|
||||
|
||||
u.handleKeyPress(key.NameReturn, 0)
|
||||
if got := u.currentPath; !slices.Equal(got, []string{"Root"}) {
|
||||
t.Fatalf("currentPath after breadcrumb activation = %v, want [Root]", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIKeyboardShortcutsMoveFocusForSearchAndNewEntry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{
|
||||
Entries: []vault.Entry{
|
||||
{
|
||||
ID: "vault-console",
|
||||
Title: "Vault Console",
|
||||
Username: "dannyocean",
|
||||
Password: "token-1",
|
||||
URL: "https://vault.crew.example.invalid",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
},
|
||||
})
|
||||
u.showEntriesSection()
|
||||
u.currentPath = []string{"Root", "Internet"}
|
||||
u.filter()
|
||||
u.state.SelectedEntryID = "vault-console"
|
||||
u.loadSelectedEntryIntoEditor()
|
||||
u.keyboardFocus = listFocusID(0)
|
||||
|
||||
u.handleKeyPress("F", key.ModShortcut)
|
||||
if got := u.keyboardFocus; got != focusSearch {
|
||||
t.Fatalf("keyboardFocus after shortcut search = %q, want %q", got, focusSearch)
|
||||
}
|
||||
|
||||
u.handleKeyPress("N", key.ModShortcut)
|
||||
if got := u.state.SelectedEntryID; got != "" {
|
||||
t.Fatalf("SelectedEntryID after shortcut new-entry = %q, want empty", got)
|
||||
}
|
||||
if got := u.keyboardFocus; got != detailFocusID(detailFieldTitle) {
|
||||
t.Fatalf("keyboardFocus after shortcut new-entry = %q, want %q", got, detailFocusID(detailFieldTitle))
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIAccessibilityLabelsDescribeFocusableControls(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
u := newUIWithModel("desktop", vault.Model{
|
||||
Entries: []vault.Entry{
|
||||
{
|
||||
ID: "vault-console",
|
||||
Title: "Vault Console",
|
||||
Path: []string{"Root", "Internet"},
|
||||
},
|
||||
},
|
||||
})
|
||||
u.showEntriesSection()
|
||||
u.currentPath = []string{"Root", "Internet"}
|
||||
u.filter()
|
||||
|
||||
if got := u.accessibilityLabel(focusSearch); got != "Search vault" {
|
||||
t.Fatalf("accessibilityLabel(search) = %q, want %q", got, "Search vault")
|
||||
}
|
||||
if got := u.accessibilityLabel(breadcrumbFocusID(1)); got != "Navigate to Root" {
|
||||
t.Fatalf("accessibilityLabel(breadcrumb) = %q, want %q", got, "Navigate to Root")
|
||||
}
|
||||
if got := u.accessibilityLabel(listFocusID(0)); got != "Select entry Vault Console" {
|
||||
t.Fatalf("accessibilityLabel(list) = %q, want %q", got, "Select entry Vault Console")
|
||||
}
|
||||
if got := u.accessibilityLabel(detailFocusID(detailFieldPassword)); got != "Edit Password" {
|
||||
t.Fatalf("accessibilityLabel(detail password) = %q, want %q", got, "Edit Password")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFieldFocusAppearanceScalesForHighDPI(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lo := fieldFocusAppearance(unit.Metric{PxPerDp: 1, PxPerSp: 1}, true)
|
||||
hi := fieldFocusAppearance(unit.Metric{PxPerDp: 2.5, PxPerSp: 2.5}, true)
|
||||
unfocused := fieldFocusAppearance(unit.Metric{PxPerDp: 1, PxPerSp: 1}, false)
|
||||
|
||||
if got := lo.MinHeight; got != 44 {
|
||||
t.Fatalf("fieldFocusAppearance(low).MinHeight = %d, want 44", got)
|
||||
}
|
||||
if got := hi.MinHeight; got != 110 {
|
||||
t.Fatalf("fieldFocusAppearance(high).MinHeight = %d, want 110", got)
|
||||
}
|
||||
if got := lo.OutlineWidth; got < 2 {
|
||||
t.Fatalf("fieldFocusAppearance(low).OutlineWidth = %d, want >= 2", got)
|
||||
}
|
||||
if hi.OutlineWidth <= lo.OutlineWidth {
|
||||
t.Fatalf("fieldFocusAppearance(high).OutlineWidth = %d, want > %d", hi.OutlineWidth, lo.OutlineWidth)
|
||||
}
|
||||
if lo.OutlineColor == unfocused.OutlineColor {
|
||||
t.Fatalf("fieldFocusAppearance().OutlineColor focused = %#v, want distinct from unfocused %#v", lo.OutlineColor, unfocused.OutlineColor)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUIActionErrorsAndStatusMessagesAreCapturedForDisplay(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user