<commit_before>// fake it until you make it!

(function() {
  'use strict';

  //
  function MiteGoal(options) {
    var apiKey = options.apiKey;
    var accountName;
    var mite;
    var params = {};
    var goal = 0;
    var startDate = Date.today();
    var endDate = Date.today().add(14).days();

    //
    function expect(url) {
      var matches = url.match(/(\w+)\.mite.yo.lk\/reports\/time_entries\/\?#(.*)$/);
      accountName = matches[1];
      var query = matches[2];
      query.split(/&/).forEach(function(keyAndValue) {
        keyAndValue = keyAndValue.split('=');
        var key = keyAndValue[0];
        var value = decodeURIComponent(keyAndValue[1]);

        params[key] = value;
      });

      // group_by is always "day"
      params.group_by = 'day';

      return {
        toBe: toBe
      };
    }

    //
    function toBe(hours) {
      goal = hours * 60;
      return {
        by: by,
        render: render
      };
    }

    //
    function by(dateString) {
      startDate = Date.parse('2013-09-01');
      endDate = Date.parse( dateString );
      return {
        render: render
      };
    }

    //
    function render() {
      google.load("visualization", "1", {packages:["corechart"]});
      function drawChart() {
        var table = google.visualization.arrayToDataTable(chartData);

        var options = {
          chartArea: {
            top: '30%',
            left: 0,
            width: '100%',
            height: '70%'
          },
          theme: 'maximized',
          colors: ['#eee', '#FF8215'],
          legend: { position: 'none' },
          vAxis: {
            textPosition: 'none',
            gridlines: { color: 'transparent'}
          },
          hAxis: {
            textPosition: 'none'
          },
          aggregationTarget: 'auto'
        };

        var chart = new google.visualization.AreaChart(document.getElementById('chart'));
        chart.draw(table, options);
      }
      window.onresize = drawChart

      var chartData = [['Date', 'projected', 'actual']];
      google.setOnLoadCallback(function() {
        mite = new Mite({account: accountName, api_key: apiKey})
        console.log(params)
        mite.TimeEntry.all(params, function(response) {
          var miteData = response.map( function(item) { return item.time_entry_group } );
          var today = Date.today()
          var todayString = JSON.stringify(today).substr(0,11).replace(/"/g, '')

          var oneDay = 24*60*60*1000;
          var days = Math.round(Math.abs((startDate.getTime() - endDate.getTime())/(oneDay))) + 1;
          var perDay = goal / days;

          var currentDay = startDate;
          var current = 0;
          var currentTotal = 0;
          var daysLeft;

          for (var day = 1; day <= days; day++) {
            var dayString = JSON.stringify(currentDay).substr(0,11).replace(/"/g, '')
            if (! miteData.length) {
              if (! currentTotal) {
                currentTotal = current
              }
              current = null
            } else if (dayString == miteData[0].day) {
              current += miteData.shift().minutes
            }
            if (todayString >= dayString) {
              daysLeft = days - day;
            }
            chartData.push([
              dayString,
              perDay * day,
              current
            ])
            currentDay = currentDay.add(1).day()
          };

          function hoursToString(hours) {
            var h = Math.floor(hours)
            var m = Math.floor((hours - h) * 60);
            if (m < 10) {
              m = '0' + m;
            }
            return [h,m].join(':')
          }

          var resultCurrent = hoursToString(Math.round(currentTotal / 6) / 10)
          var resultGoal = hoursToString(Math.round(goal / 6) / 10)
          var shouldBe = goal / days * (days - daysLeft);
          var resultShouldBe = hoursToString(Math.round(shouldBe / 6) / 10)
          var catchUpHours = hoursToString(Math.round((goal - shouldBe + currentTotal) / daysLeft / 6) / 10)
          var resultDaysLeft = daysLeft;

          var $summary = $('

').html(
            'You made '+resultCurrent+' ' +
            'out of your '+resultGoal+' goal. ' +
            'By plan, you should be at '+resultShouldBe+' today. ' +
            'To catch up, you have to work '+catchUpHours+' for ' +
            'the next '+resultDaysLeft+' days. '
          );
          $summary.appendTo(document.body);
          $summary.fitText();
          $('.summary').addClass('show');

          $('

').appendTo(document.body);
          drawChart()
        });
      });
    }

    return {
      expect: expect
    };
  }

  // export
  window.MiteGoal = MiteGoal;
})();
<commit_msg>moar fixes<commit_after>// fake it until you make it!

(function() {
  'use strict';

  //
  function MiteGoal(options) {
    var apiKey = options.apiKey;
    var accountName;
    var mite;
    var params = {};
    var goal = 0;
    var startDate = Date.today();
    var endDate = Date.today().add(14).days();

    //
    function expect(url) {
      var matches = url.match(/(\w+)\.mite.yo.lk\/reports\/time_entries\/?#(.*)$/);
      accountName = matches[1];
      var query = matches[2];
      query.split(/&/).forEach(function(keyAndValue) {
        keyAndValue = keyAndValue.split('=');
        var key = keyAndValue[0];
        var value = decodeURIComponent(keyAndValue[1]);

        params[key] = value;
      });

      // group_by is always "day"
      params.group_by = 'day';
      params.sort = 'date ASC';

      return {
        toBe: toBe
      };
    }

    //
    function toBe(hours) {
      goal = hours * 60;
      return {
        by: by,
        render: render
      };
    }

    //
    function by(dateString) {
      startDate = Date.parse('2013-09-01');
      endDate = Date.parse( dateString );
      return {
        render: render
      };
    }

    //
    function render() {
      google.load("visualization", "1", {packages:["corechart"]});
      function drawChart() {
        var table = google.visualization.arrayToDataTable(chartData);

        var options = {
          chartArea: {
            top: '30%',
            left: 0,
            width: '100%',
            height: '70%'
          },
          theme: 'maximized',
          colors: ['#eee', '#FF8215'],
          legend: { position: 'none' },
          vAxis: {
            textPosition: 'none',
            gridlines: { color: 'transparent'}
          },
          hAxis: {
            textPosition: 'none'
          },
          aggregationTarget: 'auto'
        };

        var chart = new google.visualization.AreaChart(document.getElementById('chart'));
        chart.draw(table, options);
      }
      window.onresize = drawChart

      var chartData = [['Date', 'projected', 'actual']];
      google.setOnLoadCallback(function() {
        mite = new Mite({account: accountName, api_key: apiKey})
        mite.TimeEntry.all(params, function(response) {
          var miteData = response.map( function(item) { return item.time_entry_group } );
          var today = Date.today()
          var todayString = JSON.stringify(today).substr(0,11).replace(/"/g, '')

          var oneDay = 24*60*60*1000;
          var days = Math.round(Math.abs((startDate.getTime() - endDate.getTime())/(oneDay))) + 1;
          var perDay = goal / days;

          var currentDay = startDate;
          var current = 0;
          var currentTotal = 0;
          var daysLeft;

          for (var day = 1; day <= days; day++) {
            var dayString = JSON.stringify(currentDay).substr(0,11).replace(/"/g, '')
            if (! miteData.length) {
              if (! currentTotal) {
                currentTotal = current
              }
              current = null
            } else if (dayString == miteData[0].day) {
              current += miteData.shift().minutes
            }
            if (todayString >= dayString) {
              daysLeft = days - day;
            }
            chartData.push([
              dayString,
              perDay * day,
              current
            ])
            currentDay = currentDay.add(1).day()
          };

          function hoursToString(hours) {
            var h = Math.floor(hours)
            var m = Math.floor((hours - h) * 60);
            if (m < 10) {
              m = '0' + m;
            }
            return [h,m].join(':')
          }

          var resultCurrent = hoursToString(Math.round(currentTotal / 6) / 10)
          var resultGoal = hoursToString(Math.round(goal / 6) / 10)
          var shouldBe = goal / days * (days - daysLeft);
          var resultShouldBe = hoursToString(Math.round(shouldBe / 6) / 10)
          var catchUpHours = hoursToString(Math.round((goal - currentTotal) / daysLeft / 6) / 10)
          var resultDaysLeft = daysLeft;

          var $summary = $('

').html(
            'You made '+resultCurrent+' ' +
            'out of your '+resultGoal+' goal. ' +
            'By plan, you should be at '+resultShouldBe+' today. ' +
            'To catch up, you have to work '+catchUpHours+' for ' +
            'the next '+resultDaysLeft+' days. '
          );
          $summary.appendTo(document.body);
          $summary.fitText();
          $('.summary').addClass('show');

          $('

').appendTo(document.body);
          drawChart()
        });
      });
    }

    return {
      expect: expect
    };
  }

  // export
  window.MiteGoal = MiteGoal;
})();
<|endoftext|>


<commit_before>var expect = require('chai').expect;
var util = require('../..');
var native = util.native;

describe('cli-util:', function() {

  // to
  it('should convert to native true', function(done) {
    expect(native.to('true')).to.eql(true);
    done();
  });
  it('should convert to native false', function(done) {
    expect(native.to('false')).to.eql(false);
    done();
  });
  it('should convert to native null', function(done) {
    expect(native.to('null')).to.eql(null);
    done();
  });
  it('should convert to native undefined', function(done) {
    expect(native.to('undefined')).to.eql(undefined);
    done();
  });
  it('should convert to native number (positive integer)', function(done) {
    expect(native.to('10')).to.eql(10);
    done();
  });
  it('should convert to native number (negative integer)', function(done) {
    expect(native.to('-10')).to.eql(-10);
    done();
  });
  it('should convert to native number (float)', function(done) {
    expect(native.to('3.14')).to.eql(3.14);
    done();
  });
  it('should convert to native array (,)', function(done) {
    expect(native.to('a,b,c', ',')).to.eql(['a', 'b', 'c']);
    done();
  });
  it('should convert to native array (,)', function(done) {
    expect(native.to('1,2,3', ',')).to.eql([1,2,3]);
    done();
  });
  it('should convert to native object (json)', function(done) {
    expect(native.to('{"arr":[1,2,3]}', null, true))
      .to.eql({arr: [1,2,3]});
    done();
  });
  it('should throw error on malformed json', function(done) {
    function fn() {
      return native.to('{arr:[1,2,3]}', null, true)
    }
    expect(fn).throws(Error);
    done();
  });
  it('should convert to string', function(done) {
    expect(native.to('value')).to.eql('value');
    done();
  });

  //from
  it('should convert to string true', function(done) {
    expect(native.from(true)).to.eql('true');
    done();
  });
  it('should convert to string false', function(done) {
    expect(native.from(false)).to.eql('false');
    done();
  });
  it('should convert to string null', function(done) {
    expect(native.from(null)).to.eql('null');
    done();
  });
  it('should convert to string undefined', function(done) {
    expect(native.from(undefined)).to.eql('undefined');
    done();
  });
  it('should convert to string number', function(done) {
    expect(native.from(10)).to.eql('10');
    done();
  });
  it('should convert to string array (,)', function(done) {
    expect(native.from(['a,b,c'], ',')).to.eql('a,b,c');
    done();
  });
  it('should convert to string object (json)', function(done) {
    expect(native.from({arr: [1,2,3]}, null, true))
      .to.eql('{"arr":[1,2,3]}');
    done();
  });
  it('should convert to string', function(done) {
    expect(native.from('value')).to.eql('value');
    done();
  });
})
<commit_msg>Update native type tests.<commit_after>var expect = require('chai').expect;
var util = require('../..');
var native = util.native;

describe('cli-util:', function() {

  // to
  it('should return non-string type', function(done) {
    expect(native.to({})).to.eql({});
    done();
  });
  it('should convert to native true', function(done) {
    expect(native.to('true')).to.eql(true);
    done();
  });
  it('should convert to native false', function(done) {
    expect(native.to('false')).to.eql(false);
    done();
  });
  it('should convert to native null', function(done) {
    expect(native.to('null')).to.eql(null);
    done();
  });
  it('should convert to native undefined', function(done) {
    expect(native.to('undefined')).to.eql(undefined);
    done();
  });
  it('should convert to native number (positive integer)', function(done) {
    expect(native.to('10')).to.eql(10);
    done();
  });
  it('should convert to native number (negative integer)', function(done) {
    expect(native.to('-10')).to.eql(-10);
    done();
  });
  it('should convert to native number (float)', function(done) {
    expect(native.to('3.14')).to.eql(3.14);
    done();
  });
  it('should convert to native array (,)', function(done) {
    expect(native.to('a,b,c', ',')).to.eql(['a', 'b', 'c']);
    done();
  });
  it('should convert to native array (,)', function(done) {
    expect(native.to('1,2,3', ',')).to.eql([1,2,3]);
    done();
  });
  it('should convert to native object (json)', function(done) {
    expect(native.to('{"arr":[1,2,3]}', null, true))
      .to.eql({arr: [1,2,3]});
    done();
  });
  it('should throw error on malformed json', function(done) {
    function fn() {
      return native.to('{arr:[1,2,3]}', null, true)
    }
    expect(fn).throws(Error);
    done();
  });
  it('should convert to string', function(done) {
    expect(native.to('value')).to.eql('value');
    done();
  });

  //from
  it('should convert to string true', function(done) {
    expect(native.from(true)).to.eql('true');
    done();
  });
  it('should convert to string false', function(done) {
    expect(native.from(false)).to.eql('false');
    done();
  });
  it('should convert to string null', function(done) {
    expect(native.from(null)).to.eql('null');
    done();
  });
  it('should convert to string undefined', function(done) {
    expect(native.from(undefined)).to.eql('undefined');
    done();
  });
  it('should convert to string number', function(done) {
    expect(native.from(10)).to.eql('10');
    done();
  });
  it('should convert to string array (,)', function(done) {
    expect(native.from(['a,b,c'], ',')).to.eql('a,b,c');
    done();
  });
  it('should convert to string object (json)', function(done) {
    expect(native.from({arr: [1,2,3]}, null, true))
      .to.eql('{"arr":[1,2,3]}');
    done();
  });
  it('should convert to string', function(done) {
    expect(native.from('value')).to.eql('value');
    done();
  });
})
<|endoftext|>


<commit_before>var ScriptTest = function() {
  Test.call(this);

  var script_dir = path.join(Gourdian.ROOT, "script");

  this.script_path_exists = function() {
    var async = this.start();

    path.exists(script_dir, function(exists) {
      assert.ok(exists);
      async.finish();
    });
  }

  this.server_script_runs_and_gets_to_repl_without_stderr = function() {
    var async = this.start()
      , data = "";

    this.spawner("./script/server.js", [],
      { "stdout": function(datum) {
          data += datum;
          if (data.length >= 6) {
            assert.equal(data.substring(0, 6), "gourd>");
            async.finish();
          }
        }
      , "stderr": function(datum) {
          async.message("received stderr: " + datum);
        }
      }
    );
  }
}

inherits(ScriptTest, Test);
module.exports = ScriptTest;
<commit_msg>fixed a bug in script unit test<commit_after>var ScriptTest = function() {
  Test.call(this);

  var script_dir = path.join(Gourdian.ROOT, "script");

  this.script_path_exists = function() {
    var async = this.start();

    path.exists(script_dir, function(exists) {
      assert.ok(exists);
      async.finish();
    });
  }

  this.server_script_runs_and_gets_to_repl_without_stderr = function() {
    var async = this.start()
      , data = "";

    this.spawner("./script/server.js", [],
      { "stdout": function(datum) {
          data += datum;
          if (data.length >= 6) {
            data += ""; /* data needs to be manipulated in order for this test to work
                           presumably because it is buffer but is coerced this way */
            assert.ok(data.indexOf("gourd>") >= 0);
            async.finish();
          }
        }
      , "stderr": function(datum) {
          async.message("received stderr: " + datum);
        }
      }
    );
  }
}

inherits(ScriptTest, Test);
module.exports = ScriptTest;
<|endoftext|>


<commit_before>QUnit.module('timing');

QUnit.test('Function#debounce(wait, leading=false)', function(assert) {
  assert.expect(4);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50);

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 0, 'Debounces the function invocation to a later time.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'Invokes the function after `wait` milliseconds.');
    done();
  }, 50);
});

QUnit.test('Function#debounce(wait, leading=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50, true);

  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the debounced function is called.');

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Debounces subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    debounced.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when debounced is called after `wait` milliseconds.');

    done();
  }, 50);
});

QUnit.test('Function#delay', function(assert) {
  assert.expect(16);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    done1();
  }).delay(50, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    done2();
  }).delay(50, args, thisArg);

  var done3 = assert.async();
  (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    done3();
  }).delay(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    done4();
  }).delay(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).delay(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  (function() {
    testVal6++;
  }).delay(50).exec(false);

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec()` with the parameter `false` does not cancel the delayed callback.');
    done6();
  }, 50);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).delay(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#every', function(assert) {
  assert.expect(19);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    this.cancel();
    done1();
  }).every(50, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  var ref2 = (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    ref2.cancel();
    done2();
  }).every(50, args, thisArg);

  var done3 = assert.async();
  var ref3 = (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    ref3.cancel();
    done3();
  }).every(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  var ref4 = (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    ref4.cancel();
    done4();
  }).every(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).every(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  var ref6 = (function() {
    testVal6++;
  }).every(50);

  ref6.exec(false);
  assert.equal(testVal6, 1, 'Calling `.exec(false)` executes the function immediately.');

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec(false)` does not cancel the delayed callback.');

    // Set to `false` to test if it will get set to `true` on the next interval
    ref6.hasExecuted = false;
  }, 50);

  setTimeout(function() {
    assert.ok(testVal6 > 2, 'Causes the function to be executed repeatedly at regularish intervals.');

    assert.strictEqual(ref6.hasExecuted, true,
      'Resets `.hasExecuted` to `true` each time the function executes.');

    ref6.cancel();
    done6();
  }, 150);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).every(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=false)', function(assert) {
  assert.expect(13);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 2, 'The function is invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Throttles calls after the wait timeout if invoked during the wait period.');

    setTimeout(function() {
      throttled.apply(context, args);

      assert.equal(callCount, 4, 'Calls immediately after a wait period when `throttled` was not called.');

      done();
    }, 150);
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50, true);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when throttled is called after `wait` milliseconds.');

    done();
  }, 50);
});
<commit_msg>test: Try to fix some timing tests<commit_after>QUnit.module('timing');

