ngneat/spectator

Angular 19: Unexpected "HostComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call

Closed this issue ยท 14 comments

Is this a regression?

No

Description

I just updated a library to use Angular 19 and Spectator 19 and I'm having the following issue for all my tests:

Failed: Unexpected "HostComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "HostComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?
    at forEach (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:769:23)
    at Array.forEach (<anonymous>)
    at assertNoStandaloneComponents (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:765:11)
    at TestBedCompiler.configureTestingModule (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:844:13)
    at TestBedImpl.configureTestingModule (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:1974:23)
    at Function.configureTestingModule (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:1789:37)
    at UserContext.apply (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@ngneat/spectator/fesm2022/ngneat-spectator.mjs:1707:17)
    at _ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/fesm2015/zone.js:369:28)
    at AsyncTestZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:892:39)
    at ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:2079:39)

Here is the test:

describe('ng-ui-badge', () => {
  let spectator: SpectatorHost<BadgeComponent>;
  const createComponent = createHostFactory(BadgeComponent);

  it('should display the value passed into the value property', () => {
    spectator = createComponent(`<ng-ui-badge value="12"></ng-ui-badge>`);

    expect(spectator.element).toHaveText('12');
  });
});

And the badge component in itself is not really complex. It's a simple component with the "standalone" flag set to "true". I don't think it's related to the component itself as I have tests for 30+ components and they all fail with the same error.

I tried by using the imports array but it did not change anything:

const createComponent = createHostFactory({
  component: BadgeComponent,
  imports: [
    BadgeComponent
  ]
});

I also try setting declareComponent to false with no luck.

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

Failed: Unexpected "HostComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "HostComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?
    at forEach (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:769:23)
    at Array.forEach (<anonymous>)
    at assertNoStandaloneComponents (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:765:11)
    at TestBedCompiler.configureTestingModule (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:844:13)
    at TestBedImpl.configureTestingModule (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:1974:23)
    at Function.configureTestingModule (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@angular/core/fesm2022/testing.mjs:1789:37)
    at UserContext.apply (http://localhost:9876/_karma_webpack_/webpack:/node_modules/@ngneat/spectator/fesm2022/ngneat-spectator.mjs:1707:17)
    at _ZoneDelegate.invoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/fesm2015/zone.js:369:28)
    at AsyncTestZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:892:39)
    at ProxyZoneSpec.onInvoke (http://localhost:9876/_karma_webpack_/webpack:/node_modules/zone.js/fesm2015/zone-testing.js:2079:39)

Please provide the environment you discovered this bug in

Angular CLI: 19.0.0
Node: 20.18.0
Package Manager: yarn 1.22.22
OS: win32 x64

Angular: 19.0.0
... animations, cli, common, compiler, compiler-cli, core
... elements, forms, language-service, platform-browser
... platform-browser-dynamic, router

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1900.0
@angular-devkit/build-angular   19.0.0
@angular-devkit/core            19.0.0
@angular-devkit/schematics      19.0.0
@ngneat/spectator               19.0.0
@schematics/angular             19.0.0
ng-packagr                      19.0.0
rxjs                            7.8.1
typescript                      5.5.4
zone.js                         0.15.0

Anything else?

No response

Do you want to create a pull request?

No

I have the same issue with MockComponent

Failed: Unexpected "MockOfImageRevertButtonComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "MockOfImageRevertButtonComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?

adding this to imports did nothing

imports: [
      MatDialogModule,
      BrowserAnimationsModule,
      ImageEditorComponent,
      MockComponent(ImageRevertButtonComponent),
      MockComponent(ImageAttributesFormComponent),
    ],

I now have a ton of broken tests after the Angular upgrade

So there is a workaround that i got to work for pipes and directives.

Create a Dummy component and specify standalone: false.

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'lib-ngneat-host-component',
    template: '',
    standalone: false,
})
export class StandaloneHostFixComponent {}

Then for a pipe or override the host option with the component.

Pipe example:

