With Angular 9 (Ivy), we no longer have to provide entryComponents.
This seems to fail if we're using Jest as our test runner.
The same test does pass with Jasmine/Karma.
If we run it with Jest, we're getting the following error
No component factory found for DynamicButtonComponent. Did you add it to @NgModule.entryComponents?
at noComponentFactoryError (../packages/core/src/linker/component_factory_resolver.ts:17:17)
at CodegenComponentFactoryResolver.Object.<anonymous>.CodegenComponentFactoryResolver.resolveComponentFactory (../packages/core/src/linker/component_factory_resolver.ts:72:13)
at NgComponentOutlet.Object.<anonymous>.NgComponentOutlet.ngOnChanges (../packages/common/src/directives/ng_component_outlet.ts:105:36)
at checkAndUpdateDirectiveInline (../packages/core/src/view/provider.ts:207:15)
at checkAndUpdateNodeInline (../packages/core/src/view/view.ts:429:14)
at checkAndUpdateNode (../packages/core/src/view/view.ts:389:12)
at debugCheckAndUpdateNode (../packages/core/src/view/services.ts:430:44)
at debugCheckDirectivesFn (../packages/core/src/view/services.ts:391:7)
at Object.eval [as updateDirectives] (ng:/DynamicHostModule/DynamicHostComponent.ngfactory.js:14:9)
at Object.debugUpdateDirectives [as updateDirectives] (../packages/core/src/view/services.ts:385:19)
at checkAndUpdateView (../packages/core/src/view/view.ts:359:12)
at callViewAction (../packages/core/src/view/view.ts:615:11)
at execComponentViewsAction (../packages/core/src/view/view.ts:559:7)
at checkAndUpdateView (../packages/core/src/view/view.ts:370:3)
at callWithDebugContext (../packages/core/src/view/services.ts:629:23)
at Object.debugCheckAndUpdateView [as checkAndUpdateView] (../packages/core/src/view/services.ts:346:10)
at ViewRef_.Object.<anonymous>.ViewRef_.detectChanges (../packages/core/src/view/refs.ts:260:16)
at ComponentFixture.Object.<anonymous>.ComponentFixture._tick (../../packages/core/testing/src/component_fixture.ts:107:28)
at ../../packages/core/testing/src/component_fixture.ts:120:36
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:396:30)
at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:395:36)
at Object.onInvoke (../packages/core/src/zone/ng_zone.ts:302:25)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:395:36)
at Zone.run (node_modules/zone.js/dist/zone.js:153:47)
at NgZone.Object.<anonymous>.NgZone.run (../packages/core/src/zone/ng_zone.ts:178:50)
at ComponentFixture.Object.<anonymous>.ComponentFixture.detectChanges (../../packages/core/testing/src/component_fixture.ts:120:19)
at src/app/dynamic-host.component.spec.ts:15:13
at node_modules/tslib/tslib.js:113:75
at new ZoneAwarePromise (node_modules/zone.js/dist/zone.js:915:33)
at Object.__awaiter (node_modules/tslib/tslib.js:109:16)
at src/app/dynamic-host.component.spec.ts:13:49
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:396:30)
at ProxyZoneSpec.onInvoke (node_modules/zone.js/dist/proxy.js:117:43)
at ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:395:36)
at Zone.run (node_modules/zone.js/dist/zone.js:153:47)
See https://github.com/timdeschryver/jest-dynamic-ng-repro for a reproduction.
Ok, so this is related to ngComponentOutlet and dynamic components.
I think Ivy and its ASTTransformers are doing more under the hood to gather the metadata for entry components.
So we basically have two options:
ts-jest to provide a better compiling runtime with ts-program capabilities so we can use the angular transformersUntil then, I guess you have to declare it in .spec files as entryComponents (should work - not tested).
FYI, according to ts-jest source code it only takes into account of transformers which are manually defined by users (via ts-jest config). It doesn鈥檛 know anything about angular.
I wonder if providing angular transformers to ts-jest can solve the issue (point 1)
I agree that we need to know what ivy does to find out a proper way. (point 2)
Can you verify whether getTestBed (called in setupJest.ts) returns an instance of TestBedViewEngine or TestBedRender3?
It was using the TestBedViewEngine , after manually transforming the code, the test passes.
While this is a solution, it isn't optimal.
It means that after every change made to the component, we will have to run this command (which isn't fast enough to do on a regular basis imho), and that we can't use the jest watch command.
"pretest": "ngcc --properties main"
See https://github.com/angular/angular-cli/issues/17110#issuecomment-592349783
related to the discussion in #347, see explanation
It should only be necessary to run ngcc after npm install/yarn install which it already does when using ng update.
I don't know what I did wrong on Sunday, but after running ngcc --properties main it does work.
Even when the are component's modified.
Ngcc should only modify internal angular codes I think, so run once should be enough to be compatible with Ivy and it鈥檚 unrelated to the project鈥檚 code base.
ngcc runs on all libraries. Our app, Angular, 3rd party dependencies. Every time a package update/addition happens, it will need to be rerun (and with the right parameters for Jest).
I can confirm that I was able to override entryComponents as suggested by @wtho. For context, the error I was getting was:
Error: No component factory found for VerifyLedgerComponent. Did you add it to @NgModule.entryComponents?
I added VerifyLedgerComponent to declarations and then used overrideModule to put it in the entryComponents. Here's what worked for me:
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LedgerComponent, VerifyLedgerComponent],
imports: [LedgerCoreModule, MatDialogModule, NoopAnimationsModule],
schemas: [NO_ERRORS_SCHEMA]
})
.overrideModule(BrowserDynamicTestingModule, {
set:
{entryComponents: [VerifyLedgerComponent]}
})
.compileComponents();
}));
Most helpful comment
I can confirm that I was able to override
entryComponentsas suggested by @wtho. For context, the error I was getting was:Error: No component factory found for VerifyLedgerComponent. Did you add it to @NgModule.entryComponents?I added
VerifyLedgerComponenttodeclarationsand then usedoverrideModuleto put it in theentryComponents. Here's what worked for me: