5

I am writing the unit test case for Ag-grid in Angular where I have Angular Grid: External Filter which is toggling filter checkbox. I'm getting "TypeError: Cannot read property 'onFilterChanged' of undefined"

I'm testing this method:

toggleCheckboxMethod({ checked }): void {
    isChecked = checked;
    this.gridApi.onFilterChanged(); //when this method initiates it causes for test to fail
  }
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([]),
        HttpClientModule,
        HttpClientTestingModule,
        AgGridModule.withComponents([]),
        MatDialogModule,
        BrowserAnimationsModule
      ],
      declarations: [ TestComponent ],
      providers: []

    })
    .compileComponents();
  }));

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

  it('should toggle checkbox', () => {
    let isChecked = false;
    spyOn(component, 'toggleCheckboxMethod').and.callThrough();
    component.toggleCheckboxMethod({ checked: true });
    expect(component.toggleCheckboxMethod).toHaveBeenCalled();
    expect(isChecked).toEqual(true);
  });
Bozhinovski
  • 2,496
  • 3
  • 20
  • 38
  • I hope isChecked should the member of component class? say this.isChecked = checked; Clarify in your question – HelloWorld Jul 09 '21 at 15:52
  • @HelloWorld isChecked is not part of that component. It should act as global variable but I've add it for example. Please check the official example: https://www.ag-grid.com/angular-grid/filter-external/#:~:text=Grids%20for%202020.-,Angular%20Grid%3A%20External%20Filter,()%20and%20doesExternalFilterPass(node)%20. – Bozhinovski Jul 09 '21 at 16:29

1 Answers1

0

I think gridApi is undefined at that moment in time where you're asserting. Testing ag-grid can be strange where you have to wait for its asynchronous tasks to complete before asserting.

I would do something like this:

Create a utility function like so where you can wait for something to be true before carrying forward:

import { interval } from 'rxjs';
.....
export const waitUntil = async (untilTruthy: Function): Promise<boolean> => {
  while (!untilTruthy()) {
    await interval(25).pipe(take(1)).toPromise();
  }
  return Promise.resolve(true);
};
it('should toggle checkbox', async (done: DoneFn) => {
    let isChecked = false;
    spyOn(component, 'toggleCheckboxMethod').and.callThrough();
    // wait until component.gridApi is truthy before carrying forward
    await waitUntil(() => !!component.gridApi);
    component.toggleCheckboxMethod({ checked: true });
    expect(component.toggleCheckboxMethod).toHaveBeenCalled();
    expect(isChecked).toEqual(true);
    done(); // call done to let Jasmine know you're done (this could be optional)
  });

This is another person having the same issue as you and you may want to check the answer's solution if you don't like mine.

AliF50
  • 16,947
  • 1
  • 21
  • 37
  • I'm getting 'Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL) at '. – Bozhinovski Jul 12 '21 at 07:01
  • That means either `gridApi` is private or `gridApi` never becomes defined (i.e. `onGridReady` never gets called). Try making it public and seeing if the test passes and if it doesn't, you have to find out why it is undefined. Basically where are you defining it and make sure it is defined. – AliF50 Jul 12 '21 at 12:33
  • 1
    I've wrapped it with setTimeout and it worked. The setTimeout worked as you posted link from the answer. It's not the best and ideal solution but it works due to using third party packages can cause errors. I would accept this as an answer. Thanks for providing suggestions. – Bozhinovski Jul 12 '21 at 14:26