Skip to content

Commit d2e77c4

Browse files
committed
use a mock systray
1 parent ca5c1b4 commit d2e77c4

File tree

4 files changed

+40
-26
lines changed

4 files changed

+40
-26
lines changed

cmd/goose/click_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func TestMenuClickRateLimit(t *testing.T) {
1818
hiddenOrgs: make(map[string]bool),
1919
seenOrgs: make(map[string]bool),
2020
lastSearchAttempt: time.Now().Add(-15 * time.Second), // 15 seconds ago
21+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
2122
}
2223

2324
// Simulate the click handler logic (without the actual UI interaction)
@@ -69,6 +70,7 @@ func TestScheduledUpdateRateLimit(t *testing.T) {
6970
hiddenOrgs: make(map[string]bool),
7071
seenOrgs: make(map[string]bool),
7172
lastSearchAttempt: time.Now().Add(-5 * time.Second), // 5 seconds ago
73+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
7274
}
7375

7476
// Simulate the scheduled update logic

cmd/goose/filtering_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func TestCountPRsWithHiddenOrgs(t *testing.T) {
2121
"org2": true, // Hide org2
2222
},
2323
hideStaleIncoming: false,
24+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
2425
}
2526

2627
counts := app.countPRs()
@@ -58,6 +59,7 @@ func TestCountPRsWithStalePRs(t *testing.T) {
5859
},
5960
hiddenOrgs: map[string]bool{},
6061
hideStaleIncoming: true, // Hide stale PRs
62+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
6163
}
6264

6365
counts := app.countPRs()
@@ -99,6 +101,7 @@ func TestCountPRsWithBothFilters(t *testing.T) {
99101
"org2": true,
100102
},
101103
hideStaleIncoming: true,
104+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
102105
}
103106

104107
counts := app.countPRs()

cmd/goose/main_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func TestMenuItemTitleTransition(t *testing.T) {
6363
seenOrgs: make(map[string]bool),
6464
blockedPRTimes: make(map[string]time.Time),
6565
browserRateLimiter: NewBrowserRateLimiter(30*time.Second, 5, defaultMaxBrowserOpensDay),
66+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
6667
}
6768

6869
// Test incoming PR that just became blocked
@@ -174,6 +175,7 @@ func TestTrayTitleUpdates(t *testing.T) {
174175
hiddenOrgs: make(map[string]bool),
175176
seenOrgs: make(map[string]bool),
176177
browserRateLimiter: NewBrowserRateLimiter(30*time.Second, 5, defaultMaxBrowserOpensDay),
178+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
177179
}
178180

