fix(ivy): detach ViewRef from ApplicationRef on destroy (#27276)
Currently we store the `_appRef` when a `ViewRef` is attached, however we don't use it for anything. These changes use it to detach the view from the `ApplicationRef` when it is destroyed. These changes also fix that the `ComponentRef` doesn't remove its `ViewRef` on destroy. PR Close #27276
This commit is contained in:
parent
0487fbe236
commit
4622d0b23a
|
@ -59,7 +59,9 @@ export class ViewRef<T> implements viewEngine_EmbeddedViewRef<T>, viewEngine_Int
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
if (this._viewContainerRef && viewAttached(this._view)) {
|
if (this._appRef) {
|
||||||
|
this._appRef.detachView(this);
|
||||||
|
} else if (this._viewContainerRef && viewAttached(this._view)) {
|
||||||
this._viewContainerRef.detach(this._viewContainerRef.indexOf(this));
|
this._viewContainerRef.detach(this._viewContainerRef.indexOf(this));
|
||||||
this._viewContainerRef = null;
|
this._viewContainerRef = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class SomeComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
fixmeIvy('unknown') && describe('bootstrap', () => {
|
describe('bootstrap', () => {
|
||||||
let mockConsole: MockConsole;
|
let mockConsole: MockConsole;
|
||||||
|
|
||||||
beforeEach(() => { mockConsole = new MockConsole(); });
|
beforeEach(() => { mockConsole = new MockConsole(); });
|
||||||
|
@ -74,66 +74,68 @@ class SomeComponent {
|
||||||
return MyModule;
|
return MyModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should bootstrap a component from a child module',
|
fixmeIvy('unknown') &&
|
||||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
it('should bootstrap a component from a child module',
|
||||||
@Component({
|
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||||
selector: 'bootstrap-app',
|
@Component({
|
||||||
template: '',
|
selector: 'bootstrap-app',
|
||||||
})
|
template: '',
|
||||||
class SomeComponent {
|
})
|
||||||
}
|
class SomeComponent {
|
||||||
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
providers: [{provide: 'hello', useValue: 'component'}],
|
providers: [{provide: 'hello', useValue: 'component'}],
|
||||||
declarations: [SomeComponent],
|
declarations: [SomeComponent],
|
||||||
entryComponents: [SomeComponent],
|
entryComponents: [SomeComponent],
|
||||||
})
|
})
|
||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
createRootEl();
|
createRootEl();
|
||||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||||
const module = modFactory.create(TestBed);
|
const module = modFactory.create(TestBed);
|
||||||
const cmpFactory =
|
const cmpFactory =
|
||||||
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
||||||
const component = app.bootstrap(cmpFactory);
|
const component = app.bootstrap(cmpFactory);
|
||||||
|
|
||||||
// The component should see the child module providers
|
// The component should see the child module providers
|
||||||
expect(component.injector.get('hello')).toEqual('component');
|
expect(component.injector.get('hello')).toEqual('component');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
it('should bootstrap a component with a custom selector',
|
fixmeIvy('unknown') &&
|
||||||
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
it('should bootstrap a component with a custom selector',
|
||||||
@Component({
|
async(inject([ApplicationRef, Compiler], (app: ApplicationRef, compiler: Compiler) => {
|
||||||
selector: 'bootstrap-app',
|
@Component({
|
||||||
template: '',
|
selector: 'bootstrap-app',
|
||||||
})
|
template: '',
|
||||||
class SomeComponent {
|
})
|
||||||
}
|
class SomeComponent {
|
||||||
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
providers: [{provide: 'hello', useValue: 'component'}],
|
providers: [{provide: 'hello', useValue: 'component'}],
|
||||||
declarations: [SomeComponent],
|
declarations: [SomeComponent],
|
||||||
entryComponents: [SomeComponent],
|
entryComponents: [SomeComponent],
|
||||||
})
|
})
|
||||||
class SomeModule {
|
class SomeModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
createRootEl('custom-selector');
|
createRootEl('custom-selector');
|
||||||
const modFactory = compiler.compileModuleSync(SomeModule);
|
const modFactory = compiler.compileModuleSync(SomeModule);
|
||||||
const module = modFactory.create(TestBed);
|
const module = modFactory.create(TestBed);
|
||||||
const cmpFactory =
|
const cmpFactory =
|
||||||
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
module.componentFactoryResolver.resolveComponentFactory(SomeComponent) !;
|
||||||
const component = app.bootstrap(cmpFactory, 'custom-selector');
|
const component = app.bootstrap(cmpFactory, 'custom-selector');
|
||||||
|
|
||||||
// The component should see the child module providers
|
// The component should see the child module providers
|
||||||
expect(component.injector.get('hello')).toEqual('component');
|
expect(component.injector.get('hello')).toEqual('component');
|
||||||
})));
|
})));
|
||||||
|
|
||||||
describe('ApplicationRef', () => {
|
describe('ApplicationRef', () => {
|
||||||
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
|
beforeEach(() => { TestBed.configureTestingModule({imports: [createModule()]}); });
|
||||||
|
|
||||||
it('should throw when reentering tick', () => {
|
fixmeIvy('unknown') && it('should throw when reentering tick', () => {
|
||||||
@Component({template: '{{reenter()}}'})
|
@Component({template: '{{reenter()}}'})
|
||||||
class ReenteringComponent {
|
class ReenteringComponent {
|
||||||
reenterCount = 1;
|
reenterCount = 1;
|
||||||
|
@ -174,28 +176,31 @@ class SomeComponent {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be called when a component is bootstrapped',
|
fixmeIvy('unknown') && it('should be called when a component is bootstrapped',
|
||||||
inject([ApplicationRef], (ref: ApplicationRef) => {
|
inject([ApplicationRef], (ref: ApplicationRef) => {
|
||||||
createRootEl();
|
createRootEl();
|
||||||
const compRef = ref.bootstrap(SomeComponent);
|
const compRef = ref.bootstrap(SomeComponent);
|
||||||
expect(capturedCompRefs).toEqual([compRef]);
|
expect(capturedCompRefs).toEqual([compRef]);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('bootstrap', () => {
|
describe('bootstrap', () => {
|
||||||
it('should throw if an APP_INITIIALIZER is not yet resolved',
|
fixmeIvy('unknown') &&
|
||||||
withModule(
|
it('should throw if an APP_INITIIALIZER is not yet resolved',
|
||||||
{
|
withModule(
|
||||||
providers: [
|
{
|
||||||
{provide: APP_INITIALIZER, useValue: () => new Promise(() => {}), multi: true}
|
providers: [{
|
||||||
]
|
provide: APP_INITIALIZER,
|
||||||
},
|
useValue: () => new Promise(() => {}),
|
||||||
inject([ApplicationRef], (ref: ApplicationRef) => {
|
multi: true
|
||||||
createRootEl();
|
}]
|
||||||
expect(() => ref.bootstrap(SomeComponent))
|
},
|
||||||
.toThrowError(
|
inject([ApplicationRef], (ref: ApplicationRef) => {
|
||||||
'Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.');
|
createRootEl();
|
||||||
})));
|
expect(() => ref.bootstrap(SomeComponent))
|
||||||
|
.toThrowError(
|
||||||
|
'Cannot bootstrap as there are still asynchronous initializers running. Bootstrap components in the `ngDoBootstrap` method of the root module.');
|
||||||
|
})));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -206,99 +211,112 @@ class SomeComponent {
|
||||||
defaultPlatform = _platform;
|
defaultPlatform = _platform;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should wait for asynchronous app initializers', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
let resolve: (result: any) => void;
|
it('should wait for asynchronous app initializers', async(() => {
|
||||||
const promise: Promise<any> = new Promise((res) => { resolve = res; });
|
let resolve: (result: any) => void;
|
||||||
let initializerDone = false;
|
const promise: Promise<any> = new Promise((res) => { resolve = res; });
|
||||||
setTimeout(() => {
|
let initializerDone = false;
|
||||||
resolve(true);
|
setTimeout(() => {
|
||||||
initializerDone = true;
|
resolve(true);
|
||||||
}, 1);
|
initializerDone = true;
|
||||||
|
}, 1);
|
||||||
|
|
||||||
defaultPlatform
|
defaultPlatform
|
||||||
.bootstrapModule(
|
.bootstrapModule(createModule(
|
||||||
createModule([{provide: APP_INITIALIZER, useValue: () => promise, multi: true}]))
|
[{provide: APP_INITIALIZER, useValue: () => promise, multi: true}]))
|
||||||
.then(_ => { expect(initializerDone).toBe(true); });
|
.then(_ => { expect(initializerDone).toBe(true); });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should rethrow sync errors even if the exceptionHandler is not rethrowing', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
defaultPlatform
|
it('should rethrow sync errors even if the exceptionHandler is not rethrowing',
|
||||||
.bootstrapModule(createModule(
|
async(() => {
|
||||||
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]))
|
defaultPlatform
|
||||||
.then(() => expect(false).toBe(true), (e) => {
|
.bootstrapModule(createModule([
|
||||||
expect(e).toBe('Test');
|
{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}
|
||||||
// Error rethrown will be seen by the exception handler since it's after
|
]))
|
||||||
// construction.
|
.then(() => expect(false).toBe(true), (e) => {
|
||||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
expect(e).toBe('Test');
|
||||||
});
|
// Error rethrown will be seen by the exception handler since it's after
|
||||||
}));
|
// construction.
|
||||||
|
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
fixmeIvy('unknown') &&
|
||||||
async(() => {
|
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
||||||
defaultPlatform
|
async(() => {
|
||||||
.bootstrapModule(createModule([
|
defaultPlatform
|
||||||
{provide: APP_INITIALIZER, useValue: () => Promise.reject('Test'), multi: true}
|
.bootstrapModule(createModule([{
|
||||||
]))
|
provide: APP_INITIALIZER,
|
||||||
.then(() => expect(false).toBe(true), (e) => {
|
useValue: () => Promise.reject('Test'),
|
||||||
expect(e).toBe('Test');
|
multi: true
|
||||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
}]))
|
||||||
});
|
.then(() => expect(false).toBe(true), (e) => {
|
||||||
}));
|
expect(e).toBe('Test');
|
||||||
|
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should throw useful error when ApplicationRef is not configured', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
@NgModule()
|
it('should throw useful error when ApplicationRef is not configured', async(() => {
|
||||||
class EmptyModule {
|
@NgModule()
|
||||||
}
|
class EmptyModule {
|
||||||
|
}
|
||||||
|
|
||||||
return defaultPlatform.bootstrapModule(EmptyModule)
|
return defaultPlatform.bootstrapModule(EmptyModule)
|
||||||
.then(() => fail('expecting error'), (error) => {
|
.then(() => fail('expecting error'), (error) => {
|
||||||
expect(error.message)
|
expect(error.message)
|
||||||
.toEqual('No ErrorHandler. Is platform module (BrowserModule) included?');
|
.toEqual('No ErrorHandler. Is platform module (BrowserModule) included?');
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should call the `ngDoBootstrap` method with `ApplicationRef` on the main module',
|
fixmeIvy('unknown') &&
|
||||||
async(() => {
|
it('should call the `ngDoBootstrap` method with `ApplicationRef` on the main module',
|
||||||
const ngDoBootstrap = jasmine.createSpy('ngDoBootstrap');
|
async(() => {
|
||||||
defaultPlatform.bootstrapModule(createModule({ngDoBootstrap: ngDoBootstrap}))
|
const ngDoBootstrap = jasmine.createSpy('ngDoBootstrap');
|
||||||
.then((moduleRef) => {
|
defaultPlatform.bootstrapModule(createModule({ngDoBootstrap: ngDoBootstrap}))
|
||||||
const appRef = moduleRef.injector.get(ApplicationRef);
|
.then((moduleRef) => {
|
||||||
expect(ngDoBootstrap).toHaveBeenCalledWith(appRef);
|
const appRef = moduleRef.injector.get(ApplicationRef);
|
||||||
});
|
expect(ngDoBootstrap).toHaveBeenCalledWith(appRef);
|
||||||
}));
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should auto bootstrap components listed in @NgModule.bootstrap', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
defaultPlatform.bootstrapModule(createModule({bootstrap: [SomeComponent]}))
|
it('should auto bootstrap components listed in @NgModule.bootstrap', async(() => {
|
||||||
.then((moduleRef) => {
|
defaultPlatform.bootstrapModule(createModule({bootstrap: [SomeComponent]}))
|
||||||
const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
.then((moduleRef) => {
|
||||||
expect(appRef.componentTypes).toEqual([SomeComponent]);
|
const appRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
|
||||||
});
|
expect(appRef.componentTypes).toEqual([SomeComponent]);
|
||||||
}));
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should error if neither `ngDoBootstrap` nor @NgModule.bootstrap was specified',
|
fixmeIvy('unknown') &&
|
||||||
async(() => {
|
it('should error if neither `ngDoBootstrap` nor @NgModule.bootstrap was specified',
|
||||||
defaultPlatform.bootstrapModule(createModule({ngDoBootstrap: false}))
|
async(() => {
|
||||||
.then(() => expect(false).toBe(true), (e) => {
|
defaultPlatform.bootstrapModule(createModule({ngDoBootstrap: false}))
|
||||||
const expectedErrMsg =
|
.then(() => expect(false).toBe(true), (e) => {
|
||||||
`The module MyModule was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. Please define one of these.`;
|
const expectedErrMsg =
|
||||||
expect(e.message).toEqual(expectedErrMsg);
|
`The module MyModule was bootstrapped, but it does not declare "@NgModule.bootstrap" components nor a "ngDoBootstrap" method. Please define one of these.`;
|
||||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Error: ' + expectedErrMsg);
|
expect(e.message).toEqual(expectedErrMsg);
|
||||||
});
|
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Error: ' + expectedErrMsg);
|
||||||
}));
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should add bootstrapped module into platform modules list', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
defaultPlatform.bootstrapModule(createModule({bootstrap: [SomeComponent]}))
|
it('should add bootstrapped module into platform modules list', async(() => {
|
||||||
.then(module => expect((<any>defaultPlatform)._modules).toContain(module));
|
defaultPlatform.bootstrapModule(createModule({bootstrap: [SomeComponent]}))
|
||||||
}));
|
.then(module => expect((<any>defaultPlatform)._modules).toContain(module));
|
||||||
|
}));
|
||||||
|
|
||||||
it('should bootstrap with NoopNgZone', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
defaultPlatform
|
it('should bootstrap with NoopNgZone', async(() => {
|
||||||
.bootstrapModule(createModule({bootstrap: [SomeComponent]}), {ngZone: 'noop'})
|
defaultPlatform
|
||||||
.then((module) => {
|
.bootstrapModule(createModule({bootstrap: [SomeComponent]}), {ngZone: 'noop'})
|
||||||
const ngZone = module.injector.get(NgZone);
|
.then((module) => {
|
||||||
expect(ngZone instanceof NoopNgZone).toBe(true);
|
const ngZone = module.injector.get(NgZone);
|
||||||
});
|
expect(ngZone instanceof NoopNgZone).toBe(true);
|
||||||
}));
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('bootstrapModuleFactory', () => {
|
describe('bootstrapModuleFactory', () => {
|
||||||
|
@ -307,47 +325,58 @@ class SomeComponent {
|
||||||
createRootEl();
|
createRootEl();
|
||||||
defaultPlatform = _platform;
|
defaultPlatform = _platform;
|
||||||
}));
|
}));
|
||||||
it('should wait for asynchronous app initializers', async(() => {
|
fixmeIvy('unknown') &&
|
||||||
let resolve: (result: any) => void;
|
it('should wait for asynchronous app initializers', async(() => {
|
||||||
const promise: Promise<any> = new Promise((res) => { resolve = res; });
|
let resolve: (result: any) => void;
|
||||||
let initializerDone = false;
|
const promise: Promise<any> = new Promise((res) => { resolve = res; });
|
||||||
setTimeout(() => {
|
let initializerDone = false;
|
||||||
resolve(true);
|
setTimeout(() => {
|
||||||
initializerDone = true;
|
resolve(true);
|
||||||
}, 1);
|
initializerDone = true;
|
||||||
|
}, 1);
|
||||||
|
|
||||||
const compilerFactory: CompilerFactory =
|
const compilerFactory: CompilerFactory =
|
||||||
defaultPlatform.injector.get(CompilerFactory, null);
|
defaultPlatform.injector.get(CompilerFactory, null);
|
||||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(
|
const moduleFactory =
|
||||||
createModule([{provide: APP_INITIALIZER, useValue: () => promise, multi: true}]));
|
compilerFactory.createCompiler().compileModuleSync(createModule(
|
||||||
defaultPlatform.bootstrapModuleFactory(moduleFactory).then(_ => {
|
[{provide: APP_INITIALIZER, useValue: () => promise, multi: true}]));
|
||||||
expect(initializerDone).toBe(true);
|
defaultPlatform.bootstrapModuleFactory(moduleFactory).then(_ => {
|
||||||
});
|
expect(initializerDone).toBe(true);
|
||||||
}));
|
|
||||||
|
|
||||||
it('should rethrow sync errors even if the exceptionHandler is not rethrowing', async(() => {
|
|
||||||
const compilerFactory: CompilerFactory =
|
|
||||||
defaultPlatform.injector.get(CompilerFactory, null);
|
|
||||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
|
|
||||||
[{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}]));
|
|
||||||
expect(() => defaultPlatform.bootstrapModuleFactory(moduleFactory)).toThrow('Test');
|
|
||||||
// Error rethrown will be seen by the exception handler since it's after
|
|
||||||
// construction.
|
|
||||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
|
||||||
async(() => {
|
|
||||||
const compilerFactory: CompilerFactory =
|
|
||||||
defaultPlatform.injector.get(CompilerFactory, null);
|
|
||||||
const moduleFactory = compilerFactory.createCompiler().compileModuleSync(createModule(
|
|
||||||
[{provide: APP_INITIALIZER, useValue: () => Promise.reject('Test'), multi: true}]));
|
|
||||||
defaultPlatform.bootstrapModuleFactory(moduleFactory)
|
|
||||||
.then(() => expect(false).toBe(true), (e) => {
|
|
||||||
expect(e).toBe('Test');
|
|
||||||
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
fixmeIvy('unknown') &&
|
||||||
|
it('should rethrow sync errors even if the exceptionHandler is not rethrowing',
|
||||||
|
async(() => {
|
||||||
|
const compilerFactory: CompilerFactory =
|
||||||
|
defaultPlatform.injector.get(CompilerFactory, null);
|
||||||
|
const moduleFactory =
|
||||||
|
compilerFactory.createCompiler().compileModuleSync(createModule([
|
||||||
|
{provide: APP_INITIALIZER, useValue: () => { throw 'Test'; }, multi: true}
|
||||||
|
]));
|
||||||
|
expect(() => defaultPlatform.bootstrapModuleFactory(moduleFactory)).toThrow('Test');
|
||||||
|
// Error rethrown will be seen by the exception handler since it's after
|
||||||
|
// construction.
|
||||||
|
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
||||||
|
}));
|
||||||
|
|
||||||
|
fixmeIvy('unknown') &&
|
||||||
|
it('should rethrow promise errors even if the exceptionHandler is not rethrowing',
|
||||||
|
async(() => {
|
||||||
|
const compilerFactory: CompilerFactory =
|
||||||
|
defaultPlatform.injector.get(CompilerFactory, null);
|
||||||
|
const moduleFactory =
|
||||||
|
compilerFactory.createCompiler().compileModuleSync(createModule([{
|
||||||
|
provide: APP_INITIALIZER,
|
||||||
|
useValue: () => Promise.reject('Test'),
|
||||||
|
multi: true
|
||||||
|
}]));
|
||||||
|
defaultPlatform.bootstrapModuleFactory(moduleFactory)
|
||||||
|
.then(() => expect(false).toBe(true), (e) => {
|
||||||
|
expect(e).toBe('Test');
|
||||||
|
expect(mockConsole.res[0].join('#')).toEqual('ERROR#Test');
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('attachView / detachView', () => {
|
describe('attachView / detachView', () => {
|
||||||
|
@ -427,28 +456,29 @@ class SomeComponent {
|
||||||
expect(appRef.viewCount).toBe(0);
|
expect(appRef.viewCount).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow to attach a view to both, a view container and the ApplicationRef',
|
fixmeIvy('unknown') &&
|
||||||
() => {
|
it('should not allow to attach a view to both, a view container and the ApplicationRef',
|
||||||
const comp = TestBed.createComponent(MyComp);
|
() => {
|
||||||
let hostView = comp.componentRef.hostView;
|
const comp = TestBed.createComponent(MyComp);
|
||||||
const containerComp = TestBed.createComponent(ContainerComp);
|
let hostView = comp.componentRef.hostView;
|
||||||
containerComp.detectChanges();
|
const containerComp = TestBed.createComponent(ContainerComp);
|
||||||
const vc = containerComp.componentInstance.vc;
|
containerComp.detectChanges();
|
||||||
const appRef: ApplicationRef = TestBed.get(ApplicationRef);
|
const vc = containerComp.componentInstance.vc;
|
||||||
|
const appRef: ApplicationRef = TestBed.get(ApplicationRef);
|
||||||
|
|
||||||
vc.insert(hostView);
|
vc.insert(hostView);
|
||||||
expect(() => appRef.attachView(hostView))
|
expect(() => appRef.attachView(hostView))
|
||||||
.toThrowError('This view is already attached to a ViewContainer!');
|
.toThrowError('This view is already attached to a ViewContainer!');
|
||||||
hostView = vc.detach(0) !;
|
hostView = vc.detach(0) !;
|
||||||
|
|
||||||
appRef.attachView(hostView);
|
appRef.attachView(hostView);
|
||||||
expect(() => vc.insert(hostView))
|
expect(() => vc.insert(hostView))
|
||||||
.toThrowError('This view is already attached directly to the ApplicationRef!');
|
.toThrowError('This view is already attached directly to the ApplicationRef!');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
fixmeIvy('unknown') && describe('AppRef', () => {
|
describe('AppRef', () => {
|
||||||
@Component({selector: 'sync-comp', template: `<span>{{text}}</span>`})
|
@Component({selector: 'sync-comp', template: `<span>{{text}}</span>`})
|
||||||
class SyncComp {
|
class SyncComp {
|
||||||
text: string = '1';
|
text: string = '1';
|
||||||
|
@ -535,20 +565,22 @@ class SomeComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('isStable should fire on synchronous component loading',
|
fixmeIvy('unknown') && it('isStable should fire on synchronous component loading',
|
||||||
async(() => { expectStableTexts(SyncComp, ['1']); }));
|
async(() => { expectStableTexts(SyncComp, ['1']); }));
|
||||||
|
|
||||||
it('isStable should fire after a microtask on init is completed',
|
fixmeIvy('unknown') && it('isStable should fire after a microtask on init is completed',
|
||||||
async(() => { expectStableTexts(MicroTaskComp, ['11']); }));
|
async(() => { expectStableTexts(MicroTaskComp, ['11']); }));
|
||||||
|
|
||||||
it('isStable should fire after a macrotask on init is completed',
|
fixmeIvy('unknown') && it('isStable should fire after a macrotask on init is completed',
|
||||||
async(() => { expectStableTexts(MacroTaskComp, ['11']); }));
|
async(() => { expectStableTexts(MacroTaskComp, ['11']); }));
|
||||||
|
|
||||||
it('isStable should fire only after chain of micro and macrotasks on init are completed',
|
fixmeIvy('unknown') &&
|
||||||
async(() => { expectStableTexts(MicroMacroTaskComp, ['111']); }));
|
it('isStable should fire only after chain of micro and macrotasks on init are completed',
|
||||||
|
async(() => { expectStableTexts(MicroMacroTaskComp, ['111']); }));
|
||||||
|
|
||||||
it('isStable should fire only after chain of macro and microtasks on init are completed',
|
fixmeIvy('unknown') &&
|
||||||
async(() => { expectStableTexts(MacroMicroTaskComp, ['111']); }));
|
it('isStable should fire only after chain of macro and microtasks on init are completed',
|
||||||
|
async(() => { expectStableTexts(MacroMicroTaskComp, ['111']); }));
|
||||||
|
|
||||||
describe('unstable', () => {
|
describe('unstable', () => {
|
||||||
let unstableCalled = false;
|
let unstableCalled = false;
|
||||||
|
@ -569,19 +601,19 @@ class SomeComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should be fired after app becomes unstable', async(() => {
|
fixmeIvy('unknown') && it('should be fired after app becomes unstable', async(() => {
|
||||||
const fixture = TestBed.createComponent(ClickComp);
|
const fixture = TestBed.createComponent(ClickComp);
|
||||||
const appRef: ApplicationRef = TestBed.get(ApplicationRef);
|
const appRef: ApplicationRef = TestBed.get(ApplicationRef);
|
||||||
const zone: NgZone = TestBed.get(NgZone);
|
const zone: NgZone = TestBed.get(NgZone);
|
||||||
appRef.attachView(fixture.componentRef.hostView);
|
appRef.attachView(fixture.componentRef.hostView);
|
||||||
zone.run(() => appRef.tick());
|
zone.run(() => appRef.tick());
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable().then(() => {
|
||||||
expectUnstable(appRef);
|
expectUnstable(appRef);
|
||||||
const element = fixture.debugElement.children[0];
|
const element = fixture.debugElement.children[0];
|
||||||
dispatchEvent(element.nativeElement, 'click');
|
dispatchEvent(element.nativeElement, 'click');
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue