omarCreativeDev
omarCreativeDev

Reputation: 202

angular @Input() binding on attribute component for tr html tag failing unit tests

I am using an attribute selector component called app-product-row on a tr tag and im passing product in an @Input() as follows;

class

@Component({
  selector: '[app-product-row]',
  templateUrl: './product-row.component.html',
  styleUrls: ['./product-row.component.scss']
})
export class ProductRowComponent {
  @Input() public product = null;
}

TestBed

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

import { ProductRowComponent } from './product-row.component';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ ProductRowComponent ]
    })
    .compileComponents();
  }));

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

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

markup

<tr *ngFor="let product of searchResults"
    [product]="product"
    (showPipelineModalChange)="onShowPipelineModalChange($event)"
    app-product-row>
</tr>

the code compiles successfully upon ng serve and everything works as expected on the browser however unit tests are throwing

Chrome 63.0.3239 (Mac OS X 10.13.3)
Can't bind to 'product' since it isn't a known property of 'tr'

My implementation took inspiration from Angular2 table rows as component however in that example @Input()'s dont seem to be used and there is no mention of unit testing either.

could someone please point me in the right direction?

Upvotes: 2

Views: 741

Answers (3)

Nicholas Murray
Nicholas Murray

Reputation: 13529

I was receiving the same error

Chrome 77.0.3865 (Windows 10.0.0) MyResultsComponent should create FAILED
    Can't bind to 'entry' since it isn't a known property of 'tr'. ("  <tr my-result [ERROR ->][entry]="entry" *ngFor="let entry of entries"><tr>"):

Due to having not included the attribute selector component MyResultComponent in the declarations

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ MyResultsComponent]
    })
    .compileComponents();
  }));

Adding MyResultComponent to the declarations resolved the test failure.

MyResults component typescript:

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

class Entry {
  name: string;
  time: string;
}

@Component({
  selector: 'my-results, [my-results]',
  templateUrl: './my-results.component.html',
  styleUrls: ['./my-results.component.scss']
})
export class MyResultsComponent implements OnInit {

  entries: Entry[] = [
    { name: 'Entry One', time: '10:00'},
    { name: 'Entry Two', time: '10:05 '},
    { name: 'Entry Three', time: '10:10'},
  ];

  constructor() { }

  ngOnInit() {
  }
}

MyResults component template:

  <tr my-result [entry]="entry" *ngFor="let entry of entries"><tr>

Updated MyResults component test spec declarations:

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

import { MyResultsComponent } from './my-results.component';
import { MyResultComponent } from './../my-result/my-result.component';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ MyResultsComponent, MyResultComponent ]
    })
    .compileComponents();
  }));

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

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

Attribute component MyResult typescript:

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

@Component({
  selector: '[my-result]',
  templateUrl: './my-result.component.html',
  styleUrls: ['./my-result.component.scss']
})
export class MyResultComponent implements OnInit {

  @Input() entry: any;

  constructor() { }

  ngOnInit() {
  }
}

Attribute component MyResult template:

  <td>{{ entry.name }}</td>
  <td>{{ entry.time }}</td>

Upvotes: 1

omarCreativeDev
omarCreativeDev

Reputation: 202

a fellow front end dev has solved this problem for me! I had to declare the child component in the parent spec like so

TestBed.configureTestingModule({
  declarations: [
    SearchResultsListingComponent,
    ProductRowComponent
  ],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents();

Upvotes: -1

vince
vince

Reputation: 8306

This error usually happens when you haven't declared the component in your TestBed. Essentially, Angular doesn't recognize app-product-row as anything special, as if you had used it in your app without including it in your @NgModule. Try adding, in your test:

TestBed.configureTestingModule({
   declarations: [ProductRowComponent]
})

Each TestBed is kind of like a mini-module in the sense that you need to declare the components / directives you want to test.

Read more about setup here: https://angular.io/guide/testing#test-a-component

Upvotes: 2

Related Questions