11import { describe , it , expect , vi , beforeEach , afterEach } from "vitest" ;
2- import { render , screen , waitFor } from "@solidjs/testing-library" ;
2+ import { render , screen , waitFor , fireEvent } from "@solidjs/testing-library" ;
33import userEvent from "@testing-library/user-event" ;
44import { makeIssue , makePullRequest , makeWorkflowRun } from "../helpers/index" ;
55import type { DashboardData } from "../../src/app/services/poll" ;
@@ -839,6 +839,7 @@ describe("DashboardPage — tracked tab", () => {
839839 viewStore . updateViewState ( {
840840 trackedItems : [ {
841841 id : 999 ,
842+ number : 99 ,
842843 type : "issue" as const ,
843844 repoFullName : "org/repo" ,
844845 title : "Will be pruned" ,
@@ -872,6 +873,7 @@ describe("DashboardPage — tracked tab", () => {
872873 viewStore . updateViewState ( {
873874 trackedItems : [ {
874875 id : 888 ,
876+ number : 88 ,
875877 type : "issue" as const ,
876878 repoFullName : "org/deselected-repo" ,
877879 title : "Should be kept" ,
@@ -896,4 +898,94 @@ describe("DashboardPage — tracked tab", () => {
896898 expect ( viewStore . viewState . trackedItems [ 0 ] . id ) . toBe ( 888 ) ;
897899 } ) ;
898900 } ) ;
901+
902+ it ( "does not prune tracked items when hasFetchedFresh is false (cold start)" , async ( ) => {
903+ render ( ( ) => < DashboardPage /> ) ;
904+ configStore . updateConfig ( {
905+ enableTracking : true ,
906+ selectedRepos : [ { owner : "org" , name : "repo" , fullName : "org/repo" } ] ,
907+ } ) ;
908+ viewStore . updateViewState ( {
909+ trackedItems : [ {
910+ id : 777 ,
911+ number : 77 ,
912+ type : "issue" as const ,
913+ repoFullName : "org/repo" ,
914+ title : "Should survive cold start" ,
915+ addedAt : Date . now ( ) ,
916+ } ] ,
917+ } ) ;
918+ // hasFetchedFresh stays false (its initial state) — do NOT call _resetHasFetchedFresh(true)
919+ // Do NOT trigger a poll (which would set hasFetchedFresh=true internally).
920+ // The prune effect should not fire against stale cached data.
921+
922+ // Allow reactive effects to settle
923+ await waitFor ( ( ) => {
924+ // Item should NOT be pruned — hasFetchedFresh is false
925+ expect ( viewStore . viewState . trackedItems . length ) . toBe ( 1 ) ;
926+ expect ( viewStore . viewState . trackedItems [ 0 ] . id ) . toBe ( 777 ) ;
927+ } ) ;
928+ } ) ;
929+
930+ it ( "prunes tracked items from upstream repos" , async ( ) => {
931+ render ( ( ) => < DashboardPage /> ) ;
932+ configStore . updateConfig ( {
933+ enableTracking : true ,
934+ selectedRepos : [ ] ,
935+ upstreamRepos : [ { owner : "ext" , name : "upstream" , fullName : "ext/upstream" } ] ,
936+ } ) ;
937+ viewStore . updateViewState ( {
938+ trackedItems : [ {
939+ id : 666 ,
940+ number : 66 ,
941+ type : "issue" as const ,
942+ repoFullName : "ext/upstream" ,
943+ title : "Upstream item closed" ,
944+ addedAt : Date . now ( ) ,
945+ } ] ,
946+ } ) ;
947+ _resetHasFetchedFresh ( true ) ;
948+
949+ if ( capturedFetchAll ) {
950+ vi . mocked ( pollService . fetchAllData ) . mockResolvedValue ( {
951+ issues : [ ] ,
952+ pullRequests : [ ] ,
953+ workflowRuns : [ ] ,
954+ errors : [ ] ,
955+ } ) ;
956+ await capturedFetchAll ( ) ;
957+ }
958+
959+ await waitFor ( ( ) => {
960+ expect ( viewStore . viewState . trackedItems . length ) . toBe ( 0 ) ;
961+ } ) ;
962+ } ) ;
963+
964+ it ( "resolveInitialTab falls back to issues when tracked tab disabled" , ( ) => {
965+ viewStore . updateViewState ( { lastActiveTab : "tracked" } ) ;
966+ configStore . updateConfig ( { rememberLastTab : true , enableTracking : false } ) ;
967+ render ( ( ) => < DashboardPage /> ) ;
968+ // Should show Issues content, not Tracked content
969+ expect ( screen . queryByText ( "No tracked items" ) ) . toBeNull ( ) ;
970+ } ) ;
971+
972+ it ( "redirects away from tracked tab when tracking disabled at runtime" , async ( ) => {
973+ configStore . updateConfig ( { enableTracking : true } ) ;
974+ render ( ( ) => < DashboardPage /> ) ;
975+
976+ // Switch to tracked tab
977+ const trackedTab = screen . getByText ( "Tracked" ) ;
978+ fireEvent . click ( trackedTab ) ;
979+
980+ await waitFor ( ( ) => {
981+ expect ( viewStore . viewState . lastActiveTab ) . toBe ( "tracked" ) ;
982+ } ) ;
983+
984+ // Disable tracking — should redirect to issues
985+ configStore . updateConfig ( { enableTracking : false } ) ;
986+
987+ await waitFor ( ( ) => {
988+ expect ( viewStore . viewState . lastActiveTab ) . toBe ( "issues" ) ;
989+ } ) ;
990+ } ) ;
899991} ) ;
0 commit comments