@@ -155,15 +155,72 @@ describe('LibraryComponent', () => {
expect ( link ) . not . toBeNull ( ) ;
} ) ;
// ---- Pagination: US1 ----
// ---- Pagination: page window (T002) ----
it ( 'page indicator shows "Page 1 of 2" when totalPages > 1 ' , ( ) = > {
it ( 'pageW indow returns [1,2,3,4] on page 1 of 20 ' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
fixture . componentInstance . currentPage = 1 ;
fixture . componentInstance . totalPages = 20 ;
expect ( fixture . componentInstance . pageWindow ) . toEqual ( [ 1 , 2 , 3 , 4 ] ) ;
} ) ;
it ( 'pageWindow returns [17,18,19,20] on page 20 of 20' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
fixture . componentInstance . currentPage = 20 ;
fixture . componentInstance . totalPages = 20 ;
expect ( fixture . componentInstance . pageWindow ) . toEqual ( [ 17 , 18 , 19 , 20 ] ) ;
} ) ;
it ( 'pageWindow returns [6,7,8,9] on page 7 of 20' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
fixture . componentInstance . currentPage = 7 ;
fixture . componentInstance . totalPages = 20 ;
expect ( fixture . componentInstance . pageWindow ) . toEqual ( [ 6 , 7 , 8 , 9 ] ) ;
} ) ;
it ( 'pageWindow returns all pages when totalPages < 4' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
fixture . componentInstance . currentPage = 2 ;
fixture . componentInstance . totalPages = 3 ;
expect ( fixture . componentInstance . pageWindow ) . toEqual ( [ 1 , 2 , 3 ] ) ;
} ) ;
it ( 'pageWindow returns [1] when totalPages is 1' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
fixture . componentInstance . currentPage = 1 ;
fixture . componentInstance . totalPages = 1 ;
expect ( fixture . componentInstance . pageWindow ) . toEqual ( [ 1 ] ) ;
} ) ;
it ( 'numbered page buttons are rendered (T002)' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const indicator = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.page-indicator ' ) ;
expect ( indicator ? . textContent ) . toContain ( 'Page 1 of 2' ) ;
const pageBtns = ( fixture . nativeElement as HTMLElement ) . querySelectorAll ( '.page-btn ' ) ;
expect ( pageBtns . length ) . toBe ( 2 ) ; // 2 total pages
expect ( pageBtns [ 0 ] . textContent ? . trim ( ) ) . toBe ( '1' ) ;
expect ( pageBtns [ 1 ] . textContent ? . trim ( ) ) . toBe ( '2' ) ;
} ) ;
it ( 'active page button has .active class' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const activeBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.page-btn.active' ) ;
expect ( activeBtn ) . not . toBeNull ( ) ;
expect ( activeBtn ? . textContent ? . trim ( ) ) . toBe ( '1' ) ;
} ) ;
it ( 'goToPage() calls imageService.list with correct offset' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
const listSpy = spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
listSpy . calls . reset ( ) ;
fixture . componentInstance . goToPage ( 2 ) ;
expect ( listSpy ) . toHaveBeenCalledWith ( jasmine . any ( Array ) , jasmine . any ( Number ) , 24 ) ;
} ) ;
it ( 'total count renders with correct number' , ( ) = > {
@@ -175,32 +232,6 @@ describe('LibraryComponent', () => {
expect ( el ? . textContent ) . toContain ( '48' ) ;
} ) ;
it ( '"Next" button present when not on last page' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
expect ( ( fixture . nativeElement as HTMLElement ) . querySelector ( '.next-btn' ) ) . not . toBeNull ( ) ;
} ) ;
it ( '"Previous" button absent on first page' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
expect ( ( fixture . nativeElement as HTMLElement ) . querySelector ( '.prev-btn' ) ) . toBeNull ( ) ;
} ) ;
it ( '"Previous" present and "Next" absent on last page' , ( ) = > {
TestBed . overrideProvider ( ActivatedRoute , { useValue : makeActivatedRoute ( { page : '2' } ) } ) ;
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
expect ( ( fixture . nativeElement as HTMLElement ) . querySelector ( '.prev-btn' ) ) . not . toBeNull ( ) ;
expect ( ( fixture . nativeElement as HTMLElement ) . querySelector ( '.next-btn' ) ) . toBeNull ( ) ;
} ) ;
it ( 'no pagination controls when all images fit on one page' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
@@ -242,7 +273,58 @@ describe('LibraryComponent', () => {
expect ( listSpy ) . toHaveBeenCalledWith ( [ 'cat' ] , jasmine . any ( Number ) , 0 ) ;
} ) ;
// ---- Pagination: US2 — URL state ----
// ---- Pagination: ‹ › disabled states (T006) ----
it ( 'prev-btn (‹ ) is disabled on page 1' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const prevBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.prev-btn' ) as HTMLButtonElement ;
expect ( prevBtn ) . not . toBeNull ( ) ;
expect ( prevBtn . disabled ) . toBeTrue ( ) ;
} ) ;
it ( 'next-btn (› ) is disabled on last page' , ( ) = > {
TestBed . overrideProvider ( ActivatedRoute , { useValue : makeActivatedRoute ( { page : '2' } ) } ) ;
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const nextBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.next-btn' ) as HTMLButtonElement ;
expect ( nextBtn ) . not . toBeNull ( ) ;
expect ( nextBtn . disabled ) . toBeTrue ( ) ;
} ) ;
it ( 'prev-btn (‹ ) is enabled when not on page 1' , ( ) = > {
TestBed . overrideProvider ( ActivatedRoute , { useValue : makeActivatedRoute ( { page : '2' } ) } ) ;
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const prevBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.prev-btn' ) as HTMLButtonElement ;
expect ( prevBtn . disabled ) . toBeFalse ( ) ;
} ) ;
it ( 'next-btn (› ) is enabled when not on last page' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const nextBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.next-btn' ) as HTMLButtonElement ;
expect ( nextBtn . disabled ) . toBeFalse ( ) ;
} ) ;
it ( 'both prev and next buttons always rendered when totalPages > 1' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
expect ( ( fixture . nativeElement as HTMLElement ) . querySelector ( '.prev-btn' ) ) . not . toBeNull ( ) ;
expect ( ( fixture . nativeElement as HTMLElement ) . querySelector ( '.next-btn' ) ) . not . toBeNull ( ) ;
} ) ;
// ---- Pagination: URL state ----
it ( 'reads ?page=2 from queryParamMap on init and calls list with offset=24' , ( ) = > {
TestBed . overrideProvider ( ActivatedRoute , { useValue : makeActivatedRoute ( { page : '2' } ) } ) ;
@@ -260,7 +342,6 @@ describe('LibraryComponent', () => {
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
// After load, totalPages=2, currentPage should be clamped to 2 (not 9999), then router corrects URL
expect ( fixture . componentInstance . currentPage ) . toBeLessThanOrEqual ( fixture . componentInstance . totalPages ) ;
} ) ;
@@ -304,4 +385,69 @@ describe('LibraryComponent', () => {
card . click ( ) ;
expect ( router . navigate ) . toHaveBeenCalledWith ( [ '/i' , 'ShrtImg1' ] ) ;
} ) ;
// ---- Pagination: « » first/last (T008) ----
it ( 'firstPage() navigates to page 1' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
const listSpy = spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
fixture . componentInstance . currentPage = 2 ;
fixture . componentInstance . totalPages = 2 ;
listSpy . calls . reset ( ) ;
fixture . componentInstance . firstPage ( ) ;
expect ( listSpy ) . toHaveBeenCalledWith ( jasmine . any ( Array ) , jasmine . any ( Number ) , 0 ) ;
expect ( fixture . componentInstance . currentPage ) . toBe ( 1 ) ;
} ) ;
it ( 'lastPage() navigates to last page' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
const listSpy = spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
listSpy . calls . reset ( ) ;
fixture . componentInstance . lastPage ( ) ;
expect ( fixture . componentInstance . currentPage ) . toBe ( fixture . componentInstance . totalPages ) ;
} ) ;
it ( 'first-page button («) is disabled on page 1' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const firstBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.first-btn' ) as HTMLButtonElement ;
expect ( firstBtn ) . not . toBeNull ( ) ;
expect ( firstBtn . disabled ) . toBeTrue ( ) ;
} ) ;
it ( 'last-page button (») is disabled on last page' , ( ) = > {
TestBed . overrideProvider ( ActivatedRoute , { useValue : makeActivatedRoute ( { page : '2' } ) } ) ;
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const lastBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.last-btn' ) as HTMLButtonElement ;
expect ( lastBtn ) . not . toBeNull ( ) ;
expect ( lastBtn . disabled ) . toBeTrue ( ) ;
} ) ;
it ( 'first-page button («) is enabled when not on page 1' , ( ) = > {
TestBed . overrideProvider ( ActivatedRoute , { useValue : makeActivatedRoute ( { page : '2' } ) } ) ;
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const firstBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.first-btn' ) as HTMLButtonElement ;
expect ( firstBtn . disabled ) . toBeFalse ( ) ;
} ) ;
it ( 'last-page button (») is enabled when not on last page' , ( ) = > {
const fixture = TestBed . createComponent ( LibraryComponent ) ;
const imgSvc = TestBed . inject ( ImageService ) ;
spyOn ( imgSvc , 'list' ) . and . returnValue ( of ( MULTI_PAGE ) ) ;
fixture . detectChanges ( ) ;
const lastBtn = ( fixture . nativeElement as HTMLElement ) . querySelector ( '.last-btn' ) as HTMLButtonElement ;
expect ( lastBtn . disabled ) . toBeFalse ( ) ;
} ) ;
} ) ;