const createPipe: SpectatorPipeFactory<CcDatePipe, StandaloneHostFixComponent> = createPipeFactory({
  pipe: CustomDatePipe,
  host: StandaloneHostFixComponent,
});

Directive example:

createDirectiveFactory({
  directive: CustomDirective,
  host: StandaloneHostFixComponent
});

@ssougnez For your case this should work.

const createComponent = createHostFactory({
  component: BadgeComponent,
  host: StandaloneHostFixComponent
});

Basically the issue is this component here because it does not specify standalone: false so when using NG19 it will automatically be standalone: true which will cause the error. But change it to standalone: false will lead to other issues so i couldn't come up with an PR without updating angular.

This is not a work around for the case of mocking standalone components which is what I need

So there is a workaround that i got to work for pipes and directives.

Create a Dummy component and specify standalone: false.

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'lib-ngneat-host-component',
    template: '',
    standalone: false,
})
export class StandaloneHostFixComponent {}

Then for a pipe or override the host option with the component.

Pipe example:

const createPipe: SpectatorPipeFactory<CcDatePipe, StandaloneHostFixComponent> = createPipeFactory({
  pipe: CustomDatePipe,
  host: StandaloneHostFixComponent,
});

Directive example:

createDirectiveFactory({
  directive: CustomDirective,
  host: StandaloneHostFixComponent
});

@ssougnez For your case this should work.

const createComponent = createHostFactory({
  component: BadgeComponent,
  host: StandaloneHostFixComponent
});

Basically the issue is this component here because it does not specify standalone: false so when using NG19 it will automatically be standalone: true which will cause the error. But change it to standalone: false will lead to other issues so i couldn't come up with an PR without updating angular.

Thanks a lot for the investigation, but wouldn't it be more clean to have a new major version of the library only supporting ng 19 and taking into account standalone component?

So there is a workaround that i got to work for pipes and directives.
Create a Dummy component and specify standalone: false.

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'lib-ngneat-host-component',
    template: '',
    standalone: false,
})
export class StandaloneHostFixComponent {}

Then for a pipe or override the host option with the component.
Pipe example:

const createPipe: SpectatorPipeFactory<CcDatePipe, StandaloneHostFixComponent> = createPipeFactory({
  pipe: CustomDatePipe,
  host: StandaloneHostFixComponent,
});

Directive example:

createDirectiveFactory({
  directive: CustomDirective,
  host: StandaloneHostFixComponent
});

@ssougnez For your case this should work.

const createComponent = createHostFactory({
  component: BadgeComponent,
  host: StandaloneHostFixComponent
});

Basically the issue is this component here because it does not specify standalone: false so when using NG19 it will automatically be standalone: true which will cause the error. But change it to standalone: false will lead to other issues so i couldn't come up with an PR without updating angular.

Thanks a lot for the investigation, but wouldn't it be more clean to have a new major version of the library only supporting ng 19 and taking into account standalone component?

Sure you are right but this project doesn't look to active does it? So the last release isn't even in npm ans over 2 months ago so don't know when an update will come. So we will have a workaround for now to proceede with NG19 and if there is an major update fine if not we will move away from it.

This is not a work around for the case of mocking standalone components which is what I need

The workaround is just recreating the current behaviour of the HostComponent in the project but with a NG19 component. The componen you are testing is still standalone his is just the "host component" around your component that you set via component. We are using it with standalone components

So there is a workaround that i got to work for pipes and directives.
Create a Dummy component and specify standalone: false.

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'lib-ngneat-host-component',
    template: '',
    standalone: false,
})
export class StandaloneHostFixComponent {}

Then for a pipe or override the host option with the component.
Pipe example:

const createPipe: SpectatorPipeFactory<CcDatePipe, StandaloneHostFixComponent> = createPipeFactory({
  pipe: CustomDatePipe,
  host: StandaloneHostFixComponent,
});

Directive example:

createDirectiveFactory({
  directive: CustomDirective,
  host: StandaloneHostFixComponent
});

@ssougnez For your case this should work.

