I'm trying to test whether the value of "showToolbar" is true or false based on a spyOn on getScrollTop().
My code :
@Component({
selector: 'app-header',
standalone: true,
imports: [
MatToolbar,
MatIcon,
MatIconButton,
NgOptimizedImage,
MatAnchor,
MatIconAnchor,
RouterLink
],
templateUrl: './header.component.html',
styleUrl: './header.component.scss',
animations: [
trigger('toolbarAnimation', [
state('open', style({ transform: 'translateY(0px)'})),
state('close', style({ transform: 'translateY(-100px)'})),
transition('open => close',[animate('300ms ease-out')]),
transition('close => open',[animate('300ms ease-in')]),
]),
],
})
export class HeaderComponent {
protected readonly sidenavOpeningService :SidenavOpeningService = inject(SidenavOpeningService);
showToolbar: Signal<boolean>;
private readonly topLimitShowToolbar = 349;
constructor(private scrollDispatcher: ScrollDispatcher, private viewportRuler: ViewportRuler) {
const scrollLimit = toSignal(this.scrollLimit(scrollDispatcher,viewportRuler,this.topLimitShowToolbar),{ initialValue: true }) ;
const directionScroll = toSignal(this.directionScroll(scrollDispatcher,viewportRuler),{ initialValue: true });
this.showToolbar = computed(() => directionScroll() || scrollLimit());
effect(() => this.showToolbar());
}
getScrollTop(scrollDispatcher:ScrollDispatcher,viewportRuler:ViewportRuler): Observable<number> {
return scrollDispatcher.scrolled().pipe(
map(() => viewportRuler.getViewportScrollPosition().top),
tap((_getScrollTop) => console.log('getScrollTop',_getScrollTop))
);
}
scrollLimit(scrollDispatcher:ScrollDispatcher, viewportRuler:ViewportRuler, limit:number): Observable<boolean> {
return this.getScrollTop(scrollDispatcher,viewportRuler).pipe(
map((top) => top < limit),
tap((_scrollLimit) => console.log('scrollLimit',_scrollLimit))
);
}
directionScroll(scrollDispatcher:ScrollDispatcher, viewportRuler:ViewportRuler): Observable<boolean> {
return this.getScrollTop(scrollDispatcher,viewportRuler).pipe(
scan((acc: number[], current: number) => [acc[1], current], [viewportRuler.getViewportScrollPosition().top, 0]),
skip(1), // On a besoin d'au moins deux valeurs pour savoir si le défilement monte ou descent
map(([prev, current]) => prev >= current),
tap((_directionScroll) => console.log('directionScroll',_directionScroll))
);
}
}
and
describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
let scrollDispatcher: ScrollDispatcher;
let viewportRuler: ViewportRuler;
let sidenavOpeningService: SidenavOpeningService;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BrowserAnimationsModule,HeaderComponent,MatIconTestingModule],
providers: [
{ provide: ActivatedRoute, useValue: {params: of([{id: 1}]),},},
]
}).compileComponents();
fixture = TestBed.createComponent(HeaderComponent);
component = fixture.componentInstance;
fixture.detectChanges();
// Mock services
scrollDispatcher = TestBed.inject(ScrollDispatcher);
viewportRuler = TestBed.inject(ViewportRuler);
sidenavOpeningService = TestBed.inject(SidenavOpeningService);
});
it('showToolbar should emit ture when scrolling down or up before the topLimit ', async() => {
const topLimitShowToolbar = component['topLimitShowToolbar'];
// Simuler la position de défilement et le comportement attendu
spyOn(component, 'getScrollTop').and.returnValues(of(topLimitShowToolbar-100),of(topLimitShowToolbar+1), of(topLimitShowToolbar-50));
fixture.detectChanges();
expect(component.showToolbar()).toBeTrue();
});
});
I expect the "returnValues()" to trigger the entire programming chain up to computed(), but this never happens, none of the console.log of the scollLimit and directionScroll functions triggers
The component's constructor is running before the spyOn function is called. Therefore you are not able to provide the desired mock values. Try moving the constructor logic into the ngOnInit lifecycle method.
After that be sure to remove the fixture.detectChanges() call from the beforeEach section of the spec file as it would also call the ngOnInit lifecycle hook before the spyOn call.
Instead change the spec to something like this: