3

So I have an Open Source library named Angular-Slickgrid which doesn't have tests yet and I'm trying to use Jest with it but it's really hard to get going with it. The library is a wrapper of and old jQuery datagrid library (SlickGrid) which also uses jQuery UI. I think I got partially over the jQuery problem (not even sure), but jQuery UI still complains. Also note that I'm new to Jest and Unit Testing in Angular but I really want this to work and make my lib safer.

You can see a commit on GitHub of all the code change I made to try implementing Jest with my Open Source lib. The commit is here. Feel free to create a PR if that is easier. I use the previous version of Jest (23.6.0) instead of the latest, because I have other kind of issues with latest.

This is the error I have currently

FAIL  src/app/modules/angular-slickgrid/components/angular-slickgrid.component.spec.ts
Test suite failed to run 
TypeError: Cannot read property 'ui' of undefined
  at node_modules/jquery-ui-dist/jquery-ui.js:18:10
  at Object.<anonymous>.$.ui (node_modules/jquery-ui-dist/jquery-ui.js:14:3)
  at Object.<anonymous> (node_modules/jquery-ui-dist/jquery-ui.js:16:2)
  at Object.<anonymous> (src/app/modules/angular-slickgrid/components/angular-slickgrid.component.ts:11193:1)
  at Object.<anonymous> (src/app/modules/angular-slickgrid/components/angular-slickgrid.component.spec.ts:7:37)

I tried to use unmock('jquery') and unmock('jquery-ui') but that doesn't seem to help. Here's the test that fails

jest.unmock('jquery');
jest.unmock('jquery-ui');
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';

import { AngularSlickgridComponent } from './angular-slickgrid.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AngularSlickgridComponent
      ],
      providers: [],
      imports: [RouterTestingModule]
    }).compileComponents();
  }));

  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AngularSlickgridComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));
});

Also my jest.config.js

module.exports = {
  globals: {
    'ts-jest': {
      tsConfigFile: './src/tsconfig.spec.json',
    },
    __TRANSFORM_HTML__: true,
  },
  testMatch: ['**/__tests__/**/*.+(ts|js)', '**/+(*.)+(spec|test).+(ts|js)'],
  setupFiles: ['<rootDir>/test-env.ts'],
  setupTestFrameworkScriptFile: '<rootDir>/node_modules/@angular-builders/jest/src/jest-config/setup.js',
  transform: {
    '^.+\\.(ts|html)$': '<rootDir>/node_modules/jest-preset-angular/preprocessor.js',
  },
  transformIgnorePatterns: ['node_modules/(?!@ngrx)'],
  moduleDirectories: [
    "node_modules",
    "src/app",
  ],
  collectCoverage: true,
  moduleFileExtensions: [
    'ts',
    'json',
    'js'
  ],
  testResultsProcessor: 'jest-sonar-reporter',
  moduleNameMapper: {
    "app/(.*)": "<rootDir>/src/app/$1",
    "@common/(.*)": "<rootDir>/src/app/common/$1",
  }
};

and finally a Test Setup to import jQuery globally for Jest

import jQuery from 'jquery';
declare var window: any;
declare var global: any;
window.$ = window.jQuery = jQuery;
global.$ = global.jQuery = jQuery;

What I wish to accomplish is to test at least my Angular Services and the Component creation, that would a good start but I can't get passed the jQuery and jQuery UI issue, even though I don't want to test any of the core library (SlickGrid), neither jQuery, jQuery UI.

EDIT

Thanks to @brian-lives-outdoors for the answer on jQuery and jQuery-UI, I got much further. Now I have another small issue with an @Inject() used directly in the Constructor (that is to pass configs to my Component library), which I'm not sure how to get around it, if someone knows how please help.

constructor(
  private elm: ElementRef,
  // ... more Services import
  //
  @Inject('config') private forRootConfig: GridOption
) {}

and the error is

StaticInjectorError(DynamicTestModule)[config]:
  StaticInjectorError(Platform: core)[config]:
    NullInjectorError: No provider for config!

at NullInjector.get (../packages/core/src/di/injector.ts:43:13)
at resolveToken (../packages/core/src/di/injector.ts:346:20)
...

ANSWER of last edit question

I found how to fix the Constructor and @Inject(), I can do that one with overrideComponent() inside the beforeEach as shown below

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [
      AngularSlickgridComponent,
      SlickPaginationComponent
    ],
    providers: [
      // ... all Services
    ],
    imports: [
      RouterTestingModule,
      TranslateModule.forRoot()
    ]
  })
  // THIS LINE is for the @Inject('config')
  .overrideComponent(AngularSlickgridComponent, {
    set: { providers: [{ provide: 'config', useValue: {} }] },
  })
  .compileComponents();
}));

