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!!!
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.