Make group navigation controller-driven
This commit is contained in:
@@ -597,6 +597,30 @@ func (s *State) MoveSelectedEntry(path []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *State) DeleteCurrentGroup() error {
|
||||||
|
session, ok := s.Session.(MutableSession)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("session is not mutable")
|
||||||
|
}
|
||||||
|
|
||||||
|
model, err := session.Current()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := model.DeleteGroup(s.CurrentPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
session.Replace(model)
|
||||||
|
if len(s.CurrentPath) > 0 {
|
||||||
|
s.CurrentPath = append([]string(nil), s.CurrentPath[:len(s.CurrentPath)-1]...)
|
||||||
|
}
|
||||||
|
s.SelectedEntryID = ""
|
||||||
|
s.Dirty = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *State) AddAttachmentToSelectedEntry(name string, content []byte) error {
|
func (s *State) AddAttachmentToSelectedEntry(name string, content []byte) error {
|
||||||
session, ok := s.Session.(MutableSession)
|
session, ok := s.Session.(MutableSession)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
@@ -967,6 +967,38 @@ func TestRenameCurrentGroupUpdatesPathAndMarksDirty(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDeleteCurrentGroupRemovesItNavigatesToParentAndMarksDirty(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
model := testVaultModel()
|
||||||
|
model.CreateGroup([]string{"Root"}, "Finance")
|
||||||
|
sess := &mutableStubSession{model: model}
|
||||||
|
state := State{
|
||||||
|
Session: sess,
|
||||||
|
CurrentPath: []string{"Root", "Finance"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := state.DeleteCurrentGroup(); err != nil {
|
||||||
|
t.Fatalf("DeleteCurrentGroup() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(state.CurrentPath, []string{"Root"}) {
|
||||||
|
t.Fatalf("CurrentPath = %v, want [Root]", state.CurrentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
got, err := state.ChildGroups()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ChildGroups() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Equal(got, []string{"Home Assistant", "Internet"}) {
|
||||||
|
t.Fatalf("ChildGroups() = %v, want [Home Assistant Internet]", got)
|
||||||
|
}
|
||||||
|
if !state.Dirty {
|
||||||
|
t.Fatal("Dirty = false, want true after DeleteCurrentGroup")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMoveSelectedEntryPersistsPathChangeAndMarksDirty(t *testing.T) {
|
func TestMoveSelectedEntryPersistsPathChangeAndMarksDirty(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,6 @@ type ui struct {
|
|||||||
state appstate.State
|
state appstate.State
|
||||||
masterKeyMode vault.MasterKeyMode
|
masterKeyMode vault.MasterKeyMode
|
||||||
visible []entry
|
visible []entry
|
||||||
currentPath []string
|
|
||||||
selectedHistoryIndex int
|
selectedHistoryIndex int
|
||||||
showPassword bool
|
showPassword bool
|
||||||
togglePassword widget.Clickable
|
togglePassword widget.Clickable
|
||||||
@@ -224,7 +223,6 @@ func newUIWithState(mode string, sess appstate.CurrentSession) *ui {
|
|||||||
|
|
||||||
func (u *ui) filter() {
|
func (u *ui) filter() {
|
||||||
u.state.SearchQuery = u.search.Text()
|
u.state.SearchQuery = u.search.Text()
|
||||||
u.state.CurrentPath = append([]string(nil), u.currentPath...)
|
|
||||||
visible, err := u.state.VisibleEntries()
|
visible, err := u.state.VisibleEntries()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.visible = nil
|
u.visible = nil
|
||||||
@@ -238,25 +236,24 @@ func (u *ui) filter() {
|
|||||||
|
|
||||||
func (u *ui) showEntriesSection() {
|
func (u *ui) showEntriesSection() {
|
||||||
u.state.Section = appstate.SectionEntries
|
u.state.Section = appstate.SectionEntries
|
||||||
u.currentPath = nil
|
u.state.NavigateToPath(nil)
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) showTemplatesSection() {
|
func (u *ui) showTemplatesSection() {
|
||||||
u.state.Section = appstate.SectionTemplates
|
u.state.Section = appstate.SectionTemplates
|
||||||
u.currentPath = nil
|
u.state.NavigateToPath(nil)
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) showRecycleBinSection() {
|
func (u *ui) showRecycleBinSection() {
|
||||||
u.state.Section = appstate.SectionRecycleBin
|
u.state.Section = appstate.SectionRecycleBin
|
||||||
u.currentPath = nil
|
u.state.NavigateToPath(nil)
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) childGroups() []string {
|
func (u *ui) childGroups() []string {
|
||||||
u.state.SearchQuery = u.search.Text()
|
u.state.SearchQuery = u.search.Text()
|
||||||
u.state.CurrentPath = append([]string(nil), u.currentPath...)
|
|
||||||
groups, err := u.state.ChildGroups()
|
groups, err := u.state.ChildGroups()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@@ -364,7 +361,6 @@ func (u *ui) createVaultAction() error {
|
|||||||
if err := u.state.CreateVault(key); err != nil {
|
if err := u.state.CreateVault(key); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.currentPath = nil
|
|
||||||
u.filter()
|
u.filter()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -377,7 +373,6 @@ func (u *ui) openVaultAction() error {
|
|||||||
if err := u.state.OpenVault(strings.TrimSpace(u.vaultPath.Text()), key); err != nil {
|
if err := u.state.OpenVault(strings.TrimSpace(u.vaultPath.Text()), key); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.currentPath = nil
|
|
||||||
u.filter()
|
u.filter()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -411,7 +406,6 @@ func (u *ui) openRemoteAction() error {
|
|||||||
if err := u.state.OpenRemoteVault(client, strings.TrimSpace(u.remotePath.Text()), key); err != nil {
|
if err := u.state.OpenRemoteVault(client, strings.TrimSpace(u.remotePath.Text()), key); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
u.currentPath = nil
|
|
||||||
u.filter()
|
u.filter()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -553,8 +547,8 @@ func (u *ui) detailPlaceholderMessage() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) ensureNavClickables() {
|
func (u *ui) ensureNavClickables() {
|
||||||
if len(u.breadcrumbs) < len(u.currentPath)+1 {
|
if len(u.breadcrumbs) < len(u.state.CurrentPath)+1 {
|
||||||
u.breadcrumbs = make([]widget.Clickable, len(u.currentPath)+1)
|
u.breadcrumbs = make([]widget.Clickable, len(u.state.CurrentPath)+1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +599,7 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
|||||||
for u.addEntry.Clicked(gtx) {
|
for u.addEntry.Clicked(gtx) {
|
||||||
u.state.SelectedEntryID = ""
|
u.state.SelectedEntryID = ""
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
u.entryPath.SetText(strings.Join(u.currentPath, " / "))
|
u.entryPath.SetText(strings.Join(u.state.CurrentPath, " / "))
|
||||||
u.statusMessage = "new entry form ready"
|
u.statusMessage = "new entry form ready"
|
||||||
u.errorMessage = ""
|
u.errorMessage = ""
|
||||||
}
|
}
|
||||||
@@ -1229,9 +1223,9 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
|
|||||||
return lbl.Layout(gtx)
|
return lbl.Layout(gtx)
|
||||||
}
|
}
|
||||||
|
|
||||||
crumbs := append([]string{"Vault"}, append([]string{}, u.currentPath...)...)
|
crumbs := append([]string{"Vault"}, append([]string{}, u.state.CurrentPath...)...)
|
||||||
if u.state.Section == appstate.SectionTemplates {
|
if u.state.Section == appstate.SectionTemplates {
|
||||||
crumbs = append([]string{"Templates"}, append([]string{}, u.currentPath...)...)
|
crumbs = append([]string{"Templates"}, append([]string{}, u.state.CurrentPath...)...)
|
||||||
}
|
}
|
||||||
return layout.Flex{Alignment: layout.Middle}.Layout(gtx, func() []layout.FlexChild {
|
return layout.Flex{Alignment: layout.Middle}.Layout(gtx, func() []layout.FlexChild {
|
||||||
children := make([]layout.FlexChild, 0, len(crumbs)*2)
|
children := make([]layout.FlexChild, 0, len(crumbs)*2)
|
||||||
@@ -1241,9 +1235,9 @@ func (u *ui) pathBar(gtx layout.Context) layout.Dimensions {
|
|||||||
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
for u.breadcrumbs[index].Clicked(gtx) {
|
for u.breadcrumbs[index].Clicked(gtx) {
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
u.currentPath = nil
|
u.state.NavigateToPath(nil)
|
||||||
} else {
|
} else {
|
||||||
u.currentPath = append([]string{}, crumbs[1:index+1]...)
|
u.state.NavigateToPath(crumbs[1 : index+1])
|
||||||
}
|
}
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
@@ -1280,7 +1274,7 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions {
|
|||||||
name := group
|
name := group
|
||||||
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
children = append(children, layout.Rigid(func(gtx layout.Context) layout.Dimensions {
|
||||||
for u.groupClicks[idx].Clicked(gtx) {
|
for u.groupClicks[idx].Clicked(gtx) {
|
||||||
u.currentPath = append(append([]string{}, u.currentPath...), name)
|
u.state.EnterGroup(name)
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
btn := material.Button(u.theme, &u.groupClicks[idx], "Folder: "+name)
|
btn := material.Button(u.theme, &u.groupClicks[idx], "Folder: "+name)
|
||||||
|
|||||||
+121
-24
@@ -31,7 +31,7 @@ func TestUIFiltersUsingVaultModelPathsAndSearch(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
u.currentPath = []string{"Crew", "Internet"}
|
u.state.NavigateToPath([]string{"Crew", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
if got := u.filteredTitles(); !slices.Equal(got, []string{"Bellagio", "Vault Console"}) {
|
if got := u.filteredTitles(); !slices.Equal(got, []string{"Bellagio", "Vault Console"}) {
|
||||||
t.Fatalf("filteredTitles() = %v, want [Bellagio Vault Console]", got)
|
t.Fatalf("filteredTitles() = %v, want [Bellagio Vault Console]", got)
|
||||||
@@ -137,7 +137,7 @@ func TestUIChildGroupsComeFromVaultModel(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
u.currentPath = []string{"Crew"}
|
u.state.NavigateToPath([]string{"Crew"})
|
||||||
if got := u.childGroups(); !slices.Equal(got, []string{"Home Assistant", "Internet"}) {
|
if got := u.childGroups(); !slices.Equal(got, []string{"Home Assistant", "Internet"}) {
|
||||||
t.Fatalf("childGroups() = %v, want [Home Assistant Internet]", got)
|
t.Fatalf("childGroups() = %v, want [Home Assistant Internet]", got)
|
||||||
}
|
}
|
||||||
@@ -153,7 +153,7 @@ func TestUISelectedEntryFollowsApplicationStateSelection(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
u.currentPath = []string{"Crew", "Internet"}
|
u.state.NavigateToPath([]string{"Crew", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "2"
|
u.state.SelectedEntryID = "2"
|
||||||
|
|
||||||
@@ -215,7 +215,7 @@ func TestUILifecycleActionsCreateSaveOpenLockAndUnlockLocalVault(t *testing.T) {
|
|||||||
if err := u.lockAction(); err != nil {
|
if err := u.lockAction(); err != nil {
|
||||||
t.Fatalf("lockAction() error = %v", err)
|
t.Fatalf("lockAction() error = %v", err)
|
||||||
}
|
}
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
if got := u.filteredTitles(); len(got) != 0 {
|
if got := u.filteredTitles(); len(got) != 0 {
|
||||||
t.Fatalf("filteredTitles() = %v, want empty while locked", got)
|
t.Fatalf("filteredTitles() = %v, want empty while locked", got)
|
||||||
@@ -235,7 +235,7 @@ func TestUILifecycleActionsCreateSaveOpenLockAndUnlockLocalVault(t *testing.T) {
|
|||||||
if err := reopened.openVaultAction(); err != nil {
|
if err := reopened.openVaultAction(); err != nil {
|
||||||
t.Fatalf("openVaultAction() error = %v", err)
|
t.Fatalf("openVaultAction() error = %v", err)
|
||||||
}
|
}
|
||||||
reopened.currentPath = []string{"Root", "Internet"}
|
reopened.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
reopened.filter()
|
reopened.filter()
|
||||||
if got := reopened.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
if got := reopened.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
||||||
t.Fatalf("reopened filteredTitles() = %v, want [Vault Console]", got)
|
t.Fatalf("reopened filteredTitles() = %v, want [Vault Console]", got)
|
||||||
@@ -322,7 +322,7 @@ func TestUIMasterKeyModesCreateOpenAndUnlockLocalVault(t *testing.T) {
|
|||||||
if err := reopened.openVaultAction(); err != nil {
|
if err := reopened.openVaultAction(); err != nil {
|
||||||
t.Fatalf("openVaultAction() error = %v", err)
|
t.Fatalf("openVaultAction() error = %v", err)
|
||||||
}
|
}
|
||||||
reopened.currentPath = []string{"Root", "Internet"}
|
reopened.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
reopened.filter()
|
reopened.filter()
|
||||||
if got := reopened.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
if got := reopened.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
||||||
t.Fatalf("reopened filteredTitles() = %v, want [Vault Console]", got)
|
t.Fatalf("reopened filteredTitles() = %v, want [Vault Console]", got)
|
||||||
@@ -395,7 +395,7 @@ func TestUIChangeMasterKeyModeForExistingVault(t *testing.T) {
|
|||||||
if err := reopened.openVaultAction(); err != nil {
|
if err := reopened.openVaultAction(); err != nil {
|
||||||
t.Fatalf("openVaultAction() with updated key error = %v", err)
|
t.Fatalf("openVaultAction() with updated key error = %v", err)
|
||||||
}
|
}
|
||||||
reopened.currentPath = []string{"Root", "Internet"}
|
reopened.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
reopened.filter()
|
reopened.filter()
|
||||||
if got := reopened.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
if got := reopened.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
||||||
t.Fatalf("reopened filteredTitles() = %v, want [Vault Console]", got)
|
t.Fatalf("reopened filteredTitles() = %v, want [Vault Console]", got)
|
||||||
@@ -706,13 +706,110 @@ func TestUISectionNavigationShowsTemplatesAndRecycleBin(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
if got := u.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
if got := u.filteredTitles(); !slices.Equal(got, []string{"Vault Console"}) {
|
||||||
t.Fatalf("entry filteredTitles() = %v, want [Vault Console]", got)
|
t.Fatalf("entry filteredTitles() = %v, want [Vault Console]", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIGroupManagementAndPathNavigationAreControllerDriven(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
u := newUIWithModel("desktop", vault.Model{
|
||||||
|
Entries: []vault.Entry{
|
||||||
|
{ID: "entry-1", Title: "Vault Console", Path: []string{"Root", "Internet"}},
|
||||||
|
{ID: "entry-2", Title: "Home Assistant", Path: []string{"Root", "Home Assistant"}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
u.showEntriesSection()
|
||||||
|
u.state.NavigateToPath([]string{"Root"})
|
||||||
|
u.filter()
|
||||||
|
|
||||||
|
u.groupName.SetText("Finance")
|
||||||
|
if err := u.createGroupAction(); err != nil {
|
||||||
|
t.Fatalf("createGroupAction() error = %v", err)
|
||||||
|
}
|
||||||
|
if got := u.childGroups(); !slices.Equal(got, []string{"Finance", "Home Assistant", "Internet"}) {
|
||||||
|
t.Fatalf("childGroups() after create = %v, want [Finance Home Assistant Internet]", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.state.EnterGroup("Finance")
|
||||||
|
u.filter()
|
||||||
|
u.groupName.SetText("Budget")
|
||||||
|
if err := u.renameGroupAction(); err != nil {
|
||||||
|
t.Fatalf("renameGroupAction() error = %v", err)
|
||||||
|
}
|
||||||
|
if !slices.Equal(u.state.CurrentPath, []string{"Root", "Budget"}) {
|
||||||
|
t.Fatalf("state.CurrentPath after rename = %v, want [Root Budget]", u.state.CurrentPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.state.NavigateToPath([]string{"Root"})
|
||||||
|
u.filter()
|
||||||
|
if got := u.childGroups(); !slices.Equal(got, []string{"Budget", "Home Assistant", "Internet"}) {
|
||||||
|
t.Fatalf("childGroups() after rename = %v, want [Budget Home Assistant Internet]", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.state.NavigateToPath([]string{"Root", "Budget"})
|
||||||
|
u.filter()
|
||||||
|
if err := u.deleteCurrentGroupAction(); err != nil {
|
||||||
|
t.Fatalf("deleteCurrentGroupAction() error = %v", err)
|
||||||
|
}
|
||||||
|
if !slices.Equal(u.state.CurrentPath, []string{"Root"}) {
|
||||||
|
t.Fatalf("state.CurrentPath after delete = %v, want [Root]", u.state.CurrentPath)
|
||||||
|
}
|
||||||
|
if got := u.childGroups(); !slices.Equal(got, []string{"Home Assistant", "Internet"}) {
|
||||||
|
t.Fatalf("childGroups() after delete = %v, want [Home Assistant Internet]", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUISavingEntryWithDifferentPathMovesItBetweenGroups(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"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "ha",
|
||||||
|
Title: "Home Assistant",
|
||||||
|
Username: "rustyryan",
|
||||||
|
Password: "token-2",
|
||||||
|
URL: "https://ha.example.test",
|
||||||
|
Path: []string{"Root", "Home Assistant"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
u.showEntriesSection()
|
||||||
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
|
u.filter()
|
||||||
|
u.state.SelectedEntryID = "vault-console"
|
||||||
|
u.loadSelectedEntryIntoEditor()
|
||||||
|
u.entryPath.SetText("Root / Home Assistant")
|
||||||
|
|
||||||
|
if err := u.saveEntryAction(); err != nil {
|
||||||
|
t.Fatalf("saveEntryAction() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
|
u.filter()
|
||||||
|
if got := u.filteredTitles(); len(got) != 0 {
|
||||||
|
t.Fatalf("filteredTitles() in source group = %v, want empty after move", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
u.state.NavigateToPath([]string{"Root", "Home Assistant"})
|
||||||
|
u.filter()
|
||||||
|
if got := u.filteredTitles(); !slices.Equal(got, []string{"Home Assistant", "Vault Console"}) {
|
||||||
|
t.Fatalf("filteredTitles() in destination group = %v, want [Vault Console Home Assistant]", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUISavesDuplicatesDeletesAndRestoresEntriesFromTheEditor(t *testing.T) {
|
func TestUISavesDuplicatesDeletesAndRestoresEntriesFromTheEditor(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -729,7 +826,7 @@ func TestUISavesDuplicatesDeletesAndRestoresEntriesFromTheEditor(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
@@ -764,7 +861,7 @@ func TestUISavesDuplicatesDeletesAndRestoresEntriesFromTheEditor(t *testing.T) {
|
|||||||
t.Fatalf("restoreSelectedRecycleEntryAction() error = %v", err)
|
t.Fatalf("restoreSelectedRecycleEntryAction() error = %v", err)
|
||||||
}
|
}
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
if got := u.filteredTitles(); !slices.Equal(got, []string{"Vault Console", "Vault Console (Copy)"}) {
|
if got := u.filteredTitles(); !slices.Equal(got, []string{"Vault Console", "Vault Console (Copy)"}) {
|
||||||
t.Fatalf("filteredTitles() after restore = %v, want restored copy", got)
|
t.Fatalf("filteredTitles() after restore = %v, want restored copy", got)
|
||||||
@@ -900,7 +997,7 @@ func TestUITemplateAndAttachmentActionsWorkThroughEditor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "entry-1"
|
u.state.SelectedEntryID = "entry-1"
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
@@ -973,7 +1070,7 @@ func TestUIRestoresSelectedEntryHistoryVersion(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
@@ -1038,7 +1135,7 @@ func TestUISelectingEntryHistoryVersionTracksSelectedVersion(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
@@ -1080,7 +1177,7 @@ func TestUIKeyboardShortcutActionsDispatchExpectedCommands(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
@@ -1124,7 +1221,7 @@ func TestUIKeyboardNavigationMovesAcrossBreadcrumbsListAndDetail(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
|
|
||||||
if got := u.keyboardFocus; got != focusSearch {
|
if got := u.keyboardFocus; got != focusSearch {
|
||||||
@@ -1171,7 +1268,7 @@ func TestUIKeyboardNavigationActivatesBreadcrumbs(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.keyboardFocus = breadcrumbFocusID(0)
|
u.keyboardFocus = breadcrumbFocusID(0)
|
||||||
|
|
||||||
@@ -1181,8 +1278,8 @@ func TestUIKeyboardNavigationActivatesBreadcrumbs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.handleKeyPress(key.NameReturn, 0)
|
u.handleKeyPress(key.NameReturn, 0)
|
||||||
if got := u.currentPath; !slices.Equal(got, []string{"Root"}) {
|
if got := u.state.CurrentPath; !slices.Equal(got, []string{"Root"}) {
|
||||||
t.Fatalf("currentPath after breadcrumb activation = %v, want [Root]", got)
|
t.Fatalf("state.CurrentPath after breadcrumb activation = %v, want [Root]", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1202,7 +1299,7 @@ func TestUIKeyboardShortcutsMoveFocusForSearchAndNewEntry(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
@@ -1235,7 +1332,7 @@ func TestUIAccessibilityLabelsDescribeFocusableControls(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
|
|
||||||
if got := u.accessibilityLabel(focusSearch); got != "Search vault" {
|
if got := u.accessibilityLabel(focusSearch); got != "Search vault" {
|
||||||
@@ -1294,7 +1391,7 @@ func TestUIActionErrorsAndStatusMessagesAreCapturedForDisplay(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
u.runAction("copy username", func() error { return u.copySelectedFieldAction(clipboard.TargetUsername) })
|
u.runAction("copy username", func() error { return u.copySelectedFieldAction(clipboard.TargetUsername) })
|
||||||
@@ -1444,7 +1541,7 @@ func TestUICopyActionsWriteExpectedClipboardContentsAndSanitizedFeedback(t *test
|
|||||||
writer := &memoryClipboardWriter{}
|
writer := &memoryClipboardWriter{}
|
||||||
u.clipboardWriter = writer
|
u.clipboardWriter = writer
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
|
|
||||||
@@ -1483,7 +1580,7 @@ func TestUICopyActionSanitizesClipboardBackendErrors(t *testing.T) {
|
|||||||
})
|
})
|
||||||
u.clipboardWriter = failingClipboardWriter{err: os.ErrPermission}
|
u.clipboardWriter = failingClipboardWriter{err: os.ErrPermission}
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
|
|
||||||
@@ -1515,7 +1612,7 @@ func TestUIPasswordRevealTogglesDisplayedPasswordAndLockResetsIt(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
u.showEntriesSection()
|
u.showEntriesSection()
|
||||||
u.currentPath = []string{"Root", "Internet"}
|
u.state.NavigateToPath([]string{"Root", "Internet"})
|
||||||
u.filter()
|
u.filter()
|
||||||
u.state.SelectedEntryID = "vault-console"
|
u.state.SelectedEntryID = "vault-console"
|
||||||
|
|
||||||
|
|||||||
+2
-18
@@ -6,7 +6,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"git.julianfamily.org/keepassgo/appstate"
|
|
||||||
"git.julianfamily.org/keepassgo/clipboard"
|
"git.julianfamily.org/keepassgo/clipboard"
|
||||||
"git.julianfamily.org/keepassgo/passwords"
|
"git.julianfamily.org/keepassgo/passwords"
|
||||||
"git.julianfamily.org/keepassgo/vault"
|
"git.julianfamily.org/keepassgo/vault"
|
||||||
@@ -25,7 +24,7 @@ func (u *ui) loadSelectedEntryIntoEditor() {
|
|||||||
u.entryURL.SetText("")
|
u.entryURL.SetText("")
|
||||||
u.entryNotes.SetText("")
|
u.entryNotes.SetText("")
|
||||||
u.entryTags.SetText("")
|
u.entryTags.SetText("")
|
||||||
u.entryPath.SetText(strings.Join(u.currentPath, " / "))
|
u.entryPath.SetText(strings.Join(u.state.CurrentPath, " / "))
|
||||||
u.entryFields.SetText("")
|
u.entryFields.SetText("")
|
||||||
u.attachmentName.SetText("")
|
u.attachmentName.SetText("")
|
||||||
u.attachmentPath.SetText("")
|
u.attachmentPath.SetText("")
|
||||||
@@ -161,24 +160,9 @@ func (u *ui) renameGroupAction() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) deleteCurrentGroupAction() error {
|
func (u *ui) deleteCurrentGroupAction() error {
|
||||||
session, ok := u.state.Session.(appstate.MutableSession)
|
if err := u.state.DeleteCurrentGroup(); err != nil {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("session is not mutable")
|
|
||||||
}
|
|
||||||
|
|
||||||
model, err := session.Current()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := model.DeleteGroup(u.currentPath); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
session.Replace(model)
|
|
||||||
if len(u.currentPath) > 0 {
|
|
||||||
u.currentPath = append([]string(nil), u.currentPath[:len(u.currentPath)-1]...)
|
|
||||||
u.state.CurrentPath = append([]string(nil), u.currentPath...)
|
|
||||||
}
|
|
||||||
u.state.Dirty = true
|
|
||||||
u.filter()
|
u.filter()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -63,7 +63,7 @@ func (u *ui) performShortcut(name string) error {
|
|||||||
case shortcutNewEntry:
|
case shortcutNewEntry:
|
||||||
u.state.SelectedEntryID = ""
|
u.state.SelectedEntryID = ""
|
||||||
u.loadSelectedEntryIntoEditor()
|
u.loadSelectedEntryIntoEditor()
|
||||||
u.entryPath.SetText(strings.Join(u.currentPath, " / "))
|
u.entryPath.SetText(strings.Join(u.state.CurrentPath, " / "))
|
||||||
u.keyboardFocus = detailFocusID(detailFieldTitle)
|
u.keyboardFocus = detailFocusID(detailFieldTitle)
|
||||||
return nil
|
return nil
|
||||||
case shortcutCopyUser:
|
case shortcutCopyUser:
|
||||||
|
|||||||
Reference in New Issue
Block a user