2017-09-01 57 views
0

在測試具有帶有<ng-content>的帶有插槽的角度組件時,我們沒有 明確的方法來檢查轉置的內容是否按照預期放置在組件內。 例如:如何在Angular中測試transcluded內容?

// base-button.component.ts 
@Component({ 
    selector: 'base-button', 
    template: `<button [type]="type"> 
    <ng-content></ng-content> 
    </button>`, 
}) 
export class BaseButtonComponent { 
    @Input() type = 'button'; 
} 

基本上,在規範文件中創建一個組件實例時,我們這樣做:

// base-button.component.spec.ts 
it('should reflect the `type` property into the "type" attribute of the button',() => { 
    const fixture = TestBed.createComponent(BaseButtonComponent); 
    fixture.detectChanges(); 

    const { componentInstance, nativeElement } = fixture; 
    componentInstance.type = 'reset'; 

    const button = nativeElement.querySelector('button'); 
    expect(button.type === 'reset'); 
}); 

我們可以爲每個屬性和組件的方法,但如何做到這一點 transcluded內容?一種解決方法來創建用於測試主機組件:

// base-button.component.spec.ts 
... 
@Component({ 
    template: `<base-button>Foo bar</base-button>` 
}) 
export class BaseButtonHostComponent {} 
... 

    beforeEach(async(() => { 
    TestBed.configureTestingModule({ 
     declarations: [ BaseButtonComponent, BaseButtonHostComponent ] 
    }) 
    .compileComponents(); 
    })); 

    it('should transclude the content correctly',() => { 
    const hostFixture = TestBed.createComponent(BaseButtonHostComponent); 
    hostFixture.detectChanges(); 
    const button = hostFixture.nativeElement.querySelector('button'); 
    expect(button.textContent === 'Foo bar'); 
    }); 
... 

但是,正如你可以想像,這是相當不方便,也因爲這已經做 與transcluded內容的每一個部件,並可能爲每<ng-content>元素 在其模板中。有沒有另一種方法來做到這一點?

回答

0

確實有一個相當晦澀的方法來做到這一點。基本上,TestBed.createComponent調用 組件的工廠create方法,該方法還支持將可投影的DOM節點插入到插入到插槽中的 。

// @angular/core/testing.js 
createComponent(component) { 
    ... 
    const componentFactory = this._compiler.getComponentFactory(component); 
    ... 
    const componentRef = componentFactory.create(Injector.NULL, [], `#${rootElId}`, this._moduleRef); 
    ... 
} 

我們必須做同樣的,和這裏的訣竅:

// base-button.component.spec.ts 
describe('BaseButtonComponent',() => { 
    let factory: ComponentFactory<BaseButtonComponent>; 

    beforeEach(async(() => { 
    TestBed.configureTestingModule({ 
     declarations: [ BaseButtonComponent ] 
    }) 
    .overrideModule(BrowserDynamicTestingModule, { 
     set: { 
     entryComponents: [ BaseButtonComponent ] 
     } 
    }) 
    .compileComponents(); 

    const resolver = <ComponentFactoryResolver>TestBed.get(ComponentFactoryResolver, null); 
    factory = resolver.resolveComponentFactory(BaseButtonComponent); 
    })); 

    it('should transclude the provided nodes into the button',() => { 
    const tnode = document.createTextNode('Foo bar'); 
    const componentRef = factory.create(Injector.NULL, [[ tnode ]]); 
    const button = componentRef.location.nativeElement.querySelector('button'); 
    expect(button.textContent === 'Foo bar'); 
    }); 
}); 

TestBed.get允許我們檢索ComponentFactoryResolver服務。但是,爲了檢索組件的工廠 ,組件的類必須列在模塊的entryComponents 屬性中。有問題的模塊是BrowserDynamicTestingModuleTestBed公開了方便的 方法來更改其屬性。

一旦你有了工廠,就會得到這個訣竅。唯一惱人的部分產生用手所有 可投影節點,這樣你就可以創造一個效用函數:

function createComponentWithContents(factory, ...contents) { 
    const template = document.createElement('template'); 
    const projectableNodes = contents.map(html => { 
    template.innerHTML = html; 
    return [ ...template.content.childNodes ]; 
    }); 
    return factory.create(Injector.NULL, projectableNodes); 
} 

const componentRef = createComponentWithContents(factory, '<i class="fa fa-star"></i> Win!'); 

這是一個恥辱,TestBed.createComponent不允許這樣做的時候了。