I'm trying to mock the default DOM Image
object for unit testing an Angular service.
The service is simple, it checks the "webP" format support :
import {Injectable} from '@angular/core';
import {Promise} from 'es6-promise';
import {environment} from '../../../../environments/environment';
@Injectable({
providedIn: 'root'
})
export class AppLoadService {
constructor() {
}
initializeApp(): Promise<any> {
return new Promise((resolve) => {
console.log(`initializeApp:: inside promise`);
if (typeof Image === 'undefined') {
console.log(`initializeApp:: Image undefined`);
resolve();
return;
}
const webP = new Image();
webP.onload = () => {
console.log(`initializeApp:: WebP support: true`);
environment.webP = true;
resolve();
};
webP.onerror = () => {
console.log(`initializeApp:: WebP support: false`);
resolve();
};
webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
});
}
}
I found a way to check the webP support (default in chromium where Karma is running), and a way to check fallback on Image undefined
.
But I cannot find a way to checks the onerror
fallback...
Here is my spec file :
import {TestBed} from '@angular/core/testing';
import {AppLoadService} from './app-load.service';
import {environment} from '../../../../environments/environment';
describe('AppLoadService', () => {
let service: AppLoadService;
const originalImage = Image;
beforeEach(() => TestBed.configureTestingModule({}));
beforeEach(() => {
service = TestBed.get(AppLoadService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should resolve with webP', (done) => {
const test = () => {
Image = originalImage;
service.initializeApp().then(() => {
expect(environment.webP).toBe(true);
done();
});
};
test();
});
it('should resolve without webP (A)', (done) => {
const test = () => {
Image = undefined;
service.initializeApp().then(() => {
expect(environment.webP).toBe(false);
done();
});
};
test();
});
it('should resolve without webP (B)', (done) => {
// How to force Image to throw "onerror" ?
const test = () => {
Image = originalImage;
service.initializeApp().then(() => {
expect(environment.webP).toBe(false);
done();
});
};
test();
});
});
The question is on should resolve without webP (B)
test, at the end of the file..
Moreover, is there a better way to check undefined Image object or onload
callback ?
Thanks !
EDIT
Can't get it works as it is, so I change the service constructor to provide "Image" dependency.
constructor(@Inject('Image') image: typeof Image) {
this.image = image;
}
Have to load the module like that :
providers: [
AppLoadService,
// [...]
{provide: 'Image', useValue: Image},
]
And each resolve()
now includes environment.webP
result. Otherwise, individual test are a real pain, environment
is randomly rewritten before being tested.
With a simple Mock it works like this :
import {TestBed} from '@angular/core/testing';
import {AppLoadService} from './app-load.service';
class MockImageFail {
public onerror: Function;
private _src: string;
set src(src) {
this._src = src;
if (this.onerror) {
this.onerror();
}
}
}
describe('AppLoadService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{provide: 'Image', useValue: Image}]
});
});
it('should be created', () => {
const service = TestBed.get(AppLoadService);
expect(service).toBeTruthy();
});
it('should resolve with webP', (done) => {
const service = TestBed.get(AppLoadService);
service.initializeApp().then((supportWebP) => {
expect(supportWebP).toBe(true);
done();
});
});
it('should not resolve without webP (A)', (done) => {
TestBed.overrideProvider('Image', {useValue: undefined});
const service = TestBed.get(AppLoadService);
service.initializeApp().then((supportWebP) => {
expect(supportWebP).toBe(false);
done();
});
});
it('should not resolve without webP (B)', (done) => {
TestBed.overrideProvider('Image', {useValue: MockImageFail});
const service = TestBed.get(AppLoadService);
service.initializeApp().then((supportWebP) => {
expect(supportWebP).toBe(false);
done();
});
});
});
I'm not really happy with that and I'm sure there is another better way :/