And finally I can now say that I have Jest running!!!

ghiscoding
  • 12,308
  • 6
  • 69
  • 112
  • One more question. Why are you including the jQuery. et al scripts in the Angular.json file while at the same time using module imports of the same files? – Randy Casburn Mar 18 '19 at 15:03
  • 1
    I very briefly looked at your repo and you've done everything correctly. I agree, My answer is not very helpful. I'll delete it until I can assist further – Randy Casburn Mar 18 '19 at 15:03
  • To give a bit of an overview, before trying with Jest everything was with `import`, including jQuery and jQuery-UI but I read somewhere that Jest works better with `require` so I changed them but I would prefer to keep them as `import`, Jest might be better at it now. I literally just want to get this going and I tried a lot of things without much luck yet – ghiscoding Mar 18 '19 at 15:42

2 Answers2

4

The issue is that jQuery was being imported like this:

import jQuery from 'jquery';

...which doesn't work right and causes jQuery to be undefined.

Because jQuery is imported as undefined the global $ is set to undefined and when jQuery UI tries to load it throws an error.


This problem is strange because that syntax for importing jQuery shows up a lot, even as an example of import syntax in the official TypeScript docs.


In any case, you can fix the problem by importing jQuery using this syntax:

import * as jQuery from 'jquery';


Change your test-env.ts to this:

import * as jQuery from 'jquery';
declare var window: any;
declare var global: any;
window.$ = window.jQuery = jQuery;
global.$ = global.jQuery = jQuery;

...change the top of angular-slickgrid.components.ts to this:

// import 3rd party vendor libs
// only import the necessary core lib, each will be imported on demand when enabled (via require)
import 'jquery-ui-dist/jquery-ui';
import 'slickgrid/lib/jquery.event.drag-2.3.0';
import 'slickgrid/slick.core';
import 'slickgrid/slick.grid';
import 'slickgrid/slick.dataview';

// ...then everything else...
import { AfterViewInit, Component, ElementRef, EventEmitter, Inject, Injectable, Input, Output, OnDestroy, OnInit } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { GlobalGridOptions } from './../global-grid-options';
// ...

...and change your angular-slickgrid.component.spec.ts to this:

import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';

import { AngularSlickgridComponent } from './angular-slickgrid.component';

describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AngularSlickgridComponent
      ],
      providers: [],
      imports: [RouterTestingModule]
    }).compileComponents();
  }));

  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AngularSlickgridComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));

  it(`should have as title 'Angular SlickGrid Demo'`, async(() => {
    const fixture = TestBed.createComponent(AngularSlickgridComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app.title).toEqual('Angular SlickGrid Demo');
  }));
});

...and that will get you past your initial jQuery errors.

Brian Adams
  • 43,011
  • 9
  • 113
  • 111
  • ahh that is awesome, thanks a lot. I have yet another small issue with an `@Inject` in a Constructor, if you happen to know how to fix that too, that would be awesome. Nonetheless, I'm still going to accept your answer by the end of the day. Thanks big time – ghiscoding Mar 19 '19 at 16:00
  • @ghiscoding with the `jQuery` issue resolved I'm getting `Tempate parse errors: Can't bind to 'dataView' since it isn't a known property of 'slick-pagination'`. I'm not sure how much more help I can be since I'm more familiar with `React` and haven't done a lot with the newer `Angular` stuff. – Brian Adams Mar 19 '19 at 19:13
  • yeah it's ok, I was asking in case you already knew the answer, but it's fine you did answer my original question. For the other errors you've seen, I provided the necessary Services and I got further, it's in this [commit](https://github.com/ghiscoding/Angular-Slickgrid/commit/99f9eb5b8be99146743cf750b51e00dce8c9e451). I'm just blocked at that `@Inject()` thing, doing more research on it, might be a separate SO question. – ghiscoding Mar 19 '19 at 19:27
-1

Add in your jest.config.js this line:

setupFiles: ['<rootDir>/src/test-env.js']

Change your test-env.ts to this:

import * as jQuery from 'jquery';
window.$ = window.jQuery = jQuery;
global.$ = global.jQuery = jQuery;

import 'jquery-ui/ui/version';
import 'jquery-ui/ui/plugin';
import 'jquery-ui/ui/widget';
import 'jquery-ui/ui/widgets/mouse';
Fuad Javadov
  • 148
  • 2
  • 4