angularangular-unit-test

How to access nativeElement attribute using debugeElement in angular unit test


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
      }
    )*/



  }

}

Solution

  • 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.