'Angular test fails with pipe in dynamically generated child component

I have an angular component which serves as a facade for some other components. This is implemented using a ComponentFactoryResolver. When I introduced a pipe to one of the generated components, my unit tests started failing in a strange way.

I was hoping someone would be able to explain why it fails in this way?

        Failed: NG0302: The pipe 'displayValue' could not be found!. Find more at https://angular.io/errors/NG0302
        error properties: Object({ code: '302' })
        Error: NG0302: The pipe 'displayValue' could not be found!. Find more at https://angular.io/errors/NG0302
            at getPipeDef$1 (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:25738:1)
            at ɵɵpipe (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:25695:1)        
            at UsesPipeComponent_Template (ng:///UsesPipeComponent.js:7:9)
            at executeTemplate (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9544:1)
            at renderView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9351:1)     
            at renderComponent (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10626:1)
            at renderChildComponents (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9216:1)
            at renderView (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9376:1)     
            at ComponentFactory$1.create (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:25079:1)
            at ViewContainerRef.createComponent (node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:23118:1)
Chrome 88.0.4324.190 (Windows 10): Executed 1 of 1 (1 FAILED) (0.082 secs / 0.074 secs)

My test

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent,
        DisplayValuePipe
      ],
      imports: [CommonModule]
    }).compileComponents();
  }));

  it('should create the app', waitForAsync(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.componentInstance.title = 'testing';

    fixture.detectChanges();

    const app = fixture.componentInstance;
    console.log(fixture.nativeElement.innerHTML);
    expect(app).toBeTruthy();
  }));

AppComponent

@Component({
  selector: 'app-root',
  template: `<div>{{"test" | displayValue}}</div><ng-template #control></ng-template>`,
})
export class AppComponent implements AfterViewInit {
  title = 'testing-pipe';

  @ViewChild('control', { read: ViewContainerRef })
  public controlView: ViewContainerRef | undefined;

  constructor(private factoryResolver: ComponentFactoryResolver) { }

  ngAfterViewInit(): void {
    const factory = this.factoryResolver.resolveComponentFactory(UsesPipeComponent);
    this.controlView?.clear();
    const comp = this.controlView?.createComponent(factory);
    comp?.changeDetectorRef.detach();
    comp?.changeDetectorRef.detectChanges();
  }
}

Uses-Pipe

@Component({
  selector: 'app-uses-pipe',
  template: `{{"test" | displayValue}}`,
})
export class UsesPipeComponent {
  constructor() { }
}

The pipe

@Pipe({
  name: 'displayValue'
})
export class DisplayValuePipe implements PipeTransform {
  transform(value: string, ...args: unknown[]): string {
    return value.toUpperCase();
  }
}


Solution 1:[1]

How I fixed it

I missed the declaration of the dynamic component in my TestBed.configureTestModule like so :

beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent,
        DisplayValuePipe,
        UsesPipeComponent // this one is required
      ],
      imports: [CommonModule]
    }).compileComponents();
  }));

This makes sense since the TestBed would otherwise not know of this component, however, I fail to see how the test fails because of the pipe being used inside the child component if the TestBed does not have it registered.

Any insight about why it fails this way would be greatly appreciated!


I posted the issue on github as well and someone replied an answser : https://github.com/angular/angular/issues/41083

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1