IgniteUI / ignite-ui

Ignite UI for jQuery by Infragistics

Home Page:https://bit.ly/2kuu1fT

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

unit test grid column updating validatorOptions

ruslanss opened this issue · comments

Question

Hey guys, is it possible to unit test a call to custom validator using ig grid's API in TypeScript with Jasmin/Karma?

here's test (currently failing):

  describe('validation', () => {
    let vs: ValidationService;
    beforeEach(() => {
      vs = <ValidationService>fixture.debugElement.injector.get(ValidationService);
      spyOn(vs, 'dateIsOnOrAfter').and.returnValue(true);
      expect(vs.dateIsOnOrAfter).toBeDefined('ValidationService not found');
    });

    it('validates dates', () => {
      component.gridOptions.dataSource[0]["openingDate"] = '01/01/2019'; // !!! this does not raise validation event :(
      expect(vs.dateIsOnOrAfter).toHaveBeenCalled();
    });
  });

the component code:


      features: [
        {
          name: 'Updating',
          enableAddRow: !isReadOnly,
          editMode: 'cell',
          {
              columnKey: 'openingDate',
              required: true,
              readOnly: isReadOnly,
              editorType: 'datepicker',
              editorOptions: {
                validatorOptions: {
                  keepFocus: true,
                  errorMessage: 'Opening date must be on or before closing date.',
                  custom: this.validateOpeningDate.bind(this) // this makes a call to ValidationService
                }
              }
            },

Preface: IMHO, you can probably unit test only your method and service. Our internal test suites have coverage for Grid editing with validation and the igValidator has unit tests on its own.

The data change is kind of expected to not trigger a validation event or other events, really.
Changing the data may force the Grid to re-render (w/ Angular Wrappers), but it is a programmatic change that isn't expected to trigger much of anything and will definitely not validate the new data.

As you can tell, the validation is tied to the Updating Feature's editors, i.e. an edit must be in progress for the editors to be created and to cause validation - usually through user interaction. So, should you want to be thorough, you will have to start the edit to get to the validation.
Possible to do with Updating methods - namely startEdit.
You can set the value by either accessing the editorForKey or using setCellValue.
You can call findInvalid to trigger validation.

Note: Keep in mind, if you are using Row Editing, due to checks to allow the Done button, validation will be called on multiple occasions, even initially when starting the edit.

@damyanpetev thanks. i understand i don't need to test ig-grid's internals. what i really need to test i guess is this binding:

validatorOptions.custom: this.validateOpeningDate.bind(this)

tried this but no luck:

  it('validates dates', () => {
     const $ = jQuery;
     console.log('$(#cdcGrid)', $('#cdcGrid'));
     $('#cdcGrid').igGridUpdating('startEdit', 1, 'openingDate');
     $('#cdcGrid').igGridUpdating('setCellValue', 1, 'openingDate', '01/01/2019');
     fixture.detectChanges();
     expect(vs.dateIsOnOrAfter).toHaveBeenCalled();
   });

I've checked, even if the initial edit start doesn't trigger a validation, setting cell value definately causes a call for findInvalid to establish validity of the edit. Here's a quick demo http://jsfiddle.net/q24u5gwe/2/ - I got it reasonably close in setup I think.

Can you test with a log in the original service method? Maybe it's the way the validation service is provided, causing separate instances for the component and test fixture injectors?

yes, that's pretty close except we don't use jQuery, we use angular component & IgGridComponent.

i have console.log inside editRowStarted, editCellStarting, editCellEnded and none of them are firing. also setCellValue(rowId: number, columnKey: string, value: any) event isn't firing in the ig-grid.component.ts not sure what gives. I don't suppose you have any jsfiddle setup with angular instead of jquery?

Even with the IgGridComponent from igniteui-angular-wrappers , you are still using the same jQuery widget, so the basic functionality doesn't change - that's why the fiddle still is a valid representation. If the methods trigger validation there, they will do so in Angular env as well.

As for the test - the log should be in the service method to verify it's being called first without a spy, and then with spy to verify it's mocking on the correct instance.
Note the API methods linked above, like startEdit have a parameter to explicitly trigger events, so the editRowStarted and other handlers won't be triggered. I don't believe setCellValue is related to any specific event.

I might be able to do something in Stackblitz later.

Hey, @damyanpetev , thanks and yest, i can confirm this does work with a button on the page.
I figured the issue is with the fact data grid is not bound by the time the unit test is executed because $gridControl.igGrid('allRows').length returns 3 on the page and 0 in the unit test. I think I need to figure out a way for the unit test to wait until the grid is bound.

I don't suppose there is an ig-grid event that could be listened to for this matter?

There's dataBound for when the data is processed, however, in your use case I'd say you need to wait for at least dataRendered (for actual rows to be available) or even rendered to be on the safe side.

yes, 'iggriddatarendered' appears to trigger when grid data is already loaded. thanks. though I haven't yet figured out how to incorporate it in the unit tests.

$(document).on("iggriddatarendered", "#cdcGrid", function (evt, ui) {
    console.log('allRows' , $('#cdcGrid').igGrid('allRows').length);  // returns 3
});

@ruslanss That'd really depend on your test fixture setup.
The simplest thing to do it to attach the listener right after creating the grid just like you did above and make use of Jasmine's done() callback.
Depending on where you create the grid this can be either in it or also beforeEach or some other hook, I believe Jasmine supports async on all of them.

Edit: Snippet for rendered event was generally meaningless.
beforeEach((done) => {
// Create fixture with Grid here
// Then attach listener:
$("#cdcGrid").on("iggriddatarendered", function (evt, ui) {
done();
});
});

This will guarantee specs after this can use the grid safely.

Fancier alternatives include adding the handler in options (or markup, see Events section on angular-wrappers for their specifics) and resolve a fixture-owned Promise or Observable you can wait on after creating it.

i think we're getting closer but still no luck - this times out for all 3 events i tried:

beforeEach((done) => {
    // Create fixture with Grid here
    fixture = TestBed.createComponent(CdcComponent);
    component = fixture.componentInstance;
  
    // check grid is on the page
    expect($("#cdcGrid")).toBeDefined();
   
    // Then attach listener:
    //$(document).on("iggriddatarendered", "#cdcGrid", function (evt, ui) {
    //$(document).on("iggridrendered", "#cdcGrid", function (evt, ui) {
    $(document).on("iggriddatabound", "#cdcGrid", function (evt, ui) {
      console.warn('!!!done!!!');
      done();
  });

  fixture.detectChanges();
});


fit('should be created', () => {
  expect(component).toBeTruthy();
});

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

@ruslanss The timeout would mean the event did not get triggered at all.
I didn't pay much attention to the Grid rendering the last few posts. Actually the Grid renders synchronously, a.k.a the event + async done callback are not required at all (last post edited).
Meaning after you instantiate the Grid, it should be ready to use.

The lack of rows (but working Grid widget) should mean there's something else missing in the test config - either the data isn't there or it's not bound properly (mismatched data and config). You can check out the wrapper tests that do something quite similar:
https://github.com/IgniteUI/igniteui-angular-wrappers/blob/c028956d3cd75fa411c2c38c47f1a74c625356e0/tests/unit/iggrid/grid.spec.ts#L403-L420

This has been inactive for a while, I'll consider the thread resolved and close it.
Should you need further help, feel free to open a new issue.