angulartypescriptng-apexcharts

`ng-apexcharts` breaks unit tests


I'm trying to use apexcharts and ng-apexcharts on my home component. It was easy to get it working, but it's breaking my unit tests. I tried to look it up, but couldn't find anything that worked for me. The error happens on cleanup.

HomeComponent:

@Component({
  selector: 'hu-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss'],
  standalone: true,
  imports: [
    NavbarComponent,
    BarChartComponent
  ],
})
export class HomeComponent {}

home html:

<hu-navbar></hu-navbar>
<hu-bar-chart test-id="bar-chart"></hu-bar-chart>

BarChartComponent:

@Component({
  selector: 'hu-bar-chart',
  standalone: true,
  imports: [CommonModule, NgApexchartsModule],
  templateUrl: './bar-chart.component.html',
  styleUrls: ['./bar-chart.component.scss'],
})
export class BarChartComponent {
  @Input() series: ApexAxisChartSeries | ApexNonAxisChartSeries;
  @Input() chart: ApexChart;

  constructor() {
    this.series = [...];
    this.chart = {...};
  }
}

bar chart html:

<apx-chart test-id="bar-chart" [series]="series" [chart]="chart"></apx-chart>

HomeComponent's spec file:

describe('HomeComponent', () => {
  let component: HomeComponent;
  let fixture: ComponentFixture<HomeComponent>;
  let authenticationMock: AuthenticationMockService; // use mock to decouple unit tests from Firebase
  let page: HTMLElement;

  beforeEach(async () => {
    authenticationMock = new AuthenticationMockService();

    await TestBed.configureTestingModule({
      imports: [HomeComponent],
    })
      .overrideProvider(AuthenticationService, { useValue: authenticationMock })
      .compileComponents();

    fixture = TestBed.createComponent(HomeComponent);
    fixture.detectChanges();

    component = fixture.componentInstance;
    page = fixture.debugElement.nativeElement;
  });

  it('should create', () => {
    fixture.whenStable().then(() => {
      expect(component).toBeTruthy();
    });
  });

  it('should show the navbar', () => {
    expect(navbar()).not.toBeNull();
  });

  it('should show the bar chart', () => {
    expect(barChart()).not.toBeNull();
  });

  function navbar() {
    return page.querySelector('hu-navbar');
  }

  function barChart() {
    return page.querySelector('[test-id="bar-chart"]');
  }
});

And finally, the error:

console.error
    Error during cleanup of component {
      component: HomeComponent { __ngContext__: 7 },
      stacktrace: TypeError: Cannot read properties of undefined (reading 'node')
      ...

This error happens in every test. So far, I have only tried to add the following cleanup:

  afterEach(() => {
    setTimeout(() => {
      fixture.destroy();
    }, 100);
  });

But that didn't help. I'm thinking maybe a solution is to mock it, but couldn't find info on how to do it.

There is a smiliar question that hasn't been answered, but it's about react-apexcharts, not ng-apexcharts.


Solution

  • The solution to this problem is to add the following code to your .spec.ts. This should go inside the beforeEach that is auto-generated with ng generate:

    beforeEach(async () => {
        await TestBed.configureTestingModule({
          imports: [...],
        }).compileComponents();
    
        // Add the code below
        Object.defineProperty(window, 'ResizeObserver', {
          writable: true,
          value:
            window.ResizeObserver ||
            jest.fn().mockImplementation(() => ({
              observe: jest.fn(),
              unobserve: jest.fn(),
              disconnect: jest.fn(),
            })),
        });
    
        Object.defineProperty(global.SVGElement.prototype, 'getScreenCTM', {
          writable: true,
          value: jest.fn(),
        });
    
        Object.defineProperty(global.SVGElement.prototype, 'createSVGMatrix', {
          writable: true,
          value: jest.fn().mockReturnValue({
            x: 10,
            y: 10,
            // eslint-disable-next-line @typescript-eslint/no-empty-function
            inverse: () => {},
            // eslint-disable-next-line @typescript-eslint/no-empty-function
            multiply: () => {},
          }),
        });
        // Code to add ends here
       
        fixture = TestBed.createComponent(TheComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });