Restore entries tab state and preload recent vault
This commit is contained in:
@@ -90,6 +90,7 @@ type statePaths struct {
|
|||||||
type recentVaultRecord struct {
|
type recentVaultRecord struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
LastGroup []string `json:"lastGroup,omitempty"`
|
LastGroup []string `json:"lastGroup,omitempty"`
|
||||||
|
UsedAt string `json:"usedAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type recentRemoteRecord struct {
|
type recentRemoteRecord struct {
|
||||||
@@ -98,12 +99,20 @@ type recentRemoteRecord struct {
|
|||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
LastGroup []string `json:"lastGroup,omitempty"`
|
LastGroup []string `json:"lastGroup,omitempty"`
|
||||||
|
UsedAt string `json:"usedAt,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type uiPreferences struct {
|
type uiPreferences struct {
|
||||||
GroupControlsHidden bool `json:"groupControlsHidden"`
|
GroupControlsHidden bool `json:"groupControlsHidden"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type entriesSectionState struct {
|
||||||
|
Path []string
|
||||||
|
SearchQuery string
|
||||||
|
SelectedEntryID string
|
||||||
|
Editing bool
|
||||||
|
}
|
||||||
|
|
||||||
type syncSourceMode string
|
type syncSourceMode string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -286,6 +295,8 @@ type ui struct {
|
|||||||
recentVaults []string
|
recentVaults []string
|
||||||
recentRemotes []recentRemoteRecord
|
recentRemotes []recentRemoteRecord
|
||||||
recentVaultGroups map[string][]string
|
recentVaultGroups map[string][]string
|
||||||
|
recentVaultUsedAt map[string]time.Time
|
||||||
|
entriesState entriesSectionState
|
||||||
deleteGroupPath []string
|
deleteGroupPath []string
|
||||||
apiPolicyGroupScope bool
|
apiPolicyGroupScope bool
|
||||||
apiTokenSecret string
|
apiTokenSecret string
|
||||||
@@ -394,6 +405,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
|||||||
uiPreferencesPath: paths.UIPreferencesPath,
|
uiPreferencesPath: paths.UIPreferencesPath,
|
||||||
recentRemotesPath: paths.RecentRemotesPath,
|
recentRemotesPath: paths.RecentRemotesPath,
|
||||||
recentVaultGroups: map[string][]string{},
|
recentVaultGroups: map[string][]string{},
|
||||||
|
recentVaultUsedAt: map[string]time.Time{},
|
||||||
now: time.Now,
|
now: time.Now,
|
||||||
syncSourceMode: syncSourceLocal,
|
syncSourceMode: syncSourceLocal,
|
||||||
syncDirection: syncDirectionPull,
|
syncDirection: syncDirectionPull,
|
||||||
@@ -416,6 +428,7 @@ func newUIWithState(mode string, sess appstate.CurrentSession, paths statePaths)
|
|||||||
u.setCustomFieldRows(nil)
|
u.setCustomFieldRows(nil)
|
||||||
u.loadRecentVaults()
|
u.loadRecentVaults()
|
||||||
u.loadRecentRemotes()
|
u.loadRecentRemotes()
|
||||||
|
u.restoreStartupLifecycleTarget()
|
||||||
u.loadUIPreferences()
|
u.loadUIPreferences()
|
||||||
u.filter()
|
u.filter()
|
||||||
return u
|
return u
|
||||||
@@ -512,26 +525,28 @@ func (u *ui) selectedAttachmentNames() []string {
|
|||||||
|
|
||||||
func (u *ui) showEntriesSection() {
|
func (u *ui) showEntriesSection() {
|
||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
preservedPath := append([]string(nil), u.currentPath...)
|
|
||||||
u.state.ShowSection(appstate.SectionEntries)
|
u.state.ShowSection(appstate.SectionEntries)
|
||||||
u.restoreEntriesPath(preservedPath)
|
u.restoreEntriesSectionState()
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) showTemplatesSection() {
|
func (u *ui) showTemplatesSection() {
|
||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionTemplates)
|
u.state.ShowSection(appstate.SectionTemplates)
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) showRecycleBinSection() {
|
func (u *ui) showRecycleBinSection() {
|
||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionRecycleBin)
|
u.state.ShowSection(appstate.SectionRecycleBin)
|
||||||
u.filter()
|
u.filter()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ui) showAPITokensSection() {
|
func (u *ui) showAPITokensSection() {
|
||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionAPITokens)
|
u.state.ShowSection(appstate.SectionAPITokens)
|
||||||
u.loadSelectedAPITokenIntoEditor()
|
u.loadSelectedAPITokenIntoEditor()
|
||||||
u.filter()
|
u.filter()
|
||||||
@@ -539,6 +554,7 @@ func (u *ui) showAPITokensSection() {
|
|||||||
|
|
||||||
func (u *ui) showAPIAuditSection() {
|
func (u *ui) showAPIAuditSection() {
|
||||||
u.resetPasswordPeek()
|
u.resetPasswordPeek()
|
||||||
|
u.rememberEntriesSectionState()
|
||||||
u.state.ShowSection(appstate.SectionAPIAudit)
|
u.state.ShowSection(appstate.SectionAPIAudit)
|
||||||
u.selectedAuditIndex = -1
|
u.selectedAuditIndex = -1
|
||||||
u.filter()
|
u.filter()
|
||||||
@@ -897,11 +913,15 @@ func (u *ui) noteRecentVault(path string) {
|
|||||||
if u.recentVaultGroups == nil {
|
if u.recentVaultGroups == nil {
|
||||||
u.recentVaultGroups = map[string][]string{}
|
u.recentVaultGroups = map[string][]string{}
|
||||||
}
|
}
|
||||||
|
if u.recentVaultUsedAt == nil {
|
||||||
|
u.recentVaultUsedAt = map[string]time.Time{}
|
||||||
|
}
|
||||||
if len(u.currentPath) > 0 {
|
if len(u.currentPath) > 0 {
|
||||||
u.recentVaultGroups[path] = append([]string(nil), u.currentPath...)
|
u.recentVaultGroups[path] = append([]string(nil), u.currentPath...)
|
||||||
} else if _, ok := u.recentVaultGroups[path]; !ok {
|
} else if _, ok := u.recentVaultGroups[path]; !ok {
|
||||||
u.recentVaultGroups[path] = nil
|
u.recentVaultGroups[path] = nil
|
||||||
}
|
}
|
||||||
|
u.recentVaultUsedAt[path] = u.now()
|
||||||
next := []string{path}
|
next := []string{path}
|
||||||
for _, existing := range u.recentVaults {
|
for _, existing := range u.recentVaults {
|
||||||
if existing == path {
|
if existing == path {
|
||||||
@@ -929,6 +949,7 @@ func (u *ui) loadRecentVaults() {
|
|||||||
}
|
}
|
||||||
u.recentVaults = nil
|
u.recentVaults = nil
|
||||||
u.recentVaultGroups = map[string][]string{}
|
u.recentVaultGroups = map[string][]string{}
|
||||||
|
u.recentVaultUsedAt = map[string]time.Time{}
|
||||||
var records []recentVaultRecord
|
var records []recentVaultRecord
|
||||||
switch {
|
switch {
|
||||||
case json.Unmarshal(content, &records) == nil:
|
case json.Unmarshal(content, &records) == nil:
|
||||||
@@ -960,7 +981,13 @@ func (u *ui) applyRecentVaultRecords(records []recentVaultRecord) {
|
|||||||
if u.recentVaultGroups == nil {
|
if u.recentVaultGroups == nil {
|
||||||
u.recentVaultGroups = map[string][]string{}
|
u.recentVaultGroups = map[string][]string{}
|
||||||
}
|
}
|
||||||
|
if u.recentVaultUsedAt == nil {
|
||||||
|
u.recentVaultUsedAt = map[string]time.Time{}
|
||||||
|
}
|
||||||
u.recentVaultGroups[path] = append([]string(nil), record.LastGroup...)
|
u.recentVaultGroups[path] = append([]string(nil), record.LastGroup...)
|
||||||
|
if usedAt, err := time.Parse(time.RFC3339Nano, strings.TrimSpace(record.UsedAt)); err == nil {
|
||||||
|
u.recentVaultUsedAt[path] = usedAt
|
||||||
|
}
|
||||||
if len(filtered) == 6 {
|
if len(filtered) == 6 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -1020,6 +1047,7 @@ func (u *ui) saveRecentVaults() {
|
|||||||
records = append(records, recentVaultRecord{
|
records = append(records, recentVaultRecord{
|
||||||
Path: path,
|
Path: path,
|
||||||
LastGroup: append([]string(nil), u.recentVaultGroups[path]...),
|
LastGroup: append([]string(nil), u.recentVaultGroups[path]...),
|
||||||
|
UsedAt: u.recentVaultUsedAt[path].Format(time.RFC3339Nano),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
content, err := json.MarshalIndent(records, "", " ")
|
content, err := json.MarshalIndent(records, "", " ")
|
||||||
@@ -1084,6 +1112,7 @@ func (u *ui) noteRecentRemote(baseURL, path, username, password string, remember
|
|||||||
BaseURL: baseURL,
|
BaseURL: baseURL,
|
||||||
Path: path,
|
Path: path,
|
||||||
LastGroup: append([]string(nil), u.currentPath...),
|
LastGroup: append([]string(nil), u.currentPath...),
|
||||||
|
UsedAt: u.now().Format(time.RFC3339Nano),
|
||||||
}
|
}
|
||||||
if len(record.LastGroup) == 0 {
|
if len(record.LastGroup) == 0 {
|
||||||
record.LastGroup = u.recentRemoteGroup(baseURL, path)
|
record.LastGroup = u.recentRemoteGroup(baseURL, path)
|
||||||
@@ -1120,6 +1149,53 @@ func (u *ui) recentRemoteGroup(baseURL, path string) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) restoreStartupLifecycleTarget() {
|
||||||
|
localPath, localUsedAt := u.latestRecentVault()
|
||||||
|
remoteRecord, hasRemote, remoteUsedAt := u.latestRecentRemote()
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case hasRemote && (localPath == "" || remoteUsedAt.After(localUsedAt)):
|
||||||
|
u.lifecycleMode = "remote"
|
||||||
|
u.applyRecentRemoteRecord(remoteRecord)
|
||||||
|
case localPath != "":
|
||||||
|
u.lifecycleMode = "local"
|
||||||
|
u.vaultPath.SetText(localPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) latestRecentVault() (string, time.Time) {
|
||||||
|
for _, path := range u.recentVaults {
|
||||||
|
if strings.TrimSpace(path) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return path, u.recentVaultUsedAt[path]
|
||||||
|
}
|
||||||
|
return "", time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) latestRecentRemote() (recentRemoteRecord, bool, time.Time) {
|
||||||
|
for _, record := range u.recentRemotes {
|
||||||
|
if strings.TrimSpace(record.BaseURL) == "" || strings.TrimSpace(record.Path) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
usedAt, err := time.Parse(time.RFC3339Nano, strings.TrimSpace(record.UsedAt))
|
||||||
|
if err != nil {
|
||||||
|
usedAt = time.Time{}
|
||||||
|
}
|
||||||
|
return record, true, usedAt
|
||||||
|
}
|
||||||
|
return recentRemoteRecord{}, false, time.Time{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) applyRecentRemoteRecord(record recentRemoteRecord) {
|
||||||
|
u.remoteBaseURL.SetText(record.BaseURL)
|
||||||
|
u.remotePath.SetText(record.Path)
|
||||||
|
u.remoteUsername.SetText(record.Username)
|
||||||
|
u.remotePassword.SetText(record.Password)
|
||||||
|
u.remotePassword.Mask = '•'
|
||||||
|
u.rememberRemoteAuth.Value = strings.TrimSpace(record.Username) != "" || record.Password != ""
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) noteCurrentRemotePath() {
|
func (u *ui) noteCurrentRemotePath() {
|
||||||
status, ok := u.state.Session.(sessionStatus)
|
status, ok := u.state.Session.(sessionStatus)
|
||||||
if !ok || !status.IsRemote() || status.IsLocked() {
|
if !ok || !status.IsRemote() || status.IsLocked() {
|
||||||
@@ -1241,6 +1317,28 @@ func (u *ui) restoreEntriesPath(path []string) {
|
|||||||
u.enterHiddenVaultRoot()
|
u.enterHiddenVaultRoot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ui) rememberEntriesSectionState() {
|
||||||
|
if u.state.Section != appstate.SectionEntries {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u.entriesState = entriesSectionState{
|
||||||
|
Path: append([]string(nil), u.currentPath...),
|
||||||
|
SearchQuery: u.search.Text(),
|
||||||
|
SelectedEntryID: u.state.SelectedEntryID,
|
||||||
|
Editing: u.editingEntry,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ui) restoreEntriesSectionState() {
|
||||||
|
u.search.SetText(u.entriesState.SearchQuery)
|
||||||
|
u.restoreEntriesPath(u.entriesState.Path)
|
||||||
|
u.state.SelectedEntryID = u.entriesState.SelectedEntryID
|
||||||
|
u.editingEntry = u.entriesState.Editing && strings.TrimSpace(u.entriesState.SelectedEntryID) != ""
|
||||||
|
if u.editingEntry || strings.TrimSpace(u.state.SelectedEntryID) != "" {
|
||||||
|
u.loadSelectedEntryIntoEditor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ui) displayPath() []string {
|
func (u *ui) displayPath() []string {
|
||||||
path := append([]string(nil), u.currentPath...)
|
path := append([]string(nil), u.currentPath...)
|
||||||
root := u.hiddenVaultRoot()
|
root := u.hiddenVaultRoot()
|
||||||
@@ -1743,13 +1841,7 @@ func (u *ui) layout(gtx layout.Context) layout.Dimensions {
|
|||||||
for i := range u.recentRemoteClicks {
|
for i := range u.recentRemoteClicks {
|
||||||
for u.recentRemoteClicks[i].Clicked(gtx) {
|
for u.recentRemoteClicks[i].Clicked(gtx) {
|
||||||
if i < len(u.recentRemotes) {
|
if i < len(u.recentRemotes) {
|
||||||
record := u.recentRemotes[i]
|
u.applyRecentRemoteRecord(u.recentRemotes[i])
|
||||||
u.remoteBaseURL.SetText(record.BaseURL)
|
|
||||||
u.remotePath.SetText(record.Path)
|
|
||||||
u.remoteUsername.SetText(record.Username)
|
|
||||||
u.remotePassword.SetText(record.Password)
|
|
||||||
u.remotePassword.Mask = '•'
|
|
||||||
u.rememberRemoteAuth.Value = strings.TrimSpace(record.Username) != "" || record.Password != ""
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+106
@@ -2227,6 +2227,45 @@ func TestUIShowEntriesSectionRestoresHiddenRootAfterLeavingEntries(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIShowEntriesSectionRestoresEntriesViewState(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
u := newUIWithModel("desktop", vault.Model{
|
||||||
|
Entries: []vault.Entry{
|
||||||
|
{ID: "amazon", Title: "Amazon", Username: "joe@julianfamily.org", Path: []string{"keepass", "Joe", "Internet"}},
|
||||||
|
{ID: "aws", Title: "Amazon AWS", Username: "joe@julianfamily.org", Path: []string{"keepass", "Joe", "Internet"}},
|
||||||
|
{ID: "git", Title: "Git Server", Username: "joejulian", Path: []string{"keepass", "Joe", "Internet"}},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
u.showEntriesSection()
|
||||||
|
u.setCurrentPath([]string{"keepass", "Joe", "Internet"})
|
||||||
|
u.search.SetText("amazon")
|
||||||
|
u.filter()
|
||||||
|
u.state.SelectedEntryID = "amazon"
|
||||||
|
u.editingEntry = true
|
||||||
|
u.loadSelectedEntryIntoEditor()
|
||||||
|
|
||||||
|
u.showAPITokensSection()
|
||||||
|
u.showEntriesSection()
|
||||||
|
|
||||||
|
if got := u.currentPath; !slices.Equal(got, []string{"keepass", "Joe", "Internet"}) {
|
||||||
|
t.Fatalf("currentPath after returning to entries = %v, want [keepass Joe Internet]", got)
|
||||||
|
}
|
||||||
|
if got := u.search.Text(); got != "amazon" {
|
||||||
|
t.Fatalf("search text after returning to entries = %q, want amazon", got)
|
||||||
|
}
|
||||||
|
if got := u.state.SelectedEntryID; got != "amazon" {
|
||||||
|
t.Fatalf("SelectedEntryID after returning to entries = %q, want amazon", got)
|
||||||
|
}
|
||||||
|
if !u.editingEntry {
|
||||||
|
t.Fatal("editingEntry = false, want true after returning to entries")
|
||||||
|
}
|
||||||
|
if got := u.filteredTitles(); !slices.Equal(got, []string{"Amazon", "Amazon AWS"}) {
|
||||||
|
t.Fatalf("filteredTitles() after returning to entries = %v, want [Amazon Amazon AWS]", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUINoteRecentVaultDeduplicatesAndOrdersMostRecentFirst(t *testing.T) {
|
func TestUINoteRecentVaultDeduplicatesAndOrdersMostRecentFirst(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -2262,6 +2301,34 @@ func TestUILoadsRecentVaultsFromPersistedConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIStartupPreselectsMostRecentLocalVault(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
configPath := filepath.Join(t.TempDir(), "recent-vaults.json")
|
||||||
|
first := newUIWithSession("desktop", &session.Manager{})
|
||||||
|
first.recentVaultsPath = configPath
|
||||||
|
first.recentVaults = nil
|
||||||
|
first.recentVaultUsedAt = map[string]time.Time{}
|
||||||
|
first.now = func() time.Time { return time.Date(2026, 3, 30, 12, 0, 0, 0, time.UTC) }
|
||||||
|
first.noteRecentVault("/tmp/older.kdbx")
|
||||||
|
first.now = func() time.Time { return time.Date(2026, 3, 30, 13, 0, 0, 0, time.UTC) }
|
||||||
|
first.noteRecentVault("/tmp/newer.kdbx")
|
||||||
|
|
||||||
|
second := newUIWithSession("desktop", &session.Manager{}, statePaths{
|
||||||
|
DefaultSaveAsPath: filepath.Join(t.TempDir(), "vault.kdbx"),
|
||||||
|
RecentVaultsPath: configPath,
|
||||||
|
RecentRemotesPath: filepath.Join(t.TempDir(), "recent-remotes.json"),
|
||||||
|
UIPreferencesPath: filepath.Join(t.TempDir(), "ui-prefs.json"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if got := second.lifecycleMode; got != "local" {
|
||||||
|
t.Fatalf("lifecycleMode = %q, want local", got)
|
||||||
|
}
|
||||||
|
if got := second.vaultPath.Text(); got != "/tmp/newer.kdbx" {
|
||||||
|
t.Fatalf("vaultPath = %q, want /tmp/newer.kdbx", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIRecentVaultsPersistLastOpenedGroupPerVault(t *testing.T) {
|
func TestUIRecentVaultsPersistLastOpenedGroupPerVault(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -2382,6 +2449,44 @@ func TestUIRecentRemoteConnectionsPersistAndReload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUIStartupPreselectsNewestTargetAcrossLocalAndRemote(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
dir := t.TempDir()
|
||||||
|
vaultsPath := filepath.Join(dir, "recent-vaults.json")
|
||||||
|
remotesPath := filepath.Join(dir, "recent-remotes.json")
|
||||||
|
paths := statePaths{
|
||||||
|
DefaultSaveAsPath: filepath.Join(dir, "vault.kdbx"),
|
||||||
|
RecentVaultsPath: vaultsPath,
|
||||||
|
RecentRemotesPath: remotesPath,
|
||||||
|
UIPreferencesPath: filepath.Join(dir, "ui-prefs.json"),
|
||||||
|
}
|
||||||
|
|
||||||
|
first := newUIWithSession("desktop", &session.Manager{}, paths)
|
||||||
|
first.now = func() time.Time { return time.Date(2026, 3, 30, 12, 0, 0, 0, time.UTC) }
|
||||||
|
first.noteRecentVault("/tmp/local.kdbx")
|
||||||
|
first.now = func() time.Time { return time.Date(2026, 3, 30, 13, 0, 0, 0, time.UTC) }
|
||||||
|
first.noteRecentRemote("https://dav.example.com", "vaults/home.kdbx", "alice", "secret-1", true)
|
||||||
|
|
||||||
|
second := newUIWithSession("desktop", &session.Manager{}, paths)
|
||||||
|
|
||||||
|
if got := second.lifecycleMode; got != "remote" {
|
||||||
|
t.Fatalf("lifecycleMode = %q, want remote", got)
|
||||||
|
}
|
||||||
|
if got := second.remoteBaseURL.Text(); got != "https://dav.example.com" {
|
||||||
|
t.Fatalf("remoteBaseURL = %q, want https://dav.example.com", got)
|
||||||
|
}
|
||||||
|
if got := second.remotePath.Text(); got != "vaults/home.kdbx" {
|
||||||
|
t.Fatalf("remotePath = %q, want vaults/home.kdbx", got)
|
||||||
|
}
|
||||||
|
if got := second.remoteUsername.Text(); got != "alice" {
|
||||||
|
t.Fatalf("remoteUsername = %q, want alice", got)
|
||||||
|
}
|
||||||
|
if got := second.remotePassword.Text(); got != "secret-1" {
|
||||||
|
t.Fatalf("remotePassword = %q, want secret-1", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUIGroupToolsDisclosureStatePersists(t *testing.T) {
|
func TestUIGroupToolsDisclosureStatePersists(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
@@ -3106,6 +3211,7 @@ func TestUILocalLifecycleActionErrorsAreVisibleAndSpecific(t *testing.T) {
|
|||||||
|
|
||||||
u := newUIWithSession("desktop", &session.Manager{})
|
u := newUIWithSession("desktop", &session.Manager{})
|
||||||
u.masterPassword.SetText("correct horse battery staple")
|
u.masterPassword.SetText("correct horse battery staple")
|
||||||
|
u.vaultPath.SetText("")
|
||||||
|
|
||||||
u.runAction("open vault", u.openVaultAction)
|
u.runAction("open vault", u.openVaultAction)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user