How to access nativeElement attribute using debugeElement in angular unit test
Asked Answered
A

3

17

I want to write a simple unit test to check if button is disable when certain value is null or empty.

Below is my spec file:

test.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import 'reflect-metadata';
import { Component,
         DebugElement,
         ViewChild }
         from  "@angular/core";

import {By} from "@angular/platform-browser";
import { FormsModule } from '@angular/forms';
import { HttpClientModule, HttpClient } from '@angular/common/http';

import { ListCommentsComponent } from './list-comments.component';
import {CommentsDataService} from '../../services/comments/comments-data.service'








describe('ListCommentsComponent', () => {



  let component: ListCommentsComponent;
  let fixture: ComponentFixture<ListCommentsComponent>;
  let debugElement:DebugElement;
  let htmlElement:HTMLElement;
  let addCommentBtn:DebugElement;


  beforeEach(async(() => {
    TestBed.configureTestingModule({

      imports: [ FormsModule, HttpClientModule],

      declarations: [ ListCommentsComponent ],

      providers:[{provide: CommentsDataService}],
     // providers:[HttpClientModule, HttpClient]
    })
    .compileComponents();
  }));

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

    fixture.detectChanges();


  });



  fit('should  be disabled when comment is empty', () => {

     component.comment = '';
addCommentBtn = fixture.debugElement.query(By.css('button.addCommentBtn'));



expect(addCommentBtn.nativeElement.getAttribute('disabled')).toBeTruthy();

  });


});

Test failed:

Expected undefined to be 'disabled'.

I have searched internet to find debugElement functions to get properties of element but could not find exact solution.

This is a reference similar SO Question

UPDATE:

Below is HTML of component

HTML

       <div class="container">
<!--<h1 id="hello"></h1>-->
    <div class="row listCommentsContainer">
        <div class="col-lg-8 col-sm-8" *ngFor="let commentData of commentsData| async ; let i = index">
          <ol style="list-style: none;">
          <li class="listComments">

            <div  style="display: block">
            <div style="display:inline-block;">
              <a class="avatar">

                <img style="" src="{{commentData.profile_image}}">
              </a>
            </div>
            <a class="commentPostByUserName">
              <span class="commentPostByUserName" style="">{{commentData.first_name}}</span>
            </a>
              <div class="commentTime">{{commentData.time_stamp}}</div>
            </div>
            <div class="commentTextDisplay">{{commentData.object}}</div>

            <br>

            <!-- Reply Link -->
            <div class="addReplyContainer" >
              <a  class="addReplyLink"  (click)="showReplyTextArea($event,i )">Reply</a>
            </div>

            <!-- Add Reply -->
            <div [attr.commentId] = "commentData.target_id" *ngIf = "selectedindex == i "
                 class="addReplyContainer replyTextAreaContainer" >
              <textarea [(ngModel)]="reply" style="width:100%"
                        class="replyText commentText addReplyTextarea form-control"></textarea>
              <button [attr.commentId]="commentData.id" [disabled] = "!reply || reply.length>500" class="btn btn-success addCommentBtn" (click)="addReply(commentData.target_id)">Add</button>
            </div>
            <!-- -----Add Reply end------ -->

            <!-- List Replies -->
            <div class="replies col-lg-8 col-sm-8" *ngFor="let reply of commentData.comment">
              <ol style="list-style: none;">
                <li class="listComments listReplies">

                  <div  style="display: block">
                    <div style="display:inline-block;">
                      <a class="avatar">

                        <img style="" src="{{reply.profile_image}}">
                      </a>
                    </div>
                    <a class="commentPostByUserName">
                      <span class="commentPostByUserName" style="">{{reply.first_name}}</span>
                    </a>

                  </div>
                  <div class="commentTextDisplay replyTextDisplay">{{reply.object}}</div>
                </li>
              </ol>
            </div>
            <!-- -------list reply end-------- -->

          </li>
          </ol>
        </div>

    </div>

    <!-- Add Comment-->
    <div class="row">
      <div  class="addCommentContainer col-lg-6 col-sm-12">

          <textarea maxlength="500"
                    [(ngModel)]="comment" class="commentText form-control"
                    placeholder="Add Comment">
          </textarea>

        <button #addCommentBtn [disabled] = "!comment || comment.length>500" (click)="addComment()" class="btn addCommentBtn btn-success">Add</button>

      </div>
    </div>
  <!-- Add Comment end-->
</div>

At the end of the html there is textarea and button which I am testing to be disabled when textarea is empty.

In component class variable name is comment which should be empty for button to be disabled. This is assertion of my test.

Below is Component class:

import {Component, OnInit, ElementRef, ViewChild, Renderer2} from '@angular/core';
import { CommentsDataService} from "../../services/comments/comments-data.service";
import { Observable } from 'rxjs/Observable';
import { mergeMap } from 'rxjs/operators';
import {IComment} from "../../comments";
import {ICommentType} from "../../commentType";



declare let jQuery: any;
@Component({
  selector: 'app-list-comments',
  templateUrl: './list-comments.component.html',
  styleUrls: ['./list-comments.component.css'],
  providers:[CommentsDataService]

})
export class ListCommentsComponent implements OnInit {

  constructor(private commentsDataService:CommentsDataService, private el: ElementRef) {
  }

