From 4a5467bac6a253888abf9e748e0c876fcc44f9be Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Wed, 22 Jan 2020 15:46:34 -0800 Subject: [PATCH] fix(ivy): ensure multi providers in ModuleWithProviders are not duplicated (#34914) The current logic pulls multiproviders up to the parent module's provider list. The result is that the multi provider being defined both in the imported ModuleWithProviders and the parent and getting an extra item in the multi provided array of values. This PR fixes that problem by not pulling providers in ModuleWithProviders up to the parent module. PR Close #34914 --- packages/core/test/test_bed_spec.ts | 23 +++++++++++++++---- .../core/testing/src/r3_test_bed_compiler.ts | 9 +------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/packages/core/test/test_bed_spec.ts b/packages/core/test/test_bed_spec.ts index 2c28381644..1f064d1833 100644 --- a/packages/core/test/test_bed_spec.ts +++ b/packages/core/test/test_bed_spec.ts @@ -345,13 +345,19 @@ describe('TestBed', () => { describe('multi providers', () => { const multiToken = new InjectionToken('multiToken'); const singleToken = new InjectionToken('singleToken'); + const multiTokenToOverrideAtModuleLevel = + new InjectionToken('moduleLevelMultiOverride'); @NgModule({providers: [{provide: multiToken, useValue: 'valueFromModule', multi: true}]}) class MyModule { } @NgModule({ providers: [ - {provide: singleToken, useValue: 't1'}, + {provide: singleToken, useValue: 't1'}, { + provide: multiTokenToOverrideAtModuleLevel, + useValue: 'multiTokenToOverrideAtModuleLevelOriginal', + multi: true + }, {provide: multiToken, useValue: 'valueFromModule2', multi: true}, {provide: multiToken, useValue: 'secondValueFromModule2', multi: true} ] @@ -361,14 +367,23 @@ describe('TestBed', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [MyModule, MyModule2], + imports: [ + MyModule, { + ngModule: MyModule2, + providers: + [{provide: multiTokenToOverrideAtModuleLevel, useValue: 'override', multi: true}] + } + ], }); }); it('is preserved when other provider is overridden', () => { TestBed.overrideProvider(singleToken, {useValue: ''}); - const value = TestBed.inject(multiToken); - expect(value.length).toEqual(3); + expect(TestBed.inject(multiToken).length).toEqual(3); + expect(TestBed.inject(multiTokenToOverrideAtModuleLevel).length).toEqual(2); + expect(TestBed.inject(multiTokenToOverrideAtModuleLevel)).toEqual([ + 'multiTokenToOverrideAtModuleLevelOriginal', 'override' + ]); }); it('overridden with an array', () => { diff --git a/packages/core/testing/src/r3_test_bed_compiler.ts b/packages/core/testing/src/r3_test_bed_compiler.ts index f64995cf71..c4028b9ec1 100644 --- a/packages/core/testing/src/r3_test_bed_compiler.ts +++ b/packages/core/testing/src/r3_test_bed_compiler.ts @@ -394,15 +394,8 @@ export class R3TestBedCompiler { const injectorDef: any = (moduleType as any)[NG_INJ_DEF]; if (this.providerOverridesByToken.size > 0) { - // Extract the list of providers from ModuleWithProviders, so we can define the final list of - // providers that might have overrides. - // Note: second `flatten` operation is needed to convert an array of providers - // (e.g. `[[], []]`) into one flat list, also eliminating empty arrays. - const providersFromModules = flatten(flatten( - injectorDef.imports, (imported: NgModuleType| ModuleWithProviders) => - isModuleWithProviders(imported) ? imported.providers : [])); const providers = [ - ...providersFromModules, ...injectorDef.providers, + ...injectorDef.providers, ...(this.providerOverridesByModule.get(moduleType as InjectorType) || []) ]; if (this.hasProviderOverrides(providers)) {