/
elements-loader.spec.ts
144 lines (112 loc) Β· 4.77 KB
/
elements-loader.spec.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import {
ComponentFactory,
ComponentFactoryResolver, ComponentRef, Injector, NgModuleFactory, NgModuleFactoryLoader,
NgModuleRef,
Type
} from '@angular/core';
import {TestBed, fakeAsync, tick} from '@angular/core/testing';
import { ElementsLoader } from './elements-loader';
import { ELEMENT_MODULE_PATHS_TOKEN, WithCustomElementComponent } from './element-registry';
const actualCustomElements = window.customElements;
class FakeComponentFactory extends ComponentFactory<any> {
selector: string;
componentType: Type<any>;
ngContentSelectors: string[];
inputs = [{propName: this.identifyingInput, templateName: this.identifyingInput}];
outputs = [];
constructor(private identifyingInput: string) { super(); }
create(injector: Injector,
projectableNodes?: any[][],
rootSelectorOrNode?: string | any,
ngModule?: NgModuleRef<any>): ComponentRef<string> {
return jasmine.createSpyObj('ComponentRef', ['methods']);
};
}
const FAKE_COMPONENT_FACTORIES = new Map([
['element-a-module-path', new FakeComponentFactory('element-a-input')]
]);
describe('ElementsLoader', () => {
let elementsLoader: ElementsLoader;
let injectedModuleRef: NgModuleRef<any>;
let fakeCustomElements;
// ElementsLoader uses the window's customElements API. Provide a fake for this test.
beforeEach(() => {
fakeCustomElements = jasmine.createSpyObj('customElements', ['define', 'whenDefined']);
window.customElements = fakeCustomElements;
});
afterEach(() => {
window.customElements = actualCustomElements;
});
beforeEach(() => {
const injector = TestBed.configureTestingModule({
providers: [
ElementsLoader,
{ provide: NgModuleFactoryLoader, useClass: FakeModuleFactoryLoader },
{ provide: ELEMENT_MODULE_PATHS_TOKEN, useValue: new Map([
['element-a-selector', 'element-a-module-path']
])},
]
});
injectedModuleRef = injector.get(NgModuleRef);
elementsLoader = injector.get(ElementsLoader);
});
it('should be able to register an element', fakeAsync(() => {
// Verify that the elements loader considered `element-a-selector` to be unregistered.
expect(elementsLoader.elementsToLoad.has('element-a-selector')).toBeTruthy();
const hostEl = document.createElement('div');
hostEl.innerHTML = `<element-a-selector></element-a-selector>`;
elementsLoader.loadContainingCustomElements(hostEl);
tick();
const defineArgs = fakeCustomElements.define.calls.argsFor(0);
expect(defineArgs[0]).toBe('element-a-selector');
// Verify the right component was loaded/created
expect(defineArgs[1].observedAttributes[0]).toBe('element-a-input');
expect(elementsLoader.elementsToLoad.has('element-a-selector')).toBeFalsy();
}));
it('should only register an element one time', fakeAsync(() => {
const hostEl = document.createElement('div');
hostEl.innerHTML = `<element-a-selector></element-a-selector>`;
elementsLoader.loadContainingCustomElements(hostEl);
tick(); // Tick for the module factory loader's async `load` function
// Call again to to check how many times registerAsCustomElements was called.
elementsLoader.loadContainingCustomElements(hostEl);
tick(); // Tick for the module factory loader's async `load` function
// Should have only been called once, since the second load would not query for element-a
expect(window.customElements.define).toHaveBeenCalledTimes(1);
}));
});
// TEST CLASSES/HELPERS
class FakeCustomElementModule implements WithCustomElementComponent {
customElementComponent: Type<any>;
}
class FakeComponentFactoryResolver extends ComponentFactoryResolver {
constructor(private modulePath) { super(); }
resolveComponentFactory(component: Type<any>): ComponentFactory<any> {
return FAKE_COMPONENT_FACTORIES.get(this.modulePath)!;
}
}
class FakeModuleRef extends NgModuleRef<WithCustomElementComponent> {
injector = jasmine.createSpyObj('injector', ['get']);
componentFactoryResolver = new FakeComponentFactoryResolver(this.modulePath);
instance: WithCustomElementComponent = new FakeCustomElementModule();
constructor(private modulePath) {
super();
this.injector.get.and.returnValue(this.componentFactoryResolver);
}
destroy() {}
onDestroy(callback: () => void) {}
}
class FakeModuleFactory extends NgModuleFactory<any> {
moduleType: Type<any>;
moduleRefToCreate = new FakeModuleRef(this.modulePath);
constructor(private modulePath) { super(); }
create(parentInjector: Injector | null): NgModuleRef<any> {
return this.moduleRefToCreate;
}
}
class FakeModuleFactoryLoader extends NgModuleFactoryLoader {
load(modulePath: string): Promise<NgModuleFactory<any>> {
const fakeModuleFactory = new FakeModuleFactory(modulePath);
return Promise.resolve(fakeModuleFactory);
}
}