Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ Styles from theme config. Format helpers (timestamps, durations). `RunProgram` r
- Unit tests per package
- E2E tests in `internal/app/e2e_test.go` + `e2e_helpers_test.go` use mock HTTP server + mock player
- E2E tests drive the bubbletea Update loop programmatically (no real TUI)
- `feedCmd` in `e2e_helpers_test.go` skips a command that needs more than 200 ms, so that a test does not wait for a `tea.Tick`. A request to an address with no server, or a load that writes to a database file, can need more on Windows in CI. For such a command, use `feedCmdWithin` with `slowCmdWait`.
- `internal/app/model_test.go`, `playback_test.go`, `bookmarks_test.go`, `render_test.go` for app-level tests
- `internal/ui/program_unix_test.go` starts the test program again as a child process, sends SIGHUP to it, and checks that it gets to the place where a `main` calls `Cleanup`. The child runs its program with `ui.RunProgram`, as each `main` does. It does not run on Windows.
- `internal/cache/*_test.go` for cache store and helper behavior
Expand Down
23 changes: 20 additions & 3 deletions internal/app/e2e_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,27 @@ func newE2EModelUnauthWithDB(t *testing.T, srv *httptest.Server, mp *mockPlayer)
// E2E helpers — driving the Update→Cmd→Msg loop
// ---------------------------------------------------------------------------

// feedCmdWait is the time that feedCmd waits for a command.
const feedCmdWait = 200 * time.Millisecond

// slowCmdWait is the time that feedCmdWithin waits for a command that a test
// needs and that can be slow. It is longer than the timeout of one request of
// abs.Client, which is 10 seconds.
const slowCmdWait = 15 * time.Second

// feedCmd executes a tea.Cmd, feeds the resulting message back into Update,
// and returns the new model + the cmd returned by Update.
// Commands that block longer than 200ms (e.g. tea.Tick) are skipped.
// Commands that block longer than feedCmdWait (e.g. tea.Tick) are skipped.
func feedCmd(m Model, cmd tea.Cmd) (Model, tea.Cmd) {
return feedCmdWithin(m, cmd, feedCmdWait)
}

// feedCmdWithin is feedCmd with a wait of its own. Use slowCmdWait for a
// command that the test must not skip and that can need more than feedCmdWait
// on a slow machine, for example on Windows in CI: a request to an address
// with no server, or a load that writes to a database file. A tea.Tick that
// is shorter than the wait is not skipped.
func feedCmdWithin(m Model, cmd tea.Cmd, wait time.Duration) (Model, tea.Cmd) {
if cmd == nil {
return m, nil
}
Expand All @@ -543,13 +560,13 @@ func feedCmd(m Model, cmd tea.Cmd) (Model, tea.Cmd) {
if batch, ok := r.msg.(tea.BatchMsg); ok {
var lastCmd tea.Cmd
for _, c := range batch {
m, lastCmd = feedCmd(m, c)
m, lastCmd = feedCmdWithin(m, c, wait)
}
return m, lastCmd
}
res, nextCmd := m.Update(r.msg)
return res.(Model), nextCmd
case <-time.After(200 * time.Millisecond):
case <-time.After(wait):
// Drain the channel in case the goroutine completes after timeout
go func() { <-ch; <-done }()
return m, nil
Expand Down
13 changes: 9 additions & 4 deletions internal/app/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ func TestE2E_LoginFailure(t *testing.T) {
// Press enter
m, cmd := e2ePressSpecial(m, tea.KeyEnter)

// Execute login command — should fail (unreachable)
m, _ = feedCmd(m, cmd)
// Execute login command — should fail (unreachable). A refused connection
// can need more than feedCmdWait on Windows.
m, _ = feedCmdWithin(m, cmd, slowCmdWait)

// Should still be on login with error
assertScreen(t, m, ScreenLogin)
Expand Down Expand Up @@ -2264,7 +2265,10 @@ func TestE2E_CacheSurvivesRestart(t *testing.T) {
m1 := NewWithPlayer(cfg, store, cachedClient, cacheStore, mp)
m1 = e2eSetSize(m1, 120, 40)

m1 = feedCmdChain(m1, m1.Init(), 5)
// The load of the home screen sends its requests and writes to the
// database file in one command, which can need more than feedCmdWait.
m1, cmd := feedCmdWithin(m1, m1.Init(), slowCmdWait)
m1 = feedCmdChain(m1, cmd, 4)

if m1.home.Loading() {
t.Fatal("home should not be loading after first init")
Expand All @@ -2279,7 +2283,8 @@ func TestE2E_CacheSurvivesRestart(t *testing.T) {
m2 := NewWithPlayer(cfg, store, cachedClient, cacheStore, mp)
m2 = e2eSetSize(m2, 120, 40)

m2 = feedCmdChain(m2, m2.Init(), 5)
m2, cmd = feedCmdWithin(m2, m2.Init(), slowCmdWait)
m2 = feedCmdChain(m2, cmd, 4)

if m2.home.Loading() {
t.Fatal("home should not be loading after second init (cache should serve data)")
Expand Down
Loading