//product.model.js

(function () {
  'use strict';

  angular
    .module('conta')
    .factory('Product', ProductModel);

  /**
   * @ngdoc factory
   * @name ProductModel
   */
  function ProductModel() {
    /**
     * Defaults
     */
    var defaults = {
      taxRate: 15
    };

    /**
     * Product Constructor.
     *
     * @param {Object} properties
     * @constructor
     */
    function Product(properties) {
      // should set model on the product
      if (properties) {
        angular.forEach(properties, function (value, key) {
          this[key] = value;
        }, this);
      }
    }

    /**
     * Public
     */
    angular.extend(Product.prototype, {
      getPriceIncVat: getPriceIncVat,
      setTaxRate: setTaxRate,
      taxRate: defaults.taxRate
    });

    /**
     * Return constructor
     */
    return Product;

    /**
     * Get price incl. vat.
     *
     * @returns {Number}
     */
    function getPriceIncVat() {
      return Number.parseFloat((this.price + (this.price * this.taxRate / 100)).toFixed(2));
    }

    /**
     * Set taxRate
     *
     * @param {Number} newTaxRate
     * @returns {Number} newTaxRate
     */
    function setTaxRate(newTaxRate) {
      this.taxRate = Number.parseFloat((newTaxRate).toFixed(3));
      return this.taxRate;
    }
  }
}());


//product.model.spec.js

describe('Model: Product', function () {
  var product, model, Product;

  beforeEach(function () {
    module('conta');

    inject(function ($injector) {
      Product = $injector.get('Product');
    });

    model = {
      name: 'Beer',
      taxRate: 8.975,
      price: 19.99
    };

    product = new Product(model);
  });

  it('should be an instance of Product', function () {
    expect(product instanceof Product).toBeTruthy();
  });

  describe('on initialization', function () {
    it('should set default values on an empty product', function () {
      product = new Product();

      expect(product.taxRate).toBe(15);
    });

    it('should set model on the product', function () {
      expect(product.name).toBe('Beer');
      expect(product.price).toBe(19.99);
      expect(product.taxRate).toBe(8.975);
    });

    it('should override any default values', function () {
      model = {taxRate: 55};

      product = new Product(model);

      expect(product.taxRate).toBe(55);
    });

    it('should set default values on the product if they are missing', function () {
      model = {name: 'Beer'};

      product = new Product(model);

      expect(product.name).toBe(model.name);
      expect(product.taxRate).toBe(15);
    });

    describe('filed "taxRate" should be', function () {
      it('a number or higher than 0 and lower than 100', function () {
        product = new Product();
        product.setTaxRate(12.33);

        expect(product.taxRate).toEqual(jasmine.any(Number));

        expect(product.taxRate).toBeGreaterThan(0);
        expect(product.taxRate).not.toBeGreaterThan(100);

      });

      it('maximum with three decimals', function () {
        product.setTaxRate(7.12345678);
        expect(product.taxRate).toBe(7.123);
      });

    });

  });

  describe('getPriceIncVat()', function () {
    it('should return the price incl. VAT with two decimals', function () {
      expect(product.getPriceIncVat()).toBe(21.78);
    });
  });

});