Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test a dropdown list in Jasmine/Angularjs

I am trying to unit test a directive that makes a dropdown list using some JSON to specify the details of the list. The directive works fine, but I'm having issues while trying to unit test it.

Here's the test:

/* global inject, expect, angular */

define(function(require){
  'use strict';
  require('angular');
  require('angularMock');
  require('reporting/js/directives/app.directives');
  require('reporting/js/directives/drop.down.field.directive');

  describe("drop down field", function() {
    // debugger;
    var directive, scope;
    beforeEach(module('app.directives'));
    beforeEach(inject(function($compile, $rootScope) {
      scope = $rootScope;

      scope.dropDownResponses = {};
      scope.dropDownField = {
        "name": "Test Drop Down",
        "type": "dropdown",
        "hidden": "false",
        "defaultValue": "None",
        "values": [
          {
            "key": "1",
            "value": "FL",
            "select": "true"
          },
          {
            "key": "2",
            "value": "GA",
            "select": "false"
          },
          {
            "key": "3",
            "value": "TX",
            "select": "false"
          }
        ],
        "validation": null
      };
      directive = angular.element('<div drop-down-field="dropDownField" drop-down-responses="dropDownResponses"></div>');
      $compile(directive)(scope);
      scope.$digest();
    }));
    it("should build three dropdown choices", function() {
      expect(directive.find('option').length).toBe(4);
    });
    it('should have one dropdown', function() {
      expect(directive.find("select").length).toBe(1);
    });
    it('should update the model when a new choice is selected', function() {
      angular.element(directive.find("select")[0]).val('1');
      angular.element(directive.find("select")[0]).change();
      expect(scope.dropDownResponses[scope.dropDownField.name]).toBe("1");
    });
  });
});

Here's the directive:

define(function(require) {
  'use strict';

  var module = require('reporting/js/directives/app.directives');
  var template = require('text!reporting/templates/drop.down.field.tpl');

  module.directive('dropDownField', function () {
    return {
      restrict: 'A',
      replace: true,
      template:template,
      scope: {
        dropDownField : "=",
        dropDownResponses : "="
      }
    };
  });

  return module;
});

Here's the markup:

<div>
  {{dropDownField.name}}
  <select ng-model="dropDownResponses[dropDownField.name]" ng-options="value.key as value.value for value in dropDownField.values"></select>
</div>

The last it block is what is of concern here. When I fire the change event, the value on the model always winds up being one more that expected. For instance, the value stored in scope.dropDownResponses in this case winds up being 2.

Any ideas?

like image 284
Dane Avatar asked Mar 03 '14 20:03

Dane


People also ask

How do you write a test scenario for a dropdown list?

Verify that the drop-down list should be scrolled down by pressing the down arrow key on the keyboard. Verify that the by default selected value is shown on the dropdown or not. Verify that the dropdown should not be editable. Check the order of the values in the dropdown list same as the requirement or not.

What does detectChanges do in Angular Jasmine tests?

detectChanges() tells Angular to run change-detection. Finally! Every time it is called, it updates data bindings like ng-if, and re-renders the component based on the updated data. Calling this function will cause ngOnInit to run only the first time it is called.


1 Answers

Its coming up to this questions first birthday and I found it intriguing as to why the test is not passing.

I have come to the conclusion that the premise of the test is wrong as the test

expect(scope.dropDownResponses[scope.dropDownField.name]).toBe("1");

should be

expect(scope.dropDownResponses[scope.dropDownField.name]).toBe("2");

The reason for this is that the the value stored in scope.dropDownResponses is in fact 2 as the questioner found.

When you are selecting by val('1') you are selecting the second option in the select element

<select ng-model="dropDownResponses[dropDownField.name]" ng-options="value.key as value.value for value in dropDownField.values" class="ng-valid ng-dirty">
  <option value="0" selected="selected">FL</option>
  <option value="1">GA</option>
  <option value="2">TX</option>
</select>

which reflects the second item in the array in the spec

{
  "key": "2",
  "value": "GA",
  "select": "false"
},

You can see this in action in this jsfiddle where the console.log output

it('should update the model when a new choice is selected', function() {
  console.log(angular.element(directive.find("select")));
  console.log('selected before = ' + angular.element(directive.find("select option:selected")).text());
  angular.element(directive.find("select")[0]).val(1);
  console.log('selected after = ' + angular.element(directive.find("select option:selected")).text());
  angular.element(directive.find("select")[0]).change();
  console.log('selected Text value = ' + angular.element(directive.find("select option:selected")).text());
  expect(scope.dropDownResponses[scope.dropDownField.name]).toBe("2");
  console.log(scope.dropDownResponses[scope.dropDownField.name]);
  //console.log('selectedIndex=' + angular.element(directive.find("select")).selectIndex());
  console.log(angular.element(directive.find("select"))[0]);
});

is

selected before = 
(index):98 selected after = GA
(index):100 selected Text value = GA
(index):102 2
like image 83
Nicholas Murray Avatar answered Oct 13 '22 03:10

Nicholas Murray