|
6 | 6 | * found in the LICENSE file at https://angular.io/license |
7 | 7 | */ |
8 | 8 |
|
9 | | -import {APP_INITIALIZER, ChangeDetectorRef, Compiler, Component, Directive, ElementRef, ErrorHandler, getNgModuleById, inject, Inject, Injectable, InjectFlags, InjectionToken, InjectOptions, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelementEnd as elementEnd, ɵɵelementStart as elementStart, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core'; |
| 9 | +import {PLATFORM_BROWSER_ID} from '@angular/common/src/platform_id'; |
| 10 | +import {APP_INITIALIZER, ChangeDetectorRef, Compiler, Component, Directive, ElementRef, ErrorHandler, getNgModuleById, inject, Inject, Injectable, InjectFlags, InjectionToken, InjectOptions, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Pipe, PLATFORM_ID, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelementEnd as elementEnd, ɵɵelementStart as elementStart, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core'; |
10 | 11 | import {DeferBlockBehavior} from '@angular/core/testing'; |
11 | 12 | import {TestBed, TestBedImpl} from '@angular/core/testing/src/test_bed'; |
12 | 13 | import {By} from '@angular/platform-browser'; |
@@ -1655,15 +1656,90 @@ describe('TestBed', () => { |
1655 | 1656 | .toBe('Override of a root template! Override of a nested template! CmpA!'); |
1656 | 1657 | }); |
1657 | 1658 |
|
1658 | | - it('should allow import overrides on components with async metadata', async () => { |
| 1659 | + it('should override providers on dependencies of dynamically loaded components', async () => { |
| 1660 | + function timer(delay: number): Promise<void> { |
| 1661 | + return new Promise<void>((resolve) => { |
| 1662 | + setTimeout(() => resolve(), delay); |
| 1663 | + }); |
| 1664 | + } |
| 1665 | + |
| 1666 | + @Injectable({providedIn: 'root'}) |
| 1667 | + class ImportantService { |
| 1668 | + value = 'original'; |
| 1669 | + } |
| 1670 | + |
| 1671 | + @NgModule({ |
| 1672 | + providers: [ImportantService], |
| 1673 | + }) |
| 1674 | + class ThisModuleProvidesService { |
| 1675 | + } |
| 1676 | + |
1659 | 1677 | @Component({ |
1660 | 1678 | standalone: true, |
1661 | | - selector: 'cmp-a', |
1662 | | - template: 'CmpA!', |
| 1679 | + selector: 'child', |
| 1680 | + imports: [ThisModuleProvidesService], |
| 1681 | + template: '<h1>{{value}}</h1>', |
1663 | 1682 | }) |
1664 | | - class CmpA { |
| 1683 | + class ChildCmp { |
| 1684 | + service = inject(ImportantService); |
| 1685 | + value = this.service.value; |
| 1686 | + } |
| 1687 | + |
| 1688 | + @Component({ |
| 1689 | + standalone: true, |
| 1690 | + selector: 'parent', |
| 1691 | + imports: [ChildCmp], |
| 1692 | + template: ` |
| 1693 | + @defer (when true) { |
| 1694 | + <child /> |
| 1695 | + } |
| 1696 | + `, |
| 1697 | + }) |
| 1698 | + class ParentCmp { |
1665 | 1699 | } |
1666 | 1700 |
|
| 1701 | + const deferrableDependencies = [ChildCmp]; |
| 1702 | + setClassMetadataAsync( |
| 1703 | + ParentCmp, |
| 1704 | + function() { |
| 1705 | + const promises: Array<Promise<Type<unknown>>> = deferrableDependencies.map( |
| 1706 | + // Emulates a dynamic import, e.g. `import('./cmp-a').then(m => m.CmpA)` |
| 1707 | + dep => new Promise((resolve) => setTimeout(() => resolve(dep)))); |
| 1708 | + return promises; |
| 1709 | + }, |
| 1710 | + function(...deferrableSymbols) { |
| 1711 | + setClassMetadata( |
| 1712 | + ParentCmp, [{ |
| 1713 | + type: Component, |
| 1714 | + args: [{ |
| 1715 | + selector: 'parent', |
| 1716 | + standalone: true, |
| 1717 | + imports: [...deferrableSymbols], |
| 1718 | + template: `<div>root cmp!</div>`, |
| 1719 | + }] |
| 1720 | + }], |
| 1721 | + null, null); |
| 1722 | + }); |
| 1723 | + |
| 1724 | + // Set `PLATFORM_ID` to a browser platform value to trigger defer loading |
| 1725 | + // while running tests in Node. |
| 1726 | + const COMMON_PROVIDERS = [{provide: PLATFORM_ID, useValue: PLATFORM_BROWSER_ID}]; |
| 1727 | + |
| 1728 | + TestBed.configureTestingModule({imports: [ParentCmp], providers: [COMMON_PROVIDERS]}); |
| 1729 | + TestBed.overrideProvider(ImportantService, {useValue: {value: 'overridden'}}); |
| 1730 | + |
| 1731 | + await TestBed.compileComponents(); |
| 1732 | + |
| 1733 | + const fixture = TestBed.createComponent(ParentCmp); |
| 1734 | + fixture.detectChanges(); |
| 1735 | + |
| 1736 | + await timer(10); |
| 1737 | + fixture.detectChanges(); |
| 1738 | + |
| 1739 | + expect(fixture.nativeElement.textContent).toContain('overridden'); |
| 1740 | + }); |
| 1741 | + |
| 1742 | + it('should allow import overrides on components with async metadata', async () => { |
1667 | 1743 | const NestedAotComponent = getAOTCompiledComponent('nested-cmp', [], []); |
1668 | 1744 | const RootAotComponent = getAOTCompiledComponent('root', [], []); |
1669 | 1745 |
|
|
0 commit comments