Как смоделировать объект Image с помощью Karma (Angular 7)? - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь смоделировать объект DOM Image по умолчанию для модульного тестирования службы Angular.

Сервис простой, он проверяет поддержку формата "webP":

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';
    });
  }
}

Я нашел способ проверить поддержку webP (по умолчанию в chromium, где работает Karma) и способ проверить откат на Image undefined.

Но я не могу найти способ проверить onerror запасной вариант ...

Вот мой файл спецификаций:

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();
  });
});

Вопрос по should resolve without webP (B) test, в конце файла ..

Кроме того, есть ли лучший способ проверить неопределенный объект Image или onload callback?

Спасибо!


EDIT

Не могу заставить его работать так, как есть, поэтому я изменяю конструктор службы, чтобы обеспечить зависимость "Изображение".

constructor(@Inject('Image') image: typeof Image) {
  this.image = image;
}

Нужно загрузить модуль так:

providers: [
  AppLoadService,
  // [...]
  {provide: 'Image', useValue: Image},
]

И каждый resolve() теперь включает в себя environment.webP результат. В противном случае индивидуальный тест представляет собой настоящую боль, environment случайным образом переписывается перед тестированием.

С простым макетом это работает так:

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();
    });
  });
});

Я не очень доволен этим и уверен, что есть другой лучший способ: /

...