/*globals describe before beforeEach afterEach it assert sinon ckan jQuery */
describe('ckan.module.CustomFieldsModule()', function () {
  var CustomFieldsModule = ckan.module.registry['custom-fields'];

  before(function (done) {
    this.loadFixture('custom_fields.html', function (template) {
      this.template = template;
      done();
    });
  });

  beforeEach(function () {
    this.fixture.html(this.template);
    this.el = this.fixture.find('[data-module]');
    this.sandbox = ckan.sandbox();
    this.sandbox.body = this.fixture;
    this.module = new CustomFieldsModule(this.el, {}, this.sandbox);
  });

  afterEach(function () {
    this.module.teardown();
  });

  describe('.initialize()', function () {
    it('should bind all functions beginning with _on to the module scope', function () {
      var target = sinon.stub(jQuery, 'proxyAll');

      this.module.initialize();

      assert.called(target);
      assert.calledWith(target, this.module, /_on/);

      target.restore();
    });

    it('should listen for changes to the last "key" input', function () {
      var target = sinon.stub(this.module, '_onChange');

      this.module.initialize();
      this.module.$('input[name*=key]').change();

      assert.calledOnce(target);
    });

    it('should listen for changes to all checkboxes', function () {
      var target = sinon.stub(this.module, '_onRemove');

      this.module.initialize();
      this.module.$(':checkbox').trigger('change');

      assert.calledOnce(target);
    });

    it('should add "button" classes to the remove input', function () {
      this.module.initialize();

      assert.equal(this.module.$('.checkbox.btn').length, 1, 'each item should have the .btn class');
      assert.equal(this.module.$('.checkbox.icon-remove').length, 1, 'each item shoud have the .icon-remove class');
    });
  });

  describe('.newField(element)', function () {
    it('should append a new field to the element', function () {
      var element = document.createElement('div');
      sinon.stub(this.module, 'cloneField').returns(element);

      this.module.newField();

      assert.ok(jQuery.contains(this.module.el[0], element));
    });
  });

  describe('.cloneField(element)', function () {
    it('should clone the provided field', function () {
      var element = document.createElement('div');
      var init  = sinon.stub(jQuery.fn, 'init', jQuery.fn.init);
      var clone = sinon.stub(jQuery.fn, 'clone', jQuery.fn.clone);

      this.module.cloneField(element);

      assert.called(init);
      assert.calledWith(init, element);
      assert.called(clone);

      init.restore();
      clone.restore();
    });

    it('should return the cloned element', function () {
      var element = document.createElement('div');
      var cloned  = document.createElement('div');
      var init  = sinon.stub(jQuery.fn, 'init', jQuery.fn.init);
      var clone = sinon.stub(jQuery.fn, 'clone').returns(jQuery(cloned));

      assert.ok(this.module.cloneField(element)[0] === cloned);

      init.restore();
      clone.restore();
    });
  });

  describe('.resetField(element)', function () {
    beforeEach(function () {
      this.field = jQuery('<div><label for="field-1">Field 1</label><input name="field-1" value="value" /></div>');
    });

    it('should empty all input values', function () {
      var target = this.module.resetField(this.field);
      assert.equal(target.find(':input').val(), '');
    });

    it('should increment any integers in the input names by one', function () {
      var target = this.module.resetField(this.field);
      assert.equal(target.find(':input').attr('name'), 'field-2');
    });

    it('should increment any numbers in the label text by one', function () {
      var target = this.module.resetField(this.field);
      assert.equal(target.find('label').text(), 'Field 2');
    });

    it('should increment any numbers in the label for by one', function () {
      var target = this.module.resetField(this.field);
      assert.equal(target.find('label').attr('for'), 'field-2');
    });
  });

  describe('.disableField(field, disable)', function () {
    beforeEach(function () {
      this.target = this.module.$('.control-custom:first');
    });

    it('should add a .disable class to the element', function () {
      this.module.disableField(this.target);
      assert.isTrue(this.target.hasClass('disabled'));
    });

    it('should remove a .disable class to the element if disable is false', function () {
      this.target.addClass('disable');

      this.module.disableField(this.target, false);
      assert.isFalse(this.target.hasClass('disabled'));
    });

  });

  describe('._onChange(event)', function () {
    it('should call .newField() with the custom control', function () {
      var target = sinon.stub(this.module, 'newField');
      var field  = this.module.$('[name*=key]:last').val('test');

      this.module._onChange(jQuery.Event('change', {target: field[0]}));

      assert.called(target);
    });

    it('should not call .newField() if the target field is empty', function () {
      var target = sinon.stub(this.module, 'newField');
      var field  = this.module.$('[name*=key]:last').val('');

      this.module._onChange(jQuery.Event('change', {target: field[0]}));

      assert.notCalled(target);
    });
  });

  describe('._onRemove(event)', function () {
    it('should call .disableField() with the custom control', function () {
      var target = sinon.stub(this.module, 'disableField');
      this.module._onRemove(jQuery.Event('change', {target: this.module.$(':checkbox')[0]}));

      assert.called(target);
    });
  });
});
