@@ -23,7 +23,9 @@ import {
2323 screen ,
2424 waitFor ,
2525} from 'spec/helpers/testing-library' ;
26+ import userEvent from '@testing-library/user-event' ;
2627import fetchMock from 'fetch-mock' ;
28+ import { TableProps } from '@superset-ui/core/components/Table' ;
2729import DatasetUsageTab from '.' ;
2830
2931jest . mock ( '@superset-ui/core/components/Table' , ( ) => {
@@ -34,22 +36,22 @@ jest.mock('@superset-ui/core/components/Table', () => {
3436 | ( ( page : number , pageSize ?: number ) => void )
3537 | null = null ;
3638 let latestPaginationPageSize : number | undefined ;
39+ let latestPaginationCurrent : number | undefined ;
40+ let latestPaginationTotal : number | undefined ;
3741 let latestOnChangeHandler :
3842 | ( ( pagination : { current : number ; pageSize ?: number } ) => void )
3943 | null = null ;
4044
41- const MockTable = ( { pagination, onChange, ...rest } : any ) => {
42- if ( pagination ?. pageSize ) {
43- latestPaginationPageSize = pagination . pageSize ;
44- }
45-
46- if ( typeof pagination ?. onChange === 'function' ) {
47- latestPaginationHandler = pagination . onChange ;
48- }
49-
50- if ( typeof onChange === 'function' ) {
51- latestOnChangeHandler = onChange ;
52- }
45+ const MockTable = < RecordType extends object > ( {
46+ pagination,
47+ onChange,
48+ ...rest
49+ } : TableProps < RecordType > ) => {
50+ latestPaginationPageSize = pagination ?. pageSize ;
51+ latestPaginationCurrent = pagination ?. current ;
52+ latestPaginationTotal = pagination ?. total ;
53+ latestPaginationHandler = pagination ?. onChange ;
54+ latestOnChangeHandler = onChange ;
5355
5456 return (
5557 < ActualTable pagination = { pagination } onChange = { onChange } { ...rest } />
@@ -62,10 +64,14 @@ jest.mock('@superset-ui/core/components/Table', () => {
6264 default : MockTable ,
6365 __getLatestPaginationHandler : ( ) => latestPaginationHandler ,
6466 __getLatestPaginationPageSize : ( ) => latestPaginationPageSize ,
67+ __getLatestPaginationCurrent : ( ) => latestPaginationCurrent ,
68+ __getLatestPaginationTotal : ( ) => latestPaginationTotal ,
6569 __getLatestOnChangeHandler : ( ) => latestOnChangeHandler ,
6670 __resetPaginationHandler : ( ) => {
6771 latestPaginationHandler = null ;
6872 latestPaginationPageSize = undefined ;
73+ latestPaginationCurrent = undefined ;
74+ latestPaginationTotal = undefined ;
6975 latestOnChangeHandler = null ;
7076 } ,
7177 } ;
@@ -76,6 +82,8 @@ const tableMock = jest.requireMock('@superset-ui/core/components/Table') as {
7682 | ( ( page : number , pageSize ?: number ) => void )
7783 | null ;
7884 __getLatestPaginationPageSize : ( ) => number | undefined ;
85+ __getLatestPaginationCurrent : ( ) => number | undefined ;
86+ __getLatestPaginationTotal : ( ) => number | undefined ;
7987 __resetPaginationHandler : ( ) => void ;
8088 __getLatestOnChangeHandler : ( ) =>
8189 | ( ( pagination : { current : number ; pageSize ?: number } ) => void )
@@ -471,3 +479,187 @@ test('scrolls table to top after pagination with delay', async () => {
471479 behavior : 'smooth' ,
472480 } ) ;
473481} ) ;
482+
483+ test ( 'displays correct pagination and navigates between pages' , async ( ) => {
484+ const manyCharts = Array . from ( { length : 30 } , ( _ , i ) => ( {
485+ id : i + 1 ,
486+ slice_name : `Test Chart ${ i + 1 } ` ,
487+ url : `/explore/${ i + 1 } /` ,
488+ owners : [ ] ,
489+ changed_on_delta_humanized : '1 day ago' ,
490+ changed_by : null ,
491+ dashboards : [ ] ,
492+ } ) ) ;
493+
494+ const { mockOnFetchCharts } = setupTest ( {
495+ charts : manyCharts . slice ( 0 , 25 ) ,
496+ totalCount : 30 ,
497+ } ) ;
498+
499+ // Page 1: Verify UI shows page 1 is active (using aria-current for robustness)
500+ await waitFor ( ( ) => {
501+ const page1Item = screen . getByRole ( 'listitem' , { name : '1' } ) ;
502+ expect ( page1Item ) . toHaveAttribute ( 'aria-current' , 'page' ) ;
503+ } ) ;
504+
505+ // Soft assertion: verify component passes correct props to Table
506+ // eslint-disable-next-line no-underscore-dangle
507+ expect ( tableMock . __getLatestPaginationCurrent ( ) ) . toBe ( 1 ) ;
508+ // eslint-disable-next-line no-underscore-dangle
509+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 30 ) ;
510+ // eslint-disable-next-line no-underscore-dangle
511+ expect ( tableMock . __getLatestPaginationPageSize ( ) ) . toBe ( 25 ) ;
512+
513+ // User clicks page 2 (using userEvent for realistic interaction)
514+ const page2Link = screen . getByRole ( 'link' , { name : '2' } ) ;
515+ await userEvent . click ( page2Link ) ;
516+
517+ // Data fetch called for page 2
518+ await waitFor ( ( ) =>
519+ expect ( mockOnFetchCharts ) . toHaveBeenCalledWith (
520+ 2 ,
521+ 25 ,
522+ expect . any ( String ) ,
523+ expect . any ( String ) ,
524+ ) ,
525+ ) ;
526+
527+ // Verify UI reflects page 2 is now active (aria-current is more resilient than class names)
528+ await waitFor ( ( ) => {
529+ const page2Item = screen . getByRole ( 'listitem' , { name : '2' } ) ;
530+ expect ( page2Item ) . toHaveAttribute ( 'aria-current' , 'page' ) ;
531+ } ) ;
532+
533+ // Soft assertion: Table still receives correct total (catches the bug!)
534+ // eslint-disable-next-line no-underscore-dangle
535+ expect ( tableMock . __getLatestPaginationCurrent ( ) ) . toBe ( 2 ) ;
536+ // eslint-disable-next-line no-underscore-dangle
537+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 30 ) ;
538+ // eslint-disable-next-line no-underscore-dangle
539+ expect ( tableMock . __getLatestPaginationPageSize ( ) ) . toBe ( 25 ) ;
540+ } ) ;
541+
542+ test ( 'maintains pagination state across multiple page changes' , async ( ) => {
543+ const manyCharts = Array . from ( { length : 60 } , ( _ , i ) => ( {
544+ id : i + 1 ,
545+ slice_name : `Test Chart ${ i + 1 } ` ,
546+ url : `/explore/${ i + 1 } /` ,
547+ owners : [ ] ,
548+ changed_on_delta_humanized : '1 day ago' ,
549+ changed_by : null ,
550+ dashboards : [ ] ,
551+ } ) ) ;
552+
553+ const { mockOnFetchCharts } = setupTest ( {
554+ charts : manyCharts . slice ( 0 , 25 ) ,
555+ totalCount : 60 ,
556+ } ) ;
557+
558+ // Verify initial state (page 1) using aria-current
559+ await waitFor ( ( ) => {
560+ const page1Item = screen . getByRole ( 'listitem' , { name : '1' } ) ;
561+ expect ( page1Item ) . toHaveAttribute ( 'aria-current' , 'page' ) ;
562+ } ) ;
563+
564+ // Soft assertion: initial props are correct
565+ // eslint-disable-next-line no-underscore-dangle
566+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 60 ) ;
567+
568+ // User navigates to page 2
569+ await userEvent . click ( screen . getByRole ( 'link' , { name : '2' } ) ) ;
570+
571+ await waitFor ( ( ) => {
572+ expect ( mockOnFetchCharts ) . toHaveBeenCalledWith (
573+ 2 ,
574+ 25 ,
575+ expect . any ( String ) ,
576+ expect . any ( String ) ,
577+ ) ;
578+ const page2Item = screen . getByRole ( 'listitem' , { name : '2' } ) ;
579+ expect ( page2Item ) . toHaveAttribute ( 'aria-current' , 'page' ) ;
580+ } ) ;
581+
582+ // Soft assertion: props update correctly after navigation
583+ // eslint-disable-next-line no-underscore-dangle
584+ expect ( tableMock . __getLatestPaginationCurrent ( ) ) . toBe ( 2 ) ;
585+ // eslint-disable-next-line no-underscore-dangle
586+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 60 ) ;
587+
588+ // User navigates to page 3
589+ await userEvent . click ( screen . getByRole ( 'link' , { name : '3' } ) ) ;
590+
591+ await waitFor ( ( ) => {
592+ expect ( mockOnFetchCharts ) . toHaveBeenCalledWith (
593+ 3 ,
594+ 25 ,
595+ expect . any ( String ) ,
596+ expect . any ( String ) ,
597+ ) ;
598+ const page3Item = screen . getByRole ( 'listitem' , { name : '3' } ) ;
599+ expect ( page3Item ) . toHaveAttribute ( 'aria-current' , 'page' ) ;
600+ } ) ;
601+
602+ // Soft assertion: total remains consistent across all navigations
603+ // eslint-disable-next-line no-underscore-dangle
604+ expect ( tableMock . __getLatestPaginationCurrent ( ) ) . toBe ( 3 ) ;
605+ // eslint-disable-next-line no-underscore-dangle
606+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 60 ) ;
607+ // eslint-disable-next-line no-underscore-dangle
608+ expect ( tableMock . __getLatestPaginationPageSize ( ) ) . toBe ( 25 ) ;
609+ } ) ;
610+
611+ test ( 'handles total count below one page (no pagination needed)' , async ( ) => {
612+ const fewCharts = Array . from ( { length : 5 } , ( _ , i ) => ( {
613+ id : i + 1 ,
614+ slice_name : `Test Chart ${ i + 1 } ` ,
615+ url : `/explore/${ i + 1 } /` ,
616+ owners : [ ] ,
617+ changed_on_delta_humanized : '1 day ago' ,
618+ changed_by : null ,
619+ dashboards : [ ] ,
620+ } ) ) ;
621+
622+ setupTest ( {
623+ charts : fewCharts ,
624+ totalCount : 5 ,
625+ } ) ;
626+
627+ // Pagination should not show page 2 link since only 1 page exists
628+ await waitFor ( ( ) => {
629+ expect ( screen . queryByRole ( 'link' , { name : '2' } ) ) . not . toBeInTheDocument ( ) ;
630+ } ) ;
631+
632+ // Table receives correct props
633+ // eslint-disable-next-line no-underscore-dangle
634+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 5 ) ;
635+ // eslint-disable-next-line no-underscore-dangle
636+ expect ( tableMock . __getLatestPaginationCurrent ( ) ) . toBe ( 1 ) ;
637+ } ) ;
638+
639+ test ( 'handles server returning fewer rows than page size' , async ( ) => {
640+ const partialPage = Array . from ( { length : 12 } , ( _ , i ) => ( {
641+ id : i + 1 ,
642+ slice_name : `Test Chart ${ i + 1 } ` ,
643+ url : `/explore/${ i + 1 } /` ,
644+ owners : [ ] ,
645+ changed_on_delta_humanized : '1 day ago' ,
646+ changed_by : null ,
647+ dashboards : [ ] ,
648+ } ) ) ;
649+
650+ setupTest ( {
651+ charts : partialPage ,
652+ totalCount : 12 ,
653+ } ) ;
654+
655+ // Only page 1 should exist
656+ await waitFor ( ( ) => {
657+ expect ( screen . queryByRole ( 'link' , { name : '2' } ) ) . not . toBeInTheDocument ( ) ;
658+ } ) ;
659+
660+ // Table receives correct total (not data.length!)
661+ // eslint-disable-next-line no-underscore-dangle
662+ expect ( tableMock . __getLatestPaginationTotal ( ) ) . toBe ( 12 ) ;
663+ // eslint-disable-next-line no-underscore-dangle
664+ expect ( tableMock . __getLatestPaginationPageSize ( ) ) . toBe ( 25 ) ;
665+ } ) ;
0 commit comments