const createComponent = createHostFactory({
  component: BadgeComponent,
  host: StandaloneHostFixComponent
});

Basically the issue is this component here because it does not specify standalone: false so when using NG19 it will automatically be standalone: true which will cause the error. But change it to standalone: false will lead to other issues so i couldn't come up with an PR without updating angular.

Thanks a lot for the investigation, but wouldn't it be more clean to have a new major version of the library only supporting ng 19 and taking into account standalone component?

Sure you are right but this project doesn't look to active does it? So the last release isn't even in npm ans over 2 months ago so don't know when an update will come. So we will have a workaround for now to proceede with NG19 and if there is an major update fine if not we will move away from it.

Yeah, this is more my concern actually.
I browsed a bit the latest issue and there don't seem to have much activity.
I'm considering dropping this library and have a look at another one...

As for you other answer, I guess it's a good workaround that I'll probably use waiting for a real solution to be implemented or for me to have time to replace this library.

Thanks a lot :-)

I'm working on a PR for this.

The lead maintainer has been quite helpful in accepting and coaching PRs in the past - I wouldn't get down on the library, just contribute! This is what open-source dev tools are all about.

This is not a work around for the case of mocking standalone components which is what I need

@carflynn2009 - to mock a standalone component, this bugfix isn't needed. You can do it right now by using the MockComponent() fn from ng-mocks (which is what spectator uses for its mocks: [] array), and put it under imports:

import { MockComponent } from 'ng-mocks';
...

  let spectator: Spectator<FooComponent>;
  const createComponent = createComponentFactory( // Or whatever factory
    {
      component: FooComponent,
      imports: [
        MockComponent(InnerComponent)
      ]
    });

This is not a work around for the case of mocking standalone components which is what I need

@carflynn2009 - to mock a standalone component, this bugfix isn't needed. You can do it right now by using the MockComponent() fn from ng-mocks (which is what spectator uses for its mocks: [] array), and put it under imports:

import { MockComponent } from 'ng-mocks';
...

  let spectator: Spectator<FooComponent>;
  const createComponent = createComponentFactory( // Or whatever factory
    {
      component: FooComponent,
      imports: [
        MockComponent(InnerComponent)
      ]
    });

Going back to my original post this is exactly what I was doing and it still gives the same error , I cannot mock standalone components since angular 19

I have the same issue with MockComponent

Failed: Unexpected "MockOfImageRevertButtonComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "MockOfImageRevertButtonComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?

adding this to imports did nothing

imports: [
      MatDialogModule,
      BrowserAnimationsModule,
      ImageEditorComponent,
      MockComponent(ImageRevertButtonComponent),
      MockComponent(ImageAttributesFormComponent),
    ],

I now have a ton of broken tests after the Angular upgrade

@johncrim if you look at the code I have it does not work

@carflynn2009 - I have tests with MockComponent in imports and it works as expected with ng19. There's nothing I see in your code snippet that is materially different from what is working for me.

Did you try ng cache clean?

That is what I I thought that it should just work but as soon as I upgraded every test I have with mock fails ,

I will try ng clean

I have the same issue with MockComponent

Failed: Unexpected "MockOfImageRevertButtonComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "MockOfImageRevertButtonComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?

adding this to imports did nothing

imports: [
      MatDialogModule,
      BrowserAnimationsModule,
      ImageEditorComponent,
      MockComponent(ImageRevertButtonComponent),
      MockComponent(ImageAttributesFormComponent),
    ],

I now have a ton of broken tests after the Angular upgrade

@johncrim if you look at the code I have it does not work

That is what I I thought that it should just work but as soon as I upgraded every test I have with mock fails ,

I will try ng clean

@carflynn2009 did your issues disappear when upgrading to the latest spectator version.

After the upgrade I still run into this

Failed: Unexpected "MockOfImageRevertButtonComponent" found in the "declarations" array of the "TestBed.configureTestingModule" call, "MockOfImageRevertButtonComponent" is marked as standalone and can't be declared in any NgModule - did you intend to import it instead (by adding it to the "imports" array)?