Search code examples
angularjasmineangular-testangular-event-emitter

Angular Integration test: testing a function that is called when event from other component is emitted, how do I mock the event data?


In my component A I have a function that updates the view based on data emitted from component B. I don't want to integrate component B and make an actual even as that's too complex for this test.

I just want to call the function and pass the data to the function. The problem is, sending the data as an 'event' to the function in component A does not seem to work:

  it('should update the video with the data from the edit component', () => {
    let event;
    event.title = 'New Title';
    event.description = 'New Description';
    event.link = 'New Link';
    event.videoCategory = 'New Category';
    event.categories = '2';
    event.a14Only = 0;

    component.updateVideoCard(event);
    fixture.detectChanges();

    expect(component.videoTitle).toBe('New Title');
    expect(component.videoLink).toBe('New Link');
    expect(component.videoDescription).toBe('New Description');
    expect(component.videoCategory).toBe('New Category');
    expect(component.categoryID).toBe('2');
    expect(component.a14Only).toBe('0');
    expect(component.editDisabled).toBeTruthy();
  });

and that event ends up as 'undefined'. I have also tried making it a javascript object called 'event' that has the key-value pairs inside it but that has yielded no luck either.

component.updateEvent(data) code:

updateVideoCard(event) {
    this.videoTitle = event.title;
    this.videoDescription = event.description;
    this.videoLink = event.link;
    this.videoCategory = event.category;
    this.categoryID = event.categories;
    if (event.a14Only === 1) {
      this.a14Only = true;
    } else {
      this.a14Only = false;
    }
    this.enableEditor = false;
    this.notification.done(`${this.videoTitle} updated successfully.`);
  }

Solution

  • I've looked at the DebugElement.triggerEvent but unfortunately the documentation was outdated and haven't had a lot of luck figuring it out by myself how to do it. It also seemed to require integrating the second component anyway.

    I ended up integrating the 2 components and just triggering it with a standard JSON object from the second component like this:

    describe('VideoCardComponent', () => {
      let component: VideoCardComponent;
      let fixture: ComponentFixture<VideoCardComponent>;
      let fixture2: ComponentFixture<EditVideoComponent>;
      let component2: EditVideoComponent;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [
            MatCardModule,
            MatButtonModule,
            BrowserAnimationsModule,
            FontAwesomeModule,
            BrowserModule,
            FlexLayoutModule,
            RouterTestingModule,
            ReactiveFormsModule,
            FormsModule,
            MatSelectModule,
            MatOptionModule,
            MatInputModule,
            MatSlideToggleModule
          ],
          declarations: [VideoCardComponent, SafepipePipe, EditVideoComponent],
          providers: [
            { provide: RestService, useClass: RestStub },
            { provide: NotificationService, useClass: NotificationStub }
          ],
          schemas: [NO_ERRORS_SCHEMA]
        })
          .compileComponents()
          .then(() => {
            fixture2 = TestBed.createComponent(EditVideoComponent);
            fixture = TestBed.createComponent(VideoCardComponent);
            component = fixture.componentInstance;
            component2 = fixture2.componentInstance;
            fixture2.detectChanges();
            fixture.detectChanges();
          });
      }));
    

    You can see I just named it component2 and fixture2 and added the dependecies for both in the same 1 test bed.

    I'll probably name them something more relevant instead of component and component2.

    The fixed test:

    it('should update the video with the data from the edit component', () => {
        const data = [
          {
            title: 'New Title',
            description: 'New Description',
            link: 'New Link',
            category: 'New Category',
            categories: '2',
            a14Only: 0
          }
        ];
    
        component2.updateVideoCard.subscribe(newVideo => {
          component.updateVideoCard(newVideo);
          expect(component.videoTitle).toBe('New Title');
          expect(component.videoLink).toBe('New Link');
          expect(component.videoDescription).toBe('New Description');
          expect(component.videoCategory).toBe('New Category');
          expect(component.categoryID).toBe('2');
          expect(component.a14Only).toBeFalsy();
          expect(component.editDisabled).toBeFalsy();
        });
    
        component2.updateLocalVideoData(data);
    
        fixture2.detectChanges();
        fixture.detectChanges();
      });