QUnit.test('Function#debounce(wait, leading=false)', function(assert) {
  assert.expect(4);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(50);

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 0, 'Debounces the function invocation to a later time.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'Invokes the function after `wait` milliseconds.');
    done();
  }, 50);
});

QUnit.test('Function#debounce(wait, leading=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var debounced = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).debounce(40, true);

  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the debounced function is called.');

  debounced.apply(context, args);
  debounced.apply(context, args);

  assert.equal(callCount, 1, 'Debounces subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    debounced.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when debounced is called after `wait` milliseconds.');

    done();
  }, 50);
});

QUnit.test('Function#delay', function(assert) {
  assert.expect(16);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    done1();
  }).delay(40, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    done2();
  }).delay(50, args, thisArg);

  var done3 = assert.async();
  (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    done3();
  }).delay(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    done4();
  }).delay(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).delay(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  (function() {
    testVal6++;
  }).delay(45).exec(false);

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec()` with the parameter `false` does not cancel the delayed callback.');
    done6();
  }, 50);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).delay(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#every', function(assert) {
  assert.expect(19);

  var args = ['a', 2, {arg: 3}];

  var done1 = assert.async();
  var ref1 = (function fn() {
    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is not specified.');

    assert.equal(this, ref1, 'Sets `this` to the returned reference object if `thisArg` is not specified.');

    assert.equal(ref1.fn, fn, '`.fn` is set to the function `.delay()` was called on.');

    assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` while the function executes.');

    this.cancel();
    done1();
  }).every(40, args);

  assert.equal(ref1.hasExecuted, false, '`.hasExecuted` is `false` before the function executes.');

  var done2 = assert.async();
  var thisArg = {};
  var ref2 = (function() {
    // Final test for ref1
    assert.equal(ref1.hasExecuted, true, '`.hasExecuted` is `true` after the function executes.');

    assert.deepEqual(Array.from(arguments), args,
      'Applies the specified arguments to the function when `thisArg` is specified.');

    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter.');

    ref2.cancel();
    done2();
  }).every(50, args, thisArg);

  var done3 = assert.async();
  var ref3 = (function() {
    assert.equal(this, thisArg, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                ' when `args` is not specified and `thisArg` is not an array.');
    ref3.cancel();
    done3();
  }).every(50, thisArg);

  var done4 = assert.async();
  var thisArgArray = [];
  var ref4 = (function() {
    assert.equal(this, thisArgArray, 'Sets `this` to the value specified by the `thisArg` parameter' +
                                     ' when `args` is `null` and `thisArg` is an array.');
    ref4.cancel();
    done4();
  }).every(50, null, thisArgArray);

  var done5 = assert.async();
  var testVal5 = 0;
  var ref5 = (function() {
    testVal5++;
  }).every(50);

  ref5.exec();
  assert.equal(testVal5, 1, 'Calling `.exec()` executes the function immediately.');
  assert.equal(ref5.hasExecuted, true,
    '`.hasExecuted` is `true` after the function is executed manually with `.exec()`.');

  setTimeout(function() {
    assert.equal(testVal5, 1,
      'Calling `.exec()` without the parameter `false` cancels the delayed callback.');
    done5();
  }, 50);

  var done6 = assert.async();
  var testVal6 = 0;
  var ref6 = (function() {
    testVal6++;
  }).every(50);

  ref6.exec(false);
  assert.equal(testVal6, 1, 'Calling `.exec(false)` executes the function immediately.');

  setTimeout(function() {
    assert.equal(testVal6, 2,
      'Calling `.exec(false)` does not cancel the delayed callback.');

    // Set to `false` to test if it will get set to `true` on the next interval
    ref6.hasExecuted = false;
  }, 50);

  setTimeout(function() {
    assert.ok(testVal6 > 2, 'Causes the function to be executed repeatedly at regularish intervals.');

    assert.strictEqual(ref6.hasExecuted, true,
      'Resets `.hasExecuted` to `true` each time the function executes.');

    ref6.cancel();
    done6();
  }, 150);

  var done7 = assert.async();
  var testVal7 = 0;
  var ref7 = (function() {
    testVal7++;
  }).every(50);

  ref7.cancel();

  setTimeout(function() {
    assert.equal(testVal7, 0, 'Calling `.cancel()` cancels the delayed callback.');

    assert.equal(ref7.hasExecuted, false, '`.hasExecuted` remains `false` when a callback is cancelled.');

    done7();
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=false)', function(assert) {
  assert.expect(13);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 2, 'The function is invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Throttles calls after the wait timeout if invoked during the wait period.');

    setTimeout(function() {
      throttled.apply(context, args);

      assert.equal(callCount, 4, 'Calls immediately after a wait period when `throttled` was not called.');

      done();
    }, 150);
  }, 50);
});

