在 Angular 7 指令上测试 @HostListening('error')
Testing @HostListening('error') on Angular 7 directive
我们有一个徽标存储库,我们可以通过 ID 查找它们,但在某些情况下它们会丢失,我们需要显示 "default" 徽标。我做了一个 angular 指令来使这个过程更容易一些。它是这样使用的:
<img [appLogoFromId]="item.id"/>
这是功能指令
import { Directive, Input, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appLogoFromId]'
})
export class LogoFromIdDirective {
private static readonly baseUrl: string = 'https://our-website.com/sitelogos/';
private static readonly fallbackImgUrl: string = LogoFromIdDirective.baseUrl + 'default.svg';
@Input() set appLogoFromId(value: string | number) {
this.nativeEl.src = LogoFromIdDirective.baseUrl + value + '.jpg';
}
private readonly nativeEl: HTMLImageElement;
private errCount: number = 0;
constructor(private elem: ElementRef) {
this.nativeEl = this.elem.nativeElement;
//This directive only works on <img> elements, so throw an error otherwise
const elTag = this.nativeEl.tagName.toLowerCase();
if (elTag !== 'img') {
throw Error(`The "appLogoFromId" directive may only be used on "<img>" elements, but this is a "<${elTag}>" element!`);
}
}
@HostListener('error') onError(): void {
//404 error on image path, so we instead load this fallback image
//but if that fallback image ever goes away we don't want to be in a loop here,
//so we ned to keep track of how many errors we've encountered
if (this.errCount < 2) {
this.nativeEl.src = LogoFromIdDirective.fallbackImgUrl;
}
this.errCount++;
}
}
我的问题是:如何测试该指令的 @HostListener('error')
部分?
我有这个测试,但它失败了。我需要做哪些不同的事情?
it('should update the img element src attribute for an invalid image', () => {
component.bankId = 'foo';
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default.svg');
});
错误信息:
Expected 'https://our-website.com/sitelogos/foo.jpg' to be 'https://our-website.com/sitelogos/default.svg'.
为了完整起见,这是我针对该指令的整个规范文件
import { LogoFromIdDirective } from './logo-from-id.directive';
import {ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
@Component({
template: `<img [appLogoFromId]="theId" />`
})
class TestLogoFromIdOnImgComponent {
theId: number | string = 5;
}
@Component({
template: `<div [appLogoFromId]="theId" />`
})
class TestLogoFromIdOnNonImgComponent {
theId: number | string = 5;
}
describe('Directive: [appLogoFromId]', () => {
describe('On an `<img>` element', () => {
let component: TestLogoFromIdOnImgComponent;
let fixture: ComponentFixture<TestLogoFromIdOnImgComponent>;
let inputEl: DebugElement;
let nativeEl: HTMLInputElement;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestLogoFromIdOnImgComponent, LogoFromIdDirective],
schemas: [ NO_ERRORS_SCHEMA ]
});
fixture = TestBed.createComponent(TestLogoFromIdOnImgComponent);
component = fixture.componentInstance;
inputEl = fixture.debugElement.query(By.css('img'));
nativeEl = inputEl.nativeElement;
});
it('should set the img element src attribute for a valid image', () => {
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/5.jpg');
});
it('should update the img element src attribute for a valid image when using a number', () => {
component.theId = 2852;
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/2852.jpg');
});
it('should update the img element src attribute for a valid image when using a string', () => {
component.theId = '3278';
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/3278.jpg');
});
it('should update the img element src attribute for an invalid image', () => {
component.theId = 'foo';
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default.svg');
});
});
describe('On a `<div>` element', () => {
it('should throw an error', () => {
TestBed.configureTestingModule({
declarations: [TestLogoFromIdOnNonImgComponent, LogoFromIdDirective],
schemas: [ NO_ERRORS_SCHEMA ]
});
expect(() => TestBed.createComponent(TestLogoFromIdOnNonImgComponent)).toThrow();
});
});
});
像这样的东西应该可以工作:
inputEl.triggerEventHandler('error', null);
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default.svg');
我终于找到了有效的解决方案!
it('should update the img element src attribute for an invalid image', () => {
const spyError = spyOn(nativeEl, 'onerror' ).and.callThrough();
component.bankId = 'foo';
fixture.detectChanges();
nativeEl.dispatchEvent(new Event('error'));
expect(spyError).toHaveBeenCalled();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default_bank.svg');
});
我们有一个徽标存储库,我们可以通过 ID 查找它们,但在某些情况下它们会丢失,我们需要显示 "default" 徽标。我做了一个 angular 指令来使这个过程更容易一些。它是这样使用的:
<img [appLogoFromId]="item.id"/>
这是功能指令
import { Directive, Input, ElementRef, HostListener } from '@angular/core';
@Directive({
selector: '[appLogoFromId]'
})
export class LogoFromIdDirective {
private static readonly baseUrl: string = 'https://our-website.com/sitelogos/';
private static readonly fallbackImgUrl: string = LogoFromIdDirective.baseUrl + 'default.svg';
@Input() set appLogoFromId(value: string | number) {
this.nativeEl.src = LogoFromIdDirective.baseUrl + value + '.jpg';
}
private readonly nativeEl: HTMLImageElement;
private errCount: number = 0;
constructor(private elem: ElementRef) {
this.nativeEl = this.elem.nativeElement;
//This directive only works on <img> elements, so throw an error otherwise
const elTag = this.nativeEl.tagName.toLowerCase();
if (elTag !== 'img') {
throw Error(`The "appLogoFromId" directive may only be used on "<img>" elements, but this is a "<${elTag}>" element!`);
}
}
@HostListener('error') onError(): void {
//404 error on image path, so we instead load this fallback image
//but if that fallback image ever goes away we don't want to be in a loop here,
//so we ned to keep track of how many errors we've encountered
if (this.errCount < 2) {
this.nativeEl.src = LogoFromIdDirective.fallbackImgUrl;
}
this.errCount++;
}
}
我的问题是:如何测试该指令的 @HostListener('error')
部分?
我有这个测试,但它失败了。我需要做哪些不同的事情?
it('should update the img element src attribute for an invalid image', () => {
component.bankId = 'foo';
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default.svg');
});
错误信息:
Expected 'https://our-website.com/sitelogos/foo.jpg' to be 'https://our-website.com/sitelogos/default.svg'.
为了完整起见,这是我针对该指令的整个规范文件
import { LogoFromIdDirective } from './logo-from-id.directive';
import {ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, DebugElement, NO_ERRORS_SCHEMA } from '@angular/core';
import { By } from '@angular/platform-browser';
@Component({
template: `<img [appLogoFromId]="theId" />`
})
class TestLogoFromIdOnImgComponent {
theId: number | string = 5;
}
@Component({
template: `<div [appLogoFromId]="theId" />`
})
class TestLogoFromIdOnNonImgComponent {
theId: number | string = 5;
}
describe('Directive: [appLogoFromId]', () => {
describe('On an `<img>` element', () => {
let component: TestLogoFromIdOnImgComponent;
let fixture: ComponentFixture<TestLogoFromIdOnImgComponent>;
let inputEl: DebugElement;
let nativeEl: HTMLInputElement;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestLogoFromIdOnImgComponent, LogoFromIdDirective],
schemas: [ NO_ERRORS_SCHEMA ]
});
fixture = TestBed.createComponent(TestLogoFromIdOnImgComponent);
component = fixture.componentInstance;
inputEl = fixture.debugElement.query(By.css('img'));
nativeEl = inputEl.nativeElement;
});
it('should set the img element src attribute for a valid image', () => {
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/5.jpg');
});
it('should update the img element src attribute for a valid image when using a number', () => {
component.theId = 2852;
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/2852.jpg');
});
it('should update the img element src attribute for a valid image when using a string', () => {
component.theId = '3278';
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/3278.jpg');
});
it('should update the img element src attribute for an invalid image', () => {
component.theId = 'foo';
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default.svg');
});
});
describe('On a `<div>` element', () => {
it('should throw an error', () => {
TestBed.configureTestingModule({
declarations: [TestLogoFromIdOnNonImgComponent, LogoFromIdDirective],
schemas: [ NO_ERRORS_SCHEMA ]
});
expect(() => TestBed.createComponent(TestLogoFromIdOnNonImgComponent)).toThrow();
});
});
});
像这样的东西应该可以工作:
inputEl.triggerEventHandler('error', null);
fixture.detectChanges();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default.svg');
我终于找到了有效的解决方案!
it('should update the img element src attribute for an invalid image', () => {
const spyError = spyOn(nativeEl, 'onerror' ).and.callThrough();
component.bankId = 'foo';
fixture.detectChanges();
nativeEl.dispatchEvent(new Event('error'));
expect(spyError).toHaveBeenCalled();
expect(nativeEl.src).toBe('https://our-website.com/sitelogos/default_bank.svg');
});