Explain empty TUI views instead of showing a bare table
An account folder is only registered in the index by an import, so a freshly configured account.toml produced an empty accounts table with nothing to say why or what to do about it. Each view now explains its own emptiness and names the key that resolves it, distinguishing configured-but-not-imported from no-folders-at-all, and telling apart a filter that matched nothing from having no data. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -257,6 +257,109 @@ func TestReportViewExcludesTransfers(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// newEmptyModel builds a model over an index with nothing in it.
|
||||
func newEmptyModel(t *testing.T, accounts []*config.Account) *Model {
|
||||
t.Helper()
|
||||
db, err := store.Open(filepath.Join(t.TempDir(), "index.db"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { db.Close() })
|
||||
|
||||
m := New("/data/root", db, accounts, rules.New(&config.Rules{}))
|
||||
if err := m.reload(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
m.Update(tea.WindowSizeMsg{Width: 120, Height: 30})
|
||||
return m
|
||||
}
|
||||
|
||||
// Configured but never imported is the confusing case: the folders exist on
|
||||
// disk, but nothing is registered in the index until an import runs.
|
||||
func TestEmptyAccountsWithConfiguredFolders(t *testing.T) {
|
||||
m := newEmptyModel(t, []*config.Account{
|
||||
{Slug: "checking", Currency: "EUR", Parser: "csv"},
|
||||
{Slug: "savings", Currency: "EUR", Parser: "csv"},
|
||||
})
|
||||
|
||||
view := m.View()
|
||||
for _, want := range []string{"No accounts imported yet", "2 account folder(s)", "/data/root", "Press i to import"} {
|
||||
if !strings.Contains(view, want) {
|
||||
t.Errorf("accounts view missing %q:\n%s", want, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyAccountsWithNoFolders(t *testing.T) {
|
||||
m := newEmptyModel(t, nil)
|
||||
|
||||
view := m.View()
|
||||
for _, want := range []string{"No account folders found", "/data/root", "account.toml"} {
|
||||
if !strings.Contains(view, want) {
|
||||
t.Errorf("accounts view missing %q:\n%s", want, view)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyTransactionsMessages(t *testing.T) {
|
||||
m := newEmptyModel(t, nil)
|
||||
key(t, m, "2")
|
||||
|
||||
if view := m.View(); !strings.Contains(view, "No transactions yet") {
|
||||
t.Errorf("expected an empty-transactions message:\n%s", view)
|
||||
}
|
||||
|
||||
// Each way of emptying the view explains itself differently.
|
||||
m.filter.AccountSlug = "checking"
|
||||
if err := m.reloadTxns(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if view := m.View(); !strings.Contains(view, "No transactions in checking") {
|
||||
t.Errorf("expected an account-scoped message:\n%s", view)
|
||||
}
|
||||
|
||||
m.filter.AccountSlug = ""
|
||||
m.filter.Search = "lidl"
|
||||
if err := m.reloadTxns(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if view := m.View(); !strings.Contains(view, "No transactions match /lidl") {
|
||||
t.Errorf("expected a search message:\n%s", view)
|
||||
}
|
||||
|
||||
m.filter.Search = ""
|
||||
m.onlyUntagged = true
|
||||
if err := m.reloadTxns(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if view := m.View(); !strings.Contains(view, "Nothing untagged") {
|
||||
t.Errorf("expected an untagged message:\n%s", view)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyReportMessage(t *testing.T) {
|
||||
m := newEmptyModel(t, nil)
|
||||
key(t, m, "3")
|
||||
if view := m.View(); !strings.Contains(view, "Nothing to report yet") {
|
||||
t.Errorf("expected an empty-report message:\n%s", view)
|
||||
}
|
||||
}
|
||||
|
||||
// With data present, the tables render instead of the empty-state text.
|
||||
func TestNoEmptyMessageWhenPopulated(t *testing.T) {
|
||||
m, _ := newTestModel(t)
|
||||
for _, k := range []string{"1", "2", "3"} {
|
||||
key(t, m, k)
|
||||
if got := m.emptyMessage(); got != "" {
|
||||
t.Errorf("view %s showed an empty message with data present: %q", k, got)
|
||||
}
|
||||
}
|
||||
key(t, m, "2")
|
||||
if !strings.Contains(m.View(), "LIDL SOFIA") {
|
||||
t.Error("expected the transactions table to render")
|
||||
}
|
||||
}
|
||||
|
||||
func findTxn(t *testing.T, db *store.DB, desc string) model.Transaction {
|
||||
t.Helper()
|
||||
txns, err := db.Transactions(store.Filter{})
|
||||
|
||||
Reference in New Issue
Block a user