Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7dcae43

Browse files
fix(tui): address auto-follow edge cases and add tests
1 parent 1604cc3 commit 7dcae43

5 files changed

Lines changed: 139 additions & 6 deletions

File tree

internal/tui/follow_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package tui
2+
3+
import (
4+
"testing"
5+
6+
"charm.land/bubbles/v2/viewport"
7+
"github.com/SurgeDM/Surge/internal/engine/events"
8+
"github.com/SurgeDM/Surge/internal/engine/types"
9+
)
10+
11+
func TestAutoFollow_BrandNewDownload(t *testing.T) {
12+
m := RootModel{
13+
activeTab: TabDone,
14+
pinnedTab: -1,
15+
list: NewDownloadList(80, 20),
16+
logViewport: viewport.New(viewport.WithWidth(40), viewport.WithHeight(5)),
17+
}
18+
19+
msg := events.DownloadStartedMsg{
20+
DownloadID: "new-1",
21+
Filename: "new-file",
22+
Total: 100,
23+
State: types.NewProgressState("new-1", 100),
24+
}
25+
26+
updated, _ := m.Update(msg)
27+
m2 := updated.(RootModel)
28+
29+
if m2.activeTab != TabActive {
30+
t.Errorf("Expected activeTab to be TabActive (1), got %d", m2.activeTab)
31+
}
32+
if m2.SelectedDownloadID != "" {
33+
t.Errorf("Expected SelectedDownloadID to be cleared, got %q", m2.SelectedDownloadID)
34+
}
35+
}
36+
37+
func TestAutoFollow_ExistingDownloadRestart(t *testing.T) {
38+
dm := NewDownloadModel("existing-1", "http://example.com", "file", 100)
39+
dm.paused = true
40+
41+
m := RootModel{
42+
activeTab: TabQueued,
43+
pinnedTab: -1,
44+
downloads: []*DownloadModel{dm},
45+
list: NewDownloadList(80, 20),
46+
logViewport: viewport.New(viewport.WithWidth(40), viewport.WithHeight(5)),
47+
}
48+
49+
msg := events.DownloadStartedMsg{
50+
DownloadID: "existing-1",
51+
Filename: "file",
52+
Total: 100,
53+
State: types.NewProgressState("existing-1", 100),
54+
}
55+
56+
updated, _ := m.Update(msg)
57+
m2 := updated.(RootModel)
58+
59+
if m2.activeTab != TabActive {
60+
t.Errorf("Expected activeTab to be TabActive (1), got %d", m2.activeTab)
61+
}
62+
}
63+
64+
func TestAutoFollow_SuppressedByPin(t *testing.T) {
65+
m := RootModel{
66+
activeTab: TabDone,
67+
pinnedTab: TabDone,
68+
list: NewDownloadList(80, 20),
69+
logViewport: viewport.New(viewport.WithWidth(40), viewport.WithHeight(5)),
70+
}
71+
72+
msg := events.DownloadStartedMsg{
73+
DownloadID: "new-1",
74+
Filename: "new-file",
75+
Total: 100,
76+
State: types.NewProgressState("new-1", 100),
77+
}
78+
79+
updated, _ := m.Update(msg)
80+
m2 := updated.(RootModel)
81+
82+
if m2.activeTab != TabDone {
83+
t.Errorf("Expected activeTab to remain TabDone (2) because it is pinned, got %d", m2.activeTab)
84+
}
85+
}
86+
87+
func TestAutoFollow_QueuedToActiveTransition(t *testing.T) {
88+
// Test that transitioning from Queued to Active (via DownloadStartedMsg)
89+
// also triggers auto-follow if we are currently on Queued tab.
90+
dm := NewDownloadModel("id-1", "http://example.com", "file", 100)
91+
// Initially it's queued (done=false, paused=false, speed=0, connections=0)
92+
93+
m := RootModel{
94+
activeTab: TabQueued,
95+
pinnedTab: -1,
96+
downloads: []*DownloadModel{dm},
97+
list: NewDownloadList(80, 20),
98+
logViewport: viewport.New(viewport.WithWidth(40), viewport.WithHeight(5)),
99+
}
100+
101+
// Update list to reflect initial state
102+
m.UpdateListItems()
103+
104+
msg := events.DownloadStartedMsg{
105+
DownloadID: "id-1",
106+
Filename: "file",
107+
Total: 100,
108+
State: types.NewProgressState("id-1", 100),
109+
}
110+
111+
updated, _ := m.Update(msg)
112+
m2 := updated.(RootModel)
113+
114+
if m2.activeTab != TabActive {
115+
t.Errorf("Expected auto-follow to Active tab, got %d", m2.activeTab)
116+
}
117+
}

