<|start_filename|>package.json<|end_filename|>
{
  "name": "feels",
  "author": "<NAME> <<EMAIL>>",
  "version": "3.0.0",
  "description": "Feels allow you to calculate apparent temperature using heat index, approximate wet-bulb globe temperature, humidex, australian apparent temperature and wind chill.",
  "engines": {
    "node": ">=6.0.0"
  },
  "keywords": [
    "humidex",
    "wind chill",
    "heat index",
    "wind",
    "chill",
    "heat",
    "index",
    "wbgt",
    "aat",
    "feels like",
    "real feel",
    "comfort",
    "temperature",
    "speed",
    "convert",
    "apparent",
    "humidity",
    "dew point",
    "frost point"
  ],
  "main": "./main.js",
  "files": [
    "lib",
    "main.js"
  ],
  "scripts": {
    "test": "mocha test",
    "lint": "eslint main.js lib/**",
    "check": "npm run lint && npm run test",
    "cover": "nyc ./node_modules/mocha/bin/_mocha && nyc report --reporter=html",
    "test-on-travis": "nyc ./node_modules/mocha/bin/_mocha && nyc report --reporter=lcovonly"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/strikeentco/feels.git"
  },
  "bugs": {
    "url": "https://github.com/strikeentco/feels/issues"
  },
  "devDependencies": {
    "eslint": "^4.18.1",
    "eslint-config-airbnb": "^16.1.0",
    "eslint-plugin-import": "^2.9.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-react": "^7.7.0",
    "mocha": "^5.0.1",
    "nyc": "^11.4.1",
    "should": "^13.2.1"
  },
  "license": "MIT"
}


<|start_filename|>lib/formulas.js<|end_filename|>
'use strict';

/* eslint-disable no-mixed-operators, no-restricted-properties */

module.exports.HI = (temp, humidity) =>
  16.923 + 0.185212 * temp + 5.37941 * humidity - 0.100254 * temp * humidity +
  9.41695 * Math.pow(10, -3) * Math.pow(temp, 2) + 7.28898 * Math.pow(10, -3) *
  Math.pow(humidity, 2) + 3.45372 * Math.pow(10, -4) * Math.pow(temp, 2) * humidity -
  8.14971 * Math.pow(10, -4) * temp * Math.pow(humidity, 2) + 1.02102 * Math.pow(10, -5) *
  Math.pow(temp, 2) * Math.pow(humidity, 2) - 3.8646 * Math.pow(10, -5) * Math.pow(temp, 3) +
  2.91583 * Math.pow(10, -5) * Math.pow(humidity, 3) + 1.42721 * Math.pow(10, -6) *
  Math.pow(temp, 3) * humidity + 1.97483 * Math.pow(10, -7) * temp * Math.pow(humidity, 3) -
  2.18429 * Math.pow(10, -8) * Math.pow(temp, 3) * Math.pow(humidity, 2) + 8.43296 *
  Math.pow(10, -10) * Math.pow(temp, 2) * Math.pow(humidity, 3) - 4.81975 * Math.pow(10, -11) *
  Math.pow(temp, 3) * Math.pow(humidity, 3);

module.exports.AWBGT = (temp, WVP) => 0.567 * temp + 0.393 * WVP + 3.94;

module.exports.HI_CA = (temp, WVP) => temp + 0.5555 * (WVP - 10.0);

module.exports.AAT = (temp, WVP, speed) => temp + 0.33 * WVP - 0.70 * speed - 4.00;

module.exports.WCI = (temp, speed) =>
  13.12 + 0.6215 * temp - 11.37 * Math.pow(speed, 0.16) + 0.3965 * temp * Math.pow(speed, 0.16);

module.exports.WVP = (temp, humidity) =>
  (humidity / 100) * 6.105 * Math.exp((17.27 * temp) / (237.7 + temp));

module.exports.WVPbyDP = temp =>
  6.11 * Math.exp(5417.7530 * (1 / 273.16 - 1 / (temp + 273.15)));

module.exports.ARH = (temp, dewPoint) => 100 - 5 * (temp - dewPoint);

module.exports.RH = (temp, WVP) =>
  (WVP / (6.105 * Math.exp((17.27 * temp) / (237.7 + temp)))) * 100;

module.exports.ADP = (temp, humidity) => temp - ((100 - humidity) / 5);

const getG = (temp, humidity, b, c, d) =>
  Math.log(humidity / 100 * Math.exp((b - temp / d) * (temp / (c + temp))));

module.exports.getT = (temp, humidity, b, c, d) =>
  (c * getG(temp, humidity, b, c, d)) / (b - getG(temp, humidity, b, c, d));


<|start_filename|>main.js<|end_filename|>
'use strict';

const { isCorrect, unitsFormat } = require('./lib/helpers');
const BaseFeels = require('./lib/base');

function apparentTemp(tempConvert, func) {
  if (!isCorrect(this.temp) || (!isCorrect(this.humidity) && !isCorrect(this.dewPoint))) {
    throw new Error('One of the required arguments are not specified');
  }

  const { temp } = this.units;
  const t = tempConvert(this.temp, temp, 'c');

  const index = (isCorrect(this.dewPoint) && !isCorrect(this.humidity)) ?
    func(t, tempConvert(this.dewPoint, temp, 'c'), { dewPoint: true }) :
    func(t, this.humidity);

  return tempConvert(index, 'c', this._units.temp, this.round);
}

class Feels extends BaseFeels {
  constructor(opts) {
    super();
    this.setOptions(opts);
    this._methods = {
      HI: 'heatIndex',
      AWBGT: 'AWBGT',
      HI_CA: 'humidex',
      AAT: 'AAT',
      WCI: 'windChill'
    };
  }

  setOptions(opts = {}) {
    this.units = unitsFormat(opts.units);
    this.temp = opts.temp;
    this.speed = opts.speed || 0;
    this.humidity = opts.humidity;
    this.dewPoint = opts.dewPoint;
    this.wvp = opts.wvp;
    this.round = opts.round;
    this._units = {
      temp: this.units.temp,
      speed: this.units.speed
    };
    return this;
  }

  registerMethod(method) {
    if (this[method]) {
      this._methods[method.toUpperCase()] = method;
      return this;
    }
    throw new Error(`${method} doesn't exists`);
  }

  registerMethods(methods) {
    if (Array.isArray(methods)) {
      for (const method of methods) {
        this.registerMethod(method);
      }
      return this;
    }
    throw new TypeError('Methods must be an array');
  }

  addMethod(method, func) {
    if (typeof func === 'function') {
      this[method] = func.bind(this);
      this.registerMethod(method);
      return this;
    }
    throw new TypeError(`${method} must be a function`);
  }

  toCelsius() {
    this._units.temp = 'c';
    return this;
  }

  toFahrenheit() {
    this._units.temp = 'f';
    return this;
  }

  toKelvin() {
    this._units.temp = 'k';
    return this;
  }

  like(methods = ['HI', 'HI_CA', 'AAT', 'WCI']) {
    if (typeof methods === 'string') {
      const method = this._methods[methods.toUpperCase()];
      if (method) {
        return this[method]();
      }
      throw new RangeError(`Methods must be one of: ${Object.keys(this._methods).join(', ')}`);
    }

    if (Array.isArray(methods)) {
      let like = 0;
      let count = methods.length;
      for (const m of methods) {
        const method = this._methods[m.toUpperCase()];
        if (method) {
          try {
            like += this[method]();
          } catch (e) {
            // eslint-disable-next-line no-plusplus
            count--;
          }
        } else {
          // eslint-disable-next-line max-len
          throw new RangeError(`Methods must be one of: ${Object.keys(this._methods).join(', ')}`);
        }
      }

      if (!count) {
        throw new Error('No valid methods for these values');
      }
      return Feels.tempConvert(like / count, '', '', this.round);
    }
    return this.like(['HI', 'HI_CA', 'AAT', 'WCI']);
  }

