11import React from 'react' ;
22import { render , screen , fireEvent , waitFor } from '@testing-library/react' ;
33import RepoIssues from '../RepoIssues' ;
4- import { useMaterialReactTable } from 'material-react-table' ;
4+
5+ // Track useMaterialReactTable call opts for assertions (must start with 'mock' for Jest hoisting)
6+ const mockUseMRTOpts = [ ] ;
57
68// Mock react-router
79jest . mock ( 'react-router' , ( ) => ( {
@@ -27,16 +29,31 @@ jest.mock('react-bootstrap', () => ({
2729jest . mock ( '../../AppNavbar' , ( ) => ( ) => < div data-testid = "navbar" > Navbar</ div > ) ;
2830jest . mock ( '../../LoadingSpinner' , ( ) => ( ) => < div data-testid = "spinner" > Loading...</ div > ) ;
2931
30- // Mock MaterialReactTable and useMaterialReactTable
32+ // Mock MaterialReactTable and useMaterialReactTable.
33+ // useMaterialReactTable is a plain function (not jest.fn) so React 19 concurrent mode
34+ // commits re-renders correctly. Calls are tracked via mockUseMRTOpts for assertions.
35+ // columnFilters from initialState are applied to table.data so MaterialReactTable
36+ // only receives the filtered rows, allowing DOM-level assertions on visibility.
3137jest . mock ( 'material-react-table' , ( ) => ( {
3238 MaterialReactTable : ( { table } ) => (
3339 < div data-testid = "mrt-table" >
34- { table && table . data && table . data . map ( ( issue , idx ) => (
40+ { ( table ? .data ?? [ ] ) . map ( ( issue , idx ) => (
3541 < div key = { idx } data-testid = "mrt-row" > { issue . title } </ div >
3642 ) ) }
3743 </ div >
3844 ) ,
39- useMaterialReactTable : jest . fn ( ( opts ) => opts ) ,
45+ useMaterialReactTable : ( opts ) => {
46+ mockUseMRTOpts . push ( opts ) ;
47+ const filters = opts ?. initialState ?. columnFilters ?? [ ] ;
48+ let data = opts ?. data ?? [ ] ;
49+ filters . forEach ( ( filter ) => {
50+ data = data . filter ( ( row ) => {
51+ const val = row [ filter . id ] ;
52+ return Array . isArray ( filter . value ) ? filter . value . includes ( val ) : val === filter . value ;
53+ } ) ;
54+ } ) ;
55+ return { ...opts , data } ;
56+ } ,
4057} ) ) ;
4158
4259// Mock @mui /material theme functions
@@ -58,6 +75,7 @@ describe('RepoIssues', () => {
5875 const mockIssuesResponse = [ ] ;
5976
6077 beforeEach ( ( ) => {
78+ mockUseMRTOpts . length = 0 ;
6179 jest . clearAllMocks ( ) ;
6280 useNavigate . mockReturnValue ( mockNavigate ) ;
6381 global . fetch = jest . fn ( ( ) =>
@@ -98,19 +116,21 @@ describe('RepoIssues', () => {
98116 expect ( screen . queryByTestId ( 'spinner' ) ) . not . toBeInTheDocument ( )
99117 ) ;
100118 expect ( screen . getByTestId ( 'mrt-table' ) ) . toBeInTheDocument ( ) ;
119+ expect ( screen . getByText ( 'Test Issue' ) ) . toBeInTheDocument ( ) ;
101120 } ) ;
102121
103- it ( 'hides state and description columns by default ' , async ( ) => {
122+ it ( 'defaults to open issues while hiding state and description columns' , async ( ) => {
104123 useParams . mockReturnValue ( { id : 'user' , repo : 'repo' } ) ;
105124 render ( < RepoIssues /> ) ;
106125 await waitFor ( ( ) =>
107126 expect ( screen . queryByTestId ( 'spinner' ) ) . not . toBeInTheDocument ( )
108127 ) ;
109128
110- expect ( useMaterialReactTable ) . toHaveBeenCalledWith (
129+ expect ( mockUseMRTOpts ) . toContainEqual (
111130 expect . objectContaining ( {
112131 initialState : expect . objectContaining ( {
113132 showColumnFilters : true ,
133+ columnFilters : [ { id : 'state' , value : [ 'open' ] } ] ,
114134 columnVisibility : {
115135 state : false ,
116136 body : false ,
@@ -120,6 +140,24 @@ describe('RepoIssues', () => {
120140 ) ;
121141 } ) ;
122142
143+ it ( 'renders open issues and excludes closed issues by default' , async ( ) => {
144+ useParams . mockReturnValue ( { id : 'user' , repo : 'repo' } ) ;
145+ global . fetch = jest . fn ( ( ) =>
146+ Promise . resolve ( {
147+ json : ( ) => Promise . resolve ( [
148+ { number : 1 , state : 'open' , labels : [ ] , title : 'Open Issue' , body : '' } ,
149+ { number : 2 , state : 'closed' , labels : [ ] , title : 'Closed Issue' , body : '' } ,
150+ ] ) ,
151+ } )
152+ ) ;
153+ window . fetch = global . fetch ;
154+ render ( < RepoIssues /> ) ;
155+ await waitFor ( ( ) =>
156+ expect ( screen . getByText ( 'Open Issue' ) ) . toBeInTheDocument ( )
157+ ) ;
158+ expect ( screen . queryByText ( 'Closed Issue' ) ) . not . toBeInTheDocument ( ) ;
159+ } ) ;
160+
123161 it ( 'navigates on form submit' , async ( ) => {
124162 useParams . mockReturnValue ( { id : 'user' , repo : 'repo' } ) ;
125163 global . fetch = jest . fn ( ( ) => Promise . resolve ( { json : ( ) => Promise . resolve ( [ ] ) } ) ) ;
0 commit comments