  commentsData :any; // Comment Data received from service.
  comment:string; // Comment text; declaration.
  reply:string;
  commentObj:any;
  commentArrayObj:any;
  public selectedindex;
  getComments;  // Declaration of function.
  saveComment;
  getUser;




  /**
   * This Function is triggered to
   * call save function and append
   * new comment.
   * Append new comment
   *
   */
  addComment()
  {

    this.comment = this.comment.trim();
    let commentObj;
    let comment = this.comment;
    commentObj ={
      "root_id":"1",
      "target_id": "1",
      "object": this.comment,
      "time_stamp":'2 secs ago',
      "profile_image":"/../../assets/images/no-user.png",
      "first_name" : "john",
    };

    //let commentObj = this.commentObj; //Put this.commentObj to push it into this.commentData

    if( typeof this.comment == "undefined" || this.comment === '' || this.comment == null ||  this.comment == '\n' || this.comment.length>500 )
    {

      return false;
    }
    else
    {
      this.commentsData.push( commentObj );
      this.comment = ''; // Empty comment Textarea.
      return comment;

    }

  }

  /**
   *
   * Function triggered when
   * reply link is clicked
   * @param event
   * @param index
   */
  showReplyTextArea(event, index);
  showReplyTextArea(event, index)
  {


    this.selectedindex = index;
    console.log( this.selectedindex);

  }

  /**
   * Append Reply to list.
   * @param event
   * @param target_id
   */
  addReply(target_id)
  {
    let commentData = this.commentsData; //creating local variable
    let reply = this.reply; //creating local variable


    if(reply == "" || reply === '/n' || reply.length <=0 ||   reply.length > 500 )
    {
      return false;
    }
    else
    {


      this.commentObj = {
        root_id:1,
        target_id: target_id,
        object: reply,
        profile_image:"/../../assets/images/no-user.png",
        actor:1,
        first_name : "john"
      };

      let  commentObj = this.commentObj;

      this.commentArrayObj =[
        {
          root_id:1,
          target_id: target_id,
          object: reply,
          actor:"user:123",
          time_stamp: "2 min ago",
          first_name : "john",
          profile_image:"/../../assets/images/no-user.png"
        }
      ];


      let commentArrayObj1 = this.commentArrayObj;

      console.log('commentObj');
      console.log(commentObj);
      jQuery.each(this.commentsData, function (index, value) {

        if(value.target_id == target_id)
        {

          if(! ('comment' in commentData[index]))
          {
            commentData[index]['comment'] = commentArrayObj1;
          }
          else
          {
            commentData[index].comment.push(commentObj);
          }


        }
      })
    }

    this.reply = ''; // Empty textarea after posting reply.

  }

  ngOnInit() {


    this.commentsData =  this.commentsDataService.getComments();
    //Service call for Comments listing.
/*    this.commentsDataService.getComments().subscribe(comments=>{
        this.commentsData = comments
      }
    )*/



  }

}
Actuate answered 19/4, 2018 at 10:5 Comment(2)
please update the question with the component you are testingAlphabetical
@TomaszKula Done.Actuate
A
32

You need to update the view after setting the component.comment = ''. Add fixture.detectChanges() call before querying for your element and making the assertions.

fit('should  be disabled when comment is empty', () => {
      component.comment = '';

      fixture.detectChanges() // <-- add this to update the view

      addCommentBtn = fixture.debugElement.query(By.css('button.addCommentBtn'));

      // this asserts that button matches <button disabled>Hello</button>    
      expect(addCommentBtn.nativeElement.getAttribute('disabled')).toEqual('');

      // this asserts that button matches <button>Hello</button>          
      expect(addCommentBtn.nativeElement.getAttribute('disabled')).toEqual(null);
    }
});

enter image description here

It's worth noting that you can inspect the DebugElement without accessing the nativeElement.

Alphabetical answered 19/4, 2018 at 10:54 Comment(7)
Tomasz, this I have already added in beforeach block. Thanks for your response. But this change has not effect. still getting Expected '' to be 'disabled'.Actuate
Adding it in the before block is too early. You must update the view after setting the comment to ''. Unless this is the default value anyway.Alphabetical
Yes, I added it but still getting error. " Expected ' ' to be 'disabled' "Actuate
If I check test on any other attribute like id, it pass.Actuate
check out my edit, it explains why you should assert that the disabled attribute matches '' or nullAlphabetical
So, basically when button is disabled it's disabled attribute will match '' empty string and when not disabled it will match with null?Actuate
Awesome, Thanks for your time and sharing knowledge. Cheers!!Actuate
R
4

here is a slight modification of the code. Try this out

expect(addCommentBtn.nativeElement.attributes).toContain['disabled'];
Repel answered 19/4, 2018 at 11:21 Comment(1)
debugElement.properties is empty in my unit test case , any solution for this ? sample html code is "<component-name [entity]='entity'> </component-name >" and in unit test const comp = fixture.debugElement.query(By.css('con-entity-form-content')); expect(comp.properties.entity).toBe(component.company); but comp.properties is {}. why?Annora
A
2

Another slight variation that worked for me

   const disabledEl = fixture.debugElement.query(By.css('.disabled-el'));
   expect(Object.keys(disabledEl.attributes)).toContain('disabled');

Unsure if this is related to my angular version, or to jasmine/karma, but it worked.

Aulos answered 1/12, 2021 at 20:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.