1

I have an Angular (using Angular 10) component with a HTML snippet like below:

<div *ngIf="someCondition()" id="myID">
     <p>Line 1</p>
     <p>Line 2</p>
     <p>Line 3</p>
     <p>Line 4</p>
     <p>Line 5</p>
     <p>Line 6</p>
     <p>Line 7</p>
</div>

Now, while unit testing, I would like to figure out if *ngIf satisfies, the corresponding <div> will have 7 child elements.

In the spec file, I have the following :

it('should check for 7 elements', async () => {    
    spyOn(component, 'someCondition()').and.returnValue(true);
    fixture.detectChanges();
    await fixture.whenRenderingDone();

    const elements = fixture.debugElement.queryAllNodes(By.css('#myID'));
    // const elements = fixture.debugElement.queryAll(By.css('#myID'));    

    console.log('check elements.... ', elements);
  })

With the above, I can not get access to the child <p> tags or the number of the children! Also, it would be good, if I could test a specific <p> tag with expected value.

How can I achieve this?

2 Answers2

3

the first thing that I noticed it is SPY

it is not correct syntax: spyOn(component, 'someCondition()').and.returnValue(true);

should be spyOn(component, 'someCondition').and.returnValue(true); - without function call

also in general I would use variable instaed of mehtod - it is easier to manipulate

Then you can do next


const resultArray = fixture.debugElement.queryAll(By.css('p'));

expect(resultArray[0].nativeElement.textContent.trim()).toBe('Line 1');
expect(resultArray[1].nativeElement.textContent.trim()).toBe('Line 2');
...

or you can check all values via loop

    resultArray.forEach((el, i) =>
      expect(el.nativeElement.textContent.trim()).toBe("Line " + (i + 1))
    );

implementation via DIV


    const div = fixture.debugElement.query(By.css("#myID"));

    div.childNodes.forEach((el, i) =>
      expect(el.nativeNode.innerText.trim()).toBe("Line " + (i + 1))
    );

demo:

https://codesandbox.io/s/adoring-khayyam-hygtd?file=/src/app/app.component.spec.ts

enter image description here

shutsman
  • 2,357
  • 1
  • 12
  • 23
  • Thank you so much. It worked like a charm. It was my mistake to post the question with the incorrect syntax of the SPY. In my code, it does not have the function call (). – Arup Bhattacharya Sep 29 '21 at 09:55
1

Is your component using change detection ? You have to trigger change detection or override it in the testing module.

TestBed.configureTestingModule({
  ...declarations,
  ...providers
})
.overrideComponent(myComponent, {
  set: { changeDetection: ChangeDetectionStrategy.Default }
})
.compileComponents();

To access childnodes , you can do :


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

fixture.whenStable().then(
        () => {
            fixture.detectChanges();
            var elementArray = fixture.debugElement. queryAll(By.css('#myID'));
            expect(elementArray.length).toBeGreaterThan(0); 
            done();
        }
    );

To get children , you can also do this way

const items: DebugElement = fixture.debugElement.query(By.css('#myId'));  
console.log(items.query(By.css('p')).children.map(e => e.nativeElement));
console.log(Array.from(items.query(By.css('p')).nativeElement.children));
sampow
  • 31
  • 7
  • Hi @sampow thank you for your suggestion. I initially thought exactly like you. But while testing I found that ChangeDetection is not actually required and the unit tests working fine without it. Though, I have not dug deep into the reason, however apparently it seems that both query() and queryAll() return a non-static reference to the element which means it will be updated when the UI is updated. I would suggest you to take a deeper look into it and post your comments here so that it would be useful for future reference. Thanks again. – Arup Bhattacharya Sep 30 '21 at 07:57