The click method in the Angular unit test does not seem to be executing successfully

I'm facing a seemingly simple issue where I am unable to confirm that a click handler is being triggered on my component.

header.component.ts

import { Component, EventEmitter, OnInit, Output } from '@angular/core';

@Component({
    selector: 'app-header',
    templateUrl: './header.component.html',
    styleUrls: ['./header.component.scss']
})
export class HeaderComponent implements OnInit {
    @Output() hamburgerToggle = new EventEmitter<void>();

    constructor() {}

    ngOnInit(): void {}

    hamburgerClicked(): void {
        this.hamburgerToggle.emit();
    }
}

header.component.html

<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
    <div class="hamburger header-toggle">
        <button (click)="hamburgerClicked()" class="btn btn-link btn-sm order-first order-first header-toggle" id="sidebarToggle" href="/">
            <i class="material-icons header-toggle">menu</i>
        </button>
    </div>
</nav>

header.component.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { HeaderComponent } from './header.component';

describe('HeaderComponent', () => {
    let component: HeaderComponent;
    let fixture: ComponentFixture<HeaderComponent>;

    beforeEach(async () => {
        await TestBed.configureTestingModule({
            declarations: [HeaderComponent]
        }).compileComponents();
    });

    beforeEach(() => {
        fixture = TestBed.createComponent(HeaderComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    });

    it('verifying if the toggle button triggers the click handler', () => {
        const fixture = TestBed.createComponent(HeaderComponent);

        const clickSpy = spyOn(component, 'hamburgerClicked');
        const toggleButton = fixture.debugElement.query(By.css('#sidebarToggle'));
        toggleButton.triggerEventHandler('click', toggleButton);

        fixture.detectChanges();

        expect(clickSpy).toHaveBeenCalledTimes(1);
    });
});

The error message states:

Expected spy hamburgerClicked to have been called once. It was called 0 times.

Even within an async block, the result remains the same:

fit('verifying if the toggle button triggers the click handler', fakeAsync(() => {
    const fixture = TestBed.createComponent(HeaderComponent);

    const clickSpy = spyOn(component, 'hamburgerClicked');
    const toggleButton = fixture.nativeElement.querySelector('#sidebarToggle');
    toggleButton.click();

    tick();
    fixture.detectChanges();

    fixture.whenStable().then(() => {
        expect(clickSpy).toHaveBeenCalledTimes(1);
    });
}));

I've spent hours trying to resolve this issue and any assistance would be greatly appreciated.

Answer №1

For each test case, a new fixture (component instance) is created. However, the component that was spied on in the beforeEach function is different. As a result, when you trigger the click event on each new component instance, they are not the same.

To ensure consistency, it is recommended to add a spy, query the DOM element, and trigger the click event on the same component instance.

The following statement should be removed from each test case:

const fixture = TestBed.createComponent(HeaderComponent);

header.component.ts:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { HeaderComponent } from './header.component';

fdescribe('HeaderComponent', () => {
  let component: HeaderComponent;
  let fixture: ComponentFixture<HeaderComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [HeaderComponent],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('clicking the toggle button invoke our click handler', () => {
    const clickSpy = spyOn(component, 'hamburgerClicked');
    const toggleButton = fixture.debugElement.query(By.css('#sidebarToggle'));
    toggleButton.triggerEventHandler('click', toggleButton);
    expect(clickSpy).toHaveBeenCalledTimes(1);
  });

  it('clicking the toggle button invoke our click handler', () => {
    const clickSpy = spyOn(component, 'hamburgerClicked');
    const toggleButton = fixture.nativeElement.querySelector('#sidebarToggle');
    toggleButton.click();
    expect(clickSpy).toHaveBeenCalledTimes(1);
  });
});

Unit test results:

Chrome 80.0.3987.87 (Mac OS 10.13.6): Executed 2 of 17 (skipped 15) SUCCESS (0.1 secs / 0.045 secs)
TOTAL: 2 SUCCESS
✔ Browser application bundle generation complete.
✔ Browser application bundle generation complete.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Replace a portion of text with a RxJS countdown timer

I am currently working on integrating a countdown timer using rxjs in my angular 12 project. Here is what I have in my typescript file: let timeLeft$ = interval(1000).pipe( map(x => this.calcTimeDiff(orderCutOffTime)), shareReplay(1) ); The calcTim ...

The error message indicates that the 'aboutData' property is not found within the 'never[]' data type

What is the correct method for printing array elements without encountering the error message "Property 'post_title' does not exist on type 'never[]'?" How can interfaces be used to define variables and utilize them in code for both ab ...

How can an Angular 2 component detect a change in the URL?

My Angular 2 component subscribes to a service based on the URL hash without calling any subcomponents. I am wondering if I should use Route or Location for this scenario, and how can I listen for and react to a pop state event? ...

Retrieve the attribute from the element that is in the active state

I'm facing a challenge in determining the active status of an element attribute. I attempted the following approach, but it incorrectly returned false even though the element had the attribute in an active state - (.c-banner.active is present) During ...

Guide to setting up identically named properties in Child container components

As I am still fairly new to React and its concepts, I may not be executing this correctly. Although the question might seem lengthy, I'll try my best to keep it simple. I have created a component for TableHead that extends some material-ui components ...

Can you choose to declare a type in TypeScript, or is it required for every variable

Has anyone encountered a problem similar to this? type B = a ? 'apple' | 'grape' | 'orange' : 'apple' | 'grape'; // This code results in an ERROR! const x = (a: boolean, b: B) => console.log('foo&a ...

Angular 7 compile error NG8002: Unable to bind object as it is not recognized as a valid property

Currently working on an Angular 7/8 test App. Encountering a compile error Can't bind 'discounter' since it isn't a known property of 'paDiscountEditor' The HTML where the error is occurring is simple... Below are all the nec ...

State in Angular stubbornly refuses to switch despite condition changes

Here is the Typescript code, followed by the HTML: public verifySelection() { let choice = false; if (typeof this.formUser.permissionsTemplateID === undefined) { choice = true; } return choice; } <div class="form-group" ...

The Angular application integrated with Keycloak experiences an endless loading loop when the page is refreshed or after the user logs in

One approach I've been trying to maintain the current state of my Angular app (the URL) when refreshing the page is by modifying the redirectUri in the isAccessAllowed function: public async isAccessAllowed( route: ActivatedRouteSnapshot, ...

Creating separate template reference variables for items in an *ngFor loop in Angular can be achieved by using the

I have a question about setting template reference variables for buttons with different attributes when rendering them using *ngFor in Angular. Specifically, I want to set the reference variable as #button1, #button2, and so on for each button, but also ne ...

Troubles with Angular 2 directive interactions: input vs. output discrepancies

I'm having trouble binding variables for inputs and outputs. I'm not sure what I'm doing incorrectly. HTML <p [timeDelta]="'2016-09-20 00:00:00'">{{delta}}</p> Below is the code for my directive: import { Directive, ...

cookies cannot be obtained from ExecutionContext

I've been trying to obtain a cookie while working with the nestjs and graphql technologies. However, I encountered an issue when it came to validating the cookies by implementing graphql on the module and creating a UseGuard. It was suggested that I ...

I am experiencing an issue where the Javascript keydown event does not recognize the character "@" in the EDGE browser

Currently, I am developing a mentioning directive that displays a list of users when the user types in an input field (a div with contentEditable=true). The user can then insert the name of the desired user in a specific format. The list is supposed to be ...

Can you generate two distinct arrays by clicking create?

My website has a customer page called customer.html: <app-z-grid title="Tip korisnika" [restPath]="'customertype'" (fillDetailParent)="reinit($event)"></app-z-grid> <app-z-grid title="Podtip korisnika" [path]='"custome ...

Encountering issues in Angular 2 when attempting to pass data into root component through ng-content and binding. Objective: Creating a reusable form component

I currently have a .NET MVC application and I'm looking to integrate Angular 2 into it. The structure of my page is as follows: <html> <head>css imports and jquery imports</head> <body> <div> a bunch of table ...

Unlock specific elements within the "sub-category" of a combined collection

If my union type is structured like this: type StateUpdate = { key: 'surname', value: string } | { key : 'age', value: number }; This setup is convenient because it allows me to determine the type of the value based on the key. Howev ...

Ways to adjust the ngx-pagination color scheme?

I am looking to customize the background color of ngx-pagination Here is my current code: <pagination-controls class="custom-pagination" id="indicadorPaginationResults" (pageChange)="p=$event" maxSize="9" directionLinks="true" autoHide="true" previ ...

Having trouble uploading a file with Angular and Spring

The issue of uploading files to BE is causing me some trouble. I have been struggling to get it right, even with the code below: Angular service public saveFile(file: File): Observable<any> { const formData = new FormData(); formDat ...

Struggling with the error message "Type 'ChangeEvent<unknown>' is not assignable to type 'ChangeEvent<MouseEvent>'" while working with React and TypeScript? Here's how you can tackle this issue

Within the App.tsx file, I encountered an issue with the Material UI Pagination component where it was throwing an error related to type mismatch. The error message stated: Argument of type 'ChangeEvent' is not assignable to parameter of type &ap ...

Angular 12 - Encountering an issue with undefined properties when trying to access 'values'

Currently in the process of upgrading Angular Version from 8 to 12. Successfully made the upgrade from 8 to 11 without any issues. However, upon updating Angular to version 12, encountered an error stating "loadComponent TypeError: Cannot read propert ...