  heatIndex() { // HI
    return apparentTemp.call(this, Feels.tempConvert, Feels.heatIndex);
  }

  AWBGT() { // AWBGT
    return apparentTemp.call(this, Feels.tempConvert, Feels.AWBGT);
  }

  humidex() { // HI_CA
    return apparentTemp.call(this, Feels.tempConvert, Feels.humidex);
  }

  AAT() { // AAT
    // eslint-disable-next-line max-len
    if (!isCorrect(this.temp, this.speed) || (!isCorrect(this.humidity) && !isCorrect(this.dewPoint))) {
      throw new Error('One of the required arguments are not specified');
    }

    const { temp, speed } = this.units;
    const u = this._units.temp;
    const t = Feels.tempConvert(this.temp, temp, 'c');
    const s = Feels.speedConvert(this.speed, speed, 'mps');

    const index = (isCorrect(this.dewPoint) && !isCorrect(this.humidity)) ?
      Feels.AAT(t, s, Feels.tempConvert(this.dewPoint, temp, 'c'), { dewPoint: true }) :
      Feels.AAT(t, s, this.humidity);

    return Feels.tempConvert(index, 'c', u, this.round);
  }

  windChill() { // WCI
    if (!isCorrect(this.temp, this.speed)) {
      throw new Error('One of the required arguments are not specified');
    }
    const { temp, speed } = this.units;
    const u = this._units.temp;
    const t = Feels.tempConvert(this.temp, temp, 'c');
    const s = Feels.speedConvert(this.speed, speed, 'mps');

    return Feels.tempConvert(Feels.windChill(t, s), 'c', u, this.round);
  }

  getWaterVapourPressure() {
    if (isCorrect(this.wvp)) {
      return this.wvp;
    } else if (isCorrect(this.humidity, this.temp)) {
      const temp = Feels.tempConvert(this.temp, this.units.temp, 'c');

      return Feels.getWVP(temp, this.humidity, this.round);
    }
    throw new Error('One of the required arguments are not specified');
  }

  getWaterVapourPressureByDewPoint() {
    if (isCorrect(this.wvp)) {
      return this.wvp;
    } else if (isCorrect(this.dewPoint)) {
      const dewPoint = Feels.tempConvert(this.dewPoint, this.units.temp, 'c');

      return Feels.getWVPbyDP(dewPoint, this.round);
    }
    throw new Error('Dew point is not specified');
  }

  getAproximateRelativeHumidity() {
    if (isCorrect(this.humidity)) {
      return this.humidity;
    } else if (isCorrect(this.temp, this.dewPoint)) {
      const { temp } = this.units;
      const t = Feels.tempConvert(this.temp, temp, 'c');
      const dewPoint = Feels.tempConvert(this.dewPoint, temp, 'c');

      return Feels.getARH(t, dewPoint, this.round);
    }
    throw new Error('One of the required arguments are not specified');
  }

  getRelativeHumidity() {
    if (isCorrect(this.humidity)) {
      return this.humidity;
    } else if (isCorrect(this.temp) && (isCorrect(this.wvp) || isCorrect(this.dewPoint))) {
      const { temp } = this.units;
      const t = Feels.tempConvert(this.temp, temp, 'c');

      return (isCorrect(this.dewPoint) && !isCorrect(this.wvp)) ?
        Feels.getRH(t, Feels.tempConvert(this.dewPoint, temp, 'c'), { dewPoint: true, round: this.round }) :
        Feels.getRH(t, this.wvp, this.round);
    }
    throw new Error('One of the required arguments are not specified');
  }

  getAproximateDewPoint() {
    if (isCorrect(this.dewPoint)) {
      return this.dewPoint;
    } else if (isCorrect(this.temp, this.humidity)) {
      const temp = Feels.tempConvert(this.temp, this.units.temp, 'c');

      return Feels.tempConvert(Feels.getADP(temp, this.humidity), 'c', this._units.temp, this.round);
    }
    throw new Error('One of the required arguments are not specified');
  }

  getDewPoint() { // dew point for [-40, 50], humidity must be in (0, 100]
    if (isCorrect(this.dewPoint)) {
      return this.dewPoint;
    } else if (isCorrect(this.temp, this.humidity)) {
      const temp = Feels.tempConvert(this.temp, this.units.temp, 'c');

      return Feels.tempConvert(Feels.getDP(temp, this.humidity), 'c', this._units.temp, this.round);
    }
    throw new Error('One of the required arguments are not specified');
  }

  getFrostPoint() { // frost point for [-80, 0], humidity must be in (0, 100]
    if (!isCorrect(this.temp, this.humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    const temp = Feels.tempConvert(this.temp, this.units.temp, 'c');
    return Feels.tempConvert(Feels.getFP(temp, this.humidity), 'c', this._units.temp, this.round);
  }
}

Feels.prototype.toC = Feels.prototype.toCelsius;
Feels.prototype.toF = Feels.prototype.toFahrenheit;
Feels.prototype.toK = Feels.prototype.toKelvin;

Feels.prototype.getWVP = Feels.prototype.getWaterVapourPressure;
Feels.prototype.getWVPbyDP = Feels.prototype.getWaterVapourPressureByDewPoint;
Feels.prototype.getARH = Feels.prototype.getAproximateRelativeHumidity;
Feels.prototype.getRH = Feels.prototype.getRelativeHumidity;
Feels.prototype.getADP = Feels.prototype.getAproximateDewPoint;
Feels.prototype.getDP = Feels.prototype.getDewPoint;
Feels.prototype.getFP = Feels.prototype.getFrostPoint;

module.exports = Feels;


<|start_filename|>lib/base.js<|end_filename|>
'use strict';

/* eslint-disable no-mixed-operators */

const { isCorrect, tempConvert, speedConvert } = require('./helpers');
const {
  HI, AWBGT, HI_CA, AAT, WCI, WVP, WVPbyDP, ARH, RH, ADP, getT
} = require('./formulas');

class BaseFeels {
  static tempConvert(temp, from, to, round = false) {
    if (round) {
      if (typeof round === 'function') {
        return round(tempConvert(temp, from, to));
      }
      return Math.round(tempConvert(temp, from, to));
    }
    return tempConvert(temp, from, to);
  }

  static speedConvert(speed, from, to, round = false) {
    if (round) {
      if (typeof round === 'function') {
        return round(speedConvert(speed, from, to));
      }
      return Math.round(speedConvert(speed, from, to));
    }
    return speedConvert(speed, from, to);
  }

  static heatIndex(temp, humidity, { dewPoint, round } = {}) { // HI
    if (!isCorrect(temp, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }
    const t = BaseFeels.tempConvert(temp, 'c', 'f');

    if (t < 68) {
      throw new RangeError('Heat Index: temp must be >= (20C, 68F, 293.15K)');
    }

    if (dewPoint) {
      humidity = BaseFeels.getRH(temp, humidity, { dewPoint: true });
    } else if (humidity <= 0 || humidity > 100) {
      throw new RangeError('Heat Index: humidity must be in (0, 100]');
    }

    return BaseFeels.tempConvert(HI(t, humidity), 'f', 'c', round);
  }

  static AWBGT(temp, humidity, { dewPoint, round } = {}) { // AWBGT
    if (!isCorrect(temp, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (temp < 15) {
      throw new RangeError('AWBGT: temp must be >= (15C, 59F, 288.15K)');
    }

    if (!dewPoint && (humidity <= 0 || humidity > 100)) {
      throw new RangeError('AWBGT: humidity must be in (0, 100]');
    }

    const wvp = dewPoint ? BaseFeels.getWVPbyDP(humidity) : BaseFeels.getWVP(temp, humidity);
    return BaseFeels.tempConvert(AWBGT(temp, wvp), '', '', round);
  }

  static humidex(temp, humidity, { dewPoint, round } = {}) { // HI_CA
    if (!isCorrect(temp, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (temp <= 0) {
      throw new RangeError('Humidex: temp must be > (0C, 32F, 273.15K)');
    }

    if (!dewPoint && (humidity <= 0 || humidity > 100)) {
      throw new RangeError('Humidex: humidity must be in (0, 100]');
    }

    const wvp = dewPoint ? BaseFeels.getWVPbyDP(humidity) : BaseFeels.getWVP(temp, humidity);
    return BaseFeels.tempConvert(HI_CA(temp, wvp), '', '', round);
  }

  static AAT(temp, speed, humidity, { dewPoint, round } = {}) { // AAT
    if (!isCorrect(temp, speed, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (speed < 0) {
      throw new RangeError('AAT: wind speed must be >= 0');
    }

    if (!dewPoint && (humidity <= 0 || humidity > 100)) {
      throw new RangeError('AAT: humidity must be in (0, 100]');
    }

    const wvp = dewPoint ? BaseFeels.getWVPbyDP(humidity) : BaseFeels.getWVP(temp, humidity);
    return BaseFeels.tempConvert(AAT(temp, wvp, speed), '', '', round);
  }

  static windChill(temp, speed, { round } = {}) { // WCI
    if (!isCorrect(temp, speed)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (temp > 0) {
      throw new RangeError('Wind Chill: temp must be <= (0C, 32F, 273.15K)');
    } else if (speed < 0) {
      throw new RangeError('Wind Chill: wind speed must be >= 0');
    }

    const s = BaseFeels.speedConvert(speed, 'mps', 'kph');
    if (s >= 5) {
      return BaseFeels.tempConvert(WCI(temp, s), '', '', round);
    }
    return BaseFeels.tempConvert(temp + ((-1.59 + 0.1345 * temp) / 5) * s, '', '', round);
  }

  static getWVP(temp, humidity, { round } = {}) {
    if (!isCorrect(humidity, temp)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (humidity <= 0 || humidity > 100) {
      throw new RangeError('Water Vapour Pressure: humidity must be in (0, 100]');
    }

    return BaseFeels.tempConvert(WVP(temp, humidity), '', '', round);
  }

  static getWVPbyDP(dewPoint, { round } = {}) {
    if (!isCorrect(dewPoint)) {
      throw new Error('Dew point is not specified');
    }

    return BaseFeels.tempConvert(WVPbyDP(dewPoint), '', '', round);
  }

  static getARH(temp, dewPoint, { round } = {}) {
    if (!isCorrect(temp, dewPoint)) {
      throw new Error('One of the required arguments are not specified');
    }

    return BaseFeels.tempConvert(ARH(temp, dewPoint), '', '', round);
  }

  static getRH(temp, wvp, { dewPoint, round } = {}) {
    if (!isCorrect(temp, wvp)) {
      throw new Error('One of the required arguments are not specified');
    }

    return BaseFeels.tempConvert(RH(temp, (dewPoint) ? BaseFeels.getWVPbyDP(wvp) : wvp), '', '', round);
  }

  static getADP(temp, humidity, { round } = {}) {
    if (!isCorrect(temp, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (humidity <= 0 || humidity > 100) {
      throw new RangeError('Aproximate Dew Point: humidity must be in (0, 100]');
    }

    return BaseFeels.tempConvert(ADP(temp, humidity), '', '', round);
  }

  static getDP(temp, humidity, { round } = {}) {
    if (!isCorrect(temp, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (temp < -40 || temp > 50) {
      throw new RangeError('Dew Point: temp must be in [-40, 50]');
    } else if (humidity <= 0 || humidity > 100) {
      throw new RangeError('Dew Point: humidity must be in (0, 100]');
    }

    const b = 18.729;
    const c = 257.87;
    const d = 273.3;
    return BaseFeels.tempConvert(getT(temp, humidity, b, c, d), '', '', round);
  }

  static getFP(temp, humidity, { round } = {}) {
    if (!isCorrect(temp, humidity)) {
      throw new Error('One of the required arguments are not specified');
    }

    if (temp < -80 || temp > 0) {
      throw new RangeError('Frost Point: temp must be in [-80, 0]');
    } else if (humidity <= 0 || humidity > 100) {
      throw new RangeError('Frost Point: humidity must be in (0, 100]');
    }

    const b = 23.036;
    const c = 279.82;
    const d = 333.7;
    return BaseFeels.tempConvert(getT(temp, humidity, b, c, d), '', '', round);
  }
}

module.exports = BaseFeels;


<|start_filename|>lib/helpers.js<|end_filename|>
'use strict';

/* eslint-disable no-mixed-operators */

const isCorrect = data => !(data == null) && Number.isFinite(data);

module.exports.isCorrect = (...args) => args.every(arg => isCorrect(arg));

const tempFormat = (temp) => {
  if (temp === 'f' || temp === 'fahrenheit') {
    return 'f';
  } else if (temp === 'k' || temp === 'kelvin') {
    return 'k';
  }
  return 'c';
};

const speedFormat = (speed) => {
  if (speed === 'mph' || speed === 'mi/h') {
    return 'mph';
  } else if (['kmh', 'kph', 'kmph', 'km/h'].includes(speed)) {
    return 'kph';
  }
  return 'mps';
};

module.exports.unitsFormat = (units) => {
  if (!units) {
    return {
      temp: 'c',
      speed: 'mps'
    };
  }

  const temp = (units.temp) ? units.temp.toLowerCase() : 'c';
  const speed = (units.speed) ? units.speed.toLowerCase() : 'mps';

  return { temp: tempFormat(temp), speed: speedFormat(speed) };
};

module.exports.tempConvert = (temp, from, to) => {
  if (!isCorrect(temp)) {
    throw new TypeError('Temp must be specified and must be a number');
  }
  if (from === to) {
    return temp;
  }
  if (!(['c', 'f', 'k'].includes(from) && ['c', 'f', 'k'].includes(to))) {
    throw new RangeError('Units must be c, f or k');
  }
  if (from === 'c') {
    return (to === 'f') ?
      (temp * 1000 * (9 / 5) + 32 * 1000) / 1000 :
      (temp * 1000 + 273.15 * 1000) / 1000;
  }
  if (from === 'f') {
    return (to === 'c') ?
      ((temp - 32) * 1000 * (5 / 9)) / 1000 :
      ((temp + 459.67) * 1000 * (5 / 9)) / 1000;
  }
  return (to === 'c') ? // k
    (temp * 1000 - 273.15 * 1000) / 1000 :
    (temp * 1000 * (9 / 5) - 459.67 * 1000) / 1000;
};

module.exports.speedConvert = (speed, from, to) => {
  if (!isCorrect(speed)) {
    throw new TypeError('Speed must be specified and must be a number');
  }
  if (from === to) {
    return speed;
  }
  if (!(['mps', 'mph', 'kph'].includes(from) && ['mps', 'mph', 'kph'].includes(to))) {
    throw new RangeError('Units must be mps, mph or kph');
  }
  if (from === 'mps') {
    return (to === 'mph') ? speed / 0.44704 : speed * 3.6;
  }
  if (from === 'mph') {
    return (to === 'mps') ? speed * 0.44704 : speed * 1.609344;
  }
  return (to === 'mps') ? speed / 3.6 : speed / 1.609344; // kph
};


<|start_filename|>test/classMethods.js<|end_filename|>
'use strict';

/* eslint-disable max-len */

const should = require('should/as-function');
const Feels = require('../main');

describe('Feels() - class methods', () => {
  describe('.heatIndex()', () => {
    it('should be approximately 24.12', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).heatIndex()).be.approximately(24.12, 0.01);
    });

    it('should be equal 24', () => {
      should(new Feels({ temp: 20, dewPoint: 18, round: true }).heatIndex()).be.eql(24);
    });

    it('should be approximately 19.03', () => {
      should(new Feels({ temp: 20, humidity: 10 }).heatIndex()).be.approximately(19.03, 0.01);
    });

    it('should be equal 19', () => {
      should(new Feels({ temp: 20, humidity: 10, round: Math.floor }).heatIndex()).be.eql(19);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().heatIndex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).heatIndex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 10 }).heatIndex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18 }).heatIndex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18, humidity: 10 }).heatIndex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', dewPoint: '18' }).heatIndex()).throw('One of the required arguments are not specified');
    });

    it('should throw when temp < (20C, 68F, 293.15K)', () => {
      should(() => new Feels({ temp: 19, humidity: 10 }).heatIndex()).throw('Heat Index: temp must be >= (20C, 68F, 293.15K)');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).heatIndex()).throw('Heat Index: humidity must be in (0, 100]');
    });
  });

  describe('.AWBGT()', () => {
    it('should be approximately 23.45', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).AWBGT()).be.approximately(23.45, 0.01);
    });

    it('should be equal 23', () => {
      should(new Feels({ temp: 20, dewPoint: 18, round: true }).AWBGT()).be.eql(23);
    });

    it('should be approximately 16.19', () => {
      should(new Feels({ temp: 20, humidity: 10 }).AWBGT()).be.approximately(16.19, 0.01);
    });

    it('should be equal 16', () => {
      should(new Feels({ temp: 20, humidity: 10, round: Math.floor }).AWBGT()).be.eql(16);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().AWBGT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).AWBGT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 10 }).AWBGT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18 }).AWBGT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18, humidity: 10 }).AWBGT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', dewPoint: '18' }).AWBGT()).throw('One of the required arguments are not specified');
    });

    it('should throw when temp < (15C, 59F, 288.15K)', () => {
      should(() => new Feels({ temp: 14, humidity: 10 }).AWBGT()).throw('AWBGT: temp must be >= (15C, 59F, 288.15K)');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).AWBGT()).throw('AWBGT: humidity must be in (0, 100]');
    });
  });

  describe('.humidex()', () => {
    it('should be approximately 26', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).humidex()).be.approximately(26, 0.01);
    });

    it('should be equal 26', () => {
      should(new Feels({ temp: 20, dewPoint: 18, round: true }).humidex()).be.eql(26);
    });

    it('should be approximately 15.74', () => {
      should(new Feels({ temp: 20, humidity: 10 }).humidex()).be.approximately(15.74, 0.01);
    });

    it('should be equal 15', () => {
      should(new Feels({ temp: 20, humidity: 10, round: Math.floor }).humidex()).be.eql(15);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().humidex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).humidex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 10 }).humidex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18 }).humidex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18, humidity: 10 }).humidex()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', dewPoint: '18' }).humidex()).throw('One of the required arguments are not specified');
    });

    it('should throw when temp < (0C, 32F, 273.15K)', () => {
      should(() => new Feels({ temp: -1, humidity: 10 }).humidex()).throw('Humidex: temp must be > (0C, 32F, 273.15K)');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).humidex()).throw('Humidex: humidity must be in (0, 100]');
    });
  });

  describe('.AAT()', () => {
    it('should be approximately 22.86', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).AAT()).be.approximately(22.86, 0.01);
    });

    it('should be equal 23', () => {
      should(new Feels({ temp: 20, dewPoint: 18, round: true }).AAT()).be.eql(23);
    });

    it('should be approximately 16.76', () => {
      should(new Feels({ temp: 20, humidity: 10 }).AAT()).be.approximately(16.76, 0.01);
    });

    it('should be equal 16', () => {
      should(new Feels({ temp: 20, humidity: 10, round: Math.floor }).AAT()).be.eql(16);
    });

    it('should be approximately 13.27', () => {
      should(new Feels({ temp: 20, speed: 5, humidity: 10 }).AAT()).be.approximately(13.27, 0.01);
      should(new Feels({
        temp: 20, speed: 18, humidity: 10, units: { speed: 'kph' }
      }).AAT()).be.approximately(13.27, 0.01);
      should(new Feels({
        temp: 20, speed: 11.1847, humidity: 10, units: { speed: 'mph' }
      }).AAT()).be.approximately(13.27, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().AAT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).AAT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 10 }).AAT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18 }).AAT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ speed: 5 }).AAT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ speed: 5, dewPoint: 18, humidity: 10 }).AAT()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', dewPoint: '18' }).AAT()).throw('One of the required arguments are not specified');
    });

    it('should throw when speed < 0', () => {
      should(() => new Feels({ temp: 20, humidity: 10, speed: -10 }).AAT()).throw('AAT: wind speed must be >= 0');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).AAT()).throw('AAT: humidity must be in (0, 100]');
    });
  });

  describe('.windChill()', () => {
    it('should be approximately -33.55', () => {
      should(new Feels({ temp: -20, speed: 10 }).windChill()).be.approximately(-33.55, 0.01);
    });

    it('should be equal -34', () => {
      should(new Feels({ temp: -20, speed: 10, round: true }).windChill()).be.eql(-34);
    });

    it('should be approximately -15.54', () => {
      should(new Feels({ temp: -10, speed: 3 }).windChill()).be.approximately(-15.54, 0.01);
      should(new Feels({ temp: -10, speed: 10.8, units: { speed: 'kph' } }).windChill()).be.approximately(-15.54, 0.01);
      should(new Feels({ temp: -10, speed: 6.71081, units: { speed: 'mph' } }).windChill()).be.approximately(-15.54, 0.01);
    });

    it('should be approximately -22.57', () => {
      should(new Feels({ temp: -20, speed: 3, units: { speed: 'kph' } }).windChill()).be.approximately(-22.57, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().windChill()).throw('One of the required arguments are not specified');
      should(() => new Feels({ speed: 5 }).windChill()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '-5', speed: '18' }).windChill()).throw('One of the required arguments are not specified');
    });

    it('should throw when temp > (0C, 32F, 273.15K)', () => {
      should(() => new Feels({ temp: 1 }).windChill()).throw('Wind Chill: temp must be <= (0C, 32F, 273.15K)');
    });

    it('should throw when speed < 0', () => {
      should(() => new Feels({ temp: -5, speed: -10 }).windChill()).throw('Wind Chill: wind speed must be >= 0');
    });
  });

  describe('.getWVP()', () => {
    it('should be approximately 2.33', () => {
      should(new Feels({ temp: 20, humidity: 10 }).getWVP()).be.approximately(2.33, 0.01);
      should(new Feels({ wvp: 2.33 }).getWVP()).be.approximately(2.33, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().getWVP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).getWVP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 10 }).getWVP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', humidity: '10' }).getWVP()).throw('One of the required arguments are not specified');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).getWVP()).throw('Water Vapour Pressure: humidity must be in (0, 100]');
    });
  });

  describe('.getWVPbyDP()', () => {
    it('should be approximately 20.81', () => {
      should(new Feels({ dewPoint: 18 }).getWVPbyDP()).be.approximately(20.81, 0.01);
      should(new Feels({ wvp: 20.81 }).getWVPbyDP()).be.approximately(20.81, 0.01);
    });

    it('should throw when dew point is not specified', () => {
      should(() => new Feels().getWVPbyDP()).throw('Dew point is not specified');
    });
  });

  describe('.getARH()', () => {
    it('should be equal 10', () => {
      should(new Feels({ temp: 20, humidity: 10, dewPoint: 18 }).getARH()).be.eql(10);
    });

    it('should be equal 90', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).getARH()).be.eql(90);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().getARH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).getARH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18 }).getARH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', dewPoint: '18' }).getARH()).throw('One of the required arguments are not specified');
    });
  });

  describe('.getRH()', () => {
    it('should be equal 10', () => {
      should(new Feels({ temp: 20, humidity: 10, dewPoint: 18 }).getRH()).be.eql(10);
    });

    it('should be approximately 52.74', () => {
      should(new Feels({ temp: 20, dewPoint: 10 }).getRH()).be.approximately(52.74, 0.01);
    });

    it('should be approximately 89.23', () => {
      should(new Feels({ temp: 20, wvp: 20.81 }).getRH()).be.approximately(89.23, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().getRH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).getRH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18 }).getRH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ wvp: 20.81 }).getRH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ dewPoint: 18, wvp: 20.81 }).getRH()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', dewPoint: '18', wvp: '20.81' }).getRH()).throw('One of the required arguments are not specified');
    });
  });

  describe('.getADP()', () => {
    it('should be equal 2', () => {
      should(new Feels({ temp: 20, humidity: 10 }).getADP()).be.eql(2);
    });

    it('should be equal 18', () => {
      should(new Feels({ temp: 20, humidity: 10, dewPoint: 18 }).getADP()).be.eql(18);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().getADP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).getADP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 10 }).getADP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', humidity: '10' }).getADP()).throw('One of the required arguments are not specified');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).getADP()).throw('Aproximate Dew Point: humidity must be in (0, 100]');
    });
  });

  describe('.getDP()', () => {
    it('should be equal 18', () => {
      should(new Feels({ temp: 20, humidity: 10, dewPoint: 18 }).getDP()).be.eql(18);
    });

    it('should be approximately -12.57', () => {
      should(new Feels({ temp: 20, humidity: 10 }).getDP()).be.approximately(-12.57, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().getDP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).getDP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 18 }).getDP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', humidity: '18' }).getDP()).throw('One of the required arguments are not specified');
    });

    it('should throw when temp is out of range', () => {
      should(() => new Feels({ temp: -60, humidity: 10 }).getDP()).throw('Dew Point: temp must be in [-40, 50]');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 101 }).getDP()).throw('Dew Point: humidity must be in (0, 100]');
    });
  });

  describe('.getFP()', () => {
    it('should be approximately -42.14', () => {
      should(new Feels({ temp: -40, humidity: 80 }).getFP()).be.approximately(-42.14, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => new Feels().getFP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: 20 }).getFP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ humidity: 18 }).getFP()).throw('One of the required arguments are not specified');
      should(() => new Feels({ temp: '20', humidity: '18' }).getFP()).throw('One of the required arguments are not specified');
    });

    it('should throw when temp is out of range', () => {
      should(() => new Feels({ temp: 20, humidity: 10 }).getFP()).throw('Frost Point: temp must be in [-80, 0]');
    });

    it('should throw when humidity is out of range', () => {
      should(() => new Feels({ temp: -10, humidity: 101 }).getFP()).throw('Frost Point: humidity must be in (0, 100]');
    });
  });

  describe('.toC()', () => {
    it('should be approximately 19.03', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toC().heatIndex()).be.approximately(19.03, 0.01);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toC().heatIndex()).be.approximately(19.03, 0.01);
    });

    it('should be approximately 24.12', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toC().heatIndex()).be.approximately(24.12, 0.01);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toC().heatIndex()).be.approximately(24.12, 0.01);
    });

    it('should be approximately 16.19', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toC().AWBGT()).be.approximately(16.19, 0.01);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toC().AWBGT()).be.approximately(16.19, 0.01);
    });

    it('should be approximately 23.45', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toC().AWBGT()).be.approximately(23.45, 0.01);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toC().AWBGT()).be.approximately(23.45, 0.01);
    });

    it('should be approximately 15.74', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toC().humidex()).be.approximately(15.74, 0.01);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toC().humidex()).be.approximately(15.74, 0.01);
    });

    it('should be approximately 26', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toC().humidex()).be.approximately(26, 0.01);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toC().humidex()).be.approximately(26, 0.01);
    });

    it('should be approximately 16.76', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toC().AAT()).be.approximately(16.76, 0.01);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toC().AAT()).be.approximately(16.76, 0.01);
    });

    it('should be approximately 22.86', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toC().AAT()).be.approximately(22.86, 0.01);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toC().AAT()).be.approximately(22.86, 0.01);
    });

    it('should be approximately -33.55', () => {
      should(new Feels({ temp: -4, speed: 10, units: { temp: 'f' } }).toC().windChill()).be.approximately(-33.55, 0.01);
      should(new Feels({ temp: 253.15, speed: 10, units: { temp: 'k' } }).toC().windChill()).be.approximately(-33.55, 0.01);
    });
  });

  describe('.toF()', () => {
    it('should be approximately 66.254', () => {
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toF().heatIndex()).be.approximately(66.254, 0.02);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toF().heatIndex()).be.approximately(66.254, 0.02);
    });

    it('should be approximately 75.416', () => {
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toF().heatIndex()).be.approximately(75.416, 0.02);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toF().heatIndex()).be.approximately(75.416, 0.02);
    });

    it('should be approximately 61.142', () => {
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toF().AWBGT()).be.approximately(61.142, 0.02);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toF().AWBGT()).be.approximately(61.142, 0.02);
    });

    it('should be approximately 74.21', () => {
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toF().AWBGT()).be.approximately(74.21, 0.02);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toF().AWBGT()).be.approximately(74.21, 0.02);
    });

    it('should be approximately 60.332', () => {
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toF().humidex()).be.approximately(60.332, 0.02);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toF().humidex()).be.approximately(60.332, 0.02);
    });

    it('should be approximately 78.8', () => {
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toF().humidex()).be.approximately(78.8, 0.02);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toF().humidex()).be.approximately(78.8, 0.02);
    });

    it('should be approximately 62.168', () => {
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toF().AAT()).be.approximately(62.168, 0.02);
      should(new Feels({ temp: 293.15, humidity: 10, units: { temp: 'k' } }).toF().AAT()).be.approximately(62.168, 0.02);
    });

    it('should be approximately 73.148', () => {
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toF().AAT()).be.approximately(73.148, 0.02);
      should(new Feels({ temp: 293.15, dewPoint: 291.15, units: { temp: 'k' } }).toF().AAT()).be.approximately(73.148, 0.02);
    });

    it('should be approximately -28.39', () => {
      should(new Feels({ temp: -20, speed: 10, units: { temp: 'c' } }).toF().windChill()).be.approximately(-28.39, 0.02);
      should(new Feels({ temp: 253.15, speed: 10, units: { temp: 'k' } }).toF().windChill()).be.approximately(-28.39, 0.02);
    });
  });

  describe('.toK()', () => {
    it('should be approximately 292.18', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toK().heatIndex()).be.approximately(292.18, 0.01);
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toK().heatIndex()).be.approximately(292.18, 0.01);
    });

    it('should be approximately 297.27', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toK().heatIndex()).be.approximately(297.27, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toK().heatIndex()).be.approximately(297.27, 0.01);
    });

    it('should be approximately 289.34', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toK().AWBGT()).be.approximately(289.34, 0.01);
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toK().AWBGT()).be.approximately(289.34, 0.01);
    });

    it('should be approximately 296.6', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toK().AWBGT()).be.approximately(296.6, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toK().AWBGT()).be.approximately(296.6, 0.01);
    });

    it('should be approximately 288.89', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toK().humidex()).be.approximately(288.89, 0.01);
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toK().humidex()).be.approximately(288.89, 0.01);
    });

    it('should be approximately 299.15', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toK().humidex()).be.approximately(299.15, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toK().humidex()).be.approximately(299.15, 0.01);
    });

    it('should be approximately 289.91', () => {
      should(new Feels({ temp: 68, humidity: 10, units: { temp: 'f' } }).toK().AAT()).be.approximately(289.91, 0.01);
      should(new Feels({ temp: 20, humidity: 10, units: { temp: 'c' } }).toK().AAT()).be.approximately(289.91, 0.01);
    });

    it('should be approximately 296.01', () => {
      should(new Feels({ temp: 68, dewPoint: 64.4, units: { temp: 'f' } }).toK().AAT()).be.approximately(296.01, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18, units: { temp: 'c' } }).toK().AAT()).be.approximately(296.01, 0.01);
    });

    it('should be approximately 239.6', () => {
      should(new Feels({ temp: -4, speed: 10, units: { temp: 'f' } }).toK().windChill()).be.approximately(239.6, 0.01);
      should(new Feels({ temp: -20, speed: 10, units: { temp: 'c' } }).toK().windChill()).be.approximately(239.6, 0.01);
    });
  });

  describe('.addMethod()', () => {
    it('should be Ok', () => {
      const feels = new Feels();
      feels.addMethod('newbie', () => (feels.heatIndex() + feels.humidex()) / 2);
      feels.addMethod('newbie2', function () {
        return (this.heatIndex() + this.humidex()) / 2;
      });
      feels.setOptions({ temp: 20, dewPoint: 18 });

      should(feels.newbie()).be.eql((feels.heatIndex() + feels.humidex()) / 2);
      should(feels.newbie2()).be.eql((feels.heatIndex() + feels.humidex()) / 2);
      should(feels.newbie()).be.eql(feels.newbie2());
    });

    it('should throw when method is not a function', () => {
      should(() => new Feels().addMethod('newbie', 'newbie')).throw('newbie must be a function');
    });
  });

  describe('.registerMethods()', () => {
    it('should be Ok', () => {
      class NewFeels extends Feels {
        newbie() {
          return (this.heatIndex() + this.humidex()) / 2;
        }
        newbie2() {
          return (this.heatIndex() + this.humidex()) / 2;
        }
      }
      const feels = new Feels({ temp: 20, dewPoint: 18 });
      const newFeels = new NewFeels({ temp: 20, dewPoint: 18 });
      should(newFeels.newbie()).be.eql((feels.heatIndex() + feels.humidex()) / 2);
      should(newFeels.newbie2()).be.eql((feels.heatIndex() + feels.humidex()) / 2);
      should(newFeels.newbie()).be.eql((newFeels.heatIndex() + newFeels.humidex()) / 2);
      should(newFeels.newbie2()).be.eql((newFeels.heatIndex() + newFeels.humidex()) / 2);
      should(newFeels.newbie()).be.eql(newFeels.newbie2());
    });

    it('should throw when the methods are not an array', () => {
      class NewFeels extends Feels {
        constructor(opts) {
          super(opts);
          this.registerMethods('newbie + newbie2');
        }
        newbie() {
          return (this.heatIndex() + this.humidex()) / 2;
        }
        newbie2() {
          return (this.heatIndex() + this.humidex()) / 2;
        }
      }
      should(() => new NewFeels({ temp: 20, dewPoint: 18 }).newbie()).throw('Methods must be an array');
    });

    it('should throw when one of the methods are does not exists', () => {
      class NewFeels extends Feels {
        constructor(opts) {
          super(opts);
          this.registerMethods(['newbie', 'newbie3']);
        }
        newbie() {
          return (this.heatIndex() + this.humidex()) / 2;
        }
        newbie2() {
          return (this.heatIndex() + this.humidex()) / 2;
        }
      }
      should(() => new NewFeels({ temp: 20, dewPoint: 18 }).newbie()).throw("newbie3 doesn't exists");
    });
  });

  describe('.like()', () => {
    class NewFeels extends Feels {
      constructor(opts) {
        super(opts);
        this.registerMethods(['newbie', 'newbie2']);
      }
      newbie() {
        return (this.heatIndex() + this.humidex()) / 2;
      }
      newbie2() {
        return (this.windChill() + this.AAT()) / 2;
      }
    }

    it('should be approximately 24.12', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).like('HI')).be.approximately(24.12, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['HI'])).be.approximately(24.12, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['HI', 'WCI'])).be.approximately(24.12, 0.01);
    });

    it('should be approximately 23.45', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).like('AWBGT')).be.approximately(23.45, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['AWBGT'])).be.approximately(23.45, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['AWBGT', 'WCI'])).be.approximately(23.45, 0.01);
    });

    it('should be approximately 26', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).like('HI_CA')).be.approximately(26, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['HI_CA'])).be.approximately(26, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['HI_CA', 'WCI'])).be.approximately(26, 0.01);
    });

    it('should be approximately 22.86', () => {
      should(new Feels({ temp: 20, dewPoint: 18 }).like('AAT')).be.approximately(22.86, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like(['AAT'])).be.approximately(22.86, 0.01);
    });

    it('should be approximately -33.55', () => {
      should(new Feels({ temp: -20, speed: 10 }).like('WCI')).be.approximately(-33.55, 0.01);
      should(new Feels({ temp: -20, speed: 10 }).like(['WCI'])).be.approximately(-33.55, 0.01);
    });

    it('should be approximately 24.33', () => {
      should(new Feels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like()).be.approximately(24.33, 0.01);
      should(new Feels({ temp: 20, humidity: 89.23 }).like()).be.approximately(24.33, 0.01);
      should(new Feels({ temp: 20, dewPoint: 18 }).like()).be.approximately(24.33, 0.01);
      should(new Feels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like({})).be.approximately(24.33, 0.01);
      should(new Feels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like(['HI', 'HI_CA', 'AAT', 'WCI'])).be.approximately(24.33, 0.01);
    });

    it('should be approximately 24.02', () => {
      const feels = new Feels({ temp: 20, humidity: 89.23, dewPoint: 18 });
      const like = (((feels.heatIndex() + feels.humidex()) / 2) + feels.heatIndex() + feels.AAT()) / 3;
      should(new NewFeels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like(['HI', 'newbie', 'AAT'])).be.approximately(24.02, 0.01);
      should(new NewFeels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like(['HI', 'newbie', 'AAT', 'newbie2'])).be.approximately(24.02, 0.01);
      should(new NewFeels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like(['HI', 'newbie', 'AAT'])).be.eql(like);
      should(new NewFeels({ temp: 20, humidity: 89.23, dewPoint: 18 }).like(['HI', 'newbie', 'AAT', 'newbie2'])).be.eql(like);
    });

    it('should throw when the methods are out of range', () => {
      should(() => new Feels({ temp: 20, dewPoint: 18 }).like('something')).throw('Methods must be one of: HI, AWBGT, HI_CA, AAT, WCI');
      should(() => new Feels({ temp: 20, dewPoint: 18 }).like(['something'])).throw('Methods must be one of: HI, AWBGT, HI_CA, AAT, WCI');
      should(() => new Feels({ temp: 20, dewPoint: 18 }).like(['AAT', 'something'])).throw('Methods must be one of: HI, AWBGT, HI_CA, AAT, WCI');
    });

    it('should throw when no valid methods exists', () => {
      should(() => new Feels({ temp: 20, dewPoint: 18 }).like(['WCI'])).throw('No valid methods for these values');
    });
  });
});


<|start_filename|>test/standaloneMethods.js<|end_filename|>
'use strict';

/* eslint-disable max-len */

const should = require('should/as-function');
const Feels = require('../main');

describe('Feels - standalone methods', () => {
  describe('.heatIndex()', () => {
    it('should be approximately 24.12', () => {
      should(Feels.heatIndex(20, 18, { dewPoint: true })).be.approximately(24.12, 0.01);
    });

    it('should be equal 24', () => {
      should(Feels.heatIndex(20, 18, { dewPoint: true, round: true })).be.eql(24);
    });

    it('should be approximately 19.03', () => {
      should(Feels.heatIndex(20, 10)).be.approximately(19.03, 0.01);
    });

    it('should be equal 19', () => {
      should(Feels.heatIndex(20, 10, { round: Math.floor })).be.eql(19);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.heatIndex()).throw('One of the required arguments are not specified');
      should(() => Feels.heatIndex(20)).throw('One of the required arguments are not specified');
      should(() => Feels.heatIndex(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.heatIndex(null, 18, true)).throw('One of the required arguments are not specified');
      should(() => Feels.heatIndex(null, null, true)).throw('One of the required arguments are not specified');
      should(() => Feels.heatIndex('20', '18', true)).throw('One of the required arguments are not specified');
    });

    it('should throw when temp < (20C, 68F, 293.15K)', () => {
      should(() => Feels.heatIndex(19, 10)).throw('Heat Index: temp must be >= (20C, 68F, 293.15K)');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.heatIndex(20, 101)).throw('Heat Index: humidity must be in (0, 100]');
    });
  });

  describe('.AWBGT()', () => {
    it('should be approximately 23.45', () => {
      should(Feels.AWBGT(20, 18, { dewPoint: true })).be.approximately(23.45, 0.01);
    });

    it('should be equal 23', () => {
      should(Feels.AWBGT(20, 18, { dewPoint: true, round: true })).be.eql(23);
    });

    it('should be approximately 16.19', () => {
      should(Feels.AWBGT(20, 10)).be.approximately(16.19, 0.01);
    });

    it('should be equal 16', () => {
      should(Feels.AWBGT(20, 10, { round: Math.floor })).be.eql(16);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.AWBGT()).throw('One of the required arguments are not specified');
      should(() => Feels.AWBGT(20)).throw('One of the required arguments are not specified');
      should(() => Feels.AWBGT(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.AWBGT(null, 18, true)).throw('One of the required arguments are not specified');
      should(() => Feels.AWBGT(null, null, true)).throw('One of the required arguments are not specified');
      should(() => Feels.AWBGT('20', '18', true)).throw('One of the required arguments are not specified');
    });

    it('should throw when temp < (15C, 59F, 288.15K)', () => {
      should(() => Feels.AWBGT(14, 10)).throw('AWBGT: temp must be >= (15C, 59F, 288.15K)');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.AWBGT(20, 101)).throw('AWBGT: humidity must be in (0, 100]');
    });
  });

  describe('.humidex()', () => {
    it('should be approximately 26', () => {
      should(Feels.humidex(20, 18, { dewPoint: true })).be.approximately(26, 0.01);
    });

    it('should be equal 26', () => {
      should(Feels.humidex(20, 18, { dewPoint: true, round: true })).be.eql(26);
    });

    it('should be approximately 15.74', () => {
      should(Feels.humidex(20, 10)).be.approximately(15.74, 0.01);
    });

    it('should be equal 15', () => {
      should(Feels.humidex(20, 10, { round: Math.floor })).be.eql(15);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.humidex()).throw('One of the required arguments are not specified');
      should(() => Feels.humidex(20)).throw('One of the required arguments are not specified');
      should(() => Feels.humidex(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.humidex(null, 18, true)).throw('One of the required arguments are not specified');
      should(() => Feels.humidex(null, null, true)).throw('One of the required arguments are not specified');
      should(() => Feels.humidex('20', '18', true)).throw('One of the required arguments are not specified');
    });

    it('should throw when temp < (0C, 32F, 273.15K)', () => {
      should(() => Feels.humidex(-1, 10)).throw('Humidex: temp must be > (0C, 32F, 273.15K)');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.humidex(20, 101)).throw('Humidex: humidity must be in (0, 100]');
    });
  });

  describe('.AAT()', () => {
    it('should be approximately 22.86', () => {
      should(Feels.AAT(20, 0, 18, { dewPoint: true })).be.approximately(22.86, 0.01);
    });

    it('should be equal 23', () => {
      should(Feels.AAT(20, 0, 18, { dewPoint: true, round: true })).be.eql(23);
    });

    it('should be approximately 16.76', () => {
      should(Feels.AAT(20, 0, 10)).be.approximately(16.76, 0.01);
    });

    it('should be equal 16', () => {
      should(Feels.AAT(20, 0, 10, { round: Math.floor })).be.eql(16);
    });

    it('should be approximately 13.27', () => {
      should(Feels.AAT(20, 5, 10)).be.approximately(13.27, 0.01);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.AAT()).throw('One of the required arguments are not specified');
      should(() => Feels.AAT(20)).throw('One of the required arguments are not specified');
      should(() => Feels.AAT(null, null, 20)).throw('One of the required arguments are not specified');
      should(() => Feels.AAT(null, null, 18, true)).throw('One of the required arguments are not specified');
      should(() => Feels.AAT(null, 5)).throw('One of the required arguments are not specified');
      should(() => Feels.AAT(null, null, null, true)).throw('One of the required arguments are not specified');
      should(() => Feels.AAT('20', '0', '18', true)).throw('One of the required arguments are not specified');
    });

    it('should throw when speed < 0', () => {
      should(() => Feels.AAT(20, -10, 10)).throw('AAT: wind speed must be >= 0');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.AAT(20, 0, 101)).throw('AAT: humidity must be in (0, 100]');
    });
  });

  describe('.windChill()', () => {
    it('should be approximately -33.55', () => {
      should(Feels.windChill(-20, 10)).be.approximately(-33.55, 0.01);
    });

    it('should be equal -34', () => {
      should(Feels.windChill(-20, 10, { round: true })).be.eql(-34);
    });

    it('should be approximately -15.54', () => {
      should(Feels.windChill(-10, 3)).be.approximately(-15.54, 0.01);
    });

    it('should be equal -16', () => {
      should(Feels.windChill(-10, 3, { round: Math.floor })).be.eql(-16);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.windChill()).throw('One of the required arguments are not specified');
      should(() => Feels.windChill(-5)).throw('One of the required arguments are not specified');
      should(() => Feels.windChill(null, 5)).throw('One of the required arguments are not specified');
      should(() => Feels.windChill('-5', '18')).throw('One of the required arguments are not specified');
    });

    it('should throw when temp > (0C, 32F, 273.15K)', () => {
      should(() => Feels.windChill(1, 0)).throw('Wind Chill: temp must be <= (0C, 32F, 273.15K)');
    });

    it('should throw when speed < 0', () => {
      should(() => Feels.windChill(-5, -10)).throw('Wind Chill: wind speed must be >= 0');
    });
  });

  describe('.getWVP()', () => {
    it('should be approximately 2.33', () => {
      should(Feels.getWVP(20, 10)).be.approximately(2.33, 0.01);
    });

    it('should be equal 2.33', () => {
      should(Feels.getWVP(20, 10, { round: true })).be.eql(2);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.getWVP()).throw('One of the required arguments are not specified');
      should(() => Feels.getWVP(20)).throw('One of the required arguments are not specified');
      should(() => Feels.getWVP(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.getWVP('20', '10')).throw('One of the required arguments are not specified');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.getWVP(20, 101)).throw('Water Vapour Pressure: humidity must be in (0, 100]');
    });
  });

  describe('.getWVPbyDP()', () => {
    it('should be approximately 20.81', () => {
      should(Feels.getWVPbyDP(18)).be.approximately(20.81, 0.01);
    });

    it('should be equal 21', () => {
      should(Feels.getWVPbyDP(18, { round: true })).be.eql(21);
    });

    it('should throw when dew point is not specified', () => {
      should(() => Feels.getWVPbyDP()).throw('Dew point is not specified');
    });
  });

  describe('.getARH()', () => {
    it('should be equal 90', () => {
      should(Feels.getARH(20, 18)).be.eql(90);
    });

    it('should be equal 90', () => {
      should(Feels.getARH(20, 18, { round: true })).be.eql(90);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.getARH()).throw('One of the required arguments are not specified');
      should(() => Feels.getARH(20)).throw('One of the required arguments are not specified');
      should(() => Feels.getARH(null, 18)).throw('One of the required arguments are not specified');
      should(() => Feels.getARH('20', '18')).throw('One of the required arguments are not specified');
    });
  });

  describe('.getRH()', () => {
    it('should be approximately 52.74', () => {
      should(Feels.getRH(20, 10, { dewPoint: true })).be.approximately(52.74, 0.01);
    });

    it('should be equal 53', () => {
      should(Feels.getRH(20, 10, { dewPoint: true, round: true })).be.eql(53);
    });

    it('should be approximately 89.23', () => {
      should(Feels.getRH(20, 20.81)).be.approximately(89.23, 0.01);
    });

    it('should be equal 89', () => {
      should(Feels.getRH(20, 20.81, { round: Math.floor })).be.eql(89);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.getRH()).throw('One of the required arguments are not specified');
      should(() => Feels.getRH(20)).throw('One of the required arguments are not specified');
      should(() => Feels.getRH(null, 18, true)).throw('One of the required arguments are not specified');
      should(() => Feels.getRH(null, 20.81)).throw('One of the required arguments are not specified');
      should(() => Feels.getRH(null, null, true)).throw('One of the required arguments are not specified');
      should(() => Feels.getRH('20', '18')).throw('One of the required arguments are not specified');
    });
  });

  describe('.getADP()', () => {
    it('should be equal 2', () => {
      should(Feels.getADP(20, 10)).be.eql(2);
    });

    it('should be equal 2', () => {
      should(Feels.getADP(20, 10, { round: true })).be.eql(2);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.getADP()).throw('One of the required arguments are not specified');
      should(() => Feels.getADP(20)).throw('One of the required arguments are not specified');
      should(() => Feels.getADP(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.getADP('20', '10')).throw('One of the required arguments are not specified');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.getADP(20, 101)).throw('Aproximate Dew Point: humidity must be in (0, 100]');
    });
  });

  describe('.getDP()', () => {
    it('should be approximately -12.57', () => {
      should(Feels.getDP(20, 10)).be.approximately(-12.57, 0.01);
    });

    it('should be equal -13', () => {
      should(Feels.getDP(20, 10, { round: true })).be.eql(-13);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.getDP()).throw('One of the required arguments are not specified');
      should(() => Feels.getDP(20)).throw('One of the required arguments are not specified');
      should(() => Feels.getDP(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.getDP('20', '10')).throw('One of the required arguments are not specified');
    });

    it('should throw when temp is out of range', () => {
      should(() => Feels.getDP(-60, 10)).throw('Dew Point: temp must be in [-40, 50]');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.getDP(20, 101)).throw('Dew Point: humidity must be in (0, 100]');
    });
  });

  describe('.getFP()', () => {
    it('should be approximately -42.14', () => {
      should(Feels.getFP(-40, 80)).be.approximately(-42.14, 0.01);
    });

    it('should be equal -42', () => {
      should(Feels.getFP(-40, 80, { round: true })).be.eql(-42);
    });

    it('should throw when one of the required arguments are not specified', () => {
      should(() => Feels.getFP()).throw('One of the required arguments are not specified');
      should(() => Feels.getFP(20)).throw('One of the required arguments are not specified');
      should(() => Feels.getFP(null, 10)).throw('One of the required arguments are not specified');
      should(() => Feels.getFP('20', '10')).throw('One of the required arguments are not specified');
    });

    it('should throw when temp is out of range', () => {
      should(() => Feels.getFP(20, 10)).throw('Frost Point: temp must be in [-80, 0]');
    });

    it('should throw when humidity is out of range', () => {
      should(() => Feels.getFP(-10, 101)).throw('Frost Point: humidity must be in (0, 100]');
    });
  });

  describe('.tempConvert()', () => {
    it('should be equal 68', () => {
      should(Feels.tempConvert(68, 'f', 'f')).be.eql(68);
      should(Feels.tempConvert(68.3, 'f', 'f', true)).be.eql(68);
      should(Feels.tempConvert(20, 'c', 'f')).be.eql(68);
      should(Feels.tempConvert(293.15, 'k', 'f')).be.eql(68);
    });

    it('should be equal 293.15', () => {
      should(Feels.tempConvert(293.15, 'k', 'k')).be.eql(293.15);
      should(Feels.tempConvert(20, 'c', 'k')).be.eql(293.15);
      should(Feels.tempConvert(68, 'f', 'k')).be.approximately(293.15, 0.01);
    });

    it('should be equal 20', () => {
      should(Feels.tempConvert(20, 'c', 'c')).be.eql(20);
      should(Feels.tempConvert(68, 'f', 'c')).be.eql(20);
      should(Feels.tempConvert(293.15, 'k', 'c')).be.eql(20);
    });

    it('should throw when temp is not specified', () => {
      should(() => Feels.tempConvert()).throw('Temp must be specified and must be a number');
    });

    it('should throw when units are incorrect', () => {
      should(() => Feels.tempConvert(20, 'a', 'c')).throw('Units must be c, f or k');
      should(() => Feels.tempConvert(20, 'c', 'b')).throw('Units must be c, f or k');
    });
  });

  describe('.speedConvert()', () => {
    it('should be equal 18', () => {
      should(Feels.speedConvert(18, 'kph', 'kph')).be.eql(18);
      should(Feels.speedConvert(18.3, 'kph', 'kph', true)).be.eql(18);
      should(Feels.speedConvert(5, 'mps', 'kph')).be.eql(18);
      should(Feels.speedConvert(11.1847, 'mph', 'kph')).be.approximately(18, 0.01);
    });

    it('should be equal 11.1847', () => {
      should(Feels.speedConvert(11.1847, 'mph', 'mph')).be.approximately(11.1847, 0.01);
      should(Feels.speedConvert(5, 'mps', 'mph')).be.approximately(11.1847, 0.01);
      should(Feels.speedConvert(18, 'kph', 'mph')).be.approximately(11.1847, 0.01);
    });

    it('should be equal 5', () => {
      should(Feels.speedConvert(5, 'mps', 'mps')).be.eql(5);
      should(Feels.speedConvert(18, 'kph', 'mps')).be.eql(5);
      should(Feels.speedConvert(11.1847, 'mph', 'mps')).be.approximately(5, 0.01);
      should(Feels.speedConvert(11.1847, 'mph', 'mps', Math.floor)).be.eql(5);
    });

    it('should throw when speed is not specified', () => {
      should(() => Feels.speedConvert()).throw('Speed must be specified and must be a number');
    });

    it('should throw when units are incorrect', () => {
      should(() => Feels.speedConvert(20, 'a', 'mps')).throw('Units must be mps, mph or kph');
      should(() => Feels.speedConvert(20, 'mps', 'b')).throw('Units must be mps, mph or kph');
    });
  });
});
