mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
test(core): add more tests for the standalone injector (#45687)
This commit reorganizes the tests around the EnvironmentInjector and its use for standalone injectors, and adds a number of new test cases. PR Close #45687
This commit is contained in:
committed by
Andrew Scott
parent
3616d7eb59
commit
e162fa3735
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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
|
||||
*/
|
||||
|
||||
import {Component, createEnvironmentInjector, importProvidersFrom, NgModule} from '@angular/core';
|
||||
|
||||
describe('environement injector and standalone components', () => {
|
||||
it('should see providers from modules imported by standalone components', () => {
|
||||
class ModuleService {}
|
||||
|
||||
@NgModule({providers: [ModuleService]})
|
||||
class Module {
|
||||
}
|
||||
|
||||
@Component({standalone: true, imports: [Module]})
|
||||
class StandaloneComponent {
|
||||
}
|
||||
|
||||
const envInjector = createEnvironmentInjector(importProvidersFrom(StandaloneComponent));
|
||||
expect(envInjector.get(ModuleService)).toBeInstanceOf(ModuleService);
|
||||
});
|
||||
|
||||
it('should see providers when exporting a standalone components', () => {
|
||||
class ModuleService {}
|
||||
|
||||
@NgModule({providers: [ModuleService]})
|
||||
class Module {
|
||||
}
|
||||
|
||||
@Component({standalone: true, imports: [Module]})
|
||||
class StandaloneComponent {
|
||||
}
|
||||
|
||||
@NgModule({imports: [StandaloneComponent], exports: [StandaloneComponent]})
|
||||
class AppModule {
|
||||
}
|
||||
|
||||
const envInjector = createEnvironmentInjector(importProvidersFrom(AppModule));
|
||||
expect(envInjector.get(ModuleService)).toBeInstanceOf(ModuleService);
|
||||
});
|
||||
|
||||
it('should not collect duplicate providers', () => {
|
||||
class ModuleService {}
|
||||
|
||||
@NgModule({providers: [{provide: ModuleService, useClass: ModuleService, multi: true}]})
|
||||
class Module {
|
||||
}
|
||||
|
||||
@Component({standalone: true, imports: [Module]})
|
||||
class StandaloneComponent1 {
|
||||
}
|
||||
|
||||
@Component({standalone: true, imports: [Module]})
|
||||
class StandaloneComponent2 {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [StandaloneComponent1, StandaloneComponent2],
|
||||
exports: [StandaloneComponent1, StandaloneComponent2]
|
||||
})
|
||||
class AppModule {
|
||||
}
|
||||
|
||||
const envInjector = createEnvironmentInjector(importProvidersFrom(AppModule));
|
||||
const services = envInjector.get(ModuleService) as ModuleService[];
|
||||
|
||||
expect(services.length).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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
|
||||
*/
|
||||
|
||||
import {Component, ComponentFactoryResolver, createEnvironmentInjector, EnvironmentInjector, Injector, NgModule, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
|
||||
describe('standalone injector', () => {
|
||||
it('should create one standalone injector for each parent EnvInjector', () => {
|
||||
let counter = 0;
|
||||
|
||||
class Service {
|
||||
value = counter++;
|
||||
}
|
||||
|
||||
@NgModule({providers: [Service]})
|
||||
class ModuleWithAService {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'standalone',
|
||||
standalone: true,
|
||||
imports: [ModuleWithAService],
|
||||
template: `({{service.value}})`
|
||||
})
|
||||
class TestComponent {
|
||||
constructor(readonly service: Service) {}
|
||||
}
|
||||
|
||||
@Component({selector: 'app', template: `<ng-template #insert></ng-template>`})
|
||||
class AppComponent {
|
||||
@ViewChild('insert', {static: true, read: ViewContainerRef}) vcRef!: ViewContainerRef;
|
||||
|
||||
createComponent(envInjector?: EnvironmentInjector): void {
|
||||
this.vcRef.createComponent(TestComponent, {environmentInjector: envInjector});
|
||||
}
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const currEnvInjector = TestBed.inject(EnvironmentInjector);
|
||||
|
||||
fixture.componentInstance.createComponent(currEnvInjector);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toBe('(0)');
|
||||
|
||||
// inserting the same standalone component second time and asserting that no new injector /
|
||||
// service instance gets created
|
||||
fixture.componentInstance.createComponent(currEnvInjector);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toBe('(0)(0)');
|
||||
|
||||
// inserting with a different EnvInjector as a parent should trigger a new service instance
|
||||
// creation
|
||||
fixture.componentInstance.createComponent(createEnvironmentInjector([], currEnvInjector));
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement.textContent).toBe('(0)(0)(1)');
|
||||
});
|
||||
|
||||
it('should create a standalone Injector for ComponentRefs that are not inserted anywhere', () => {
|
||||
class Service {
|
||||
value = 'Service value';
|
||||
}
|
||||
|
||||
@NgModule({providers: [Service]})
|
||||
class ModuleWithAService {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'standalone',
|
||||
standalone: true,
|
||||
imports: [ModuleWithAService],
|
||||
template: `{{service.value}}`
|
||||
})
|
||||
class DynamicComponent {
|
||||
constructor(readonly service: Service) {}
|
||||
}
|
||||
|
||||
@Component({})
|
||||
class AppComponent {
|
||||
}
|
||||
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
|
||||
const cfr = TestBed.inject(ComponentFactoryResolver);
|
||||
const cf = cfr.resolveComponentFactory(DynamicComponent);
|
||||
|
||||
const componentRef = cf.create(Injector.NULL);
|
||||
componentRef.changeDetectorRef.detectChanges();
|
||||
|
||||
expect(componentRef.location.nativeElement.textContent).toBe('Service value');
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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
|
||||
*/
|
||||
|
||||
import {InjectionToken, Injector, ɵRenderFlags, ɵɵdefineComponent, ɵɵdefineInjector, ɵɵelement, ɵɵStandaloneFeature} from '../../src/core';
|
||||
import {getComponentDef} from '../../src/render3/definition';
|
||||
|
||||
describe('standalone components, directives and pipes', () => {
|
||||
it('should filter out imported NgModules from a list of directives to match', () => {
|
||||
const token = new InjectionToken<String>('TestToken');
|
||||
|
||||
let count = 0;
|
||||
class TestModule {
|
||||
static ɵinj = ɵɵdefineInjector({
|
||||
providers: [{
|
||||
provide: token,
|
||||
useFactory:
|
||||
() => {
|
||||
return `From module: ${count++}`;
|
||||
}
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
class TestComponent {
|
||||
static ɵfac = () => new TestComponent();
|
||||
static ɵcmp = ɵɵdefineComponent({
|
||||
type: TestComponent,
|
||||
selectors: [['test-cmp']],
|
||||
decls: 1,
|
||||
vars: 0,
|
||||
template:
|
||||
(rf: ɵRenderFlags, ctx: TestComponent) => {
|
||||
if (rf & ɵRenderFlags.Create) {
|
||||
ɵɵelement(0, 'div');
|
||||
}
|
||||
},
|
||||
dependencies: [TestModule],
|
||||
features: [ɵɵStandaloneFeature],
|
||||
standalone: true
|
||||
});
|
||||
}
|
||||
|
||||
// simulate logic executed by ComponentFactory::create
|
||||
const def = getComponentDef(TestComponent);
|
||||
|
||||
if (def !== null && def.getStandaloneInjector !== null) {
|
||||
const parentInjector = Injector.create({providers: []});
|
||||
expect(def.getStandaloneInjector(parentInjector)!.get(token)).toEqual('From module: 0');
|
||||
|
||||
// subsequent calls with the same same parent injector should not create new provider
|
||||
// instances
|
||||
expect(def.getStandaloneInjector(parentInjector)!.get(token)).toEqual('From module: 0');
|
||||
|
||||
// but specifying a different injector _should_ create a new provider instance
|
||||
const otherParentInjector = Injector.create({providers: []});
|
||||
expect(def.getStandaloneInjector(otherParentInjector)!.get(token)).toEqual('From module: 1');
|
||||
|
||||
// again, same calls should not create new instances
|
||||
expect(def.getStandaloneInjector(parentInjector)!.get(token)).toEqual('From module: 0');
|
||||
expect(def.getStandaloneInjector(otherParentInjector)!.get(token)).toEqual('From module: 1');
|
||||
} else {
|
||||
fail('Failed to get component def or standalone feature function');
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user