QUnit.test('Function#throttle(wait, noTrailing=true)', function(assert) {
  assert.expect(8);

  var args = ['a', 2, { arg: 3 }];
  var context = {};
  var done = assert.async();
  var callCount = 0;
  var throttled = (function() {
    callCount++;

    assert.deepEqual(Array.from(arguments), args, 'Invokes the function with the proper arguments.');

    assert.equal(this, context, 'Invokes the function with the proper context.');
  }).throttle(50, true);

  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Invokes the function immediately the first time the throttled function is called.');

  throttled.apply(context, args);
  throttled.apply(context, args);

  assert.equal(callCount, 1, 'Throttles subsequent calls made within `wait` milliseconds.');

  setTimeout(function() {
    assert.equal(callCount, 1, 'The function is not invoked on the trailing edge of the wait timeout.');

    throttled.apply(context, args);

    assert.equal(callCount, 2, 'Invokes the function when throttled is called after `wait` milliseconds.');

    done();
  }, 50);
});
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash')
    , CFFunction = require('./cf-function');

function ensureValueValid(type, x) {
    const result = type.validate(x);
    if (result) {
        throw Error(`toCloudFormationValue received invalid value: ${result}`);
    }
}

const NUMBER_TO_NAME_MAP = {
    1: 'icmp',
    4: 'ipv4',
    6: 'tcp',
    17: 'udp',

    '-1': 'all',
};

function createProtocolNumberToNameMap() {
    return NUMBER_TO_NAME_MAP;
}

function createProtocolNameToNumberMap() {
    return _.reduce(NUMBER_TO_NAME_MAP, (m, v, k) => { m[v] = parseInt(k, 10); return m; }, {});
}

