5

I am trying to write a unit test for a component which has child components and are injected in using ng-content. I am trying to get the queryList of components in my test so I can test this function below but can't seem to populate the QueryList variable in my unit test.

  @ContentChildren(TabsContentComponent, { descendants: true, read: TabsContentComponent }) tabContent: QueryList<TabsContentComponent>;
onTabChange($event) {
this.tabContent.toArray().forEach((tab, index) => {
  tab.isActive = false;
  if ($event.value === tab.value) {
    tab.isActive = true;
  }
});

}

Here is my components: I have a component called TabsContentContainer which holds another component called Tabs. Which looks like this:

<div class="flex flex-direction-column tabs-container">
<app-tabs (tabChange)='onTabChange($event)' [tabs]="tabs"></app-tabs>
<ng-content></ng-content>

How I use the TabsContentContainer component I do this (This is just a mock version of it minus the app-tabs component):

<app-tabs-container>
<app-tabs-content>content goes here</app-tabs-content>
<app-tabs-content>content goes here</app-tabs-content>
<app-tabs-content>content goes here</app-tabs-content>

I have tried following another answer to figure out what to do as it is fairly old answer but I can't seem to get my mock component to return my QueryList of app-tabs-content.

Here is my spec file:

    @Component({
  selector: 'app-tabs-container-mock-component',
  template: `<app-tabs-container>
    <app-tabs-content></app-tabs-content>
    <app-tabs-content></app-tabs-content>
    <app-tabs-content></app-tabs-content>
  </app-tabs-container>`
})
export class TabsContainerMockComponent { }

fdescribe('TabsContainerComponent', () => {
  let component: TabsContainerComponent;
  let component2: TabsContainerMockComponent;
  console.log(component2);
  let fixture: ComponentFixture<TabsContainerComponent>;
  let fixture2: ComponentFixture<TabsContainerMockComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [TabsContainerComponent, TabsContainerMockComponent],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TabsContainerComponent);
    fixture2 = TestBed.createComponent(TabsContainerMockComponent);
    console.log(fixture2);
    component = fixture.componentInstance;
    component2 = fixture2.componentInstance;
    fixture.detectChanges();
    fixture2.detectChanges();
  });

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

  it('should show the correct content when changing tabs', () => {
    const container = fixture.debugElement.children[0].componentInstance;
    debugger;
    console.log(container);
  });

What I have tried:

I have tried mocking my component and trying to access it but that didn't seem to work as I get an error complaining Component 'TabsContainerMockComponent' is not included in a module and will not be available inside a template. Consider adding it to a NgModule declaration

That led me to adding a mock NgModule but that also didn't work and didn't seem right.

I am lost for what to try as I cant seem to get component.tabContent to equal a QueryList of app-tabs-content.

saunders
  • 969
  • 4
  • 14
  • 25

1 Answers1

4

The issues is that right now your TabsContainerMockComponent is actually a wrapper on top of your TabsContainerComponent. In order to access your TabsContainerComponent inside of your TabsContainerMockComponent (which should probably be renamed to something like TestHostTabsContainerComponent), you'll need to get a hold of the DebugElement and one of it's children (probably the first child) will be your TabsContainerComponent.

You'll need a variable at the top to hold your TabsContainerComponent:

let tabsContainerComponent: TabsContainerComponent;

Do this in your beforeEach:

tabsContainerComponent = fixture2.debugElement.children[0].componentInstance;

Then you can access your QueryList as it is a public property on the TabsContainerComponent:

tabsContainerComponent.tabContent

Read more about DebugElement here: https://angular.io/api/core/DebugElement

vince
  • 7,808
  • 3
  • 34
  • 41
  • Thanks for the answer I will give this a try now and get back to you – saunders Jan 27 '18 at 19:21
  • Hi, i've changed my code to your suggestions and i can access .tabContent, however the querylist is empty. `QueryList {dirty: false, _results: Array(0), changes: EventEmitter}` – saunders Jan 27 '18 at 19:30
  • That's a data / timing issue. You'll need to call `fixture2.detectChanges()` or load it up correctly (however you implemented it), but that's outside the scope of the question and I'd need to see a stackblitz or more code to fix it. Maybe open a new question if you can't figure it out? Now you can access the `QueryList`, which is what your question was asking. – vince Jan 27 '18 at 19:33
  • My question was how to populate the `QueryList`. What code do you want to see? – saunders Jan 27 '18 at 19:37
  • I think you may just need to include `TabsContentComponent` to the `declarations` in your `TestBed.configureTestingModule` config. – vince Jan 27 '18 at 19:44
  • Oh miss read what you said. let me try that. Would it be easier if i add my whole spec file to my question for you? – saunders Jan 27 '18 at 19:45
  • Sure, you can do that. You also probably don't need `schemas: [CUSTOM_ELEMENTS_SCHEMA]` assuming that you're only testing Angular components. – vince Jan 27 '18 at 19:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/164013/discussion-between-vincecampanale-and-saunders). – vince Jan 27 '18 at 19:48