I have an angular component which contains the following parts:
my.component.html (excerpt)
<button pButton
class="download ui-button ui-button-secondary"
(click)="exportFile(logEvent)"
icon="fas fa-file-download">
</button>
my.component.ts (excerpt)
import {saveAs} from 'file-saver';
exportFile(logEvent: LogEvent) {
saveAs(new Blob([logEvent.details]), 'log-details.txt');
}
This works perfectly in my application. I now wanted to test this in my unit tests. Looking for a way to make sure saveAs() has been called, I stumbled across two stackoverflow articles: mocking - Testing FileSaver in Angular 5 and Do you need spies to test if a function has been called in Jasmine?. Based on that I wrote the following test:
my.component.spec.ts (excerpt)
import * as FileSaver from 'file-saver';
beforeEach(() => {
spyOn(FileSaver, 'saveAs').and.stub();
});
it('should download a file if the download button is clicked', (fakeAsync() => {
// fakeAsync because in my real test, there are httpClient test aspects as well
advance(fixture);
expect(page.downloadButton).toBeDefined();
click(page.downloadButton);
advance(fixture);
expect(FileSaver.saveAs).toHaveBeenCalled();
}));
The two helper methods come from the Angular Testing Example:
export function advance(f: ComponentFixture<any>): void {
tick();
f.detectChanges();
}
export const ButtonClickEvents = {
left: {button: 0},
right: {button: 2}
};
export function click(el: DebugElement | HTMLElement, eventObj: any = ButtonClickEvents.left): void {
if (el instanceof HTMLElement) {
el.click();
} else {
el.triggerEventHandler('click', eventObj);
}
}
My problem is, that test fails with the following output:
Error: Expected spy saveAs to have been called.
Error: 1 timer(s) still in the queue.
So it seems that neither the stubbing nor the assertion seem to work.
If I remove the click()
call from my testm the 1 timer(s) still in the queue
error no longer shows, so I gather that the click() method works and triggers the real saveAs()
call - which I would have like to replace with the spy/mock.
How can I fix this?
Update, taking into account the proposed solutions:
I. Changing the import in my.component.spec.ts as suggested by SiddarthPal to:
import * as FileSaver from 'file-saver';
does not make any difference. The test still results in the two assertion errors.
II. Changing the spy setup as suggested by uminder to:
spyOn(FileSaver, 'saveAs').and.callFake(() => null);
does not make a difference either. The test still results in the two failed assertions.
I have tried writing the spy mock as follows:
spyOn(FileSaver, 'saveAs').and.callFake(() => {
console.log('--- faking saveAs ---');
return null;
});
Checking the output of the Karma Server output, I don't see this anywhere, so it looks like the spy does not catch my component's call to saveAs() at all.
III. The suggestion by uminder to replace:
click(page.downloadButton);
advance(fixture);
with
click(page.downloadButton);
flush();
does consume the pending timer error. Howver, that onyl hides the fact that the real saveAs()
call is used, not the spy/mock. So I am still looking for ways to get that working.