2

I have an @Output event Emitter in my component and in order to test the value emitted by it I have done something like this:

component.uploadFinished = {emit: jasmine.createSpy()};
---
---
expect(component.uploadFinished.emit).toHaveBeenCalledWith({response: jasmine.any(HttpResponse)})

Here it is not allowing me to write component.uploadFinished as it says that its a read-only property. Any help will be appreciated. Also component here is of type TestComponent

EDIT 1:

this is my ts file and the function for which I am writing the tests

  async fileUpload() {

    this.fileUploadStatus = 'uploading';
    const file = this.uploadFile[0];
    const formData = new FormData();
    formData.append(this.uploadConfig.fieldName, file, file.name);

    const req = new HttpRequest(this.uploadConfig.method, this.uploadConfig.url, formData, {
      headers: this.uploadConfig.headers
    });
    try {
      const response = await this.http.request(req).pipe(
        retry(3)
      ).toPromise() as HttpResponse<any>;
      this.fileUploadStatus = 'success';
      this.uploadFinished.emit({response});
    } catch (error) {
      this.uploadFinished.emit({error});
    }
  }

And this is unit test

it('should emit success response', async () => {

    component.uploadFinished = {emit: jasmine.createSpy()};
    // eslint-disable-next-line max-len
    component.uploadConfig = {
      method: 'POST',
      url: '/api/attachments',
      fieldName: 'file',
      // eslint-disable-next-line @typescript-eslint/naming-convention
      headers: new HttpHeaders({ 'Accept': 'application/json' })
    };

    component.handleFiles(createFileList(['matching.fmwr']));
    fixture.detectChanges();

    component.fileUpload(); // trigger upload

    // mock request
    const attachment = { id: 201, fileName: 'matching.fmwr' };
    const req = httpMock.expectOne('/api/attachments');
    req.flush(attachment);
    httpMock.verify();

    // api call is async function and there is time delay in emit
    await (new Promise((resolve, reject) => {
      setTimeout(()=>{
        expect(component.uploadFinished.emit).toHaveBeenCalledWith({response: jasmine.any(HttpResponse)})
      return resolve(true);
      }, 1000);
    }))
  });

I have to use setTimeout since due to async function emit was not getting called as needed. Would appreciate if a better way for this can be suggested.

Gini
  • 85
  • 7
  • You don't need to replace the emitter, test as shown here: https://stackoverflow.com/a/62871934/3001761 – jonrsharpe Dec 03 '21 at 14:02
  • @jonrsharpe But as this example shows my Output emitter does not get emit value on a click rather it emits on some http calls success or failure. So I dont want to trigger just the click to invoke the Output event. – Gini Dec 03 '21 at 14:12
  • _Does_ this example show that? What click are you supposed to be triggering? Give a [mre]. – jonrsharpe Dec 03 '21 at 14:13
  • @jonrsharpe I have added the example code.Please check. Also if could suggest a better way by which I can avoid using setTimeout it will be really nice. – Gini Dec 03 '21 at 15:41

2 Answers2

0

You can disable TypeScript checking for a line of code:

// @ts-ignore
expect(component.uploadFinished.emit).toHaveBeenCalledWith({response)

Note: I would suggest you only use this in spec files. In general it's a last resort

Drenai
  • 11,315
  • 9
  • 48
  • 82
0

I fixed this issue by changing

component.uploadFinished = {emit: jasmine.createSpy()};

to

spyOn(component.uploadFinished, 'emit');
Gini
  • 85
  • 7