mgcrea/angular-strap

View on GitHub
src/button/test/button.radio.spec.js

Summary

Maintainability
D
2 days
Test Coverage
'use strict';

describe('bs-radio', function () {

  var $compile, $q, $$rAF, scope, sandboxEl;

  beforeEach(module('ngAnimate'));
  beforeEach(module('ngAnimateMock'));
  beforeEach(module('mgcrea.ngStrap.button'));

  beforeEach(inject(function (_$rootScope_, _$compile_, _$q_, _$$rAF_) {
    scope = _$rootScope_;
    $$rAF = _$$rAF_;
    $compile = _$compile_;
    $q = _$q_;
    sandboxEl = $('<div>').attr('id', 'sandbox').appendTo('body');
  }));

  afterEach(function() {
    sandboxEl.remove();
    scope.$destroy();
  });

  // Templates

  var templates = {
    'radio-default': {
      element: '<div class="btn-group">' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="left" bs-radio>Left</label>' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="right" bs-radio>Right</label>' +
               '</div>'
    },
    'radio-boolean-values': {
      element: '<div class="btn-group">' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="true" bs-radio>True</label>' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="false" bs-radio>False</label>' +
               '</div>'
    },
    'radio-integer-values': {
      element: '<div class="btn-group">' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="1" bs-radio>One</label>' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="0" bs-radio>Zero</label>' +
               '</div>'
    },
    'radio-interpolated-values': {
      scope: {trueValue: 'yes', falseValue: 'no'},
      element: '<div class="btn-group">' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="{{ trueValue }}" bs-radio>Yes</label>' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" value="{{ falseValue }}" bs-radio>No</label>' +
               '</div>'
    },
    'radio-ng-value': {
      scope: {trueValue: 'yes', falseValue: 'no'},
      element: '<div class="btn-group">' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" ng-value="trueValue" bs-radio>Yes</label>' +
               '  <label class="btn"><input type="radio" ng-model="radio.value" ng-value="falseValue" bs-radio>No</label>' +
               '</div>'
    },
    'radio-button-markup': {
      element: '<div class="btn-group">' +
               '  <button type="button" class="btn" ng-model="radio.value" value="left" bs-radio>Left</button>' +
               '  <button type="button" class="btn" ng-model="radio.value" value="right" bs-radio>Right</button>' +
               '</div>'
    },
    'radio-div-markup': {
      element: '<div class="btn-group">' +
               '  <div class="btn" ng-model="radio.value" value="1" bs-radio>One</div>' +
               '  <div class="btn" ng-model="radio.value" value="0" bs-radio>Zero</div>' +
               '</div>'
    },
    'radio-group': {
      element: '<div class="btn-group" ng-model="radio.value" bs-radio-group>' +
               '  <label class="btn"><input type="radio" value="left">Left</label>' +
               '  <label class="btn"><input type="radio" value="right">Right</label>' +
               '</div>'
    },
    'radio-with-ngrepeat': {
      scope: {items: [{value: 'left', label: 'Left'}, {value: 'right', label: 'Right'}]},
      element: '<div class="btn-group">' +
               '  <label class="btn" ng-repeat="item in items"><input type="radio" ng-model="radio.value" value="{{ item.value }}" bs-radio>{{ item.label }}</label>' +
               '</div>'
    }
  };

  function compileDirective(template, locals) {
    template = templates[template];
    angular.extend(scope, template.scope, locals);
    var element = $(template.element).appendTo(sandboxEl);
    element = $compile(element)(scope);
    scope.$digest();
    $$rAF.flush();
    return jQuery(element[0]);
  }

  // Tests

  describe('model updates should correctly update the view', function () {

    // @note In memory radio buttons are messed up in latest PhantomJS
    // https://github.com/ariya/phantomjs/issues/12039

    it('with string model values', function () {
      var element = compileDirective('radio-default', {radio: {value: 'right'}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).not.toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeFalsy();
      expect(secondChild).toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeTruthy();
      scope.radio.value = 'left';
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeTruthy();
      expect(secondChild).not.toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeFalsy();
    });

    it('with boolean model values', function () {
      var element = compileDirective('radio-boolean-values', {radio: {value: false}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).not.toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeFalsy();
      expect(secondChild).toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeTruthy();
      scope.radio.value = true;
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeTruthy();
      expect(secondChild).not.toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeFalsy();
    });

    it('with integer model values', function () {
      var element = compileDirective('radio-integer-values', {radio: {value: 1}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeTruthy();
      expect(secondChild).not.toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeFalsy();
      scope.radio.value = 0;
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).not.toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeFalsy();
      expect(secondChild).toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeTruthy();
    });

    it('with @-interpolated model values', function () {
      var element = compileDirective('radio-interpolated-values', {radio: {value: 'no'}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).not.toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeFalsy();
      expect(secondChild).toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeTruthy();
      scope.radio.value = 'yes';
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).toHaveClass('active');
      // expect(firstChild.children('input').is(':checked')).toBeTruthy();
      expect(secondChild).not.toHaveClass('active');
      // expect(secondChild.children('input').is(':checked')).toBeFalsy();

      // Change true value
      scope.trueValue = 'completely different';
      scope.$digest();
      $$rAF.flush();

      expect(firstChild).not.toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');

      // Match radio value to new true value
      scope.radio.value = scope.trueValue;
      scope.$digest();
      $$rAF.flush();

      expect(firstChild).toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');
    });

    it('with ng-value interpolated values', function () {
      var element = compileDirective('radio-ng-value', {radio: {value: 'no'}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).not.toHaveClass('active');
      expect(secondChild).toHaveClass('active');
      scope.radio.value = 'yes';
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');

      // Change true value
      scope.trueValue = 'completely different';
      scope.$digest();
      $$rAF.flush();

      expect(firstChild).not.toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');

      // Match radio value to new true value
      scope.radio.value = scope.trueValue;
      scope.$digest();
      $$rAF.flush();

      expect(firstChild).toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');
    });

    // @info dropped direct support in 1.2+
    /*it('with $q promises', function () {
      var deferred = $q.defer();
      var element = compileDirective('radio-default', {radio: {value: deferred.promise}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).not.toHaveClass('active');
      expect(firstChild.children('input').is(':checked')).toBeFalsy();
      expect(secondChild).not.toHaveClass('active');
      expect(secondChild.children('input').is(':checked')).toBeFalsy();
      deferred.resolve('left');
      scope.$digest();
      expect(firstChild).toHaveClass('active');
      expect(firstChild.children('input').is(':checked')).toBeTruthy();
      expect(secondChild).not.toHaveClass('active');
      expect(secondChild.children('input').is(':checked')).toBeFalsy();
    });*/

    it('with alternative button.btn markup', function () {
      var element = compileDirective('radio-button-markup', {radio: {value: 'left'}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');
      scope.radio.value = 'right';
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).not.toHaveClass('active');
      expect(secondChild).toHaveClass('active');
    });

    it('with alternative div.btn markup', function () {
      var element = compileDirective('radio-div-markup', {radio: {value: 1}});
      var firstChild = element.children().eq(0), secondChild = element.children().eq(1);
      expect(firstChild).toHaveClass('active');
      expect(secondChild).not.toHaveClass('active');
      scope.radio.value = 0;
      scope.$digest();
      $$rAF.flush();
      expect(firstChild).not.toHaveClass('active');
      expect(secondChild).toHaveClass('active');
    });

    it('with ngrepeat markup', function () {
      var element = compileDirective('radio-with-ngrepeat', {radio: {value: 'right'}});
      var childInputs = element.find('input');
      expect(childInputs.eq(1).parent('label')).toHaveClass('active');
    });

  });

  describe('view updates should correctly update the model', function () {

    it('with string model values', function () {
      var element = compileDirective('radio-default', {radio: {value: 'right'}});
      var firstChild = element.children().eq(0);
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual('left');
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual('left');
    });

    it('with boolean model values', function () {
      var element = compileDirective('radio-boolean-values', {radio: {value: false}});
      var firstChild = element.children().eq(0);
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual(true);
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual(true);
    });

    it('with integer model values', function () {
      var element = compileDirective('radio-integer-values', {radio: {value: 1}});
      var secondChild = element.children().eq(1);
      secondChild.children('input').click();
      expect(scope.radio.value).toEqual(0);
      secondChild.children('input').click();
      expect(scope.radio.value).toEqual(0);
    });

    it('with @-interpolated model values', function () {
      var element = compileDirective('radio-interpolated-values', {radio: {value: 'no'}});
      var firstChild = element.children().eq(0);
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual('yes');
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual('yes');
    });

    // @info dropped direct support in 1.2+
    /*it('with $q promises', function () {
      var deferred = $q.defer();
      var element = compileDirective('radio-default', {radio: {value: deferred.promise}});
      var firstChild = element.children().eq(0);
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual('left');
      firstChild.children('input').click();
      expect(scope.radio.value).toEqual('left');
    });*/

    it('with alternative button.btn markup', function () {
      var element = compileDirective('radio-button-markup', {radio: {value: 'left'}});
      var secondChild = element.children().eq(1);
      secondChild.click();
      expect(scope.radio.value).toEqual('right');
      secondChild.click();
      expect(scope.radio.value).toEqual('right');
    });

    it('with alternative div.btn markup', function () {
      var element = compileDirective('radio-div-markup', {radio: {value: 1}});
      var secondChild = element.children().eq(1);
      if(!/phantomjs/i.test(navigator.userAgent)) {
        // Won't work on PhantomJS 1.9.1, no clue why
        secondChild.click();
        expect(scope.radio.value).toEqual(0);
        secondChild.click();
        expect(scope.radio.value).toEqual(0);
      }
    });

  });

  describe('group directive should', function () {

    it('correctly compile the markup', function () {
      var element = compileDirective('radio-group', {radio: {value: 'right'}});
      var firstChild = element.find('input').eq(0);
      expect(element.attr('data-toggle')).toBe('buttons');
      expect(firstChild.attr('ng-model')).toBe('radio.value');
      expect(firstChild.attr('bs-radio')).toBeDefined();
    });

    it('correctly link children directives', function () {
      var element = compileDirective('radio-group', {radio: {value: 'right'}});
      var secondChild = element.find('input').eq(1);
      expect(secondChild.parent('label')).toHaveClass('active');
    });

  });

});