|
6 | 6 |
|
7 | 7 | "github.com/Astro-Han/diffpane/internal" |
8 | 8 | tea "github.com/charmbracelet/bubbletea" |
| 9 | + "github.com/charmbracelet/lipgloss" |
9 | 10 | ) |
10 | 11 |
|
11 | 12 | func file(path string, adds int) internal.FileDiff { |
@@ -91,6 +92,31 @@ func TestModelPausedFollowTracksNewFiles(t *testing.T) { |
91 | 92 | } |
92 | 93 | } |
93 | 94 |
|
| 95 | +// TestModelPausedFollowIgnoresVanishedChangedPaths verifies transient changes |
| 96 | +// that disappear before the diff refresh lands do not inflate +N new. |
| 97 | +func TestModelPausedFollowIgnoresVanishedChangedPaths(t *testing.T) { |
| 98 | + model := NewModel("repo", "/tmp/repo", "sha", []internal.FileDiff{ |
| 99 | + file("a.txt", 1), |
| 100 | + }) |
| 101 | + model.FollowOn = false |
| 102 | + model.CurrentIdx = 0 |
| 103 | + |
| 104 | + updated, _ := model.Update(FilesUpdatedMsg{ |
| 105 | + Files: []internal.FileDiff{ |
| 106 | + file("a.txt", 1), |
| 107 | + }, |
| 108 | + ChangedPaths: []string{"temp.txt"}, |
| 109 | + }) |
| 110 | + |
| 111 | + got := updated.(Model) |
| 112 | + if got.NewCount != 0 { |
| 113 | + t.Fatalf("NewCount = %d, want 0", got.NewCount) |
| 114 | + } |
| 115 | + if len(got.NewFiles) != 0 { |
| 116 | + t.Fatalf("NewFiles = %#v, want empty", got.NewFiles) |
| 117 | + } |
| 118 | +} |
| 119 | + |
94 | 120 | // TestModelOverlayQueuesUpdates verifies overlay mode freezes the view and applies pending updates on close. |
95 | 121 | func TestModelOverlayQueuesUpdates(t *testing.T) { |
96 | 122 | model := NewModel("repo", "/tmp/repo", "sha", []internal.FileDiff{ |
@@ -230,6 +256,40 @@ func TestModelDropsQueuedOverlayUpdateAfterBaselineReset(t *testing.T) { |
230 | 256 | } |
231 | 257 | } |
232 | 258 |
|
| 259 | +// TestModelOverlayAppliesFreshUpdateAfterBaselineReset verifies a newer |
| 260 | +// baseline refresh still lands after overlay close, even if an old update was |
| 261 | +// queued earlier in the same overlay session. |
| 262 | +func TestModelOverlayAppliesFreshUpdateAfterBaselineReset(t *testing.T) { |
| 263 | + model := NewModel("repo", "/tmp/repo", "old-sha", []internal.FileDiff{ |
| 264 | + file("old.txt", 1), |
| 265 | + }) |
| 266 | + |
| 267 | + opened, _ := model.Update(tea.KeyMsg{Type: tea.KeyTab}) |
| 268 | + withOverlay := opened.(Model) |
| 269 | + |
| 270 | + queuedOld, _ := withOverlay.Update(FilesUpdatedMsg{ |
| 271 | + BaselineSHA: "old-sha", |
| 272 | + Files: []internal.FileDiff{ |
| 273 | + file("stale.txt", 1), |
| 274 | + }, |
| 275 | + ChangedPaths: []string{"stale.txt"}, |
| 276 | + }) |
| 277 | + reset, _ := queuedOld.(Model).Update(BaselineResetMsg{NewSHA: "new-sha"}) |
| 278 | + queuedNew, _ := reset.(Model).Update(FilesUpdatedMsg{ |
| 279 | + BaselineSHA: "new-sha", |
| 280 | + Files: []internal.FileDiff{ |
| 281 | + file("fresh.txt", 1), |
| 282 | + }, |
| 283 | + ChangedPaths: []string{"fresh.txt"}, |
| 284 | + }) |
| 285 | + closed, _ := queuedNew.(Model).Update(tea.KeyMsg{Type: tea.KeyEsc}) |
| 286 | + got := closed.(Model) |
| 287 | + |
| 288 | + if len(got.Files) != 1 || got.Files[0].Path != "fresh.txt" { |
| 289 | + t.Fatalf("fresh overlay update should win after reset, got %#v", got.Files) |
| 290 | + } |
| 291 | +} |
| 292 | + |
233 | 293 | // TestModelViewSmallHeightDoesNotPanic verifies tiny terminal heights do not |
234 | 294 | // trigger negative content heights in overlay rendering. |
235 | 295 | func TestModelViewSmallHeightDoesNotPanic(t *testing.T) { |
@@ -270,6 +330,54 @@ func TestModelViewEmptyStateFitsViewport(t *testing.T) { |
270 | 330 | } |
271 | 331 | } |
272 | 332 |
|
| 333 | +// TestModelViewNarrowWidthKeepsChromeSingleLine verifies header and footer stay |
| 334 | +// within the viewport width instead of relying on terminal soft-wrap. |
| 335 | +func TestModelViewNarrowWidthKeepsChromeSingleLine(t *testing.T) { |
| 336 | + model := NewModel("repo", "/tmp/repo", "sha", []internal.FileDiff{{ |
| 337 | + Path: "very/long/path/to/current-file-name.ts", |
| 338 | + AddCount: 12, |
| 339 | + DelCount: 3, |
| 340 | + }}) |
| 341 | + model.Width = 20 |
| 342 | + model.Height = 4 |
| 343 | + |
| 344 | + view := model.View() |
| 345 | + lines := strings.Split(view, "\n") |
| 346 | + if len(lines) != 4 { |
| 347 | + t.Fatalf("line count = %d, want 4; view = %q", len(lines), view) |
| 348 | + } |
| 349 | + if lipgloss.Width(lines[0]) > model.Width { |
| 350 | + t.Fatalf("header width = %d, want <= %d; header = %q", lipgloss.Width(lines[0]), model.Width, lines[0]) |
| 351 | + } |
| 352 | + if lipgloss.Width(lines[3]) > model.Width { |
| 353 | + t.Fatalf("footer width = %d, want <= %d; footer = %q", lipgloss.Width(lines[3]), model.Width, lines[3]) |
| 354 | + } |
| 355 | +} |
| 356 | + |
| 357 | +// TestModelOverlayNarrowWidthKeepsEntriesSingleLine verifies overlay rows fit |
| 358 | +// the viewport width instead of depending on soft-wrap. |
| 359 | +func TestModelOverlayNarrowWidthKeepsEntriesSingleLine(t *testing.T) { |
| 360 | + model := NewModel("repo", "/tmp/repo", "sha", []internal.FileDiff{ |
| 361 | + {Path: "very/long/path/to/current-file-name.ts", AddCount: 12, DelCount: 3}, |
| 362 | + {Path: "another/extremely/long/path/to/second-file.ts", AddCount: 1}, |
| 363 | + }) |
| 364 | + model.Width = 20 |
| 365 | + model.Height = 4 |
| 366 | + model.OverlayOpen = true |
| 367 | + model.OverlaySnapshot = append([]internal.FileDiff(nil), model.Files...) |
| 368 | + |
| 369 | + view := model.View() |
| 370 | + lines := strings.Split(view, "\n") |
| 371 | + if len(lines) != 4 { |
| 372 | + t.Fatalf("line count = %d, want 4; view = %q", len(lines), view) |
| 373 | + } |
| 374 | + for i := 1; i <= 2; i++ { |
| 375 | + if lipgloss.Width(lines[i]) > model.Width { |
| 376 | + t.Fatalf("overlay line %d width = %d, want <= %d; line = %q", i, lipgloss.Width(lines[i]), model.Width, lines[i]) |
| 377 | + } |
| 378 | + } |
| 379 | +} |
| 380 | + |
273 | 381 | // TestModelScrollOffsetStaysWithinContent verifies repeated down-navigation does |
274 | 382 | // not grow scroll state beyond the visible diff content. |
275 | 383 | func TestModelScrollOffsetStaysWithinContent(t *testing.T) { |
|
0 commit comments