const Types = {
    ensureValueValid: ensureValueValid,

    ref: function(resourceType) { //eslint-disable-line no-unused-vars
        return {
            name: 'ref',
            // TODO: test that resourceType is correct as well.
            validate: function(x) {
                if (!(x instanceof CFFunction.ref)
                    && !_.isString(x[CFFunction.REF])) {
                    return `expected CFFunction.ref or CFFunction.REF - got ${x}`;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                if (_.isString(x[CFFunction.REF])) {
                    return CFFunction.ref(x);
                }
                return x;
            },
        };
    },

    string: {
        name: 'string',
        validate: function(x) {
            if (typeof x !== 'string'
                && !_.isString(x[CFFunction.REF])
                && !(x instanceof CFFunction)) {
                return `expected string, CFFunction.REF or CFFunction - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (_.isString(x[CFFunction.REF])) {
                return CFFunction.ref(x);
            }
            return x;
        },
    },

    regex: {
        name: 'regex',
        validate: function(x) {
            if (!(x instanceof RegExp)) {
                return `expected RegExp - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x.source;
        },
    },

    boolean: {
        name: 'boolean',
        validate: function(x) {
            if (typeof x !== 'boolean') {
                return `expected boolean - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x.toString();
        },
    },

    enum: function() {
        var values = Array.prototype.slice.call(arguments);
        _.forEach(values, v => {
            if (typeof v !== 'string') {
                throw new Error('Enum values can only be strings.');
            }
        });
        return {
            name: 'enum',
            validate: function(x) {
                if (typeof x !== 'string') {
                    return `expected string - got ${x}`;
                }
                if (values.indexOf(x) === -1) {
                    return `expected one of ${values.join(',')} - got ${x}`;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                return x;
            },
        };
    },

    range: function(from, to) {
        return {
            name: 'range',
            validate: function(x) {
                if (typeof x !== 'number') {
                    return `expected number - got ${x}`;
                }
                if (!(x >= from && x <= to)) {
                    return `expected in range [${from}, ${to}] - got ${x}`;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                return x.toString();
            },
        };
    },

    number: {
        name: 'number',
        validate: function(x) {
            if (typeof x !== 'number') {
                return `expected number - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x.toString();
        },
    },

    cidr: {
        name: 'cidr',
        validate: function(x) {
            if (typeof x !== 'string') {
                return `expected string - got ${x}`;
            }

            const ok = new RegExp(
                _.map(
                    [
                        /^/,
                        /(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}/,
                        /([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/,
                        /(\/([0-9]|[1-2][0-9]|3[0-2]))/,
                        /$/,
                    ],
                    r => r.source
                ).join('')
            ).test(x);

            if (!ok) {
                return 'expected format x.x.x.x/x';
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x;
        },
    },

    protocol: {

        numberToNameMap: createProtocolNumberToNameMap(),

        nameToNumberMap: createProtocolNameToNumberMap(),

        valueAsName: function(x) {
            if (typeof x === 'number') {
                return this.numberToNameMap[x].toLowerCase();
            }
            return x.toLowerCase();
        },

        name: 'protocol',

        validate: function(x) {
            if (typeof x !== 'number' && typeof x !== 'string') {
                return `expected string or number - got ${x}`;
            }
            if (typeof x === 'number' && !(x in this.numberToNameMap)) {
                return `expected valid protocol number - got ${x}`;
            }
            if (typeof x === 'string' && !(x.toLowerCase() in this.nameToNumberMap)) {
                return `expected valid protocol name - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (typeof x === 'string') {
                x = this.nameToNumberMap[x.toLowerCase()];
            }
            return x.toString();
        },
    },

    tags: {

        validOptions: {
            'PropagateAtLaunch': 'boolean',
        },

        getValueAndOptions: function(valueOrOptions) {
            if (typeof valueOrOptions === 'object' &&
                !_.isString(valueOrOptions[CFFunction.REF]) &&
                !(valueOrOptions instanceof CFFunction)) {
                return {
                    value: valueOrOptions.Value,
                    options: _.omit(valueOrOptions, 'Value'),
                };
            }

            return {
                value: valueOrOptions,
                options: {},
            };
        },

        name: 'tags',

        validate: function(x) {
            if (typeof x !== 'object') {
                return `expected object - got ${x}`;
            }

            try {
                _.forEach(x, tagValue => {
                    var tagData = this.getValueAndOptions(tagValue);

                    // The value should match types.string
                    var result = Types.string.validate(tagData.value);
                    if (!_.isUndefined(result)) {
                        throw result;
                    }

                    _.forEach(tagData.options, (optionValue, optionName) => {
                        if (!(optionName in this.validOptions)) {
                            throw optionName + ' invalid';
                        }
                        const expectedType = this.validOptions[optionName];
                        if (typeof optionValue !== expectedType) {
                            throw `expected ${optionName} to be type ${expectedType} - got ${optionValue}`;
                        }
                    });
                });
            } catch (e) {
                return e;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return _.map(x, (tagValue, tagName) => {
                var tagData = this.getValueAndOptions(tagValue);
                return _.extend({
                    Key: tagName,
                    Value: Types.string.toCloudFormationValue(tagData.value),
                }, _.reduce(tagData.options, (m, v, k) => {m[k] = v.toString(); return m; }, {}));
            });
        },
    },

    // TODO: Make types.portrange() without arguments default to full range?
    portrange: {
        name: 'portrange',
        validate: function(x) {
            if (!_.isArray(x)) {
                return `expected array - got ${x}`;
            }

            if (x.length !== 2) {
                return `expected array length 2 - got ${x.length}`;
            }

            if (x[0] > x[1]) {
                return 'expected a[0] <= a[1]';
            }

            if (!_.every(x, p => _.isNumber(p) && p > 0 && p <= 65535)) {
                return `expected ${x} in range [1, 65535]`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return {
                From: x[0].toString(),
                To: x[1].toString(),
            };
        },
    },

    jsonobject: {
        name: 'jsonobject',
        validate: function(x) {
            if (typeof x !== 'object' && typeof x !== 'string') {
                return `expected object or string - got ${typeof x}`;
            }
            if (typeof x === 'string') {
                try {
                    JSON.parse(x);
                } catch (e) {
                    return 'failed to parse string: ' + e;
                }
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (typeof x === 'string') {
                return JSON.parse(x);
            }
            return x;
        },
    },

    jsonkeyvalues: {
        name: 'jsonkeyvalues',
        validate: function(x) {
            var result = Types.jsonobject.validate(x);
            if (!_.isUndefined(result)) {
                return result;
            }

            // Now ensure json object is only a subset of JSON.
            var object = Types.jsonobject.toCloudFormationValue(x);
            try {
                _.forEach(object, (value, key) => {
                    if (!_.includes(['number', 'string', 'boolean'], typeof value)) {
                        throw `expected ${key} to be string, number or boolean - got ${typeof value}`;
                    }
                });
            } catch (e) {
                return e;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (typeof x === 'string') {
                return JSON.parse(x);
            }
            return x;
        },
    },

    emptymap: {
        name: 'emptymap',
        validate: function(x) {
            if (!_.isObject(x) || _.isArray(x)) {
                return `expected non array object - got ${x}`;
            }
            if (!_.isEmpty(x)) {
                return `expected empty object - got ${x}`;
            }
        },
        toCloudFormationValue(x) {
            Types.ensureValueValid(this, x);
            return {};
        },
    },

    object: function(typeName, propertyDefinitions) {
        if (typeof typeName !== 'string') {
            throw new Error('Object must have typeName.');
        }

        _.forEach(propertyDefinitions, p => {
            if (!p.type) {
                throw new Error(`Properties for type object "${typeName}" missing type definition.`);
            }
        });

        // TODO: Make sure 'required' is only either true or 'conditional'
        const mandatoryPropertyNames = _.reduce(propertyDefinitions, (m, v, k) => {
            if (v.required === true) {
                m.push(k);
            }
            return m;
        }, []);

        return {
            propertyDefinitions: propertyDefinitions,
            name: typeName,
            validate: function(x, parentType) {
                // If validate is not being called recursively, we want to prefix the error
                // message with the typeName
                const errorPrefix = parentType
                    ? ''
                    : `in ${typeName} `;

                var errors = [];
                if (typeof x !== 'object') {
                    return `${errorPrefix}expected object - got ${x}`;
                }

                if (!propertyDefinitions) {
                    return;
                }

                // If a property is present but set to undefined, don't count it as present.
                const propertyNames = _.keys(_.omitBy(x, _.isUndefined));
                const diffKeys = _.difference(propertyNames, _.keys(propertyDefinitions));
                if (diffKeys.length > 0) {
                    errors.push(`${errorPrefix}unexpected propert${diffKeys.length > 1 ? 'ies' : 'y'} ${diffKeys.join(', ')}`);
                }

                const diffMandatoryKeys = _.difference(mandatoryPropertyNames, propertyNames);
                if (diffMandatoryKeys.length > 0) {
                    errors.push([
                        `${errorPrefix}missing mandatory propert${diffMandatoryKeys.length > 1 ? 'ies' : 'y'} `,
                        `${diffMandatoryKeys.join(', ')}`,
                    ].join(''));
                }

                _.forEach(x, (propertyValue, propertyName) => {
                    const propertyDefinition = propertyDefinitions[propertyName];
                    if (!propertyDefinition) {
                        return;
                    }
                    var typeValidationResult = propertyDefinition.type.validate(propertyValue, this);
                    if (!_.isUndefined(typeValidationResult)) {
                        typeValidationResult = typeValidationResult instanceof Array
                            ? typeValidationResult
                            : [typeValidationResult];
                        errors = errors.concat(_.map(typeValidationResult, result => {
                            return `in ${typeName}.${propertyName} ${result}`;
                        }));
                    }

                    const validators = typeof propertyDefinition.validator === 'function'
                        ? [propertyDefinition.validator]
                        : propertyDefinition.validator instanceof Array
                            ? propertyDefinition.validator
                            : [];

                    _.forEach(validators, validator => {
                        var validationResult = validator(propertyValue);
                        if (!_.isUndefined(validationResult)) {
                            errors.push(`in ${typeName}.${propertyName} validation failed with "${validationResult}"`);
                        }
                    });
                });

                if (!_.isEmpty(errors)) {
                    return errors;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                if (!propertyDefinitions) {
                    return x;
                }
                return _.mapValues(x, (value, key) => {
                    var type = propertyDefinitions[key].type;
                    return type.toCloudFormationValue(value);
                });
            },
        };
    },

    array: function(type) {
        return {
            name: 'array',
            validate: function(x) {
                var errors = [];
                if (!_.isArray(x)) {
                    return `expected array - got ${x}`;
                }
                _.forEach(x, (value, i) => {
                    const result = type.validate(value);
                    if (!_.isUndefined(result)) {
                        errors.push(`in [${i}] ${result}`);
                    }
                });

                if (!_.isEmpty(errors)) {
                    return errors;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                return _.map(x, v => {
                    return type.toCloudFormationValue(v);
                });
            },
        };
    },

    // TODO: make an arn type
};

module.exports = Types;
<commit_msg>move ref type for consistency with tests<commit_after>'use strict';

var _ = require('lodash')
    , CFFunction = require('./cf-function');

function ensureValueValid(type, x) {
    const result = type.validate(x);
    if (result) {
        throw Error(`toCloudFormationValue received invalid value: ${result}`);
    }
}

const NUMBER_TO_NAME_MAP = {
    1: 'icmp',
    4: 'ipv4',
    6: 'tcp',
    17: 'udp',

    '-1': 'all',
};

function createProtocolNumberToNameMap() {
    return NUMBER_TO_NAME_MAP;
}

function createProtocolNameToNumberMap() {
    return _.reduce(NUMBER_TO_NAME_MAP, (m, v, k) => { m[v] = parseInt(k, 10); return m; }, {});
}

const Types = {
    ensureValueValid: ensureValueValid,

    string: {
        name: 'string',
        validate: function(x) {
            if (typeof x !== 'string'
                && !_.isString(x[CFFunction.REF])
                && !(x instanceof CFFunction)) {
                return `expected string, CFFunction.REF or CFFunction - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (_.isString(x[CFFunction.REF])) {
                return CFFunction.ref(x);
            }
            return x;
        },
    },

    regex: {
        name: 'regex',
        validate: function(x) {
            if (!(x instanceof RegExp)) {
                return `expected RegExp - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x.source;
        },
    },

    boolean: {
        name: 'boolean',
        validate: function(x) {
            if (typeof x !== 'boolean') {
                return `expected boolean - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x.toString();
        },
    },

    enum: function() {
        var values = Array.prototype.slice.call(arguments);
        _.forEach(values, v => {
            if (typeof v !== 'string') {
                throw new Error('Enum values can only be strings.');
            }
        });
        return {
            name: 'enum',
            validate: function(x) {
                if (typeof x !== 'string') {
                    return `expected string - got ${x}`;
                }
                if (values.indexOf(x) === -1) {
                    return `expected one of ${values.join(',')} - got ${x}`;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                return x;
            },
        };
    },

    range: function(from, to) {
        return {
            name: 'range',
            validate: function(x) {
                if (typeof x !== 'number') {
                    return `expected number - got ${x}`;
                }
                if (!(x >= from && x <= to)) {
                    return `expected in range [${from}, ${to}] - got ${x}`;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                return x.toString();
            },
        };
    },

    number: {
        name: 'number',
        validate: function(x) {
            if (typeof x !== 'number') {
                return `expected number - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x.toString();
        },
    },

    cidr: {
        name: 'cidr',
        validate: function(x) {
            if (typeof x !== 'string') {
                return `expected string - got ${x}`;
            }

            const ok = new RegExp(
                _.map(
                    [
                        /^/,
                        /(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}/,
                        /([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/,
                        /(\/([0-9]|[1-2][0-9]|3[0-2]))/,
                        /$/,
                    ],
                    r => r.source
                ).join('')
            ).test(x);

            if (!ok) {
                return 'expected format x.x.x.x/x';
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return x;
        },
    },

    protocol: {

        numberToNameMap: createProtocolNumberToNameMap(),

        nameToNumberMap: createProtocolNameToNumberMap(),

        valueAsName: function(x) {
            if (typeof x === 'number') {
                return this.numberToNameMap[x].toLowerCase();
            }
            return x.toLowerCase();
        },

        name: 'protocol',

        validate: function(x) {
            if (typeof x !== 'number' && typeof x !== 'string') {
                return `expected string or number - got ${x}`;
            }
            if (typeof x === 'number' && !(x in this.numberToNameMap)) {
                return `expected valid protocol number - got ${x}`;
            }
            if (typeof x === 'string' && !(x.toLowerCase() in this.nameToNumberMap)) {
                return `expected valid protocol name - got ${x}`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (typeof x === 'string') {
                x = this.nameToNumberMap[x.toLowerCase()];
            }
            return x.toString();
        },
    },

    tags: {

        validOptions: {
            'PropagateAtLaunch': 'boolean',
        },

        getValueAndOptions: function(valueOrOptions) {
            if (typeof valueOrOptions === 'object' &&
                !_.isString(valueOrOptions[CFFunction.REF]) &&
                !(valueOrOptions instanceof CFFunction)) {
                return {
                    value: valueOrOptions.Value,
                    options: _.omit(valueOrOptions, 'Value'),
                };
            }

            return {
                value: valueOrOptions,
                options: {},
            };
        },

        name: 'tags',

        validate: function(x) {
            if (typeof x !== 'object') {
                return `expected object - got ${x}`;
            }

            try {
                _.forEach(x, tagValue => {
                    var tagData = this.getValueAndOptions(tagValue);

                    // The value should match types.string
                    var result = Types.string.validate(tagData.value);
                    if (!_.isUndefined(result)) {
                        throw result;
                    }

                    _.forEach(tagData.options, (optionValue, optionName) => {
                        if (!(optionName in this.validOptions)) {
                            throw optionName + ' invalid';
                        }
                        const expectedType = this.validOptions[optionName];
                        if (typeof optionValue !== expectedType) {
                            throw `expected ${optionName} to be type ${expectedType} - got ${optionValue}`;
                        }
                    });
                });
            } catch (e) {
                return e;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return _.map(x, (tagValue, tagName) => {
                var tagData = this.getValueAndOptions(tagValue);
                return _.extend({
                    Key: tagName,
                    Value: Types.string.toCloudFormationValue(tagData.value),
                }, _.reduce(tagData.options, (m, v, k) => {m[k] = v.toString(); return m; }, {}));
            });
        },
    },

    // TODO: Make types.portrange() without arguments default to full range?
    portrange: {
        name: 'portrange',
        validate: function(x) {
            if (!_.isArray(x)) {
                return `expected array - got ${x}`;
            }

            if (x.length !== 2) {
                return `expected array length 2 - got ${x.length}`;
            }

            if (x[0] > x[1]) {
                return 'expected a[0] <= a[1]';
            }

            if (!_.every(x, p => _.isNumber(p) && p > 0 && p <= 65535)) {
                return `expected ${x} in range [1, 65535]`;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            return {
                From: x[0].toString(),
                To: x[1].toString(),
            };
        },
    },

    jsonobject: {
        name: 'jsonobject',
        validate: function(x) {
            if (typeof x !== 'object' && typeof x !== 'string') {
                return `expected object or string - got ${typeof x}`;
            }
            if (typeof x === 'string') {
                try {
                    JSON.parse(x);
                } catch (e) {
                    return 'failed to parse string: ' + e;
                }
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (typeof x === 'string') {
                return JSON.parse(x);
            }
            return x;
        },
    },

    jsonkeyvalues: {
        name: 'jsonkeyvalues',
        validate: function(x) {
            var result = Types.jsonobject.validate(x);
            if (!_.isUndefined(result)) {
                return result;
            }

            // Now ensure json object is only a subset of JSON.
            var object = Types.jsonobject.toCloudFormationValue(x);
            try {
                _.forEach(object, (value, key) => {
                    if (!_.includes(['number', 'string', 'boolean'], typeof value)) {
                        throw `expected ${key} to be string, number or boolean - got ${typeof value}`;
                    }
                });
            } catch (e) {
                return e;
            }
        },
        toCloudFormationValue: function(x) {
            Types.ensureValueValid(this, x);
            if (typeof x === 'string') {
                return JSON.parse(x);
            }
            return x;
        },
    },

    emptymap: {
        name: 'emptymap',
        validate: function(x) {
            if (!_.isObject(x) || _.isArray(x)) {
                return `expected non array object - got ${x}`;
            }
            if (!_.isEmpty(x)) {
                return `expected empty object - got ${x}`;
            }
        },
        toCloudFormationValue(x) {
            Types.ensureValueValid(this, x);
            return {};
        },
    },

    ref: function(resourceType) { //eslint-disable-line no-unused-vars
        return {
            name: 'ref',
            // TODO: test that resourceType is correct as well.
            validate: function(x) {
                if (!(x instanceof CFFunction.ref)
                    && !_.isString(x[CFFunction.REF])) {
                    return `expected CFFunction.ref or CFFunction.REF - got ${x}`;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                if (_.isString(x[CFFunction.REF])) {
                    return CFFunction.ref(x);
                }
                return x;
            },
        };
    },

    object: function(typeName, propertyDefinitions) {
        if (typeof typeName !== 'string') {
            throw new Error('Object must have typeName.');
        }

        _.forEach(propertyDefinitions, p => {
            if (!p.type) {
                throw new Error(`Properties for type object "${typeName}" missing type definition.`);
            }
        });

        // TODO: Make sure 'required' is only either true or 'conditional'
        const mandatoryPropertyNames = _.reduce(propertyDefinitions, (m, v, k) => {
            if (v.required === true) {
                m.push(k);
            }
            return m;
        }, []);

        return {
            propertyDefinitions: propertyDefinitions,
            name: typeName,
            validate: function(x, parentType) {
                // If validate is not being called recursively, we want to prefix the error
                // message with the typeName
                const errorPrefix = parentType
                    ? ''
                    : `in ${typeName} `;

                var errors = [];
                if (typeof x !== 'object') {
                    return `${errorPrefix}expected object - got ${x}`;
                }

                if (!propertyDefinitions) {
                    return;
                }

                // If a property is present but set to undefined, don't count it as present.
                const propertyNames = _.keys(_.omitBy(x, _.isUndefined));
                const diffKeys = _.difference(propertyNames, _.keys(propertyDefinitions));
                if (diffKeys.length > 0) {
                    errors.push(`${errorPrefix}unexpected propert${diffKeys.length > 1 ? 'ies' : 'y'} ${diffKeys.join(', ')}`);
                }

                const diffMandatoryKeys = _.difference(mandatoryPropertyNames, propertyNames);
                if (diffMandatoryKeys.length > 0) {
                    errors.push([
                        `${errorPrefix}missing mandatory propert${diffMandatoryKeys.length > 1 ? 'ies' : 'y'} `,
                        `${diffMandatoryKeys.join(', ')}`,
                    ].join(''));
                }

                _.forEach(x, (propertyValue, propertyName) => {
                    const propertyDefinition = propertyDefinitions[propertyName];
                    if (!propertyDefinition) {
                        return;
                    }
                    var typeValidationResult = propertyDefinition.type.validate(propertyValue, this);
                    if (!_.isUndefined(typeValidationResult)) {
                        typeValidationResult = typeValidationResult instanceof Array
                            ? typeValidationResult
                            : [typeValidationResult];
                        errors = errors.concat(_.map(typeValidationResult, result => {
                            return `in ${typeName}.${propertyName} ${result}`;
                        }));
                    }

                    const validators = typeof propertyDefinition.validator === 'function'
                        ? [propertyDefinition.validator]
                        : propertyDefinition.validator instanceof Array
                            ? propertyDefinition.validator
                            : [];

                    _.forEach(validators, validator => {
                        var validationResult = validator(propertyValue);
                        if (!_.isUndefined(validationResult)) {
                            errors.push(`in ${typeName}.${propertyName} validation failed with "${validationResult}"`);
                        }
                    });
                });

                if (!_.isEmpty(errors)) {
                    return errors;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                if (!propertyDefinitions) {
                    return x;
                }
                return _.mapValues(x, (value, key) => {
                    var type = propertyDefinitions[key].type;
                    return type.toCloudFormationValue(value);
                });
            },
        };
    },

    array: function(type) {
        return {
            name: 'array',
            validate: function(x) {
                var errors = [];
                if (!_.isArray(x)) {
                    return `expected array - got ${x}`;
                }
                _.forEach(x, (value, i) => {
                    const result = type.validate(value);
                    if (!_.isUndefined(result)) {
                        errors.push(`in [${i}] ${result}`);
                    }
                });

                if (!_.isEmpty(errors)) {
                    return errors;
                }
            },
            toCloudFormationValue: function(x) {
                Types.ensureValueValid(this, x);
                return _.map(x, v => {
                    return type.toCloudFormationValue(v);
                });
            },
        };
    },

    // TODO: make an arn type
};

module.exports = Types;
<|endoftext|>


<commit_before>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

var test = require('tape').test;
var exec = require('child_process').exec;

test('sdcadm update --help', function (t) {
    exec('sdcadm update --help', function (err, stdout, stderr) {
        t.ifError(err);

        t.notEqual(stdout.indexOf('sdcadm update [<options>] <svc>'), -1);
        t.equal(stderr, '');

        t.end();
    });
});

test('sdcadm update --dry-run', function (t) {
    exec('sdcadm update papi --dry-run -y', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.notEqual(stdout.indexOf('Finding candidate update images for the ' +
                   '"papi" service.'), -1);

        if (stdout.indexOf('Up-to-date.') === -1) {
            t.notEqual(stdout.indexOf('update "papi" service to image'), -1,
                       'check update string present');
            t.notEqual(stdout.indexOf('Updated successfully'), -1,
                       'check update string present');
        }

        t.end();
    });
});

test('sdcadm update --just-images', function (t) {
    exec('sdcadm update papi --just-images -y', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.notEqual(stdout.indexOf('Finding candidate update images'), -1);

        if (stdout.indexOf('Up-to-date') !== -1) {
            return t.end();
        }

        var findRegex = [
            'This update will make the following changes',
            'Downloading image .+ \(papi',
            'Imported image .+ \(papi',
            'Updated successfully'
        ];

        findRegex.forEach(function (regex) {
            t.ok(regex.match(stdout), 'check update regex present');
        });

        var imgUuid = stdout.match('Imported image (.+?) \(')[0];

        var cmd = 'sdc-imgapi /images/' + imgUuid + ' | json -H';
        exec(cmd, function (err2, stdout2, stderr2) {
            t.ifError(err2);
            t.equal(stderr2, '');

            var img = JSON.parse(stdout2);
            t.equal(img.name, 'papi');

            t.end();
        });
    });
});

test('sdcadm update', function (t) {
    exec('sdcadm update papi -y', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.ok(stdout.match('Finding candidate update images .+ "papi"'));

        if (stdout.match('Up-to-date')) {
            return t.end();
        }

        var findRegex = [
            'Installing image .+ \(papi',
            'Reprovisioning papi VM',
            'Wait (60s) for papi instance',
            'Updated successfully'
        ];

        findRegex.forEach(function (regex) {
            t.ok(stdout.match(regex), 'check update string present');
        });

        var papiUuid = stdout.match('papi instance (.+?) to come up')[1];

        var cmd = 'sdc-vmapi /vms/' + papiUuid + ' | json -H';
        exec(cmd, function (err2, stdout2, stderr2) {
            t.ifError(err2);
            t.equal(stderr2, '');

            var papi = JSON.parse(stdout2);

            // TODO: should be papi.state, but there's a bug (?) right now where
            // vmapi gets the state wedged in 'provisioning', even if it's done
            t.equal(papi.zone_state, 'running');

            return t.end();
        });
    });
});

// at this point, we should definitely be on the newest image
test('sdcadm update (again)', function (t) {
    exec('sdcadm update papi', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.ok(stdout.match('Finding candidate update images .+ "papi"'));
        t.ok(stdout.match('Up-to-date'));

        t.end();
    });
});

test('sdcadm update --force-same-image', function (t) {
    var cmd = 'sdcadm update papi --force-same-image -y';
    exec(cmd, function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        var findStrings = [
            'Finding candidate update images for the "papi"',
            'update "papi" service to image',
            'Reprovisioning papi VM',
            'Wait (60s) for papi instance',
            'Updated successfully'
        ];

        findStrings.forEach(function (str) {
            t.notEqual(stdout.indexOf(str), -1, 'check update string present');
        });

        var papiUuid = stdout.match('papi instance (.+?) to come up')[1];

        cmd = 'sdc-vmapi /vms/' + papiUuid + ' | json -H';
        exec(cmd, function (err2, stdout2, stderr2) {
            t.ifError(err2);
            t.equal(stderr2, '');

            var papi = JSON.parse(stdout2);

            // TODO: papi.state bug (see other TODO above)
            t.equal(papi.zone_state, 'running');

            return t.end();
        });
    });
});

// TODO: channels<commit_msg>TOOLS-831 - fix some update tests.<commit_after>/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

/*
 * Copyright (c) 2015, Joyent, Inc.
 */

var test = require('tape').test;
var exec = require('child_process').exec;

test('setup', function (t) {
    exec('sdcadm post-setup common-external-nics',
         function (err, stdout, stderr) {
        t.ifError(err);
        t.end();
    });
});

test('sdcadm update --help', function (t) {
    exec('sdcadm update --help', function (err, stdout, stderr) {
        t.ifError(err);

        t.notEqual(stdout.indexOf('sdcadm update [<options>] <svc>'), -1);
        t.equal(stderr, '');

        t.end();
    });
});

test('sdcadm update --dry-run', function (t) {
    exec('sdcadm update papi --dry-run -y', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.notEqual(stdout.indexOf('Finding candidate update images for the ' +
                   '"papi" service.'), -1);

        if (stdout.indexOf('Up-to-date.') === -1) {
            t.notEqual(stdout.indexOf('update "papi" service to image'), -1,
                       'check update string present');
            t.notEqual(stdout.indexOf('Updated successfully'), -1,
                       'check update string present');
        }

        t.end();
    });
});

test('sdcadm update --just-images', function (t) {
    exec('sdcadm update papi --just-images -y', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.notEqual(stdout.indexOf('Finding candidate update images'), -1);

        if (stdout.indexOf('Up-to-date') !== -1) {
            return t.end();
        }

        var findRegex = [
            'This update will make the following changes',
            'Downloading image .+ \(papi',
            'Imported image .+ \(papi',
            'Updated successfully'
        ];

        findRegex.forEach(function (regex) {
            t.ok(regex.match(stdout), 'check update regex present');
        });

        // JSSTYLED
        var imgUuid = stdout.match(/Imported image (.+?) \(/)[0];

        var cmd = 'sdc-imgapi /images/' + imgUuid + ' | json -H';
        exec(cmd, function (err2, stdout2, stderr2) {
            t.ifError(err2);
            t.equal(stderr2, '');

            var img = JSON.parse(stdout2);
            t.equal(img.name, 'papi');

            t.end();
        });
    });
});

test('sdcadm update', function (t) {
    exec('sdcadm update papi -y', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.ok(stdout.match('Finding candidate update images .+ "papi"'));

        if (stdout.match('Up-to-date')) {
            return t.end();
        }

        var findRegex = [
            'Installing image .+ \\(papi',
            'Reprovisioning papi VM',
            'Wait \\(60s\\) for papi instance',
            'Updated successfully'
        ];

        findRegex.forEach(function (regex) {
            t.ok(stdout.match(regex), 'check update string present:' + regex);
        });

        var papiUuid = stdout.match('papi instance (.+?) to come up')[1];

        var cmd = 'sdc-vmapi /vms/' + papiUuid + ' | json -H';
        exec(cmd, function (err2, stdout2, stderr2) {
            t.ifError(err2);
            t.equal(stderr2, '');

            var papi = JSON.parse(stdout2);

            // TODO: should be papi.state, but there's a bug (?) right now where
            // vmapi gets the state wedged in 'provisioning', even if it's done
            t.equal(papi.zone_state, 'running');

            return t.end();
        });
    });
});

// at this point, we should definitely be on the newest image
test('sdcadm update (again)', function (t) {
    exec('sdcadm update papi', function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        t.ok(stdout.match('Finding candidate update images .+ "papi"'));
        t.ok(stdout.match('Up-to-date'));

        t.end();
    });
});

test('sdcadm update --force-same-image', function (t) {
    var cmd = 'sdcadm update papi --force-same-image -y';
    exec(cmd, function (err, stdout, stderr) {
        t.ifError(err);
        t.equal(stderr, '');

        var findStrings = [
            'Finding candidate update images for the "papi"',
            'update "papi" service to image',
            'Reprovisioning papi VM',
            'Wait (60s) for papi instance',
            'Updated successfully'
        ];

        findStrings.forEach(function (str) {
            t.notEqual(stdout.indexOf(str), -1, 'check update string present');
        });

        var papiUuid = stdout.match('papi instance (.+?) to come up')[1];

        cmd = 'sdc-vmapi /vms/' + papiUuid + ' | json -H';
        exec(cmd, function (err2, stdout2, stderr2) {
            t.ifError(err2);
            t.equal(stderr2, '');

            var papi = JSON.parse(stdout2);

            // TODO: papi.state bug (see other TODO above)
            t.equal(papi.zone_state, 'running');

            return t.end();
        });
    });
});

// TODO: channels<|endoftext|>


<commit_before>var fs = require('fs');

var Transformer = require('./transform');
var StringStream = require('./stream');

module.exports = {

  unique: function unique(input, output, callback) {

    if (!isReadStream(input)) {
      throw new Error('No input stream specified!');
    }

    if (!isWriteStream(output)) {
      throw new Error('No output stream specified!');
    }

    if (typeof input === 'string') {
      input = fs.createReadStream(input);
    }

    var stream = null;

    if (output !== 'string') {
      if (typeof output === 'string') {
        stream = fs.createWriteStream(output);
      } else {
        stream = output;
      }
    } else {
      stream = new StringStream();
    }

    var transformer = new Transformer();

    input
      .pipe(transformer)
      .pipe(stream);

    stream.once('finish', function (){
      if (callback) {
        if (output !== 'string') {
          transformer.unpipe(output);
        } else {
          transformer.unpipe(stream);
        }
        callback({
          output: output !== 'string' ? null : stream.get(),
          total: transformer.count,
          unique: transformer.set.size
        });
      }
    });

  }

};

function isReadStream(obj) {
  // some streams are readable but do not have `read()`, however
  // we can reliably detect readability by testing for `resume()`
  return isStream(obj) && typeof obj.resume === 'function';
}

function isWriteStream(obj) {
  return isStream(obj) && typeof obj.write === 'function';
}

function isStream(obj) {
  return obj && typeof obj.pipe === 'function';
}
<commit_msg>Add back 'string' check to allow for neek streams to be created just using string filenames<commit_after>var fs = require('fs');

var Transformer = require('./transform');
var StringStream = require('./stream');

module.exports = {

  unique: function unique(input, output, callback) {

    if (!isReadStream(input)) {
      throw new Error('No input stream specified!');
    }

    if (!isWriteStream(output)) {
      throw new Error('No output stream specified!');
    }

    if (typeof input === 'string') {
      input = fs.createReadStream(input);
    }

    var stream = null;

    if (output !== 'string') {
      if (typeof output === 'string') {
        stream = fs.createWriteStream(output);
      } else {
        stream = output;
      }
    } else {
      stream = new StringStream();
    }

    var transformer = new Transformer();

    input
      .pipe(transformer)
      .pipe(stream);

    stream.once('finish', function (){
      if (callback) {
        if (output !== 'string') {
          transformer.unpipe(output);
        } else {
          transformer.unpipe(stream);
        }
        callback({
          output: output !== 'string' ? null : stream.get(),
          total: transformer.count,
          unique: transformer.set.size
        });
      }
    });

  }

};

function isReadStream(obj) {
  // some streams are readable but do not have `read()`, however
  // we can reliably detect readability by testing for `resume()`
  return isStream(obj) && typeof obj.resume === 'function';
}

function isWriteStream(obj) {
  return isStream(obj) && typeof obj.write === 'function';
}

function isStream(obj) {
  return obj && (typeof obj.pipe === 'function' || typeof(obj) === 'string');
}
<|endoftext|>


<commit_before>/**
 * RcmMessageListEdit
 * @param instanceId
 * @param container
 * @constructor
 */
var RcmMessageListEdit = function (instanceId, container, pluginHandler) {

    /**
     * Always refers to this object unlike the 'this' JS variable;
     *
     * @type this
     */
    var me = this;

    me.propertyName = 'User Message List';

    me.instanceConfig = {
        source: null,
        level: null,
        showHasViewed: false,
        showDefaultMessage: false
    };

    /**
     * Called by content management system to make this plugin user-editable
     */
    me.initEdit = function () {

        pluginHandler.getInstanceConfig(
            function (instanceConfig, defaultInstanceConfig) {
                me.instanceConfig = instanceConfig;
                me.completeEditInit();
            }
        );
    };

    /**
     * Called by content management system to get this plugins data for saving
     * on the server
     *
     * @return {Object}
     */
    me.getSaveData = function () {
        return me.instanceConfig;
    };

    /**
     * completeEditInit
     */
    me.completeEditInit = function () {

        // add a border to show plugin if it is empty
        //$('.rcmPlugin.RcmMessageList').css('border', '1px dotted #DDDDDD');
        //$('.rcmPlugin.RcmMessageList').css('min-height', '1em');
        //$('.rcmPlugin.RcmMessageList .rcmMessageList').css('min-height', '1em');
        // Show mock alert
        me.buildMockAlert();

        //Add right click menu
        $.contextMenu(
            {
                selector: rcm.getPluginContainerSelector(instanceId),
                //Here are the right click menu options
                items: {
                    edit: {
                        name: 'Edit ' + me.propertyName,
                        icon: 'edit',
                        callback: function () {
                            me.showEditDialog();
                        }
                    }

                }
            }
        );
    };

    /**
     * buildMockAlert
     */
    me.buildMockAlert = function () {

      var html = '
' +
        '<button type="button" class="close" aria-label="Close">' +
        '×' +
        '</button>' +
        'Some message subject: ' +
        'Some message content here' +
        '
';

        $('.rcmPlugin.RcmMessageList .userMessageList').append(html);
    };

    /**
     * showEditDialog
     */
    me.showEditDialog = function () {

        var fields = {};
        fields.source = $.dialogIn(
            'text',
            'Message Sources to Display (source) - Leave blank to dispaly ALL.',
            me.instanceConfig.source
        );
        fields.level = $.dialogIn(
            'select',
            'Show Messages of Level - Use if only specific message levels need be displayed',
            {
                2: 'CRITICAL Only',
                4: 'ERROR Only',
                8: 'WARNING Only',
                16: 'INFO Only',
                32: 'SUCCESS Only',
                0: 'All'
            },
            me.instanceConfig.level
        );
        fields.showHasViewed = $.dialogIn(
            'select',
            'Show Viewed Messages',
            {
                1: 'Viewed_Only',
                0: 'Non-Viewed_Only',
                '': 'All'
            },
            me.instanceConfig.showHasViewed
        );

        fields.showDefaultMessage = $.dialogIn(
            'select',
            'Show Default Message (show "No Messages" when there are no messages)',
            {
                1: 'Show',
                0: 'Hide'
            },
            me.instanceConfig.showDefaultMessage
        );

        var form = $('<form></form>')
            .addClass('simple')
            .append(
            fields.source,
            fields.level,
            fields.showHasViewed,
            fields.showDefaultMessage
        )
            .dialog(
            {
                title: me.propertyName,
                modal: true,
                width: 620,
                buttons: {
                    Cancel: function () {
                        $(this).dialog("close");
                    },
                    Ok: function () {

                        //Get user-entered data from form
                        me.instanceConfig.source = fields.source.val();
                        me.instanceConfig.level = fields.level.val();
                        me.instanceConfig.showHasViewed = fields.showHasViewed.val();
                        me.instanceConfig.showDefaultMessage = fields.showDefaultMessage.val();

                        $(this).dialog('close');
                    }
                }
            }
        );
    };
};<commit_msg>Fixed issue with mock alert<commit_after>/**
 * RcmMessageListEdit
 * @param instanceId
 * @param container
 * @constructor
 */
var RcmMessageListEdit = function (instanceId, container, pluginHandler) {

    /**
     * Always refers to this object unlike the 'this' JS variable;
     *
     * @type this
     */
    var me = this;

    me.propertyName = 'User Message List';

    me.instanceConfig = {
        source: null,
        level: null,
        showHasViewed: false,
        showDefaultMessage: false
    };

    /**
     * Called by content management system to make this plugin user-editable
     */
    me.initEdit = function () {

        pluginHandler.getInstanceConfig(
            function (instanceConfig, defaultInstanceConfig) {
                me.instanceConfig = instanceConfig;
                me.completeEditInit();
            }
        );
    };

    /**
     * Called by content management system to get this plugins data for saving
     * on the server
     *
     * @return {Object}
     */
    me.getSaveData = function () {
        return me.instanceConfig;
    };

    /**
     * completeEditInit
     */
    me.completeEditInit = function () {

        // add a border to show plugin if it is empty
        //$('.rcmPlugin.RcmMessageList').css('border', '1px dotted #DDDDDD');
        //$('.rcmPlugin.RcmMessageList').css('min-height', '1em');
        //$('.rcmPlugin.RcmMessageList .rcmMessageList').css('min-height', '1em');
        // Show mock alert
        me.buildMockAlert();

        //Add right click menu
        $.contextMenu(
            {
                selector: rcm.getPluginContainerSelector(instanceId),
                //Here are the right click menu options
                items: {
                    edit: {
                        name: 'Edit ' + me.propertyName,
                        icon: 'edit',
                        callback: function () {
                            me.showEditDialog();
                        }
                    }

                }
            }
        );
    };

    /**
     * buildMockAlert
     */
    me.buildMockAlert = function () {

        var html = '
' +
            '<button type="button" class="close" aria-label="Close">' +
            '×' +
            '</button>' +
            'Some message subject: ' +
            'Some message content here' +
            '
';

        var pluginElms = $('.rcmPlugin.RcmMessageList .userMessageList');

        pluginElms.each(
            function (index) {
                if($(this).find('.alert').length < 1){
                    $(this).append(html);
                }
            }
        );
    };

    /**
     * showEditDialog
     */
    me.showEditDialog = function () {

        var fields = {};
        fields.source = $.dialogIn(
            'text',
            'Message Sources to Display (source) - Leave blank to dispaly ALL.',
            me.instanceConfig.source
        );
        fields.level = $.dialogIn(
            'select',
            'Show Messages of Level - Use if only specific message levels need be displayed',
            {
                2: 'CRITICAL Only',
                4: 'ERROR Only',
                8: 'WARNING Only',
                16: 'INFO Only',
                32: 'SUCCESS Only',
                0: 'All'
            },
            me.instanceConfig.level
        );
        fields.showHasViewed = $.dialogIn(
            'select',
            'Show Viewed Messages',
            {
                1: 'Viewed_Only',
                0: 'Non-Viewed_Only',
                '': 'All'
            },
            me.instanceConfig.showHasViewed
        );

        fields.showDefaultMessage = $.dialogIn(
            'select',
            'Show Default Message (show "No Messages" when there are no messages)',
            {
                1: 'Show',
                0: 'Hide'
            },
            me.instanceConfig.showDefaultMessage
        );

        var form = $('<form></form>')
            .addClass('simple')
            .append(
            fields.source,
            fields.level,
            fields.showHasViewed,
            fields.showDefaultMessage
        )
            .dialog(
            {
                title: me.propertyName,
                modal: true,
                width: 620,
                buttons: {
                    Cancel: function () {
                        $(this).dialog("close");
                    },
                    Ok: function () {

                        //Get user-entered data from form
                        me.instanceConfig.source = fields.source.val();
                        me.instanceConfig.level = fields.level.val();
                        me.instanceConfig.showHasViewed = fields.showHasViewed.val();
                        me.instanceConfig.showDefaultMessage = fields.showDefaultMessage.val();

                        $(this).dialog('close');
                    }
                }
            }
        );
    };
};<|endoftext|>


<commit_before>/**
 * pubfood
 * Copyright (c) 2015 Yieldbot, Inc. - All rights reserved.
 */

/* global */
/*eslint no-unused-vars: 0*/

'use strict';

var auctionexample1 = {
  slots: [
    {
      name: '/2476204/multi-size',
      sizes: [
        [300, 250],
        [300, 600]
      ],
      elementId: 'div-multi-size',
      bidProviders: {
        yieldbot: {
          slot: 'medrec'
        },
        bidderFast: {
          slot: 'fastSlot'
        },
        bidderSlow: {
          slot: 'slowSlot'
        }
      }
    }
  ],
  auctionProvider: {
    name: 'Google',
    libUri: '//www.googletagservices.com/tag/js/gpt.js',
    init: function(slots, bids, options, done) {

    },
    refresh: function(slots, targeting, done) {
    },
    trigger: function(done) {
      setTimeout(function() {
        done();
      }, 100);
    }
  },
  bidProviders: [
    {
      name: 'yieldbot',
      libUri: '//cdn.yldbt.com/js/yieldbot.intent.js',
      init: function(slots, options, pushBid, done) {
        done();
      },
      refresh: function(slots, options, done) {
      }
    },
    {
      name: 'bidderFast',
      libUri: '../test/fixture/lib.js',
      init: function(slots, options, pushBid, done) {
        done();
      },
      refresh: function(slots, options, pushBid, done) {
      }
    },
    {
      name: 'bidderSlow',
      libUri: '../test/fixture/lib.js',
      init: function(slots, options, pushBid, done) {
        done();
      },
      refresh: function(slots, options, pushBid, done) {
      }
    }
  ]
};

if(module && module.exports){
  module.exports = auctionexample1;
}
<commit_msg>update signatures<commit_after>/**
 * pubfood
 * Copyright (c) 2015 Yieldbot, Inc. - All rights reserved.
 */

/* global */
/*eslint no-unused-vars: 0*/

'use strict';

var auctionexample1 = {
  slots: [
    {
      name: '/2476204/multi-size',
      sizes: [
        [300, 250],
        [300, 600]
      ],
      elementId: 'div-multi-size',
      bidProviders: {
        yieldbot: {
          slot: 'medrec'
        },
        bidderFast: {
          slot: 'fastSlot'
        },
        bidderSlow: {
          slot: 'slowSlot'
        }
      }
    }
  ],
  auctionProvider: {
    name: 'Google',
    libUri: '//www.googletagservices.com/tag/js/gpt.js',
    init: function(targets, options, done) {
      done();
    },
    refresh: function(slots, customTargeting, done) {
    },
    trigger: function(done) {
      setTimeout(function() {
        done();
      }, 100);
    }
  },
  bidProviders: [
    {
      name: 'yieldbot',
      libUri: '//cdn.yldbt.com/js/yieldbot.intent.js',
      init: function(slots, options, pushBid, done) {
        done();
      },
      refresh: function(slots, options, pushBid, done) {
      }
    },
    {
      name: 'bidderFast',
      libUri: '../test/fixture/lib.js',
      init: function(slots, options, pushBid, done) {
        done();
      },
      refresh: function(slots, options, pushBid, done) {
      }
    },
    {
      name: 'bidderSlow',
      libUri: '../test/fixture/lib.js',
      init: function(slots, options, pushBid, done) {
        done();
      },
      refresh: function(slots, options, pushBid, done) {
      }
    }
  ]
};

if(module && module.exports){
  module.exports = auctionexample1;
}
<|endoftext|>