feat(elements): injector create (#22413)

PR Close #22413
This commit is contained in:
Andrew Seguin
2018-03-06 14:02:25 -08:00
committed by Miško Hevery
parent 46efd4b938
commit 87f60bccfd
21 changed files with 275 additions and 143 deletions

View File

@ -8,14 +8,14 @@ import { FILE_NOT_FOUND_ID, FETCHING_ERROR_ID } from 'app/documents/document.ser
import { Logger } from 'app/shared/logger.service';
import { CustomElementsModule } from 'app/custom-elements/custom-elements.module';
import { TocService } from 'app/shared/toc.service';
import { ElementsLoader } from 'app/custom-elements/elements-loader';
import {
MockTitle, MockTocService, ObservableWithSubscriptionSpies,
TestDocViewerComponent, TestModule, TestParentComponent
MockTitle, MockTocService, ObservableWithSubscriptionSpies,
TestDocViewerComponent, TestModule, TestParentComponent, MockElementsLoader
} from 'testing/doc-viewer-utils';
import { MockLogger } from 'testing/logger.service';
import { DocViewerComponent, NO_ANIMATIONS } from './doc-viewer.component';
describe('DocViewerComponent', () => {
let parentFixture: ComponentFixture<TestParentComponent>;
let parentComponent: TestParentComponent;
@ -24,7 +24,7 @@ describe('DocViewerComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [TestModule, CustomElementsModule],
imports: [CustomElementsModule, TestModule],
});
parentFixture = TestBed.createComponent(TestParentComponent);
@ -294,12 +294,16 @@ describe('DocViewerComponent', () => {
describe('#render()', () => {
let prepareTitleAndTocSpy: jasmine.Spy;
let swapViewsSpy: jasmine.Spy;
let loadElementsSpy: jasmine.Spy;
const doRender = (contents: string | null, id = 'foo') =>
new Promise<void>((resolve, reject) =>
docViewer.render({contents, id}).subscribe(resolve, reject));
beforeEach(() => {
const elementsLoader = TestBed.get(ElementsLoader) as MockElementsLoader;
loadElementsSpy =
elementsLoader.loadContainingCustomElements.and.returnValue(of([]));
prepareTitleAndTocSpy = spyOn(docViewer, 'prepareTitleAndToc');
swapViewsSpy = spyOn(docViewer, 'swapViews').and.returnValue(of(undefined));
});
@ -333,7 +337,7 @@ describe('DocViewerComponent', () => {
expect(docViewerEl.textContent).toBe('');
});
it('should prepare the title and ToC', async () => {
it('should prepare the title and ToC (before embedding components)', async () => {
prepareTitleAndTocSpy.and.callFake((targetEl: HTMLElement, docId: string) => {
expect(targetEl.innerHTML).toBe('Some content');
expect(docId).toBe('foo');
@ -342,6 +346,7 @@ describe('DocViewerComponent', () => {
await doRender('Some content', 'foo');
expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1);
expect(prepareTitleAndTocSpy).toHaveBeenCalledBefore(loadElementsSpy);
});
it('should set the title and ToC (after the content has been set)', async () => {
@ -384,6 +389,39 @@ describe('DocViewerComponent', () => {
});
});
describe('(embedding components)', () => {
it('should embed components', async () => {
await doRender('Some content');
expect(loadElementsSpy).toHaveBeenCalledTimes(1);
expect(loadElementsSpy).toHaveBeenCalledWith(docViewer.nextViewContainer);
});
it('should attempt to embed components even if the document is empty', async () => {
await doRender('');
await doRender(null);
expect(loadElementsSpy).toHaveBeenCalledTimes(2);
expect(loadElementsSpy.calls.argsFor(0)).toEqual([docViewer.nextViewContainer]);
expect(loadElementsSpy.calls.argsFor(1)).toEqual([docViewer.nextViewContainer]);
});
it('should unsubscribe from the previous "embed" observable when unsubscribed from', () => {
const obs = new ObservableWithSubscriptionSpies();
loadElementsSpy.and.returnValue(obs);
const renderObservable = docViewer.render({contents: 'Some content', id: 'foo'});
const subscription = renderObservable.subscribe();
expect(obs.subscribeSpy).toHaveBeenCalledTimes(1);
expect(obs.unsubscribeSpies[0]).not.toHaveBeenCalled();
subscription.unsubscribe();
expect(obs.subscribeSpy).toHaveBeenCalledTimes(1);
expect(obs.unsubscribeSpies[0]).toHaveBeenCalledTimes(1);
});
});
describe('(swapping views)', () => {
it('should still swap the views if the document is empty', async () => {
await doRender('');
@ -444,6 +482,25 @@ describe('DocViewerComponent', () => {
expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' });
});
it('when `EmbedComponentsService.embedInto()` fails', async () => {
const error = Error('Typical `embedInto()` error');
loadElementsSpy.and.callFake(() => {
expect(docViewer.nextViewContainer.innerHTML).not.toBe('');
throw error;
});
await doRender('Some content', 'bar');
expect(prepareTitleAndTocSpy).toHaveBeenCalledTimes(1);
expect(loadElementsSpy).toHaveBeenCalledTimes(1);
expect(swapViewsSpy).not.toHaveBeenCalled();
expect(docViewer.nextViewContainer.innerHTML).toBe('');
expect(logger.output.error).toEqual([
[`[DocViewer] Error preparing document 'bar': ${error.stack}`],
]);
expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'googlebot', content: 'noindex' });
expect(TestBed.get(Meta).addTag).toHaveBeenCalledWith({ name: 'robots', content: 'noindex' });
});
it('when `swapViews()` fails', async () => {
const error = Error('Typical `swapViews()` error');
@ -486,13 +543,24 @@ describe('DocViewerComponent', () => {
});
describe('(events)', () => {
it('should emit `docReady`', async () => {
it('should emit `docReady` after loading elements', async () => {
const onDocReadySpy = jasmine.createSpy('onDocReady');
docViewer.docReady.subscribe(onDocReadySpy);
await doRender('Some content');
expect(onDocReadySpy).toHaveBeenCalledTimes(1);
expect(loadElementsSpy).toHaveBeenCalledBefore(onDocReadySpy);
});
it('should emit `docReady` before swapping views', async () => {
const onDocReadySpy = jasmine.createSpy('onDocReady');
docViewer.docReady.subscribe(onDocReadySpy);
await doRender('Some content');
expect(onDocReadySpy).toHaveBeenCalledTimes(1);
expect(onDocReadySpy).toHaveBeenCalledBefore(swapViewsSpy);
});
it('should emit `docRendered` after swapping views', async () => {