0

There seems to be no answer to the related questions, so I'm asking this one for my specific case.

I'm looking to unit test an Angular 6 component that is a subclass of a base component. That base component has an @Input decorator. Abstract base class like this:

import { Component, Input } from '@angular/core';
import { dateToUsFormat } from '../../helpers/date-helpers';

@Component({
    selector: 'abstract-widget',
    templateUrl: './abstract-widget.component.html',
    styleUrls: ['./abstract-widget.component.scss']
})

export class AbstractWidgetComponent {
    @Input() widgetData;
    constructor() {
    }
}

The implementation class like this:

import { Component, OnInit } from '@angular/core';
import { AbstractWidgetComponent } from '../abstract-widget/abstract-widget.component';

@Component({
  selector: 'widget-title',
  templateUrl: './widget-title.component.html',
  styleUrls: ['./widget-title.component.scss']
})

export class WidgetTitleComponent extends AbstractWidgetComponent implements OnInit {

  ngOnInit() {
  }
  get titleReturn () {
    return widget_data.visual.title;
  }
}

So, it seems pretty simple. Unfortunately, writing unit tests for this little chunk of code seems to be impossible. What I want to do is instantiate the widget_title component with a AbstractWidgetComponent having a specific widgetData.

My code so far looks like:

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

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [ MatCardModule, AbstractWidgetComponent ],
            declarations: [ WidgetTitleComponent ],
            schemas: [ NO_ERRORS_SCHEMA  ]
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(WidgetTitleComponent);
        component = fixture.componentInstance;
        component.widgetData = {visual: {title: 'This is a Sample Title'}};
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
        expect(component.titleReturn()).toBe('This is a Sample Title');
    });
});

This code provides me with the error, "cannot find name 'widget_data'." To which I say, "Sure enough..... But where do I put that?" I've tried several variations but I'm a little confused.

I'm not an expert in either Angular or injection, but I've looked through several resources including: This GitHub Link, with its associated blog post and this Microsoft post. I was (possibly mistakenly) hoping for a fairly simple way to instantiate the base class and inject it to be automatically run through Jasmine. Both these links involve building an entirely new class.

Is there a way to make this simple, or am I going to need to build a test injection class to go with the base class?

Note upon resolution: I added the no_errors_schema as expected and just set the component property to have the value I wanted during the beforeEach.

Dylan Brams
  • 2,089
  • 1
  • 19
  • 36

2 Answers2

1

You should have the AbstractWidgetComponent in the declarations array. Import is for all the modules, declared with @ngModule.

You could also choose to ignore them using NO_ERRORS_SCHEMA : details in this post

dream88
  • 521
  • 2
  • 9
  • Well, I hate to say it but I tried this and still get, "Cannot find name, 'widget_data'" even with the AbstractWidgetComponent in the declarations array and NO_ERRORS_SCHEMA. – Dylan Brams Oct 18 '18 at 17:16
  • 1
    Sorry, if i wasn't clear enough. I meant to say, either: add the component in the imports Or, add the schema. Like : `declarations:[widgetcomponent], schemas: [ CUSTOM_ELEMENTS_SCHEMA ]' One of the two. Weird it doesn't work when you add it in the declaration array. – dream88 Oct 18 '18 at 17:22
1

As I see it the problem is not related to jasmine or the fact that you're extending another component (a normal serve of the code above, should give you the same error as well).

'widget_data' doesn't exists, and I guess that you wanna access some property of the extended 'widgetData' (@Input)?

If that is the case you should access it by using 'this.widgetData' instead. This should solve your error and after that you should be able to test your component as "normal" components using @Input. (and as already mentioned keep your components to declarations instead of imports).

https://angular.io/guide/testing#component-with-inputs-and-outputs

If not and you wanna access 'widget_data.visual.title' you need to declare it.

  • Turns out this was the answer. My original code didn't really instantiate widgetData appropriately. The answer below helped with the NO_ERRORS_SCHEMA addition, but I believe I needed both pieces of advice to make things work. – Dylan Brams Oct 23 '18 at 11:09