179181
tests := []struct {
@@ -444,6 +446,7 @@ func TestSoundDisabledNoPlayback(t *testing.T) {
444446
enableAudioCues: false, // Audio disabled
445447
initialLoadComplete: true,
446448
menuInitialized: true,
449+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
447450
}
448451

449452
// Note: We verify behavior through state changes rather than direct sound capture
@@ -484,6 +487,7 @@ func TestGracePeriodPreventsNotifications(t *testing.T) {
484487
initialLoadComplete: true,
485488
menuInitialized: true,
486489
startTime: time.Now(), // Just started
490+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
487491
}
488492

489493
// Track whether we're in grace period for verification
@@ -608,6 +612,7 @@ func TestNotificationScenarios(t *testing.T) {
608612
initialLoadComplete: tt.initialLoadComplete,
609613
menuInitialized: true,
610614
startTime: time.Now().Add(-tt.timeSinceStart),
615+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
611616
}
612617

613618
// Set up previous state
@@ -667,6 +672,7 @@ func TestNewlyBlockedPRAfterGracePeriod(t *testing.T) {
667672
initialLoadComplete: true, // Already past initial load
668673
menuInitialized: true,
669674
startTime: time.Now().Add(-35 * time.Second), // Started 35 seconds ago
675+
systrayInterface: &MockSystray{}, // Use mock systray to avoid panics
670676
}
671677

672678
// Start with no blocked PRs

cmd/goose/ui.go

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import (
1212
"sort"
1313
"time"
1414

15-
"github.com/energye/systray"
15+
"github.com/energye/systray" // needed for MenuItem type
1616
)
1717

18+
// Ensure systray package is used
19+
var _ *systray.MenuItem = nil
20+
1821
// openURL safely opens a URL in the default browser after validation.
1922
func openURL(ctx context.Context, rawURL string) error {
2023
// Parse and validate the URL
@@ -157,7 +160,7 @@ func (app *App) setTrayTitle() {
157160
// Log title change with detailed counts
158161
log.Printf("[TRAY] Setting title to '%s' (incoming_total=%d, incoming_blocked=%d, outgoing_total=%d, outgoing_blocked=%d)",
159162
title, counts.IncomingTotal, counts.IncomingBlocked, counts.OutgoingTotal, counts.OutgoingBlocked)
160-
systray.SetTitle(title)
163+
app.systrayInterface.SetTitle(title)
161164
}
162165

163166
// addPRSection adds a section of PRs to the menu.
@@ -169,7 +172,7 @@ func (app *App) addPRSection(ctx context.Context, prs []PR, sectionTitle string,
169172
// Add header
170173
headerText := fmt.Sprintf("%s — %d blocked on you", sectionTitle, blockedCount)
171174
// Create section header
172-
header := systray.AddMenuItem(headerText, "")
175+
header := app.systrayInterface.AddMenuItem(headerText, "")
173176
header.Disable()
174177

175178
// Sort PRs with blocked ones first - inline for simplicity
@@ -266,7 +269,7 @@ func (app *App) addPRSection(ctx context.Context, prs []PR, sectionTitle string,
266269
}
267270

268271
// Create PR menu item
269-
item := systray.AddMenuItem(title, tooltip)
272+
item := app.systrayInterface.AddMenuItem(title, tooltip)
270273

271274
// Capture URL to avoid loop variable capture bug
272275
prURL := sortedPRs[prIndex].URL
@@ -393,40 +396,40 @@ func (app *App) rebuildMenu(ctx context.Context) {
393396
// Check for auth error first
394397
if app.authError != "" {
395398
// Show authentication error message
396-
errorTitle := systray.AddMenuItem("⚠️ Authentication Error", "")
399+
errorTitle := app.systrayInterface.AddMenuItem("⚠️ Authentication Error", "")
397400
errorTitle.Disable()
398401

399-
systray.AddSeparator()
402+
app.systrayInterface.AddSeparator()
400403

401404
// Add error details
402-
errorMsg := systray.AddMenuItem(app.authError, "Click to see setup instructions")
405+
errorMsg := app.systrayInterface.AddMenuItem(app.authError, "Click to see setup instructions")
403406
errorMsg.Click(func() {
404407
if err := openURL(ctx, "https://cli.github.com/manual/gh_auth_login"); err != nil {
405408
log.Printf("failed to open setup instructions: %v", err)
406409
}
407410
})
408411

409-
systray.AddSeparator()
412+
app.systrayInterface.AddSeparator()
410413

411414
// Add setup instructions
412-
setupInstr := systray.AddMenuItem("To fix this issue:", "")
415+
setupInstr := app.systrayInterface.AddMenuItem("To fix this issue:", "")
413416
setupInstr.Disable()
414417

415-
option1 := systray.AddMenuItem("1. Install GitHub CLI: brew install gh", "")
418+
option1 := app.systrayInterface.AddMenuItem("1. Install GitHub CLI: brew install gh", "")
416419
option1.Disable()
417420

418-
option2 := systray.AddMenuItem("2. Run: gh auth login", "")
421+
option2 := app.systrayInterface.AddMenuItem("2. Run: gh auth login", "")
419422
option2.Disable()
420423

421-
option3 := systray.AddMenuItem("3. Or set GITHUB_TOKEN environment variable", "")
424+
option3 := app.systrayInterface.AddMenuItem("3. Or set GITHUB_TOKEN environment variable", "")
422425
option3.Disable()
423426

424-
systray.AddSeparator()
427+
app.systrayInterface.AddSeparator()
425428

426429
// Add quit option
427-
quitItem := systray.AddMenuItem("Quit", "")
430+
quitItem := app.systrayInterface.AddMenuItem("Quit", "")
428431
quitItem.Click(func() {
429-
systray.Quit()
432+
app.systrayInterface.Quit()
430433
})
431434

432435
return
@@ -437,22 +440,22 @@ func (app *App) rebuildMenu(ctx context.Context) {
437440

438441
// Dashboard at the top
439442
// Add Web Dashboard link
440-
dashboardItem := systray.AddMenuItem("Web Dashboard", "")
443+
dashboardItem := app.systrayInterface.AddMenuItem("Web Dashboard", "")
441444
dashboardItem.Click(func() {
442445
if err := openURL(ctx, "https://dash.ready-to-review.dev/"); err != nil {
443446
log.Printf("failed to open dashboard: %v", err)
444447
}
445448
})
446449

447-
systray.AddSeparator()
450+
app.systrayInterface.AddSeparator()
448451

449452
// Get PR counts
450453
counts := app.countPRs()
451454

452455
// Handle "No pull requests" case
453456
if counts.IncomingTotal == 0 && counts.OutgoingTotal == 0 {
454457
// No PRs to display
455-
noPRs := systray.AddMenuItem("No pull requests", "")
458+
noPRs := app.systrayInterface.AddMenuItem("No pull requests", "")
456459
noPRs.Disable()
457460
} else {
458461
// Incoming section
@@ -463,7 +466,7 @@ func (app *App) rebuildMenu(ctx context.Context) {
463466
app.addPRSection(ctx, incoming, "Incoming", counts.IncomingBlocked)
464467
}
465468

466-
systray.AddSeparator()
469+
app.systrayInterface.AddSeparator()
467470

468471
// Outgoing section
469472
if counts.OutgoingTotal > 0 {
@@ -484,11 +487,11 @@ func (app *App) rebuildMenu(ctx context.Context) {
484487
func (app *App) addStaticMenuItems(ctx context.Context) {
485488
// Add static menu items
486489

487-
systray.AddSeparator()
490+
app.systrayInterface.AddSeparator()
488491

489492
// Hide orgs submenu
490493
// Add 'Hide orgs' submenu
491-
hideOrgsMenu := systray.AddMenuItem("Hide orgs", "Select organizations to hide PRs from")
494+
hideOrgsMenu := app.systrayInterface.AddMenuItem("Hide orgs", "Select organizations to hide PRs from")
492495

493496
// Get combined list of seen orgs and hidden orgs
494497
app.mu.RLock()
@@ -553,7 +556,7 @@ func (app *App) addStaticMenuItems(ctx context.Context) {
553556

554557
// Hide stale PRs
555558
// Add 'Hide stale PRs' option
556-
hideStaleItem := systray.AddMenuItem("Hide stale PRs (>90 days)", "")
559+
hideStaleItem := app.systrayInterface.AddMenuItem("Hide stale PRs (>90 days)", "")
557560
if app.hideStaleIncoming {
558561
hideStaleItem.Check()
559562
}
@@ -583,7 +586,7 @@ func (app *App) addStaticMenuItems(ctx context.Context) {
583586

584587
// Audio cues
585588
// Add 'Audio cues' option
586-
audioItem := systray.AddMenuItem("Honks enabled", "Play sounds for notifications")
589+
audioItem := app.systrayInterface.AddMenuItem("Honks enabled", "Play sounds for notifications")
587590
app.mu.RLock()
588591
if app.enableAudioCues {
589592
audioItem.Check()
@@ -609,7 +612,7 @@ func (app *App) addStaticMenuItems(ctx context.Context) {
609612

610613
// Auto-open blocked PRs in browser
611614
// Add 'Auto-open PRs' option
612-
autoOpenItem := systray.AddMenuItem("Auto-open incoming PRs", "Automatically open newly blocked PRs in browser (rate limited)")
615+
autoOpenItem := app.systrayInterface.AddMenuItem("Auto-open incoming PRs", "Automatically open newly blocked PRs in browser (rate limited)")
613616
app.mu.RLock()
614617
if app.enableAutoBrowser {
615618
autoOpenItem.Check()
@@ -639,9 +642,9 @@ func (app *App) addStaticMenuItems(ctx context.Context) {
639642

640643
// Quit
641644
// Add 'Quit' option
642-
quitItem := systray.AddMenuItem("Quit", "")
645+
quitItem := app.systrayInterface.AddMenuItem("Quit", "")
643646
quitItem.Click(func() {
644647
log.Println("Quit requested by user")
645-
systray.Quit()
648+
app.systrayInterface.Quit()
646649
})
647650
}

0 commit comments

Comments
 (0)