internal/tui/list.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ func (m *RootModel) UpdateListItems() {
272272
var newTab int
273273
if d.done {
274274
newTab = TabDone
275-
} else if !d.paused && !d.pausing && (d.Speed > 0 || d.Connections > 0 || d.resuming) {
275+
} else if !d.paused && !d.pausing && (d.Speed > 0 || d.Connections > 0 || d.resuming || d.started) {
276276
newTab = TabActive
277277
} else {
278278
newTab = TabQueued

internal/tui/model.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ type DownloadModel struct {
9898
state *types.ProgressState // Keep for now if needed for details view, but mostly passive
9999

100100
done bool
101+
started bool // Engine has confirmed start
101102
err error
102103
paused bool
103104
pausing bool // UI state: transitioning to pause
@@ -320,22 +321,29 @@ func InitialRootModel(serverPort int, currentVersion string, service core.Downlo
320321
switch s.Status {
321322
case "completed":
322323
dm.done = true
324+
dm.started = true
323325
dm.progress.SetPercent(1.0)
324326
case "error":
325327
dm.done = true
328+
dm.started = true
326329
case "pausing":
327330
dm.pausing = true
331+
dm.started = true
328332
case "paused":
329333
if settings.General.AutoResume {
330334
dm.resuming = true
331335
dm.paused = true // Will update when resume event received
332336
} else {
333337
dm.paused = true
334338
}
339+
dm.started = true
335340
case "queued":
336341
// Always resume queued items
337342
dm.resuming = true
338343
dm.paused = true // Will update when resume event received
344+
dm.started = false
345+
case "downloading":
346+
dm.started = true
339347
}
340348

341349
if s.TotalSize > 0 {
@@ -528,12 +536,12 @@ func (m RootModel) getFilteredDownloads() []*DownloadModel {
528536
switch m.activeTab {
529537
case TabQueued:
530538
// Queued includes paused downloads and anything not currently active or done
531-
if d.done || (!d.paused && !d.pausing && (d.Speed > 0 || d.Connections > 0 || d.resuming)) {
539+
if d.done || (!d.paused && !d.pausing && (d.Speed > 0 || d.Connections > 0 || d.resuming || d.started)) {
532540
continue
533541
}
534542
case TabActive:
535543
// Active excludes paused downloads and anything without current activity
536-
if d.done || d.paused || d.pausing || (d.Speed == 0 && d.Connections == 0 && !d.resuming) {
544+
if d.done || d.paused || d.pausing || (d.Speed == 0 && d.Connections == 0 && !d.resuming && !d.started) {
537545
continue
538546
}
539547
case TabDone:

internal/tui/update_events.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,17 @@ func (m RootModel) updateEvents(msg tea.Msg) (tea.Model, tea.Cmd) {
6464
if real.Destination == "" {
6565
real.Destination = temp.Destination
6666
}
67+
68+
if m.SelectedDownloadID == msg.tempID || (m.GetSelectedDownload() != nil && m.GetSelectedDownload().ID == msg.tempID) {
69+
m.SelectedDownloadID = msg.id
70+
}
6771
_ = m.removeDownloadByID(msg.tempID)
6872
} else if temp != nil {
73+
if m.SelectedDownloadID == msg.tempID || (m.GetSelectedDownload() != nil && m.GetSelectedDownload().ID == msg.tempID) {
74+
m.SelectedDownloadID = msg.id
75+
}
6976
temp.ID = msg.id
7077
}
71-
if m.SelectedDownloadID == msg.tempID {
72-
m.SelectedDownloadID = msg.id
73-
}
7478
}
7579
m.UpdateListItems()
7680
return m, nil
@@ -165,6 +169,8 @@ func (m RootModel) updateEvents(msg tea.Msg) (tea.Model, tea.Cmd) {
165169
if d.state != nil {
166170
d.state.SetTotalSize(msg.Total) // Keep state updated for verification if needed
167171
}
172+
d.started = true
173+
m.SelectedDownloadID = msg.DownloadID
168174
m.UpdateListItems()
169175
m.addLogEntry(LogStyleStarted.Render("\u2b07 Started: " + msg.Filename))
170176
return m, tea.Batch(progressCmd, m.spinner.Tick)
@@ -176,6 +182,7 @@ func (m RootModel) updateEvents(msg tea.Msg) (tea.Model, tea.Cmd) {
176182
if msg.State != nil {
177183
newDownload.state = msg.State
178184
}
185+
newDownload.started = true
179186
m.downloads = append(m.downloads, newDownload)
180187
m.SelectedDownloadID = msg.DownloadID
181188
m.UpdateListItems()

internal/tui/update_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ func TestUpdate_EnqueueSuccessMergesOptimisticEntryAfterStart(t *testing.T) {
122122
m := RootModel{
123123
downloads: []*DownloadModel{optimistic},
124124
SelectedDownloadID: "pending-1",
125+
pinnedTab: -1,
125126
list: NewDownloadList(80, 20),
126127
logViewport: viewport.New(viewport.WithWidth(40), viewport.WithHeight(5)),
127128
}

0 commit comments

Comments
 (0)