fix(ivy): provider override via TestBed should remove old providers from the list (#33706)
While overriding providers in Ivy TestBed (via TestBed.overrideProvider call), the old providers were retained in the list, since the override takes precedence. However, presence of providers in the list might have side-effect: if a provider has the `ngOnDestroy` lifecycle hook, this hook will be registered and invoked later (when component is destroyed). This commit updates TestBed logic to clear provider list by removing the ones which have overrides. PR Close #33706
This commit is contained in:

committed by
Kara Erickson

parent
ca1ff3ea11
commit
eece138fa7
@ -16,6 +16,13 @@ import {onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
const NAME = new InjectionToken<string>('name');
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
class SimpleService {
|
||||
static ngOnDestroyCalls: number = 0;
|
||||
id: number = 1;
|
||||
ngOnDestroy() { SimpleService.ngOnDestroyCalls++; }
|
||||
}
|
||||
|
||||
// -- module: HWModule
|
||||
@Component({
|
||||
selector: 'hello-world',
|
||||
@ -32,7 +39,22 @@ export class HelloWorld {
|
||||
export class GreetingCmp {
|
||||
name: string;
|
||||
|
||||
constructor(@Inject(NAME) @Optional() name: string) { this.name = name || 'nobody!'; }
|
||||
constructor(
|
||||
@Inject(NAME) @Optional() name: string,
|
||||
@Inject(SimpleService) @Optional() service: SimpleService) {
|
||||
this.name = name || 'nobody!';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'cmp-with-providers',
|
||||
template: '<hello-world></hello-world>',
|
||||
providers: [
|
||||
SimpleService, //
|
||||
{provide: NAME, useValue: `from Component`}
|
||||
]
|
||||
})
|
||||
class CmpWithProviders {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -88,7 +110,7 @@ export class ComponentWithInlineTemplate {
|
||||
@NgModule({
|
||||
declarations: [
|
||||
HelloWorld, SimpleCmp, WithRefsCmp, InheritedCmp, SimpleApp, ComponentWithPropBindings,
|
||||
HostBindingDir
|
||||
HostBindingDir, CmpWithProviders
|
||||
],
|
||||
imports: [GreetingModule],
|
||||
providers: [
|
||||
@ -252,6 +274,22 @@ describe('TestBed', () => {
|
||||
expect(hello.nativeElement).toHaveText('Hello injected World a second time!');
|
||||
});
|
||||
|
||||
it('should not call ngOnDestroy for a service that was overridden', () => {
|
||||
SimpleService.ngOnDestroyCalls = 0;
|
||||
|
||||
TestBed.overrideProvider(SimpleService, {useValue: {id: 2, ngOnDestroy: () => {}}});
|
||||
const fixture = TestBed.createComponent(CmpWithProviders);
|
||||
fixture.detectChanges();
|
||||
|
||||
const service = TestBed.inject(SimpleService);
|
||||
expect(service.id).toBe(2);
|
||||
|
||||
fixture.destroy();
|
||||
|
||||
// verify that original `ngOnDestroy` was not called
|
||||
expect(SimpleService.ngOnDestroyCalls).toBe(0);
|
||||
});
|
||||
|
||||
describe('multi providers', () => {
|
||||
const multiToken = new InjectionToken<string[]>('multiToken');
|
||||
const singleToken = new InjectionToken<string>('singleToken');
|
||||
|
Reference in New Issue
Block a user