Home > Mobile >  Unit test error: 'X' is not a known element
Unit test error: 'X' is not a known element

Time:07-20

I am running npm run test and getting the follow error on my terminal:

1. If 'app-general-screen' is an Angular component, then verify that it is a part of an @NgModule where this component 
is declared.
2. If 'app-general-screen' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.'
Chrome Headless 103.0.5060.114 (Windows 10): Executed 30 of 32 SUCCESS (0 secs / 0.385 secs)
ERROR: 'NG0304: 'app-general-screen' is not a known element (used in the 'MenuScreen' component template):
1. If 'app-general-screen' is an Angular component, then verify that it is a part of an @NgModule where this component 
is declared.
2. If 'app-general-screen' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this compERROR: 'NG0303: Can't bind to 'generalConfig' since it isn't a known property of 'app-general-screen' (used in the 'MenuScreen' component template).
1. If 'app-general-screen' is an Angular component and it has the 'generalConfig' input, then verify that it is a part 
of an @NgModule where this component is declared.
2. If 'app-general-screen' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component.'
Chrome Headless 103.0.5060.114 (Windows 10): Executed 30 of 32 SUCCESS (0 secs / 0.385 secs)
ERROR: 'NG0303: Can't bind to 'generalConfig' since it isn't a known property of 'app-general-screen' (used in the 'MenuScreen' component template).
1. If 'app-general-screen' is an Angular component and it has the 'generalConfig' input, then verify that it is a part 
of an @NgModule where this component is declared.
2. If 'app-general-screen' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.

Most of the post on SO suggest that you declare the required component in both app.module.ts and the component.spec.ts files (which I have)

Below the relevant code extracts:

menu.screen.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { GeneralScreen } from '../general/general.screen';
import { MenuScreen } from './menu.screen';

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

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [MenuScreen, GeneralScreen],
    }).compileComponents();
  });

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

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

menu.screen.ts

import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Router } from '@angular/router';
import {
  MenuPageConfig,
  PageConfig,
} from 'src/app/interfaces/common-page-configs.interface';

@Component({
  selector: 'app-menu-screen',
  templateUrl: './menu.screen.html',
  styleUrls: ['./menu.screen.scss'],
})
export class MenuScreen implements OnInit {
  @Input() config = {} as MenuPageConfig;
  @Output() viewStateSelected = new EventEmitter<any>();

  generalConfig = {} as PageConfig;

  constructor(public router: Router) {}

  ngOnInit() {
    this.setPageConfig();
  }

  setPageConfig() {
    this.generalConfig = {
      header: this.config.header,
      subHeader: this.config.subHeader,
    };
  }

  onMenuOptionClicked(path: string | undefined, viewState: any | undefined) {
    path
      ? this.router.navigate([path])
      : this.viewStateSelected.emit(viewState);
  }
}

menu.screen.html

<div >
  <app-general-screen [generalConfig]="generalConfig"></app-general-screen>
  <div >
    <button
      mat-button
      *ngFor="let option of config.menu"
      
      (click)="onMenuOptionClicked(option.path, option.viewState)"
    >
      {{ option.display }}
    </button>
  </div>
</div>

general.screen.ts

import { Component, EventEmitter, Input, Output } from '@angular/core';
import { PageConfig } from 'src/app/interfaces/common-page-configs.interface';

@Component({
  selector: 'app-general-screen',
  templateUrl: './general.screen.html',
  styleUrls: ['./general.screen.scss'],
})
export class GeneralScreen {
  @Input() generalConfig = {} as PageConfig;
  @Output() viewStateSelected = new EventEmitter<any>();
}

general.screen.html

<div *ngIf="generalConfig.header" >
  {{ generalConfig.header }}
</div>
<div *ngIf="generalConfig.subHeader" >
  {{ generalConfig.subHeader }}
</div>

app.module.ts (extracts)

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';

import { MenuScreen } from './screens/menu/menu.screen';
import { GeneralScreen } from './screens/general/general.screen';

@NgModule({
  declarations: [
    AppComponent,
    MenuScreen,
    GeneralScreen,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,  
],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

I have been racking my brain on this for hours now and would love some insights as to what I am doing wrong here - I just can't see it...

The thing that is really stumping me is that I have the GeneralScreen component used in other component with no issue - and I am serving the project with no issues...

EDIT Adding: schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA] or schemas: [NO_ERRORS_SCHEMA] or schemas: [CUSTOM_ELEMENTS_SCHEMA] to the test have not impact in the result

ALTHOUGH removing <app-general-screen [generalConfig]="generalConfig"></app-general-screen> from menu.screen.html does seem to stop the error presenting, but removing that line is not an option

CodePudding user response:

You're right, it should work, I am not sure why it is not working.

For a quick unblock, you can try adding schemas: [NO_ERRORS_SCHEMA] to the unit test. This will treat all components that cannot be rendered into dead HTML and you can also do some research on this. This is how I do all of my unit testing because I am usually not concerned about the painting of child components.

beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      // !! Remove GeneralScreen from here !!
      declarations: [MenuScreen],
      // !! add below line !!
      schemas: [NO_ERRORS_SCHEMA]
    }).compileComponents();
  });

CodePudding user response:

Sorted after many hours - the common component GeneralScreen needed to be incorporated not only in MenuScreen BUT also in the test of the parents of MenuScreen

  • Related