import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { Component, DebugElement } from '@angular/core'; import { ComponentFactoryResolver, ElementRef, Injector, NgModule, OnInit, ViewChild } from '@angular/core'; import { Doc, DocMetadata } from '../nav-engine'; import { DocViewerComponent } from '../doc-viewer/doc-viewer.component'; import { embeddedComponents, EmbeddedComponents } from '../embedded'; /// Embedded Test Components /// ///// FooComponent ///// @Component({ selector: 'aio-foo', template: `Foo Component` }) class FooComponent { } ///// BarComponent ///// @Component({ selector: 'aio-bar', template: `

Bar Component


` }) class BarComponent implements OnInit { @ViewChild('barContent') barContentRef: ElementRef; constructor(public elementRef: ElementRef) { } // Project content in ngOnInit just like CodeExampleComponent ngOnInit() { // Security: this is a test component; never deployed this.barContentRef.nativeElement.innerHTML = this.elementRef.nativeElement.aioBarContent; } } ///// BazComponent ///// @Component({ selector: 'aio-baz', template: `
++++++++++++++

Baz Component

++++++++++++++
` }) class BazComponent implements OnInit { @ViewChild('bazContent') bazContentRef: ElementRef; constructor(public elementRef: ElementRef) { } // Project content in ngOnInit just like CodeExampleComponent ngOnInit() { // Security: this is a test component; never deployed this.bazContentRef.nativeElement.innerHTML = this.elementRef.nativeElement.aioBazContent; } } ///// Test Module ////// const embeddedTestComponents = [FooComponent, BarComponent, BazComponent, ...embeddedComponents]; @NgModule({ entryComponents: embeddedTestComponents }) class TestModule { } //// Test Component ////// @Component({ selector: 'aio-test', template: ` Test Component ` }) class TestComponent { private currentDoc: Doc; @ViewChild(DocViewerComponent) docViewer: DocViewerComponent; setDoc(doc: Doc) { if (this.docViewer) { this.docViewer.doc = doc; } } } //////// Tests ////////////// describe('DocViewerComponent', () => { const mockDocMetadata: DocMetadata = { id: 'mock', title: 'Mock Doc', url: '' }; let component: TestComponent; let docViewerDE: DebugElement; let docViewerEl: HTMLElement; let fixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ TestModule ], declarations: [ TestComponent, DocViewerComponent, embeddedTestComponents ], providers: [ {provide: EmbeddedComponents, useValue: {components: embeddedTestComponents}} ] }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(TestComponent); component = fixture.componentInstance; fixture.detectChanges(); docViewerDE = fixture.debugElement.children[0]; docViewerEl = docViewerDE.nativeElement; }); it('should create a DocViewer', () => { expect(component.docViewer).toBeTruthy(); }); it(('should display nothing when set DocViewer.doc to doc w/o content'), () => { component.docViewer.doc = { metadata: mockDocMetadata, content: '' }; expect(docViewerEl.innerHTML).toBe(''); }); it(('should display simple static content doc'), () => { const content = '

Howdy, doc viewer

'; component.docViewer.doc = { metadata: mockDocMetadata, content }; expect(docViewerEl.innerHTML).toEqual(content); }); it(('should display nothing after reset static content doc'), () => { const content = '

Howdy, doc viewer

'; component.docViewer.doc = { metadata: mockDocMetadata, content }; fixture.detectChanges(); component.docViewer.doc = { metadata: mockDocMetadata, content: '' }; expect(docViewerEl.innerHTML).toEqual(''); }); it(('should apply FooComponent'), () => { const content = `

Above Foo

Below Foo

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; const fooHtml = docViewerEl.querySelector('aio-foo').innerHTML; expect(fooHtml).toContain('Foo Component'); }); it(('should apply multiple FooComponents'), () => { const content = `

Above Foo

Holds a Ignored text

Below Foo

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; const foos = docViewerEl.querySelectorAll('aio-foo'); expect(foos.length).toBe(2); }); it(('should apply BarComponent'), () => { const content = `

Above Bar

Below Bar

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; const barHtml = docViewerEl.querySelector('aio-bar').innerHTML; expect(barHtml).toContain('Bar Component'); }); it(('should project bar content into BarComponent'), () => { const content = `

Above Bar

###bar content###

Below Bar

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; // necessary to trigger projection within ngOnInit fixture.detectChanges(); const barHtml = docViewerEl.querySelector('aio-bar').innerHTML; expect(barHtml).toContain('###bar content###'); }); it(('should include Foo and Bar'), () => { const content = `

Top

ignored

###bar content###

Bottom

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; // necessary to trigger Bar's projection within ngOnInit fixture.detectChanges(); const foos = docViewerEl.querySelectorAll('aio-foo'); expect(foos.length).toBe(2, 'should have 2 foos'); const barHtml = docViewerEl.querySelector('aio-bar').innerHTML; expect(barHtml).toContain('###bar content###', 'should have bar with projected content'); }); it(('should not include Bar within Foo'), () => { const content = `

Top

###bar content###

Bottom

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; // necessary to trigger Bar's projection within ngOnInit fixture.detectChanges(); const foos = docViewerEl.querySelectorAll('aio-foo'); expect(foos.length).toBe(2, 'should have 2 foos'); const bars = docViewerEl.querySelectorAll('aio-bar'); expect(bars.length).toBe(0, 'did not expect Bar inside Foo'); }); // because FooComponents are processed before BazComponents it(('should include Foo within Bar'), () => { const content = `

Top

Inner

Bottom

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; // necessary to trigger Bar's projection within ngOnInit fixture.detectChanges(); const foos = docViewerEl.querySelectorAll('aio-foo'); expect(foos.length).toBe(2, 'should have 2 foos'); const bars = docViewerEl.querySelectorAll('aio-bar'); expect(bars.length).toBe(1, 'should have a bar'); expect(bars[0].innerHTML).toContain('Bar Component', 'should have bar template content'); }); // The tag and its inner content is copied // But the BazComponent is not created and therefore its template content is not displayed // because BarComponents are processed before BazComponents // and no chance for first Baz inside Bar to be processed by builder. it(('should NOT include Bar within Baz'), () => { const content = `

Top

Inner ---baz stuff---

---More baz--

Bottom

`; component.docViewer.doc = { metadata: mockDocMetadata, content }; // necessary to trigger Bar's projection within ngOnInit fixture.detectChanges(); const bazs = docViewerEl.querySelectorAll('aio-baz'); // Both baz tags are there ... expect(bazs.length).toBe(2, 'should have 2 bazs'); expect(bazs[0].innerHTML).not.toContain('Baz Component', 'did not expect 1st Baz template content'); expect(bazs[1].innerHTML).toContain('Baz Component', 'expected 2nd Baz template content'); }); });