Skip to content

Bug (or Docs Bug?): Unable to test recursive standalone component due to NG0300 #50525

@bbarry

Description

@bbarry

Which @angular/* package(s) are the source of the bug?

core

Is this a regression?

Yes

Description

I have a relatively simple component that recursively nests on itself to build a node tree of div tags with various css classes and inner text based on a configuration object. When I convert this component to a standalone component, my tests fail with NG0300 but the new component works fine in the application. I'm assuming I am doing something wrong in configuring the testing module or that there is some bug in it but I'm unable to find documentation covering this case. The full component:

import { Input, Component, forwardRef } from '@angular/core';
import { NgFor, NgIf, NgClass } from '@angular/common';

export interface Node {
  cssClasses?: string;
  text?: string;
  nodes?: Node[];
}

@Component({
  selector: '[app-nester]',
  template: `<ng-container *ngFor="let node of nodes">
    <ng-container>
      <ng-container *ngIf="node.text as text">
        <div [ngClass]="node.cssClasses!">{{ text }}</div>
      </ng-container>
      <ng-container *ngIf="node.nodes as nodes">
        <div [ngClass]="node.cssClasses!" app-nester [nodes]="node.nodes"></div>
      </ng-container>
    </ng-container>
  </ng-container> `,
  // standalone: true,
  // imports: [NgFor, NgIf, NgClass, forwardRef(() => NesterComponent)],
})
export class NesterComponent {
  @Input() nodes: Node[] | null = null;
}

to convert to standalone I used the new migration and it added these commented out lines and imports.

My Tests:

import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';

import { NesterComponent } from './nester.component';

describe('NesterComponent', () => {
  let component: NesterComponent;
  let fixture: ComponentFixture<NesterComponent>;

  // remove for standalone version
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({ declarations: [NesterComponent] }).compileComponents();
  }));

  // standalone version from migration:
  // beforeEach(waitForAsync(() => {
  //   TestBed.configureTestingModule({ imports: [NesterComponent] }).compileComponents();
  // }));

  beforeEach(() => {
    fixture = TestBed.createComponent(NesterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeDefined();
  });

  it('should render text', () => {
    component.nodes = [
      { cssClasses: 'test1', text: 'test1' },
      {
        cssClasses: 'test2',
        nodes: [{ cssClasses: 'not in result' }, { text: 'test4' }],
      },
      {
        cssClasses: 'test5',
        nodes: [],
      },
    ];
    fixture.detectChanges();
    expect(fixture.nativeElement).toMatchSnapshot();
  });
});

Again in this case all the migration did was change declarations to imports

The above functions and test passes... however if I switch it to the standalone version of both files then the test fails (as far as I can tell it still works though, this is just a test problem).

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw

FAIL  src/app/shared/nester/nester.component.spec.ts (13.262 s)
  NesterComponent
    √ should create (322 ms)
    × should render text (826 ms)

  ● NesterComponent › should render text

    NG0300: Multiple components match node with tagname div: NesterComponent and NesterComponent. Find more at https://angular.io/errors/NG0300

      at throwMultipleComponentError (node_modules/@angular/core/fesm2022/core.mjs:10251:11)
      at findDirectiveDefMatches (node_modules/@angular/core/fesm2022/core.mjs:11580:29)
      at resolveDirectives (node_modules/@angular/core/fesm2022/core.mjs:11380:29)
      at elementStartFirstCreatePass (node_modules/@angular/core/fesm2022/core.mjs:15137:5)
      at ɵɵelementStart (node_modules/@angular/core/fesm2022/core.mjs:15173:9)
      at ɵɵelement (node_modules/@angular/core/fesm2022/core.mjs:15254:5)
      at NesterComponent_ng_container_0_ng_container_1_ng_container_2_Template (ng:/NesterComponent.js:25:5)
      at executeTemplate (node_modules/@angular/core/fesm2022/core.mjs:10890:17)
      at renderView (node_modules/@angular/core/fesm2022/core.mjs:12078:13)
      at TemplateRef2.createEmbeddedViewImpl (node_modules/@angular/core/fesm2022/core.mjs:22877:9)
      at ViewContainerRef2.createEmbeddedView (node_modules/@angular/core/fesm2022/core.mjs:23142:37)
      at _NgIf._updateView (node_modules/@angular/common/fesm2022/common.mjs:3316:45)
      at _NgIf.set ngIf [as ngIf] (node_modules/@angular/common/fesm2022/common.mjs:3289:14)
      at writeToDirectiveInput (node_modules/@angular/core/fesm2022/core.mjs:11766:33)
      at setInputsForProperty (node_modules/@angular/core/fesm2022/core.mjs:12000:9)
      at elementPropertyInternal (node_modules/@angular/core/fesm2022/core.mjs:11302:9)
      at ɵɵproperty (node_modules/@angular/core/fesm2022/core.mjs:15115:9)
      at NesterComponent_ng_container_0_ng_container_1_Template (ng:/NesterComponent.js:48:5)
      at ReactiveLViewConsumer.runInContext (node_modules/@angular/core/fesm2022/core.mjs:10345:13)
      at executeTemplate (node_modules/@angular/core/fesm2022/core.mjs:10885:22)
      at refreshView (node_modules/@angular/core/fesm2022/core.mjs:12395:13)
      at refreshEmbeddedViews (node_modules/@angular/core/fesm2022/core.mjs:12506:17)
      at refreshView (node_modules/@angular/core/fesm2022/core.mjs:12419:9)
      at refreshEmbeddedViews (node_modules/@angular/core/fesm2022/core.mjs:12506:17)
      at refreshView (node_modules/@angular/core/fesm2022/core.mjs:12419:9)
      at refreshComponent (node_modules/@angular/core/fesm2022/core.mjs:12542:13)
      at refreshChildComponents (node_modules/@angular/core/fesm2022/core.mjs:12589:9)
      at refreshView (node_modules/@angular/core/fesm2022/core.mjs:12445:13)
      at detectChangesInternal (node_modules/@angular/core/fesm2022/core.mjs:12337:9)
      at RootViewRef.detectChanges (node_modules/@angular/core/fesm2022/core.mjs:12867:9)
      at ComponentFixture._tick (node_modules/@angular/core/fesm2022/testing.mjs:126:32)
      at node_modules/@angular/core/fesm2022/testing.mjs:139:22
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone.umd.js:416:30)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing.umd.js:300:43)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone.umd.js:415:56)
      at Object.onInvoke (node_modules/@angular/core/fesm2022/core.mjs:26108:33)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone.umd.js:415:56)
      at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone.umd.js:173:47)
      at NgZone.run (node_modules/@angular/core/fesm2022/core.mjs:25962:28)
      at ComponentFixture.detectChanges (node_modules/@angular/core/fesm2022/testing.mjs:138:25)
      at src/app/shared/nester/nester.component.spec.ts:37:13
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone.umd.js:416:30)
      at ProxyZoneSpec.Object.<anonymous>.ProxyZoneSpec.onInvoke (node_modules/zone.js/bundles/zone-testing.umd.js:300:43)
      at _ZoneDelegate.Object.<anonymous>._ZoneDelegate.invoke (node_modules/zone.js/bundles/zone.umd.js:415:56)
      at Zone.Object.<anonymous>.Zone.run (node_modules/zone.js/bundles/zone.umd.js:173:47)
      at Object.wrappedFunc (node_modules/zone.js/bundles/zone-testing.umd.js:780:34)

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 16.0.2
Node: 18.16.0
Package Manager: npm 9.5.1
OS: win32 x64

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

Package                         Version
@angular-devkit/architect       0.1502.6
@angular-devkit/build-angular   16.0.2
@angular-devkit/core            15.2.6
@angular-devkit/schematics      16.0.2
@angular/cdk                    16.0.1
@angular/material               16.0.1
@schematics/angular             16.0.2
rxjs                            7.8.1
typescript                      5.0.4

Anything else?

It would be nice if https://angular.io/guide/testing-components-basics said something about standalone components. And/Or if https://angular.io/guide/standalone-components or https://angular.io/guide/standalone-migration covered testing at all.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions