2017-01-20 16:10:57 -05:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2017-09-22 17:29:16 -04:00
|
|
|
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, ErrorHandler, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, Renderer, Renderer2, SimpleChange, TemplateRef, ViewContainerRef,} from '@angular/core';
|
2017-01-27 16:19:00 -05:00
|
|
|
import {getDebugContext} from '@angular/core/src/errors';
|
2017-09-22 17:29:16 -04:00
|
|
|
import {ArgumentType, DepFlags, NodeFlags, Services, anchorDef, asElementData, directiveDef, elementDef, providerDef, textDef} from '@angular/core/src/view/index';
|
|
|
|
import {TestBed, withModule} from '@angular/core/testing';
|
2017-01-20 16:10:57 -05:00
|
|
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
|
|
|
|
2017-09-22 17:29:16 -04:00
|
|
|
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, createAndGetRootNodes, compViewDef, compViewDefFactory} from './helper';
|
2018-11-05 23:31:54 -05:00
|
|
|
import {fixmeIvy} from '@angular/private/testing';
|
2017-01-20 16:10:57 -05:00
|
|
|
|
2017-12-15 19:28:41 -05:00
|
|
|
{
|
2017-02-03 18:20:50 -05:00
|
|
|
describe(`View Providers`, () => {
|
2017-01-20 16:10:57 -05:00
|
|
|
|
|
|
|
describe('create', () => {
|
2017-02-01 10:27:38 -05:00
|
|
|
let instance: SomeService;
|
|
|
|
|
|
|
|
class SomeService {
|
|
|
|
constructor(public dep: any) { instance = this; }
|
|
|
|
}
|
|
|
|
|
2017-03-29 12:34:45 -04:00
|
|
|
beforeEach(() => { instance = null !; });
|
2017-02-01 10:27:38 -05:00
|
|
|
|
2017-01-20 16:10:57 -05:00
|
|
|
it('should create providers eagerly', () => {
|
2017-02-01 10:27:38 -05:00
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [])
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance instanceof SomeService).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create providers lazily', () => {
|
2017-03-29 12:34:45 -04:00
|
|
|
let lazy: LazyService = undefined !;
|
2017-02-01 10:27:38 -05:00
|
|
|
class LazyService {
|
|
|
|
constructor() { lazy = this; }
|
2017-01-20 16:10:57 -05:00
|
|
|
}
|
|
|
|
|
2017-01-23 19:59:20 -05:00
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 2, 'span'),
|
2017-02-09 17:59:57 -05:00
|
|
|
providerDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
NodeFlags.TypeClassProvider | NodeFlags.LazyProvider, null, LazyService, LazyService,
|
|
|
|
[]),
|
|
|
|
directiveDef(2, NodeFlags.None, null, 0, SomeService, [Injector])
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(lazy).toBeUndefined();
|
|
|
|
instance.dep.get(LazyService);
|
|
|
|
expect(lazy instanceof LazyService).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create value providers', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 2, 'span'),
|
|
|
|
providerDef(NodeFlags.TypeValueProvider, null, 'someToken', 'someValue', []),
|
|
|
|
directiveDef(2, NodeFlags.None, null, 0, SomeService, ['someToken']),
|
2017-01-23 19:59:20 -05:00
|
|
|
]));
|
2017-01-20 16:10:57 -05:00
|
|
|
|
2017-02-01 10:27:38 -05:00
|
|
|
expect(instance.dep).toBe('someValue');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create factory providers', () => {
|
|
|
|
function someFactory() { return 'someValue'; }
|
|
|
|
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 2, 'span'),
|
|
|
|
providerDef(NodeFlags.TypeFactoryProvider, null, 'someToken', someFactory, []),
|
|
|
|
directiveDef(2, NodeFlags.None, null, 0, SomeService, ['someToken']),
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep).toBe('someValue');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should create useExisting providers', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 3, 'span'),
|
|
|
|
providerDef(NodeFlags.TypeValueProvider, null, 'someExistingToken', 'someValue', []),
|
2017-02-01 10:27:38 -05:00
|
|
|
providerDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
NodeFlags.TypeUseExistingProvider, null, 'someToken', null, ['someExistingToken']),
|
|
|
|
directiveDef(3, NodeFlags.None, null, 0, SomeService, ['someToken']),
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep).toBe('someValue');
|
2017-01-20 16:10:57 -05:00
|
|
|
});
|
|
|
|
|
2017-01-26 20:07:37 -05:00
|
|
|
it('should add a DebugContext to errors in provider factories', () => {
|
|
|
|
class SomeService {
|
|
|
|
constructor() { throw new Error('Test'); }
|
|
|
|
}
|
|
|
|
|
|
|
|
let err: any;
|
|
|
|
try {
|
2017-02-03 18:20:50 -05:00
|
|
|
createRootView(
|
|
|
|
compViewDef([
|
2017-02-21 16:56:56 -05:00
|
|
|
elementDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
0, NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
|
|
|
() => compViewDef([textDef(0, null, ['a'])])),
|
|
|
|
directiveDef(1, NodeFlags.Component, null, 0, SomeService, [])
|
2017-02-03 18:20:50 -05:00
|
|
|
]),
|
|
|
|
TestBed.get(Injector), [], getDOM().createElement('div'));
|
2017-01-26 20:07:37 -05:00
|
|
|
} catch (e) {
|
|
|
|
err = e;
|
|
|
|
}
|
|
|
|
expect(err).toBeTruthy();
|
|
|
|
expect(err.message).toBe('Test');
|
2017-01-27 16:19:00 -05:00
|
|
|
const debugCtx = getDebugContext(err);
|
2017-01-26 20:07:37 -05:00
|
|
|
expect(debugCtx.view).toBeTruthy();
|
2017-02-03 18:20:50 -05:00
|
|
|
expect(debugCtx.nodeIndex).toBe(1);
|
2017-01-26 20:07:37 -05:00
|
|
|
});
|
|
|
|
|
2018-11-05 23:31:54 -05:00
|
|
|
fixmeIvy('unknown') && describe('deps', () => {
|
2017-01-20 16:10:57 -05:00
|
|
|
class Dep {}
|
|
|
|
|
|
|
|
it('should inject deps from the same element', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 2, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, Dep, []),
|
|
|
|
directiveDef(2, NodeFlags.None, null, 0, SomeService, [Dep])
|
2017-01-20 16:10:57 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep instanceof Dep).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should inject deps from a parent element', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 3, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, Dep, []),
|
|
|
|
elementDef(2, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(3, NodeFlags.None, null, 0, SomeService, [Dep])
|
2017-01-20 16:10:57 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep instanceof Dep).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should not inject deps from sibling root elements', () => {
|
2017-09-22 17:29:16 -04:00
|
|
|
const rootElNodes = [
|
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, Dep, []),
|
|
|
|
elementDef(2, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(3, NodeFlags.None, null, 0, SomeService, [Dep]),
|
2017-01-20 16:10:57 -05:00
|
|
|
];
|
|
|
|
|
2017-09-22 17:29:16 -04:00
|
|
|
expect(() => createAndGetRootNodes(compViewDef(rootElNodes)))
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
.toThrowError(
|
2017-12-06 04:13:50 -05:00
|
|
|
'StaticInjectorError(DynamicTestModule)[SomeService -> Dep]: \n' +
|
|
|
|
' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' +
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
' NullInjectorError: No provider for Dep!');
|
2017-01-20 16:10:57 -05:00
|
|
|
|
2017-09-22 17:29:16 -04:00
|
|
|
const nonRootElNodes = [
|
|
|
|
elementDef(0, NodeFlags.None, null, null, 4, 'span'),
|
|
|
|
elementDef(1, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(2, NodeFlags.None, null, 0, Dep, []),
|
|
|
|
elementDef(3, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(4, NodeFlags.None, null, 0, SomeService, [Dep]),
|
|
|
|
];
|
|
|
|
|
|
|
|
expect(() => createAndGetRootNodes(compViewDef(nonRootElNodes)))
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
.toThrowError(
|
2017-12-06 04:13:50 -05:00
|
|
|
'StaticInjectorError(DynamicTestModule)[SomeService -> Dep]: \n' +
|
|
|
|
' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' +
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
' NullInjectorError: No provider for Dep!');
|
2017-01-20 16:10:57 -05:00
|
|
|
});
|
|
|
|
|
2017-07-07 19:55:17 -04:00
|
|
|
it('should inject from a parent element in a parent view', () => {
|
2017-01-20 16:10:57 -05:00
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-02-21 16:56:56 -05:00
|
|
|
elementDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
0, NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
2017-01-19 13:25:03 -05:00
|
|
|
() => compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [Dep])
|
2017-01-19 13:25:03 -05:00
|
|
|
])),
|
2017-09-22 17:29:16 -04:00
|
|
|
directiveDef(1, NodeFlags.Component, null, 0, Dep, []),
|
2017-01-20 16:10:57 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep instanceof Dep).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
2017-02-01 10:27:38 -05:00
|
|
|
it('should throw for missing dependencies', () => {
|
|
|
|
expect(() => createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, ['nonExistingDep'])
|
2017-02-01 10:27:38 -05:00
|
|
|
])))
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
.toThrowError(
|
2017-12-06 04:13:50 -05:00
|
|
|
'StaticInjectorError(DynamicTestModule)[nonExistingDep]: \n' +
|
|
|
|
' StaticInjectorError(Platform: core)[nonExistingDep]: \n' +
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
' NullInjectorError: No provider for nonExistingDep!');
|
2017-02-01 10:27:38 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should use null for optional missing dependencies', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
2017-02-01 10:27:38 -05:00
|
|
|
directiveDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
1, NodeFlags.None, null, 0, SomeService,
|
|
|
|
[[DepFlags.Optional, 'nonExistingDep']])
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
expect(instance.dep).toBe(null);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should skip the current element when using SkipSelf', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 4, 'span'),
|
|
|
|
providerDef(NodeFlags.TypeValueProvider, null, 'someToken', 'someParentValue', []),
|
|
|
|
elementDef(2, NodeFlags.None, null, null, 2, 'span'),
|
|
|
|
providerDef(NodeFlags.TypeValueProvider, null, 'someToken', 'someValue', []),
|
2017-02-01 10:27:38 -05:00
|
|
|
directiveDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
4, NodeFlags.None, null, 0, SomeService, [[DepFlags.SkipSelf, 'someToken']])
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
expect(instance.dep).toBe('someParentValue');
|
|
|
|
});
|
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
it('should ask the root injector',
|
|
|
|
withModule({providers: [{provide: 'rootDep', useValue: 'rootValue'}]}, () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, ['rootDep'])
|
2017-02-03 18:20:50 -05:00
|
|
|
]));
|
2017-02-01 14:32:27 -05:00
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
expect(instance.dep).toBe('rootValue');
|
|
|
|
}));
|
2017-02-01 14:32:27 -05:00
|
|
|
|
2017-01-20 16:10:57 -05:00
|
|
|
describe('builtin tokens', () => {
|
|
|
|
it('should inject ViewContainerRef', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
anchorDef(NodeFlags.EmbeddedViews, null, null, 1),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [ViewContainerRef]),
|
2017-01-20 16:10:57 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep.createEmbeddedView).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should inject TemplateRef', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
anchorDef(NodeFlags.None, null, null, 1, null, compViewDefFactory([anchorDef(
|
|
|
|
NodeFlags.None, null, null, 0)])),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [TemplateRef]),
|
2017-01-20 16:10:57 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep.createEmbeddedView).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should inject ElementRef', () => {
|
2017-01-26 20:07:37 -05:00
|
|
|
const {view} = createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [ElementRef]),
|
2017-01-20 16:10:57 -05:00
|
|
|
]));
|
|
|
|
|
2017-01-26 20:07:37 -05:00
|
|
|
expect(instance.dep.nativeElement).toBe(asElementData(view, 0).renderElement);
|
2017-01-20 16:10:57 -05:00
|
|
|
});
|
|
|
|
|
2017-02-01 10:27:38 -05:00
|
|
|
it('should inject Injector', () => {
|
|
|
|
const {view} = createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [Injector]),
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep.get(SomeService)).toBe(instance);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should inject ChangeDetectorRef for non component providers', () => {
|
|
|
|
const {view} = createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.None, null, 0, SomeService, [ChangeDetectorRef])
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep._view).toBe(view);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should inject ChangeDetectorRef for component providers', () => {
|
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
2017-02-21 16:56:56 -05:00
|
|
|
elementDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
0, NodeFlags.None, null, null, 1, 'div', null, null, null, null,
|
2017-02-01 10:27:38 -05:00
|
|
|
() => compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 0, 'span'),
|
2017-02-01 10:27:38 -05:00
|
|
|
])),
|
2017-09-22 17:29:16 -04:00
|
|
|
directiveDef(1, NodeFlags.Component, null, 0, SomeService, [ChangeDetectorRef]),
|
2017-02-01 10:27:38 -05:00
|
|
|
]));
|
|
|
|
|
2017-02-21 16:56:56 -05:00
|
|
|
const compView = asElementData(view, 0).componentView;
|
2017-02-01 10:27:38 -05:00
|
|
|
expect(instance.dep._view).toBe(compView);
|
|
|
|
});
|
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
it('should inject RendererV1', () => {
|
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-02-21 16:56:56 -05:00
|
|
|
elementDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
0, NodeFlags.None, null, null, 1, 'span', null, null, null, null,
|
|
|
|
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)])),
|
|
|
|
directiveDef(1, NodeFlags.Component, null, 0, SomeService, [Renderer])
|
2017-02-16 16:55:55 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
expect(instance.dep.createElement).toBeTruthy();
|
|
|
|
});
|
|
|
|
|
2017-03-07 19:36:12 -05:00
|
|
|
it('should inject Renderer2', () => {
|
2017-02-16 16:55:55 -05:00
|
|
|
createAndGetRootNodes(compViewDef([
|
2017-02-21 16:56:56 -05:00
|
|
|
elementDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
0, NodeFlags.None, null, null, 1, 'span', null, null, null, null,
|
|
|
|
() => compViewDef([anchorDef(NodeFlags.None, null, null, 0)])),
|
|
|
|
directiveDef(1, NodeFlags.Component, null, 0, SomeService, [Renderer2])
|
2017-02-03 18:20:50 -05:00
|
|
|
]));
|
2017-01-20 16:10:57 -05:00
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
expect(instance.dep.createElement).toBeTruthy();
|
|
|
|
});
|
2017-02-16 16:55:55 -05:00
|
|
|
|
2017-01-20 16:10:57 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('data binding', () => {
|
2017-01-26 20:07:37 -05:00
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
2017-02-27 12:14:18 -05:00
|
|
|
it(`should update via strategy ${inlineDynamic}`, () => {
|
2017-03-29 12:34:45 -04:00
|
|
|
let instance: SomeService = undefined !;
|
2017-01-20 16:10:57 -05:00
|
|
|
|
|
|
|
class SomeService {
|
|
|
|
a: any;
|
|
|
|
b: any;
|
|
|
|
constructor() { instance = this; }
|
|
|
|
}
|
|
|
|
|
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
|
|
|
[
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
2017-03-29 12:34:45 -04:00
|
|
|
directiveDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
1, NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
2017-01-20 16:10:57 -05:00
|
|
|
],
|
2017-02-03 18:20:50 -05:00
|
|
|
(check, view) => {
|
|
|
|
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, ['v1', 'v2']);
|
2017-01-26 20:07:37 -05:00
|
|
|
}));
|
2017-01-20 16:10:57 -05:00
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.checkAndUpdateView(view);
|
2017-01-20 16:10:57 -05:00
|
|
|
|
|
|
|
expect(instance.a).toBe('v1');
|
|
|
|
expect(instance.b).toBe('v2');
|
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
const el = rootNodes[0];
|
|
|
|
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
|
2017-01-26 20:07:37 -05:00
|
|
|
});
|
2017-01-31 14:08:29 -05:00
|
|
|
|
2017-01-20 16:10:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2017-01-19 13:25:03 -05:00
|
|
|
describe('outputs', () => {
|
|
|
|
it('should listen to provider events', () => {
|
|
|
|
let emitter = new EventEmitter<any>();
|
|
|
|
let unsubscribeSpy: any;
|
|
|
|
|
|
|
|
class SomeService {
|
|
|
|
emitter = {
|
|
|
|
subscribe: (callback: any) => {
|
|
|
|
const subscription = emitter.subscribe(callback);
|
|
|
|
unsubscribeSpy = spyOn(subscription, 'unsubscribe').and.callThrough();
|
|
|
|
return subscription;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const handleEvent = jasmine.createSpy('handleEvent');
|
|
|
|
|
2017-02-20 15:15:03 -05:00
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span', null, null, null, handleEvent),
|
2017-02-20 15:15:03 -05:00
|
|
|
directiveDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
1, NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
|
2017-02-20 15:15:03 -05:00
|
|
|
]));
|
2017-01-19 13:25:03 -05:00
|
|
|
|
|
|
|
emitter.emit('someEventInstance');
|
2017-02-20 15:15:03 -05:00
|
|
|
expect(handleEvent).toHaveBeenCalledWith(view, 'someEventName', 'someEventInstance');
|
2017-01-19 13:25:03 -05:00
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.destroyView(view);
|
2017-01-19 13:25:03 -05:00
|
|
|
expect(unsubscribeSpy).toHaveBeenCalled();
|
|
|
|
});
|
2017-01-26 20:07:37 -05:00
|
|
|
|
|
|
|
it('should report debug info on event errors', () => {
|
2017-04-28 14:50:45 -04:00
|
|
|
const handleErrorSpy = spyOn(TestBed.get(ErrorHandler), 'handleError');
|
2017-01-26 20:07:37 -05:00
|
|
|
let emitter = new EventEmitter<any>();
|
|
|
|
|
|
|
|
class SomeService {
|
|
|
|
emitter = emitter;
|
|
|
|
}
|
|
|
|
|
2017-02-20 15:15:03 -05:00
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
|
|
|
elementDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
0, NodeFlags.None, null, null, 1, 'span', null, null, null,
|
2017-02-20 15:15:03 -05:00
|
|
|
() => { throw new Error('Test'); }),
|
|
|
|
directiveDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
1, NodeFlags.None, null, 0, SomeService, [], null, {emitter: 'someEventName'})
|
2017-02-20 15:15:03 -05:00
|
|
|
]));
|
2017-01-26 20:07:37 -05:00
|
|
|
|
2017-04-28 14:50:45 -04:00
|
|
|
emitter.emit('someEventInstance');
|
|
|
|
const err = handleErrorSpy.calls.mostRecent().args[0];
|
2017-01-26 20:07:37 -05:00
|
|
|
expect(err).toBeTruthy();
|
2017-01-27 16:19:00 -05:00
|
|
|
const debugCtx = getDebugContext(err);
|
2017-01-26 20:07:37 -05:00
|
|
|
expect(debugCtx.view).toBe(view);
|
|
|
|
// events are emitted with the index of the element, not the index of the provider.
|
|
|
|
expect(debugCtx.nodeIndex).toBe(0);
|
|
|
|
});
|
2017-01-19 13:25:03 -05:00
|
|
|
});
|
|
|
|
|
2017-01-20 16:10:57 -05:00
|
|
|
describe('lifecycle hooks', () => {
|
|
|
|
it('should call the lifecycle hooks in the right order', () => {
|
|
|
|
let instanceCount = 0;
|
|
|
|
let log: string[] = [];
|
|
|
|
|
|
|
|
class SomeService implements OnInit, DoCheck, OnChanges, AfterContentInit,
|
|
|
|
AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
|
|
|
|
id: number;
|
|
|
|
a: any;
|
|
|
|
ngOnInit() { log.push(`${this.id}_ngOnInit`); }
|
|
|
|
ngDoCheck() { log.push(`${this.id}_ngDoCheck`); }
|
|
|
|
ngOnChanges() { log.push(`${this.id}_ngOnChanges`); }
|
|
|
|
ngAfterContentInit() { log.push(`${this.id}_ngAfterContentInit`); }
|
|
|
|
ngAfterContentChecked() { log.push(`${this.id}_ngAfterContentChecked`); }
|
|
|
|
ngAfterViewInit() { log.push(`${this.id}_ngAfterViewInit`); }
|
|
|
|
ngAfterViewChecked() { log.push(`${this.id}_ngAfterViewChecked`); }
|
|
|
|
ngOnDestroy() { log.push(`${this.id}_ngOnDestroy`); }
|
|
|
|
constructor() { this.id = instanceCount++; }
|
|
|
|
}
|
|
|
|
|
|
|
|
const allFlags = NodeFlags.OnInit | NodeFlags.DoCheck | NodeFlags.OnChanges |
|
|
|
|
NodeFlags.AfterContentInit | NodeFlags.AfterContentChecked | NodeFlags.AfterViewInit |
|
|
|
|
NodeFlags.AfterViewChecked | NodeFlags.OnDestroy;
|
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
|
|
|
[
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 3, 'span'),
|
|
|
|
directiveDef(1, allFlags, null, 0, SomeService, [], {a: [0, 'a']}),
|
|
|
|
elementDef(2, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(3, allFlags, null, 0, SomeService, [], {a: [0, 'a']})
|
2017-01-20 16:10:57 -05:00
|
|
|
],
|
2017-02-03 18:20:50 -05:00
|
|
|
(check, view) => {
|
|
|
|
check(view, 1, ArgumentType.Inline, 'someValue');
|
|
|
|
check(view, 3, ArgumentType.Inline, 'someValue');
|
2017-01-20 16:10:57 -05:00
|
|
|
}));
|
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.checkAndUpdateView(view);
|
2017-01-20 16:10:57 -05:00
|
|
|
|
|
|
|
// Note: After... hooks are called bottom up.
|
|
|
|
expect(log).toEqual([
|
|
|
|
'0_ngOnChanges',
|
|
|
|
'0_ngOnInit',
|
|
|
|
'0_ngDoCheck',
|
|
|
|
'1_ngOnChanges',
|
|
|
|
'1_ngOnInit',
|
|
|
|
'1_ngDoCheck',
|
|
|
|
'1_ngAfterContentInit',
|
|
|
|
'1_ngAfterContentChecked',
|
|
|
|
'0_ngAfterContentInit',
|
|
|
|
'0_ngAfterContentChecked',
|
|
|
|
'1_ngAfterViewInit',
|
|
|
|
'1_ngAfterViewChecked',
|
|
|
|
'0_ngAfterViewInit',
|
|
|
|
'0_ngAfterViewChecked',
|
|
|
|
]);
|
|
|
|
|
|
|
|
log = [];
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.checkAndUpdateView(view);
|
2017-01-20 16:10:57 -05:00
|
|
|
|
|
|
|
// Note: After... hooks are called bottom up.
|
|
|
|
expect(log).toEqual([
|
|
|
|
'0_ngDoCheck', '1_ngDoCheck', '1_ngAfterContentChecked', '0_ngAfterContentChecked',
|
|
|
|
'1_ngAfterViewChecked', '0_ngAfterViewChecked'
|
|
|
|
]);
|
|
|
|
|
|
|
|
log = [];
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.destroyView(view);
|
2017-01-20 16:10:57 -05:00
|
|
|
|
|
|
|
// Note: ngOnDestroy ist called bottom up.
|
|
|
|
expect(log).toEqual(['1_ngOnDestroy', '0_ngOnDestroy']);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should call ngOnChanges with the changed values and the non minified names', () => {
|
|
|
|
let changesLog: SimpleChange[] = [];
|
|
|
|
let currValue = 'v1';
|
|
|
|
|
|
|
|
class SomeService implements OnChanges {
|
|
|
|
a: any;
|
|
|
|
ngOnChanges(changes: {[name: string]: SimpleChange}) {
|
|
|
|
changesLog.push(changes['nonMinifiedA']);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
|
|
|
[
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
2017-02-01 10:27:38 -05:00
|
|
|
directiveDef(
|
2017-09-22 17:29:16 -04:00
|
|
|
1, NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']})
|
2017-01-20 16:10:57 -05:00
|
|
|
],
|
2017-02-03 18:20:50 -05:00
|
|
|
(check, view) => { check(view, 1, ArgumentType.Inline, currValue); }));
|
2017-01-20 16:10:57 -05:00
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.checkAndUpdateView(view);
|
2017-01-20 16:10:57 -05:00
|
|
|
expect(changesLog).toEqual([new SimpleChange(undefined, 'v1', true)]);
|
|
|
|
|
|
|
|
currValue = 'v2';
|
|
|
|
changesLog = [];
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.checkAndUpdateView(view);
|
2017-01-20 16:10:57 -05:00
|
|
|
expect(changesLog).toEqual([new SimpleChange('v1', 'v2', false)]);
|
|
|
|
});
|
2017-01-26 20:07:37 -05:00
|
|
|
|
|
|
|
it('should add a DebugContext to errors in provider afterXXX lifecycles', () => {
|
|
|
|
class SomeService implements AfterContentChecked {
|
|
|
|
ngAfterContentChecked() { throw new Error('Test'); }
|
|
|
|
}
|
|
|
|
|
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.AfterContentChecked, null, 0, SomeService, [], {a: [0, 'a']}),
|
2017-01-26 20:07:37 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
let err: any;
|
|
|
|
try {
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.checkAndUpdateView(view);
|
2017-01-26 20:07:37 -05:00
|
|
|
} catch (e) {
|
|
|
|
err = e;
|
|
|
|
}
|
|
|
|
expect(err).toBeTruthy();
|
|
|
|
expect(err.message).toBe('Test');
|
2017-01-27 16:19:00 -05:00
|
|
|
const debugCtx = getDebugContext(err);
|
2017-01-26 20:07:37 -05:00
|
|
|
expect(debugCtx.view).toBe(view);
|
|
|
|
expect(debugCtx.nodeIndex).toBe(1);
|
|
|
|
});
|
|
|
|
|
2017-02-03 18:20:50 -05:00
|
|
|
it('should add a DebugContext to errors inServices.destroyView', () => {
|
2017-01-26 20:07:37 -05:00
|
|
|
class SomeService implements OnDestroy {
|
|
|
|
ngOnDestroy() { throw new Error('Test'); }
|
|
|
|
}
|
|
|
|
|
|
|
|
const {view, rootNodes} = createAndGetRootNodes(compViewDef([
|
2017-09-22 17:29:16 -04:00
|
|
|
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
|
|
|
|
directiveDef(1, NodeFlags.OnDestroy, null, 0, SomeService, [], {a: [0, 'a']}),
|
2017-01-26 20:07:37 -05:00
|
|
|
]));
|
|
|
|
|
|
|
|
let err: any;
|
|
|
|
try {
|
2017-02-03 18:20:50 -05:00
|
|
|
Services.destroyView(view);
|
2017-01-26 20:07:37 -05:00
|
|
|
} catch (e) {
|
|
|
|
err = e;
|
|
|
|
}
|
|
|
|
expect(err).toBeTruthy();
|
|
|
|
expect(err.message).toBe('Test');
|
2017-01-27 16:19:00 -05:00
|
|
|
const debugCtx = getDebugContext(err);
|
2017-01-26 20:07:37 -05:00
|
|
|
expect(debugCtx.view).toBe(view);
|
|
|
|
expect(debugCtx.nodeIndex).toBe(1);
|
|
|
|
});
|
2017-01-20 16:10:57 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|