1+ import { SharedResizeObserver } from '@angular/cdk/observers/private' ;
12import { Platform } from '@angular/cdk/platform' ;
23import { ChangeDetectionStrategy , Component , DebugElement , Provider , Type } from '@angular/core' ;
34import { ComponentFixture , fakeAsync , TestBed , tick , waitForAsync } from '@angular/core/testing' ;
45import { By } from '@angular/platform-browser' ;
56import { NoopAnimationsModule } from '@angular/platform-browser/animations' ;
67import { KbqTabNavBar } from '@koobiq/components/tabs' ;
78import { HLJSApi } from 'highlight.js' ;
9+ import { Observable , Subject } from 'rxjs' ;
810import { KBQ_CODE_BLOCK_FALLBACK_FILE_NAME , KbqCodeBlock , kbqCodeBlockLocaleConfigurationProvider } from './code-block' ;
911import {
1012 KBQ_CODE_BLOCK_FALLBACK_FILE_LANGUAGE ,
@@ -16,6 +18,18 @@ import { KbqCodeBlockFile } from './types';
1618
1719const HOVER_DEBOUNCE_TIME = 100 ;
1820
21+ class MockSharedResizeObserver {
22+ private readonly subject = new Subject < ResizeObserverEntry [ ] > ( ) ;
23+
24+ observe ( _element : Element ) : Observable < ResizeObserverEntry [ ] > {
25+ return this . subject . asObservable ( ) ;
26+ }
27+
28+ triggerResize ( ) : void {
29+ this . subject . next ( [ ] ) ;
30+ }
31+ }
32+
1933const createComponent = < T > ( component : Type < T > , providers : Provider [ ] = [ ] ) : ComponentFixture < T > => {
2034 TestBed . configureTestingModule ( { imports : [ component , NoopAnimationsModule ] , providers } ) ;
2135 const fixture = TestBed . createComponent < T > ( component ) ;
@@ -61,6 +75,12 @@ const getViewAllButtonElement = (debugElement: DebugElement): HTMLButtonElement
6175 return debugElement . nativeElement . querySelector ( '.kbq-code-block__view-all__button' ) ;
6276} ;
6377
78+ const mockPreHeight = ( debugElement : DebugElement , height : number ) : void => {
79+ const pre : HTMLElement = debugElement . nativeElement . querySelector ( '.kbq-code-block__pre' ) ;
80+
81+ Object . defineProperty ( pre , 'offsetHeight' , { get : ( ) => height , configurable : true } ) ;
82+ } ;
83+
6484@Component ( {
6585 imports : [ KbqCodeBlockModule ] ,
6686 template : `
@@ -133,6 +153,24 @@ export class TestCodeBlockWithTemplateTabLink {
133153 ] ;
134154}
135155
156+ @Component ( {
157+ imports : [ KbqCodeBlockModule ] ,
158+ template : `
159+ <kbq-code-block [files]="files" [maxHeight]="maxHeight" />
160+ ` ,
161+ changeDetection : ChangeDetectionStrategy . Default
162+ } )
163+ class MaxHeightCodeBlock {
164+ maxHeight = 200 ;
165+ files : KbqCodeBlockFile [ ] = [
166+ {
167+ language : 'html' ,
168+ filename : 'index.html' ,
169+ content : `<!DOCTYPE html>\n<html lang="en">\n\t<head>\n\t\t<title>Koobiq</title>\n\t</head>\n\t<body>\n\t\t<app-root>Loading...</app-root>\n\t</body>\n</html>`
170+ }
171+ ] ;
172+ }
173+
136174describe ( KbqCodeBlock . name , ( ) => {
137175 it ( 'should hide lineNumbers' , ( ) => {
138176 const fixture = createComponent ( BaseCodeBlock ) ;
@@ -498,24 +536,32 @@ describe(KbqCodeBlock.name, () => {
498536 expect ( codeBlock . classes [ 'kbq-code-block_show-actionbar' ] ) . toBeTruthy ( ) ;
499537 } ) ) ;
500538
501- it ( 'should show viewAll button' , ( ) => {
502- const fixture = createComponent ( BaseCodeBlock ) ;
503- const { debugElement, componentInstance } = fixture ;
539+ it ( 'should show viewAll button when content overflows maxHeight' , ( ) => {
540+ const mockResizeObserver = new MockSharedResizeObserver ( ) ;
541+ const fixture = createComponent ( MaxHeightCodeBlock , [
542+ { provide : SharedResizeObserver , useValue : mockResizeObserver }
543+ ] ) ;
544+ const { debugElement } = fixture ;
504545
505546 expect ( getViewAllButtonElement ( debugElement ) ) . toBeNull ( ) ;
506547
507- componentInstance . maxHeight = 300 ;
548+ mockPreHeight ( debugElement , 500 ) ;
549+ mockResizeObserver . triggerResize ( ) ;
508550 fixture . detectChanges ( ) ;
509551
510552 expect ( getViewAllButtonElement ( debugElement ) ) . toBeInstanceOf ( HTMLButtonElement ) ;
511553 } ) ;
512554
513555 it ( 'should toggle viewAll property by click' , ( ) => {
514- const fixture = createComponent ( BaseCodeBlock ) ;
515- const { debugElement, componentInstance } = fixture ;
556+ const mockResizeObserver = new MockSharedResizeObserver ( ) ;
557+ const fixture = createComponent ( MaxHeightCodeBlock , [
558+ { provide : SharedResizeObserver , useValue : mockResizeObserver }
559+ ] ) ;
560+ const { debugElement } = fixture ;
516561 const spy = jest . spyOn ( geCodeBlockDebugElement ( debugElement ) . componentInstance , 'toggleViewAll' ) ;
517562
518- componentInstance . maxHeight = 300 ;
563+ mockPreHeight ( debugElement , 500 ) ;
564+ mockResizeObserver . triggerResize ( ) ;
519565 fixture . detectChanges ( ) ;
520566
521567 getViewAllButtonElement ( debugElement ) . click ( ) ;
@@ -524,18 +570,65 @@ describe(KbqCodeBlock.name, () => {
524570 } ) ;
525571
526572 it ( 'should toggle viewAll property by ENTER keydown' , ( ) => {
527- const fixture = createComponent ( BaseCodeBlock ) ;
528- const { debugElement, componentInstance } = fixture ;
573+ const mockResizeObserver = new MockSharedResizeObserver ( ) ;
574+ const fixture = createComponent ( MaxHeightCodeBlock , [
575+ { provide : SharedResizeObserver , useValue : mockResizeObserver }
576+ ] ) ;
577+ const { debugElement } = fixture ;
529578 const spy = jest . spyOn ( geCodeBlockDebugElement ( debugElement ) . componentInstance , 'toggleViewAll' ) ;
530579
531- componentInstance . maxHeight = 300 ;
580+ mockPreHeight ( debugElement , 500 ) ;
581+ mockResizeObserver . triggerResize ( ) ;
532582 fixture . detectChanges ( ) ;
533583
534584 getViewAllButtonElement ( debugElement ) . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Enter' } ) ) ;
535585
536586 expect ( spy ) . toHaveBeenCalledTimes ( 1 ) ;
537587 } ) ;
538588
589+ describe ( 'viewAll button visibility' , ( ) => {
590+ it ( 'should not show viewAll button when content fits within maxHeight' , ( ) => {
591+ const fixture = createComponent ( MaxHeightCodeBlock , [
592+ { provide : SharedResizeObserver , useValue : new MockSharedResizeObserver ( ) }
593+ ] ) ;
594+
595+ // jsdom returns offsetHeight = 0 by default, which is less than maxHeight = 200
596+ expect ( getViewAllButtonElement ( fixture . debugElement ) ) . toBeNull ( ) ;
597+ } ) ;
598+
599+ it ( 'should show viewAll button when content overflows maxHeight' , ( ) => {
600+ const mockResizeObserver = new MockSharedResizeObserver ( ) ;
601+ const fixture = createComponent ( MaxHeightCodeBlock , [
602+ { provide : SharedResizeObserver , useValue : mockResizeObserver }
603+ ] ) ;
604+ const { debugElement } = fixture ;
605+
606+ mockPreHeight ( debugElement , 500 ) ;
607+ mockResizeObserver . triggerResize ( ) ;
608+ fixture . detectChanges ( ) ;
609+
610+ expect ( getViewAllButtonElement ( debugElement ) ) . toBeInstanceOf ( HTMLButtonElement ) ;
611+ } ) ;
612+
613+ it ( 'should hide viewAll button when content shrinks below maxHeight' , ( ) => {
614+ const mockResizeObserver = new MockSharedResizeObserver ( ) ;
615+ const fixture = createComponent ( MaxHeightCodeBlock , [
616+ { provide : SharedResizeObserver , useValue : mockResizeObserver }
617+ ] ) ;
618+ const { debugElement } = fixture ;
619+
620+ mockPreHeight ( debugElement , 500 ) ;
621+ mockResizeObserver . triggerResize ( ) ;
622+ fixture . detectChanges ( ) ;
623+ expect ( getViewAllButtonElement ( debugElement ) ) . toBeInstanceOf ( HTMLButtonElement ) ;
624+
625+ mockPreHeight ( debugElement , 100 ) ;
626+ mockResizeObserver . triggerResize ( ) ;
627+ fixture . detectChanges ( ) ;
628+ expect ( getViewAllButtonElement ( debugElement ) ) . toBeNull ( ) ;
629+ } ) ;
630+ } ) ;
631+
539632 it ( 'should use template for tabLink when provided' , ( ) => {
540633 const fixture = createComponent ( TestCodeBlockWithTemplateTabLink ) ;
541634 const { debugElement, componentInstance } = fixture ;
0 commit comments