The status bar only ever named Enter, while the hint for searching releases of a title not in the library said (a). Enter is now the one key for adding, and the hint says Enter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
154 lines
5.3 KiB
Go
154 lines
5.3 KiB
Go
package ui
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gdamore/tcell/v2"
|
|
|
|
"git.stinnesbeck.com/nils/arr-tui/internal/radarr"
|
|
)
|
|
|
|
func TestTraktListsInTheSearchView(t *testing.T) {
|
|
traktSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Write([]byte(`[
|
|
{"watchers":1234,"movie":{"title":"Dune","year":2021,"ids":{"trakt":1,"tmdb":438631}}},
|
|
{"watchers":900,"movie":{"title":"Unknown To Radarr","year":2020,"ids":{"trakt":2,"tmdb":1}}},
|
|
{"watchers":7,"movie":{"title":"No Ids","year":2019,"ids":{"trakt":3}}},
|
|
{"watchers":5,"movie":{"title":"Heat","year":1995,"ids":{"trakt":4,"tmdb":949}}}]`))
|
|
}))
|
|
defer traktSrv.Close()
|
|
radarrSrv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Query().Get("term") {
|
|
case "tmdb:438631":
|
|
w.Write([]byte(`[{"id":5,"title":"Dune","year":2021,"tmdbId":438631}]`))
|
|
case "tmdb:949":
|
|
w.Write([]byte(`[{"title":"Heat","year":1995,"tmdbId":949}]`))
|
|
default:
|
|
w.Write([]byte(`[]`))
|
|
}
|
|
}))
|
|
defer radarrSrv.Close()
|
|
|
|
a := newTestApp(t)
|
|
v := a.searches[0]
|
|
// Without a client ID the list says how to get one.
|
|
v.activate()
|
|
if !v.browsing || !strings.Contains(v.placeholder, "Trakt client ID") {
|
|
t.Fatalf("unconfigured: browsing %v, placeholder %q", v.browsing, v.placeholder)
|
|
}
|
|
|
|
a.radarr.Reconfigure(radarrSrv.URL, "k")
|
|
a.trakt.Reconfigure("id")
|
|
a.trakt.SetBaseURL(traktSrv.URL)
|
|
d := v.fetchList(v.lists[0])
|
|
if d.err != nil || len(d.results) != 2 {
|
|
t.Fatalf("fetched %d results, err %v", len(d.results), d.err)
|
|
}
|
|
// In the list's order; titles Radarr does not know, or without ids, are left out.
|
|
if d.results[0].title() != "Dune" || d.results[1].title() != "Heat" || !d.results[0].inLibrary() || d.results[1].inLibrary() {
|
|
t.Errorf("results %v %v", d.results[0].title(), d.results[1].title())
|
|
}
|
|
if strings.Join(d.notes, "|") != "1.2k watching|5 watching" {
|
|
t.Errorf("notes %q", d.notes)
|
|
}
|
|
|
|
// A fresh list is shown from memory, with its numbers and its name in the title.
|
|
v.discover[0] = d
|
|
v.showList()
|
|
if main, _ := v.list.GetItemText(0); !strings.Contains(main, "Dune") || !strings.Contains(main, "1.2k watching") {
|
|
t.Errorf("first row %q", main)
|
|
}
|
|
if title := v.list.GetTitle(); !strings.Contains(title, "Trending now") {
|
|
t.Errorf("title %q", title)
|
|
}
|
|
|
|
// ] and [ switch lists while browsing.
|
|
v.discover[1] = &discovered{at: time.Now()}
|
|
v.list.GetInputCapture()(tcell.NewEventKey(tcell.KeyRune, ']', 0))
|
|
if v.listIdx != 1 || !strings.Contains(v.list.GetTitle(), "Most watched") || v.placeholder != "Nothing on this list right now." {
|
|
t.Errorf("after ]: list %d, title %q, placeholder %q", v.listIdx, v.list.GetTitle(), v.placeholder)
|
|
}
|
|
v.list.GetInputCapture()(tcell.NewEventKey(tcell.KeyRune, '[', 0))
|
|
// From the empty search box too (where the cursor is when the view opens).
|
|
v.input.GetInputCapture()(tcell.NewEventKey(tcell.KeyRune, '[', 0))
|
|
if v.listIdx != len(v.lists)-1 || v.input.GetText() != "" {
|
|
t.Errorf("[ wraps around: list %d, search box %q", v.listIdx, v.input.GetText())
|
|
}
|
|
|
|
// Search results replace the list; their rows have no numbers.
|
|
v.setResults(d.results)
|
|
if v.browsing || v.notes != nil || strings.Contains(v.list.GetTitle(), "lists") {
|
|
t.Errorf("search results: browsing %v, title %q", v.browsing, v.list.GetTitle())
|
|
}
|
|
if main, _ := v.list.GetItemText(0); strings.Contains(main, "watching") {
|
|
t.Errorf("search row %q", main)
|
|
}
|
|
}
|
|
|
|
func TestListNumbersAreRightAligned(t *testing.T) {
|
|
sim := tcell.NewSimulationScreen("")
|
|
if err := sim.Init(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer sim.Fini()
|
|
sim.SetSize(60, 6)
|
|
|
|
a := newTestApp(t)
|
|
v := a.searches[0]
|
|
a.trakt.Reconfigure("id")
|
|
v.discover[0] = &discovered{at: time.Now(),
|
|
results: []result{movieResult{Movie: radarr.Movie{Title: "Dune", Year: 2021}}, movieResult{Movie: radarr.Movie{Title: "A Much Longer Title", Year: 2024}}},
|
|
notes: []string{"1.2k watching", "5 watching"}}
|
|
v.showList()
|
|
|
|
ends := func(width int) []int {
|
|
sim.Clear()
|
|
l := notesList{v.list, v}
|
|
l.SetRect(0, 0, width, 6)
|
|
l.Draw(sim)
|
|
var out []int
|
|
for y := 1; y <= 2; y++ {
|
|
row := strings.TrimRight(rowText(sim, y, 0, width), " │")
|
|
out = append(out, len([]rune(row)))
|
|
if !strings.HasSuffix(row, "watching") {
|
|
t.Errorf("row %d does not end with its number: %q", y, row)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
if e := ends(60); e[0] != e[1] {
|
|
t.Errorf("numbers end in columns %v, want the same", e)
|
|
}
|
|
// After a resize they line up at the new edge.
|
|
if e := ends(44); e[0] != e[1] || e[0] > 44 {
|
|
t.Errorf("after resize: numbers end in columns %v", e)
|
|
}
|
|
}
|
|
|
|
func TestEnterAddsAndAIsAnOrdinaryKey(t *testing.T) {
|
|
a := newTestApp(t)
|
|
v := a.searches[0]
|
|
v.setResults([]result{movieResult{Movie: radarr.Movie{Title: "Dune", Year: 2021, TMDBID: 1}}})
|
|
// a is no longer a key of the list: it goes on to the list (which ignores it).
|
|
if got := v.list.GetInputCapture()(tcell.NewEventKey(tcell.KeyRune, 'a', 0)); got == nil {
|
|
t.Error("a is still taken by the list")
|
|
}
|
|
// The hint for releases of a title not in the library names Enter.
|
|
v.interactive(0)
|
|
if msg := plainText(a.status.GetText(false)) + toastText(a); !strings.Contains(msg, "first (Enter)") {
|
|
t.Errorf("hint: %q", msg)
|
|
}
|
|
}
|
|
|
|
func toastText(a *App) string {
|
|
var b strings.Builder
|
|
for _, t := range a.toasts {
|
|
b.WriteString(t.text)
|
|
}
|
|
return b.String()
|
|
}
|