test(ui): cancel transient toaster timers
- commit
- 25599f98dbb20a2df180f2fdd26dea878d94b25f
- author
- brice · · 2026-07-29 07:46 UTC
- committer
- bnema · · 2026-08-14 02:42 UTC
- parent
- 7a23056
- signature
- unsigned
1 file · +12 -0
M internal/ui/app_browser_launch_test.go +12 -0
| @@ -959,6 +959,10 @@ func stackedViewOnActivateIsNil(t *testing.T, sv *layout.StackedView) bool { | |||
| 959 | 959 | return reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem().IsNil() | |
| 960 | 960 | } | |
| 961 | 961 | ||
| 962 | + | // newTestShellToaster builds a Toaster backed by testify mocks. | |
| 963 | + | // Callers that Show() must Hide() (or otherwise cancel the dismiss timer) | |
| 964 | + | // before the test returns — default Show arms a GLib TimeoutAdd that can | |
| 965 | + | // fire during a later test's MainContext.Iteration and panic on dead mocks. | |
| 962 | 966 | func newTestShellToaster(t *testing.T) (*component.Toaster, *layoutmocks.MockBoxWidget, *layoutmocks.MockLabelWidget) { | |
| 963 | 967 | t.Helper() | |
| 964 | 968 | ||
| @@ -991,6 +995,9 @@ func TestApp_ShowFilterStatusUsesLastFocusedBrowserWindowToaster(t *testing.T) { | |||
| 991 | 995 | toaster, box, label := newTestShellToaster(t) | |
| 992 | 996 | box.EXPECT().SetVisible(true).Once() | |
| 993 | 997 | label.EXPECT().SetText("Ad blocker loading").Once() | |
| 998 | + | // Cancel the auto-dismiss GLib timer before mock teardown so a later test | |
| 999 | + | // iterating the main context cannot fire hide() on a completed test's mocks. | |
| 1000 | + | box.EXPECT().SetVisible(false).Once() | |
| 994 | 1001 | ||
| 995 | 1002 | bw := &browserWindow{id: "window-1", appToaster: toaster} | |
| 996 | 1003 | app := &App{ | |
| @@ -999,6 +1006,7 @@ func TestApp_ShowFilterStatusUsesLastFocusedBrowserWindowToaster(t *testing.T) { | |||
| 999 | 1006 | } | |
| 1000 | 1007 | ||
| 1001 | 1008 | app.showFilterStatus(ctx, port.FilterStatus{State: port.FilterStateLoading, Message: "Ad blocker loading"}) | |
| 1009 | + | toaster.Hide() | |
| 1002 | 1010 | } | |
| 1003 | 1011 | ||
| 1004 | 1012 | func TestTestShellToasterCleanupCancelsAutoDismiss(t *testing.T) { | |
| @@ -1024,6 +1032,9 @@ func TestApp_CheckConfigMigrationUsesLastFocusedBrowserWindowToaster(t *testing. | |||
| 1024 | 1032 | toaster, box, label := newTestShellToaster(t) | |
| 1025 | 1033 | box.EXPECT().SetVisible(true).Once() | |
| 1026 | 1034 | label.EXPECT().SetText("Config has 1 new settings. Run 'dumber config migrate'").Once() | |
| 1035 | + | // Cancel the auto-dismiss GLib timer before mock teardown so a later test | |
| 1036 | + | // iterating the main context cannot fire hide() on a completed test's mocks. | |
| 1037 | + | box.EXPECT().SetVisible(false).Once() | |
| 1027 | 1038 | ||
| 1028 | 1039 | migrator := portmocks.NewMockConfigMigrator(t) | |
| 1029 | 1040 | migrator.EXPECT().CheckMigration().Return(&port.MigrationResult{MissingKeys: []string{"update.notify_on_new_settings"}}, nil).Once() | |
| @@ -1042,6 +1053,7 @@ func TestApp_CheckConfigMigrationUsesLastFocusedBrowserWindowToaster(t *testing. | |||
| 1042 | 1053 | } | |
| 1043 | 1054 | ||
| 1044 | 1055 | app.checkConfigMigration(ctx) | |
| 1056 | + | toaster.Hide() | |
| 1045 | 1057 | } | |
| 1046 | 1058 | ||
| 1047 | 1059 | func TestApp_FinalizeActivationStartsBrowserLaunchRelayOnceAndClosesOnShutdown(t *testing.T) { | |
| @@ -959,6 +959,10 @@ func stackedViewOnActivateIsNil(t *testing.T, sv *layout.StackedView) bool { | |||||
| 959 | return reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem().IsNil() | 959 | return reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem().IsNil() | ||
| 960 | } | 960 | } | ||
| 961 | 961 | ||||
| 962 | + | // newTestShellToaster builds a Toaster backed by testify mocks. | |||
| 963 | + | // Callers that Show() must Hide() (or otherwise cancel the dismiss timer) | |||
| 964 | + | // before the test returns — default Show arms a GLib TimeoutAdd that can | |||
| 965 | + | // fire during a later test's MainContext.Iteration and panic on dead mocks. | |||
| 962 | func newTestShellToaster(t *testing.T) (*component.Toaster, *layoutmocks.MockBoxWidget, *layoutmocks.MockLabelWidget) { | 966 | func newTestShellToaster(t *testing.T) (*component.Toaster, *layoutmocks.MockBoxWidget, *layoutmocks.MockLabelWidget) { | ||
| 963 | t.Helper() | 967 | t.Helper() | ||
| 964 | 968 | ||||
| @@ -991,6 +995,9 @@ func TestApp_ShowFilterStatusUsesLastFocusedBrowserWindowToaster(t *testing.T) { | |||||
| 991 | toaster, box, label := newTestShellToaster(t) | 995 | toaster, box, label := newTestShellToaster(t) | ||
| 992 | box.EXPECT().SetVisible(true).Once() | 996 | box.EXPECT().SetVisible(true).Once() | ||
| 993 | label.EXPECT().SetText("Ad blocker loading").Once() | 997 | label.EXPECT().SetText("Ad blocker loading").Once() | ||
| 998 | + | // Cancel the auto-dismiss GLib timer before mock teardown so a later test | |||
| 999 | + | // iterating the main context cannot fire hide() on a completed test's mocks. | |||
| 1000 | + | box.EXPECT().SetVisible(false).Once() | |||
| 994 | 1001 | ||||
| 995 | bw := &browserWindow{id: "window-1", appToaster: toaster} | 1002 | bw := &browserWindow{id: "window-1", appToaster: toaster} | ||
| 996 | app := &App{ | 1003 | app := &App{ | ||
| @@ -999,6 +1006,7 @@ func TestApp_ShowFilterStatusUsesLastFocusedBrowserWindowToaster(t *testing.T) { | |||||
| 999 | } | 1006 | } | ||
| 1000 | 1007 | ||||
| 1001 | app.showFilterStatus(ctx, port.FilterStatus{State: port.FilterStateLoading, Message: "Ad blocker loading"}) | 1008 | app.showFilterStatus(ctx, port.FilterStatus{State: port.FilterStateLoading, Message: "Ad blocker loading"}) | ||
| 1009 | + | toaster.Hide() | |||
| 1002 | } | 1010 | } | ||
| 1003 | 1011 | ||||
| 1004 | func TestTestShellToasterCleanupCancelsAutoDismiss(t *testing.T) { | 1012 | func TestTestShellToasterCleanupCancelsAutoDismiss(t *testing.T) { | ||
| @@ -1024,6 +1032,9 @@ func TestApp_CheckConfigMigrationUsesLastFocusedBrowserWindowToaster(t *testing. | |||||
| 1024 | toaster, box, label := newTestShellToaster(t) | 1032 | toaster, box, label := newTestShellToaster(t) | ||
| 1025 | box.EXPECT().SetVisible(true).Once() | 1033 | box.EXPECT().SetVisible(true).Once() | ||
| 1026 | label.EXPECT().SetText("Config has 1 new settings. Run 'dumber config migrate'").Once() | 1034 | label.EXPECT().SetText("Config has 1 new settings. Run 'dumber config migrate'").Once() | ||
| 1035 | + | // Cancel the auto-dismiss GLib timer before mock teardown so a later test | |||
| 1036 | + | // iterating the main context cannot fire hide() on a completed test's mocks. | |||
| 1037 | + | box.EXPECT().SetVisible(false).Once() | |||
| 1027 | 1038 | ||||
| 1028 | migrator := portmocks.NewMockConfigMigrator(t) | 1039 | migrator := portmocks.NewMockConfigMigrator(t) | ||
| 1029 | migrator.EXPECT().CheckMigration().Return(&port.MigrationResult{MissingKeys: []string{"update.notify_on_new_settings"}}, nil).Once() | 1040 | migrator.EXPECT().CheckMigration().Return(&port.MigrationResult{MissingKeys: []string{"update.notify_on_new_settings"}}, nil).Once() | ||
| @@ -1042,6 +1053,7 @@ func TestApp_CheckConfigMigrationUsesLastFocusedBrowserWindowToaster(t *testing. | |||||
| 1042 | } | 1053 | } | ||
| 1043 | 1054 | ||||
| 1044 | app.checkConfigMigration(ctx) | 1055 | app.checkConfigMigration(ctx) | ||
| 1056 | + | toaster.Hide() | |||
| 1045 | } | 1057 | } | ||
| 1046 | 1058 | ||||
| 1047 | func TestApp_FinalizeActivationStartsBrowserLaunchRelayOnceAndClosesOnShutdown(t *testing.T) { | 1059 | func TestApp_FinalizeActivationStartsBrowserLaunchRelayOnceAndClosesOnShutdown(t *testing.T) { | ||