<commit_before>;(function($) {
  var _defaults = {
    /**
     * Automatically submit the form after it is bound
     */
    submitOnBind: false,

    /**
     * The expected response dataType
     */
    dataType: 'json',

    /**
     * Called when a valid form is submitted.
     *
     * Return false to halt further event execution.
     */
    onSubmit:  $.noop,

    /**
     * Called when a form returns a successful response.
     *
     * Takes one argument: response
     * Return false to halt further event execution.
     */
    onSuccess: $.noop,

    /**
     * Called when a form returns an error response.
     *
     * Takes three arguments: jqXHR, textStatus, errorThrown
     * Return false to halt further event execution.
     */
    onError:   $.noop,

    /**
     * @see jQuery.validate.settings.showErrors
     *
     * Overrides the default, funky show/hide functionality.
     * If using this implementation, highlight and unhighlight are responsible
     * for showing and hiding elements, respectively.
     */
    showErrors: function() {
      var i, elements;
      for (i = 0; this.errorList[i]; i++) {
        var error = this.errorList[i];
        if (this.settings.highlight) {
          this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
        }

        this.showLabel(error.element, error.message);
      }

      if (this.settings.unhighlight) {
        for (i = 0, elements = this.validElements(); elements[i]; i++) {
          this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
        }
      }
    },

    /**
     * @see jQuery.validate.settings.errorPlacement
     */
    errorPlacement: function (error, element) {
      $(element).closest('.form-group').find('.help-block').html(error.text()).show();
    },

    /**
     * @see jQuery.validate.settings.highlight
     */
    highlight: function(element, errorClass, validClass) {
      $(element).closest('.form-group').addClass(errorClass).find('.help-block').show();
    },

    /**
     * @see jQuery.validate.settings.unhighlight
     */
    unhighlight: function(element, errorClass, validClass) {
      $(element).closest('.form-group').removeClass(errorClass).find('.help-block').text('').hide();
    }
  };

  var _submitHandlerFactory = function(options) {
    var _chainHandlers = function(handlerA, handlerB) {
      return function() {
        if (false !== handlerA.apply(null, arguments)) {
          handlerB.apply(null, arguments);
        }
      };
    };

    var successHandler = _chainHandlers(options.onSuccess, Orkestra.response.successHandler);
    var errorHandler = _chainHandlers(options.onError, Orkestra.response.errorHandler);

    var defaultSubmitHandler = function(form) {
      $(form).ajaxSubmit({
        dataType: options.dataType,
        success:  successHandler,
        error:    errorHandler
      });
    };

    return _chainHandlers(options.onSubmit, defaultSubmitHandler);
  };

  var _bindEnhancements = function(form) {
    var $form = $(form);

    // Date and time pickers
    if ($.isFunction($.fn.datetimepicker)) {
      $('input.date', $form).datetimepicker({
        pickTime : false,
        format : 'MM/DD/YY'
      });

      $('input.timepicker', $form).datetimepicker({
        useSeconds : false,
        pickDate : false,
        minuteStepping : 1,
        format : 'hh:mm A'
      });

      $('input.datetimepicker', $form).datetimepicker({
        useSeconds : false,
        format : 'MM/DD/YY hh:mm A'
      });
    }

    // USA Currency
    if ($.isFunction($.fn.formatCurrency)) {
      // Currency
      $('input.currency', $form).formatCurrency(true);
    }

    // USA Telephone
    if ($.isFunction($.fn.formatPhoneNumber)) {
      // Telephone
      $('input.telephone', $form).formatPhoneNumber();
    }

    // USA SSN
    if ($.isFunction($.fn.formatSocial)) {
      $('input.social', $form).formatSocial();
    }

    // USA EIN
    if ($.isFunction($.fn.formatEin)) {
      $('input.ein', $form).formatEin();
    }

    // URL
    if ($.isFunction($.fn.formatUrl)) {
      $('input.url', $form).formatUrl();
    }

    // Integer
    if ($.isFunction($.fn.formatNumber)) {
      $('input.integer', $form).formatNumber({
        initValue: true
      });
    }
  };

  var helper = function() {

  };

  helper.prototype = $.extend(helper.prototype, {
    bind: function(form, options) {
      options = $.extend({}, _defaults, options);

      options.submitHandler = options.submitHandler || _submitHandlerFactory(options);

      $(form).data('validator', null).unbind('submit').validate(options);
      _bindEnhancements.call(this, form);

      if (true === options.submitOnBind) {
        $(function() {
          $(form).submit();
        });
      }
    },

    bindEnhancements: _bindEnhancements
  });

  window.Orkestra = window.Orkestra || {};
  window.Orkestra.FormHelper = helper;
})(jQuery);
<commit_msg>Added improved help-text handling in form.js<commit_after>;(function($) {
  var _defaults = {
    /**
     * Automatically submit the form after it is bound
     */
    submitOnBind: false,

    /**
     * The expected response dataType
     */
    dataType: 'json',

    /**
     * Called when a valid form is submitted.
     *
     * Return false to halt further event execution.
     */
    onSubmit:  $.noop,

    /**
     * Called when a form returns a successful response.
     *
     * Takes one argument: response
     * Return false to halt further event execution.
     */
    onSuccess: $.noop,

    /**
     * Called when a form returns an error response.
     *
     * Takes three arguments: jqXHR, textStatus, errorThrown
     * Return false to halt further event execution.
     */
    onError:   $.noop,

    /**
     * @see jQuery.validate.settings.showErrors
     *
     * Overrides the default, funky show/hide functionality.
     * If using this implementation, highlight and unhighlight are responsible
     * for showing and hiding elements, respectively.
     */
    showErrors: function() {
      var i, elements;
      for (i = 0; this.errorList[i]; i++) {
        var error = this.errorList[i];
        if (this.settings.highlight) {
          this.settings.highlight.call(this, error.element, this.settings.errorClass, this.settings.validClass);
        }

        this.showLabel(error.element, error.message);
      }

      if (this.settings.unhighlight) {
        for (i = 0, elements = this.validElements(); elements[i]; i++) {
          this.settings.unhighlight.call(this, elements[i], this.settings.errorClass, this.settings.validClass);
        }
      }
    },

    /**
     * @see jQuery.validate.settings.errorPlacement
     */
    errorPlacement: function (error, element) {
      var $helpBlock = $(element).closest('.form-group').find('.help-block');
      $helpBlock.data('last-text', $helpBlock.html());
      $helpBlock.html(error.text()).show();
    },

    /**
     * @see jQuery.validate.settings.highlight
     */
    highlight: function(element, errorClass, validClass) {
      var $helpBlock = $(element).closest('.form-group').addClass(errorClass).find('.help-block').show();
    },

    /**
     * @see jQuery.validate.settings.unhighlight
     */
    unhighlight: function(element, errorClass, validClass) {
      var $helpBlock = $(element).closest('.form-group').removeClass(errorClass).find('.help-block');
      if ($helpBlock.data('last-text')) {
        $helpBlock.html($helpBlock.data('last-text'));
      } else {
        $helpBlock.text('').hide();
      }
    },

    errorClass: 'error has-error',

    validClass: ''
  };

  var _submitHandlerFactory = function(options) {
    var _chainHandlers = function(handlerA, handlerB) {
      return function() {
        if (false !== handlerA.apply(null, arguments)) {
          handlerB.apply(null, arguments);
        }
      };
    };

    var successHandler = _chainHandlers(options.onSuccess, Orkestra.response.successHandler);
    var errorHandler = _chainHandlers(options.onError, Orkestra.response.errorHandler);

    var defaultSubmitHandler = function(form) {
      $(form).ajaxSubmit({
        dataType: options.dataType,
        success:  successHandler,
        error:    errorHandler
      });
    };

    return _chainHandlers(options.onSubmit, defaultSubmitHandler);
  };

  var _bindEnhancements = function(form) {
    var $form = $(form);

    // Date and time pickers
    if ($.isFunction($.fn.datetimepicker)) {
      $('input.date', $form).datetimepicker({
        pickTime : false,
        format : 'MM/DD/YY'
      });

      $('input.timepicker', $form).datetimepicker({
        useSeconds : false,
        pickDate : false,
        minuteStepping : 1,
        format : 'hh:mm A'
      });

      $('input.datetimepicker', $form).datetimepicker({
        useSeconds : false,
        format : 'MM/DD/YY hh:mm A'
      });
    }

    // USA Currency
    if ($.isFunction($.fn.formatCurrency)) {
      // Currency
      $('input.currency', $form).formatCurrency(true);
    }

    // USA Telephone
    if ($.isFunction($.fn.formatPhoneNumber)) {
      // Telephone
      $('input.telephone', $form).formatPhoneNumber();
    }

    // USA SSN
    if ($.isFunction($.fn.formatSocial)) {
      $('input.social', $form).formatSocial();
    }

    // USA EIN
    if ($.isFunction($.fn.formatEin)) {
      $('input.ein', $form).formatEin();
    }

    // URL
    if ($.isFunction($.fn.formatUrl)) {
      $('input.url', $form).formatUrl();
    }

    // Integer
    if ($.isFunction($.fn.formatNumber)) {
      $('input.integer', $form).formatNumber({
        initValue: true
      });
    }
  };

  var helper = function() {

  };

  helper.prototype = $.extend(helper.prototype, {
    bind: function(form, options) {
      options = $.extend({}, _defaults, options);

      options.submitHandler = options.submitHandler || _submitHandlerFactory(options);

      $(form).data('validator', null).unbind('submit').validate(options);
      _bindEnhancements.call(this, form);

      if (true === options.submitOnBind) {
        $(function() {
          $(form).submit();
        });
      }
    },

    bindEnhancements: _bindEnhancements,

    setDefaults: function(options) {
      $.extend(_defaults, options);
    }
  });

  window.Orkestra = window.Orkestra || {};
  window.Orkestra.FormHelper = helper;
})(jQuery);
<|endoftext|>


<commit_before>import Ember from 'ember';
import { module, test } from 'qunit';
import { isSettled, getSettledState } from '@ember/test-helpers';
import { TestDebugInfo } from '@ember/test-helpers/-internal/debug-info';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';
import { _setupAJAXHooks, _teardownAJAXHooks } from '@ember/test-helpers/settled';
import { next, later, run, schedule } from '@ember/runloop';
import { buildWaiter, _reset as resetWaiters } from 'ember-test-waiters';
import Pretender from 'pretender';
import hasjQuery from '../helpers/has-jquery';
import ajax from '../helpers/ajax';

const WAITER_NAME = 'custom-waiter';

module('settled', function(hooks) {
  if (!hasEmberVersion(2, 4)) {
    return;
  }

  hooks.beforeEach(function(assert) {
    _setupAJAXHooks();

    this.confirmSettles = done => {
      return function() {
        setTimeout(() => {
          assert.strictEqual(isSettled(), true, 'post cond - isSettled');
          assert.deepEqual(
            getSettledState(),
            {
              hasPendingRequests: false,
              hasPendingTimers: false,
              hasPendingWaiters: false,
              hasPendingTransitions: null,
              hasRunLoop: false,
              pendingRequestCount: 0,
              debugInfo: new TestDebugInfo(false, false, false, false, false),
            },
            'post cond - getSettledState'
          );

          done();
        });
      };
    };

    this.server = new Pretender(function() {
      this.get(
        '/whazzits',
        function() {
          return [200, { 'Content-Type': 'text/plain' }, 'Remote Data!'];
        },
        25
      );
    });

    this._legacyWaiter = () => {
      return !this.isWaiterPending;
    };

    // In Ember < 2.8 `registerWaiter` expected to be bound to
    // `Ember.Test` 😭
    //
    // Once we have dropped support for < 2.8 we should swap this to
    // use:
    //
    // import { registerWaiter } from '@ember/test';
    Ember.Test.registerWaiter(this._legacyWaiter);

    this._testWaiter = buildWaiter(WAITER_NAME);
  });

  hooks.afterEach(function() {
    Ember.Test.unregisterWaiter(this._legacyWaiter);
    resetWaiters();
    this.server.shutdown();
    _teardownAJAXHooks();
  });

  module('isSettled', function() {
    test('when no work is scheduled, no requests, waiters completed', function(assert) {
      assert.strictEqual(isSettled(), true);
    });

    test('when work is scheduled via run.schedule', function(assert) {
      assert.expect(4);
      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      schedule('actions', this.confirmSettles(done));

      assert.strictEqual(isSettled(), false);
    });

    test('when work is scheduled via run.next', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      next(this.confirmSettles(done));

      assert.strictEqual(isSettled(), false);
    });

    test('when work is scheduled via run.later', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      later(this.confirmSettles(done), 15);

      assert.strictEqual(isSettled(), false);
    });

    test('when invocation is within a run loop', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      run(() => {
        assert.strictEqual(isSettled(), false);
      });

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when AJAX requests are pending', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      ajax('/whazzits').then(this.confirmSettles(done));

      assert.strictEqual(isSettled(), false);
    });

    test('when legacy waiters are pending', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      this.isWaiterPending = true;

      assert.strictEqual(isSettled(), false);

      this.isWaiterPending = false;

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when test waiters are pending', function(assert) {
      assert.expect(3);

      let waiterItem = {};

      assert.strictEqual(isSettled(), true, 'precond');

      this._testWaiter.beginAsync(waiterItem);

      assert.strictEqual(isSettled(), false);

      this._testWaiter.endAsync(waiterItem);

      assert.strictEqual(isSettled(), true, 'post cond');
    });
  });

  module('getSettledState', function() {
    test('when no work is scheduled, no requests, waiters completed', function(assert) {
      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(false, false, false, false, false),
      });
    });

    test('when work is scheduled via run.schedule', function(assert) {
      assert.expect(4);
      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      schedule('actions', this.confirmSettles(done));

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: true,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: true,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(true, true, false, false, false),
      });
    });

    test('when work is scheduled via run.next', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      next(this.confirmSettles(done));

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: true,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(true, false, false, false, false),
      });
    });

    test('when work is scheduled via run.later', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      later(this.confirmSettles(done), 15);

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: true,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(true, false, false, false, false),
      });
    });

    test('when invocation is within a run loop', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      run(() => {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: false,
          hasPendingWaiters: false,
          hasPendingTransitions: null,
          hasRunLoop: true,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo(false, true, false, false, false),
        });
      });

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when AJAX requests are pending', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      ajax('/whazzits').then(this.confirmSettles(done));

      /*
        When testing without jQuery `ajax` is provided by ember-fetch which uses a test waiter
        to ensure tests wait for pending `fetch` requests, but under jQuery.ajax we use global
        ajax start/stop timers
      */
      if (hasjQuery()) {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: true,
          hasPendingTimers: false,
          hasPendingWaiters: false,
          hasPendingTransitions: null,
          hasRunLoop: false,
          pendingRequestCount: 1,
          debugInfo: new TestDebugInfo(false, false, false, false, true),
        });
      } else {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: false,
          hasPendingWaiters: true,
          hasPendingTransitions: null,
          hasRunLoop: false,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo(false, false, true, false, false),
        });
      }
    });

    test('when legacy waiters are pending', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      this.isWaiterPending = true;

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: true,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(false, false, true, false, false),
      });

      this.isWaiterPending = false;

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when test waiters are pending', function(assert) {
      assert.expect(3);

      let waiterItem = {};

      assert.strictEqual(isSettled(), true, 'precond');

      this._testWaiter.beginAsync(waiterItem);

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: true,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(false, false, false, true, false),
      });

      this._testWaiter.endAsync(waiterItem);

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('all the things!', function(assert) {
      assert.expect(6);
      let done = assert.async();
      assert.strictEqual(isSettled(), true, 'precond');

      this.isWaiterPending = true;

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: true,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo(false, false, true, false, false),
      });

      run(() => {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: false,
          hasPendingWaiters: true,
          hasPendingTransitions: null,
          hasRunLoop: true,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo(false, true, true, false, false),
        });

        next(this.confirmSettles(done));

        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: true,
          hasPendingWaiters: true,
          hasPendingTransitions: null,
          hasRunLoop: true,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo(true, true, true, false, false),
        });

        this.isWaiterPending = false;
      });
    });
  });
});
<commit_msg>Fixing settled tests<commit_after>import Ember from 'ember';
import { module, test } from 'qunit';
import { isSettled, getSettledState } from '@ember/test-helpers';
import { TestDebugInfo } from '@ember/test-helpers/-internal/debug-info';
import hasEmberVersion from '@ember/test-helpers/has-ember-version';
import { _setupAJAXHooks, _teardownAJAXHooks } from '@ember/test-helpers/settled';
import { next, later, run, schedule } from '@ember/runloop';
import { buildWaiter, _reset as resetWaiters } from 'ember-test-waiters';
import Pretender from 'pretender';
import hasjQuery from '../helpers/has-jquery';
import ajax from '../helpers/ajax';

const WAITER_NAME = 'custom-waiter';

module('settled', function(hooks) {
  if (!hasEmberVersion(2, 4)) {
    return;
  }

  hooks.beforeEach(function(assert) {
    _setupAJAXHooks();

    this.confirmSettles = done => {
      return function() {
        setTimeout(() => {
          assert.strictEqual(isSettled(), true, 'post cond - isSettled');
          assert.deepEqual(
            getSettledState(),
            {
              hasPendingRequests: false,
              hasPendingTimers: false,
              hasPendingWaiters: false,
              hasPendingTransitions: null,
              hasRunLoop: false,
              pendingRequestCount: 0,
              debugInfo: new TestDebugInfo({
                hasPendingTimers: false,
                hasRunLoop: false,
                hasPendingLegacyWaiters: false,
                hasPendingTestWaiters: false,
                hasPendingRequests: false,
              }),
            },
            'post cond - getSettledState'
          );

          done();
        });
      };
    };

    this.server = new Pretender(function() {
      this.get(
        '/whazzits',
        function() {
          return [200, { 'Content-Type': 'text/plain' }, 'Remote Data!'];
        },
        25
      );
    });

    this._legacyWaiter = () => {
      return !this.isWaiterPending;
    };

    // In Ember < 2.8 `registerWaiter` expected to be bound to
    // `Ember.Test` 😭
    //
    // Once we have dropped support for < 2.8 we should swap this to
    // use:
    //
    // import { registerWaiter } from '@ember/test';
    Ember.Test.registerWaiter(this._legacyWaiter);

    this._testWaiter = buildWaiter(WAITER_NAME);
  });

  hooks.afterEach(function() {
    Ember.Test.unregisterWaiter(this._legacyWaiter);
    resetWaiters();
    this.server.shutdown();
    _teardownAJAXHooks();
  });

  module('isSettled', function() {
    test('when no work is scheduled, no requests, waiters completed', function(assert) {
      assert.strictEqual(isSettled(), true);
    });

    test('when work is scheduled via run.schedule', function(assert) {
      assert.expect(4);
      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      schedule('actions', this.confirmSettles(done));

      assert.strictEqual(isSettled(), false);
    });

    test('when work is scheduled via run.next', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      next(this.confirmSettles(done));

      assert.strictEqual(isSettled(), false);
    });

    test('when work is scheduled via run.later', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      later(this.confirmSettles(done), 15);

      assert.strictEqual(isSettled(), false);
    });

    test('when invocation is within a run loop', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      run(() => {
        assert.strictEqual(isSettled(), false);
      });

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when AJAX requests are pending', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      ajax('/whazzits').then(this.confirmSettles(done));

      assert.strictEqual(isSettled(), false);
    });

    test('when legacy waiters are pending', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      this.isWaiterPending = true;

      assert.strictEqual(isSettled(), false);

      this.isWaiterPending = false;

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when test waiters are pending', function(assert) {
      assert.expect(3);

      let waiterItem = {};

      assert.strictEqual(isSettled(), true, 'precond');

      this._testWaiter.beginAsync(waiterItem);

      assert.strictEqual(isSettled(), false);

      this._testWaiter.endAsync(waiterItem);

      assert.strictEqual(isSettled(), true, 'post cond');
    });
  });

  module('getSettledState', function() {
    test('when no work is scheduled, no requests, waiters completed', function(assert) {
      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: false,
          hasRunLoop: false,
          hasPendingLegacyWaiters: false,
          hasPendingTestWaiters: false,
          hasPendingRequests: false,
        }),
      });
    });

    test('when work is scheduled via run.schedule', function(assert) {
      assert.expect(4);
      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      schedule('actions', this.confirmSettles(done));

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: true,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: true,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: true,
          hasRunLoop: true,
          hasPendingLegacyWaiters: false,
          hasPendingTestWaiters: false,
          hasPendingRequests: false,
        }),
      });
    });

    test('when work is scheduled via run.next', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      next(this.confirmSettles(done));

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: true,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: true,
          hasRunLoop: false,
          hasPendingLegacyWaiters: false,
          hasPendingTestWaiters: false,
          hasPendingRequests: false,
        }),
      });
    });

    test('when work is scheduled via run.later', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      later(this.confirmSettles(done), 15);

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: true,
        hasPendingWaiters: false,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: true,
          hasRunLoop: false,
          hasPendingLegacyWaiters: false,
          hasPendingTestWaiters: false,
          hasPendingRequests: false,
        }),
      });
    });

    test('when invocation is within a run loop', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      run(() => {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: false,
          hasPendingWaiters: false,
          hasPendingTransitions: null,
          hasRunLoop: true,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo({
            hasPendingTimers: false,
            hasRunLoop: true,
            hasPendingLegacyWaiters: false,
            hasPendingTestWaiters: false,
            hasPendingRequests: false,
          }),
        });
      });

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when AJAX requests are pending', function(assert) {
      assert.expect(4);

      let done = assert.async();

      assert.strictEqual(isSettled(), true, 'precond');

      ajax('/whazzits').then(this.confirmSettles(done));

      /*
        When testing without jQuery `ajax` is provided by ember-fetch which uses a test waiter
        to ensure tests wait for pending `fetch` requests, but under jQuery.ajax we use global
        ajax start/stop timers
      */
      if (hasjQuery()) {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: true,
          hasPendingTimers: false,
          hasPendingWaiters: false,
          hasPendingTransitions: null,
          hasRunLoop: false,
          pendingRequestCount: 1,
          debugInfo: new TestDebugInfo({
            hasPendingTimers: false,
            hasRunLoop: false,
            hasPendingLegacyWaiters: false,
            hasPendingTestWaiters: false,
            hasPendingRequests: true,
          }),
        });
      } else {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: false,
          hasPendingWaiters: true,
          hasPendingTransitions: null,
          hasRunLoop: false,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo({
            hasPendingTimers: false,
            hasRunLoop: false,
            hasPendingLegacyWaiters: true,
            hasPendingTestWaiters: false,
            hasPendingRequests: false,
          }),
        });
      }
    });

    test('when legacy waiters are pending', function(assert) {
      assert.expect(3);

      assert.strictEqual(isSettled(), true, 'precond');

      this.isWaiterPending = true;

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: true,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: false,
          hasRunLoop: false,
          hasPendingLegacyWaiters: true,
          hasPendingTestWaiters: false,
          hasPendingRequests: false,
        }),
      });

      this.isWaiterPending = false;

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('when test waiters are pending', function(assert) {
      assert.expect(3);

      let waiterItem = {};

      assert.strictEqual(isSettled(), true, 'precond');

      this._testWaiter.beginAsync(waiterItem);

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: true,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: false,
          hasRunLoop: false,
          hasPendingLegacyWaiters: false,
          hasPendingTestWaiters: true,
          hasPendingRequests: false,
        }),
      });

      this._testWaiter.endAsync(waiterItem);

      assert.strictEqual(isSettled(), true, 'post cond');
    });

    test('all the things!', function(assert) {
      assert.expect(6);
      let done = assert.async();
      assert.strictEqual(isSettled(), true, 'precond');

      this.isWaiterPending = true;

      assert.deepEqual(getSettledState(), {
        hasPendingRequests: false,
        hasPendingTimers: false,
        hasPendingWaiters: true,
        hasPendingTransitions: null,
        hasRunLoop: false,
        pendingRequestCount: 0,
        debugInfo: new TestDebugInfo({
          hasPendingTimers: false,
          hasRunLoop: false,
          hasPendingLegacyWaiters: true,
          hasPendingTestWaiters: false,
          hasPendingRequests: false,
        }),
      });

      run(() => {
        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: false,
          hasPendingWaiters: true,
          hasPendingTransitions: null,
          hasRunLoop: true,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo({
            hasPendingTimers: false,
            hasRunLoop: true,
            hasPendingLegacyWaiters: true,
            hasPendingTestWaiters: false,
            hasPendingRequests: false,
          }),
        });

        next(this.confirmSettles(done));

        assert.deepEqual(getSettledState(), {
          hasPendingRequests: false,
          hasPendingTimers: true,
          hasPendingWaiters: true,
          hasPendingTransitions: null,
          hasRunLoop: true,
          pendingRequestCount: 0,
          debugInfo: new TestDebugInfo({
            hasPendingTimers: true,
            hasRunLoop: true,
            hasPendingLegacyWaiters: true,
            hasPendingTestWaiters: false,
            hasPendingRequests: false,
          }),
        });

        this.isWaiterPending = false;
      });
    });
  });
});
<|endoftext|>


<commit_before>import React from 'react';

import SPELLS from 'common/SPELLS';
import SpellIcon from 'common/SpellIcon';
import SpellLink from 'common/SpellLink';
import { formatPercentage } from 'common/format';

import Module from 'Parser/Core/Module';

import StatisticBox, { STATISTIC_ORDER } from 'Main/StatisticBox';

import PaladinAbilityTracker from './PaladinAbilityTracker';

class InfusionOfLightCastRatio extends Module {
  static dependencies = {
    abilityTracker: PaladinAbilityTracker,
  };

  suggestions(when) {
    const abilityTracker = this.abilityTracker;
    const getAbility = spellId => abilityTracker.getAbility(spellId);

    const flashOfLight = getAbility(SPELLS.FLASH_OF_LIGHT.id);
    const holyLight = getAbility(SPELLS.HOLY_LIGHT.id);

    const iolFlashOfLights = flashOfLight.healingIolHits || 0;
    const iolHolyLights = holyLight.healingIolHits || 0;
    const totalIols = iolFlashOfLights + iolHolyLights;

    const iolFoLToHLCastRatio = iolFlashOfLights / totalIols;

    when(iolFoLToHLCastRatio).isLessThan(0.7)
      .addSuggestion((suggest, actual, recommended) => {
        return suggest(Your IoL FoL to HL cast ratio can likely be improved. When you get an <SpellLink id={SPELLS.INFUSION_OF_LIGHT.id} /> proc try to cast <SpellLink id={SPELLS.FLASH_OF_LIGHT.id} /> as much as possible, it is a considerably stronger heal ({iolFlashOfLights} Flash of Lights.)
          .icon(SPELLS.INFUSION_OF_LIGHT.icon)
          .actual(`${formatPercentage(iolFoLToHLCastRatio)}%) to ${iolHolyLights} Holy Lights (${formatPercentage(1 - iolFoLToHLCastRatio)}%) cast with Infusion of Light`)
          .recommended(`>${formatPercentage(recommended)}% is recommended`)
          .regular(recommended - 0.1).major(recommended - 0.3);
      });
  }
  statistic() {
    const abilityTracker = this.abilityTracker;
    const getAbility = spellId => abilityTracker.getAbility(spellId);

    const flashOfLight = getAbility(SPELLS.FLASH_OF_LIGHT.id);
    const holyLight = getAbility(SPELLS.HOLY_LIGHT.id);

    const iolFlashOfLights = flashOfLight.healingIolHits || 0;
    const iolHolyLights = holyLight.healingIolHits || 0;
    const totalIols = iolFlashOfLights + iolHolyLights;

    const iolFoLToHLCastRatio = iolFlashOfLights / totalIols;

    return (
      <StatisticBox
        icon={<SpellIcon id={SPELLS.INFUSION_OF_LIGHT.id} />}
        value={`${formatPercentage(iolFoLToHLCastRatio)} %`}
        label="IoL FoL to HL cast ratio"
        tooltip={`The Infusion of Light Flash of Light to Infusion of Light Holy Light usage ratio is how many Flash of Lights you cast compared to Holy Lights during the Infusion of Light proc. You cast ${iolFlashOfLights} Flash of Lights and ${iolHolyLights} Holy Lights during Infusion of Light.`}
      />
    );
  }
  statisticOrder = STATISTIC_ORDER.CORE(40);
}

export default InfusionOfLightCastRatio;
<commit_msg>Fix IoL cast ratio suggestion text<commit_after>import React from 'react';

import SPELLS from 'common/SPELLS';
import SpellIcon from 'common/SpellIcon';
import SpellLink from 'common/SpellLink';
import { formatPercentage } from 'common/format';

import Module from 'Parser/Core/Module';

import StatisticBox, { STATISTIC_ORDER } from 'Main/StatisticBox';

import PaladinAbilityTracker from './PaladinAbilityTracker';

class InfusionOfLightCastRatio extends Module {
  static dependencies = {
    abilityTracker: PaladinAbilityTracker,
  };

  suggestions(when) {
    const abilityTracker = this.abilityTracker;
    const getAbility = spellId => abilityTracker.getAbility(spellId);

    const flashOfLight = getAbility(SPELLS.FLASH_OF_LIGHT.id);
    const holyLight = getAbility(SPELLS.HOLY_LIGHT.id);

    const iolFlashOfLights = flashOfLight.healingIolHits || 0;
    const iolHolyLights = holyLight.healingIolHits || 0;
    const totalIols = iolFlashOfLights + iolHolyLights;

    const iolFoLToHLCastRatio = iolFlashOfLights / totalIols;

    when(iolFoLToHLCastRatio).isLessThan(0.7)
      .addSuggestion((suggest, actual, recommended) => {
        return suggest(Your IoL FoL to HL cast ratio can likely be improved. When you get an <SpellLink id={SPELLS.INFUSION_OF_LIGHT.id} /> proc try to cast <SpellLink id={SPELLS.FLASH_OF_LIGHT.id} /> as much as possible, it is a considerably stronger heal.)
          .icon(SPELLS.INFUSION_OF_LIGHT.icon)
          .actual(`${iolFlashOfLights} Flash of Lights (${formatPercentage(iolFoLToHLCastRatio)}%) to ${iolHolyLights} Holy Lights (${formatPercentage(1 - iolFoLToHLCastRatio)}%) cast with Infusion of Light`)
          .recommended(`>${formatPercentage(recommended)}% is recommended`)
          .regular(recommended - 0.1).major(recommended - 0.3);
      });
  }
  statistic() {
    const abilityTracker = this.abilityTracker;
    const getAbility = spellId => abilityTracker.getAbility(spellId);

    const flashOfLight = getAbility(SPELLS.FLASH_OF_LIGHT.id);
    const holyLight = getAbility(SPELLS.HOLY_LIGHT.id);

    const iolFlashOfLights = flashOfLight.healingIolHits || 0;
    const iolHolyLights = holyLight.healingIolHits || 0;
    const totalIols = iolFlashOfLights + iolHolyLights;

    const iolFoLToHLCastRatio = iolFlashOfLights / totalIols;

    return (
      <StatisticBox
        icon={<SpellIcon id={SPELLS.INFUSION_OF_LIGHT.id} />}
        value={`${formatPercentage(iolFoLToHLCastRatio)} %`}
        label="IoL FoL to HL cast ratio"
        tooltip={`The Infusion of Light Flash of Light to Infusion of Light Holy Light usage ratio is how many Flash of Lights you cast compared to Holy Lights during the Infusion of Light proc. You cast ${iolFlashOfLights} Flash of Lights and ${iolHolyLights} Holy Lights during Infusion of Light.`}
      />
    );
  }
  statisticOrder = STATISTIC_ORDER.CORE(40);
}

export default InfusionOfLightCastRatio;
<|endoftext|>


<commit_before>(function (tempura, core) {
  // information
  tempura.name = 'tempura';
  tempura.version = '0.0.5dev';

  // settings
  tempura.templates = {};
  tempura.processors = {};
  tempura.prePipeline = function (value, valueName, index, hasNext) {
    return value;
  };
  tempura.postPipeline = function (value, valueName, index, hasNext) {
    return value == null ? '': value;
  };
  tempura.noSuchValue = function (valueName) {
    throw new Error('The value "' + valueName + '" is not found.');
  };
  tempura.noSuchPartial = function (partialName) {
    throw new Error('The partial "' + partialName + '" is not found.');
  };
  tempura.noSuchProcessor = function (processorName, value, valueName) {
    throw new Error('The processor "' + processorName + '" for the value "' + valueName + '" is not found.');
  };

  // behavior
  tempura.prepare = function (source, options) {
    var opts = {};
    if (typeof source !== 'string' && typeof source !== 'function') {
      throw new Error('The argument "source" must be a string or a function.');
    }
    options = options || {};
    opts.noSuchValue = options.noSuchValue || tempura.noSuchValue;
    opts.noSuchPartial = options.noSuchPartial || tempura.noSuchPartial;
    opts.noSuchProcessor = options.noSuchProcessor || tempura.noSuchProcessor;
    opts.prePipeline = options.prePipeline || tempura.prePipeline;
    opts.postPipeline = options.postPipeline || tempura.postPipeline;
    opts.templates = core.extend({}, options.templates, tempura.templates);
    opts.processors = core.extend({}, options.processors, tempura.processors);
    if (typeof opts.noSuchValue !== 'function') {
      throw new Error('The "noSuchValue" option or setting must be function.');
    }
    if (typeof opts.noSuchPartial !== 'function') {
      throw new Error('The "noSuchPartial" option or setting must be function.');
    }
    if (typeof opts.noSuchProcessor !== 'function') {
      throw new Error('The "noSuchProcessor" option or setting must be function.');
    }
    if (typeof opts.prePipeline !== 'function') {
      throw new Error('The "prePipeline" option or setting must be function.');
    }
    if (typeof opts.postPipeline !== 'function') {
      throw new Error('The "postPipeline" option or setting must be function.');
    }
    return core.prepare(source, opts);
  };

  if (typeof module !== 'undefined') {
    module.exports = tempura;
  }
}(typeof module !== 'undefined' ? {} : tempura,
  typeof module !== 'undefined' ? require('./internal/core') : tempura.internal.core));<commit_msg>improve error messages.<commit_after>(function (tempura, core) {
  // information
  tempura.name = 'tempura';
  tempura.version = '0.0.5dev';

  // settings
  tempura.templates = {};
  tempura.processors = {};
  tempura.prePipeline = function (value, valueName, index, hasNext) {
    return value;
  };
  tempura.postPipeline = function (value, valueName, index, hasNext) {
    return value == null ? '': value;
  };
  tempura.noSuchValue = function (valueName) {
    throw new Error('The value "' + valueName + '" is not found.');
  };
  tempura.noSuchPartial = function (partialName) {
    throw new Error('The partial "' + partialName + '" is not found.');
  };
  tempura.noSuchProcessor = function (processorName, value, valueName) {
    throw new Error('The processor "' + processorName + '" for the value "' + valueName + '" is not found.');
  };

  // behavior
  tempura.prepare = function (source, options) {
    var opts = {};
    if (typeof source !== 'string' && typeof source !== 'function') {
      throw new Error('The argument "source" must be a string or a function.');
    }
    options = options || {};
    opts.noSuchValue = options.noSuchValue || tempura.noSuchValue;
    opts.noSuchPartial = options.noSuchPartial || tempura.noSuchPartial;
    opts.noSuchProcessor = options.noSuchProcessor || tempura.noSuchProcessor;
    opts.prePipeline = options.prePipeline || tempura.prePipeline;
    opts.postPipeline = options.postPipeline || tempura.postPipeline;
    opts.templates = core.extend({}, options.templates, tempura.templates);
    opts.processors = core.extend({}, options.processors, tempura.processors);
    if (typeof opts.noSuchValue !== 'function') {
      throw new Error('The "noSuchValue" option or setting must be a function.');
    }
    if (typeof opts.noSuchPartial !== 'function') {
      throw new Error('The "noSuchPartial" option or setting must be a function.');
    }
    if (typeof opts.noSuchProcessor !== 'function') {
      throw new Error('The "noSuchProcessor" option or setting must be a function.');
    }
    if (typeof opts.prePipeline !== 'function') {
      throw new Error('The "prePipeline" option or setting must be a function.');
    }
    if (typeof opts.postPipeline !== 'function') {
      throw new Error('The "postPipeline" option or setting must be a function.');
    }
    return core.prepare(source, opts);
  };

  if (typeof module !== 'undefined') {
    module.exports = tempura;
  }
}(typeof module !== 'undefined' ? {} : tempura,
  typeof module !== 'undefined' ? require('./internal/core') : tempura.internal.core));<|endoftext|>


<commit_before>
var config = require('./config').config;

var fs = require('fs');

var dontPersist = process.argv.length > 1 && (process.argv.slice(-1)[0] == ('--no-persistence'));

exports.handler = (function() {
  var url=require('url'),
    crypto=require('crypto'),
    tokens, lastModified, contentType, content;

  function resetState() {
    tokens = {}, lastModified = {}, contentType = {}, content = {};
  }

  function saveState(name, value) {
    fs.writeFile("server-state/" + name + ".json", JSON.stringify(value), function() { });
  }

  function loadState(name) {
    if(fs.existsSync("server-state/" + name + ".json")) {
      return JSON.parse(fs.readFileSync("server-state/" + name + ".json"));
    } else {
      return {}
    }
  }

  function saveData() {
    if(dontPersist) {
      return;
    }
    if(! fs.existsSync("server-state")) {
      fs.mkdirSync("server-state");
    }
    saveState('tokens', tokens);
    saveState('lastModified', lastModified);
    saveState('contentType', contentType);
    saveState('content', content);
  }

  function loadData() {
    if(dontPersist) {
      return;
    }
    tokens = loadState('tokens');
    lastModified = loadState('lastModified');
    contentType = loadState('contentType');
    content = loadState('content');

    console.log("DATA LOADED", tokens, lastModified, contentType, content);
  }

  function makeScopePaths(userName, scopes) {
    var scopePaths=[];
    for(var i=0; i<scopes.length; i++) {
      var thisScopeParts = scopes[i].split(':');
      if(thisScopeParts[0]=='') {
        scopePaths.push(userName+'/:'+thisScopeParts[1]);
      } else {
        scopePaths.push(userName+'/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
        scopePaths.push(userName+'/public/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
      }
    }
    return scopePaths;
  }

  function createInitialTokens() {
    if(! config.initialTokens) {
      return;
    }
    for(var token in config.initialTokens) {
      var scopePaths = makeScopePaths(
        config.defaultUserName, config.initialTokens[token]
      );
      console.log('adding ',scopePaths,' for', token);
      tokens[token] = scopePaths;
    }
  }

  function createToken(userName, scopes, cb) {
    crypto.randomBytes(48, function(ex, buf) {
      var token = buf.toString('hex');
      var scopePaths = makeScopePaths(userName, scopes);
      console.log('createToken ',userName,scopes);
      console.log('adding ',scopePaths,' for',token);
      tokens[token] = scopePaths;
      cb(token);
    });
  }
  function mayRead(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          } else {
            console.log(path.substring(0, scopeParts[0].length)+' != '+ scopeParts[0]);
          }
        }
      }
    } else {
      var pathParts = path.split('/');
      return (pathParts[0]=='' && pathParts[2]=='public' && path.substr(-1) != '/');
    }
  }
  function mayWrite(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(scopeParts.length==2 && scopeParts[1]=='rw' && path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          }
        }
      }
    }
  }
  function writeHead(res, status, origin, timestamp, contentType) {
    var headers = {
      'Access-Control-Allow-Origin': (origin?origin:'*'),
      'Access-Control-Allow-Headers': 'Content-Type, Authorization, Origin',
      'Access-Control-Allow-Methods': 'GET, PUT, DELETE',
    };
    if(timestamp) {
      headers['last-modified']= new Date(timestamp).toString();
    }
    if(contentType) {
      headers['content-type']= contentType;
    }
    res.writeHead(status, headers);
  }
  function writeRaw(res, contentType, content, origin, timestamp) {
    console.log('access-control-allow-origin:'+ (origin?origin:'*'));
    console.log(contentType);
    console.log(content);
    writeHead(res, 200, origin, timestamp, contentType);
    res.write(content);
    res.end();
  }
  function writeJson(res, obj, origin, timestamp) {
    writeRaw(res, 'application/json', JSON.stringify(obj), origin, timestamp);
  }
  function writeHtml(res, html) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">'+html+'');
    res.end();
  }
  function give404(res, origin) {
    console.log('404');
    console.log(content);
    writeHead(res, 404, origin, 'now');
    res.end();
  }
  function computerSaysNo(res, origin) {
    console.log('COMPUTER_SAYS_NO');
    console.log(tokens);
    writeHead(res, 401, origin, 'now');
    res.end();
  }

  function toHtml(str) {
    return str
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"');
  }
  function portal(urlObj, res) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">
');
    var scopes = {
      'http://todomvc.michiel.5apps.com/': ['tasks:rw'],
      'http://litewrite.michiel.5apps.com/': ['documents:rw'],
      'http://docrastinate.michiel.5apps.com/': ['documents:rw'],
      'http://expenses.michiel.5apps.com/': ['money:rw'],
      'http://evanw-continuous-calendar.michiel.5apps.com/': ['calendar:rw'],
      'http://seven20.epic720.5apps.com/manage.htm': [':rw']
    };
    var outstanding = 0;
    for(var i in scopes) {
      outstanding++;
      (function(i) {
        createToken(config.defaultUserName, scopes[i], function(token) {
          res.write('
'+i+'
');
            +'&access_token='+token+'">'+i+'
');
          outstanding--;
          if(outstanding==0) {
            res.write('
');
            res.end();
          }
        });
      })(i);
    }
  }
  function webfinger(urlObj, res) {
    console.log('WEBFINGER');
    if(urlObj.query['resource']) {
      userAddress = urlObj.query['resource'].substring('acct:'.length);
      userName = userAddress.split('@')[0];
    }
    writeJson(res, {
      links:[{
        href: config.protocol+'://'+config.host+'/storage/'+userName,
        rel: "remoteStorage",
        type: "https://www.w3.org/community/rww/wiki/read-write-web-00#simple",
        properties: {
          'auth-method': "https://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2",
          'auth-endpoint': config.protocol+'://'+config.host+'/auth/'+userName
        }
      }]
    });
  }
  function oauth(urlObj, res) {
    console.log('OAUTH');
    var scopes = decodeURIComponent(urlObj.query['scope']).split(' '),
      clientId = decodeURIComponent(urlObj.query['client_id']),
      redirectUri = decodeURIComponent(urlObj.query['redirect_uri']),
      clientIdToMatch,
      userName;
    if(redirectUri.split('://').length<2) {
      clientIdToMatch=redirectUri;
    } else {
      clientIdToMatch = redirectUri.split('://')[1].split('/')[0];
    }
    if(clientId != clientIdToMatch) {
      writeHtml(res, 'we do not trust this combination of client_id and redirect_uri');
    } else {
      var userName = urlObj.pathname.substring('/auth/'.length);
      createToken(userName, scopes, function(token) {
        writeHtml(res, 'Allow');
      });
    }
  }
  function storage(req, urlObj, res) {
    var path=urlObj.pathname.substring('/storage/'.length);
    if(req.method=='OPTIONS') {
      console.log('OPTIONS ', req.headers);
      writeJson(res, null, req.headers.origin);
    } else if(req.method=='GET') {
      console.log('GET');
      if(mayRead(req.headers.authorization, path)) {
        if(content[path]) {
          if(path.substr(-1)=='/') {
            writeJson(res, content[path], req.headers.origin, 0);
          } else {
            writeRaw(res, contentType[path], content[path], req.headers.origin, lastModified[path]);
          }
        } else {
          if(path.substr(-1)=='/') {
            writeJson(res, {}, req.headers.origin, 0);
          } else {
            give404(res, req.headers.origin);
          }
        }
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='PUT') {
      console.log('PUT');
      if(mayWrite(req.headers.authorization, path) && path.substr(-1)!='/') {
        var dataStr = '';
        req.on('data', function(chunk) {
          dataStr+=chunk;
        });
        req.on('end', function(chunk) {
          var timestamp = new Date().getTime();
          content[path]=dataStr;
          contentType[path]=req.headers['content-type'];
          console.log('stored '+path, content[path], contentType[path]);
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var timestamp=new Date().getTime();
          console.log(pathParts);
          var fileItself=true;
          while(pathParts.length > 1) {
            var thisPart = pathParts.pop();
            if(fileItself) {
              fileItself=false;
            } else {
              thisPart += '/';
            }
            if(!content[pathParts.join('/')+'/']) {
              content[pathParts.join('/')+'/'] = {};
            }
            content[pathParts.join('/')+'/'][thisPart]=timestamp;
            console.log('stored parent '+pathParts.join('/')+'/ ['+thisPart+']='+timestamp, content[pathParts.join('/')+'/']);
          }
          console.log('content:', content);
          console.log('contentType:', contentType);
          console.log('lastModified:', lastModified);
          writeJson(res, null, req.headers.origin, timestamp);
        });
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='DELETE') {
      console.log('DELETE');
      if(mayWrite(req.headers.authorization, path)) {
          var timestamp = new Date().getTime();
          delete content[path];
          delete contentType[path];
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var thisPart = pathParts.pop();
          if(content[pathParts.join('/')+'/']) {
            console.log('delete content['+pathParts.join('/')+'/]['+thisPart+']');
            delete content[pathParts.join('/')+'/'][thisPart];
          }
          console.log(content);
          writeJson(res, null, req.headers.origin, timestamp);
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else {
      console.log('ILLEGAL '+req.method);
      computerSaysNo(res, req.headers.origin);
    }
  }
  function serve(req, res, staticsMap) {
    var urlObj = url.parse(req.url, true), userAddress, userName;
    console.log(urlObj);
    if(urlObj.pathname == '/') {
      console.log('PORTAL');
      portal(urlObj, res);
    } else if(urlObj.pathname == '/.well-known/host-meta.json') {//TODO: implement rest of webfinger
      console.log('HOST-META');
      webfinger(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/auth/'.length) == '/auth/') {
      console.log('OAUTH');
      oauth(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/storage/'.length) == '/storage/') {
      console.log('STORAGE');
      storage(req, urlObj, res);
    } else if(req.method == 'POST' && urlObj.pathname.substring(0, '/reset'.length) == '/reset') { // clear data; used in tests.
      resetState();
      writeJson(res, { forgot: 'everything' });
    } else {
      console.log('UNKNOWN');
      writeJson(res, urlObj.query);
    }
  }

  resetState();
  loadData();
  createInitialTokens();

  return {
    serve: serve
  };
})();

if(require.main==module) {//if this file is directly called from the CLI
  require('http').createServer(exports.handler.serve).listen(config.port);
  console.log("Example server started on 0.0.0.0:" + config.port);
}

<commit_msg>example server: added fs.existsSync for older node versions<commit_after>
var config = require('./config').config;

var fs = require('fs');

var dontPersist = process.argv.length > 1 && (process.argv.slice(-1)[0] == ('--no-persistence'));

if(! fs.existsSync) {
  fs.existsSync = function(path) {
    try {
      fs.statSync(path);
      return true;
    } catch(e) {
      return false;
    }
  }
}

exports.handler = (function() {
  var url=require('url'),
    crypto=require('crypto'),
    tokens, lastModified, contentType, content;

  function resetState() {
    tokens = {}, lastModified = {}, contentType = {}, content = {};
  }

  function saveState(name, value) {
    fs.writeFile("server-state/" + name + ".json", JSON.stringify(value), function() { });
  }

  function loadState(name) {
    if(fs.existsSync("server-state/" + name + ".json")) {
      return JSON.parse(fs.readFileSync("server-state/" + name + ".json"));
    } else {
      return {}
    }
  }

  function saveData() {
    if(dontPersist) {
      return;
    }
    if(! fs.existsSync("server-state")) {
      fs.mkdirSync("server-state");
    }
    saveState('tokens', tokens);
    saveState('lastModified', lastModified);
    saveState('contentType', contentType);
    saveState('content', content);
  }

  function loadData() {
    if(dontPersist) {
      return;
    }
    tokens = loadState('tokens');
    lastModified = loadState('lastModified');
    contentType = loadState('contentType');
    content = loadState('content');

    console.log("DATA LOADED", tokens, lastModified, contentType, content);
  }

  function makeScopePaths(userName, scopes) {
    var scopePaths=[];
    for(var i=0; i<scopes.length; i++) {
      var thisScopeParts = scopes[i].split(':');
      if(thisScopeParts[0]=='') {
        scopePaths.push(userName+'/:'+thisScopeParts[1]);
      } else {
        scopePaths.push(userName+'/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
        scopePaths.push(userName+'/public/'+thisScopeParts[0]+'/:'+thisScopeParts[1]);
      }
    }
    return scopePaths;
  }

  function createInitialTokens() {
    if(! config.initialTokens) {
      return;
    }
    for(var token in config.initialTokens) {
      var scopePaths = makeScopePaths(
        config.defaultUserName, config.initialTokens[token]
      );
      console.log('adding ',scopePaths,' for', token);
      tokens[token] = scopePaths;
    }
  }

  function createToken(userName, scopes, cb) {
    crypto.randomBytes(48, function(ex, buf) {
      var token = buf.toString('hex');
      var scopePaths = makeScopePaths(userName, scopes);
      console.log('createToken ',userName,scopes);
      console.log('adding ',scopePaths,' for',token);
      tokens[token] = scopePaths;
      cb(token);
    });
  }
  function mayRead(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          } else {
            console.log(path.substring(0, scopeParts[0].length)+' != '+ scopeParts[0]);
          }
        }
      }
    } else {
      var pathParts = path.split('/');
      return (pathParts[0]=='' && pathParts[2]=='public' && path.substr(-1) != '/');
    }
  }
  function mayWrite(authorizationHeader, path) {
    if(authorizationHeader) {
      var scopes = tokens[authorizationHeader.substring('Bearer '.length)];
      if(scopes) {
        for(var i=0; i<scopes.length; i++) {
          var scopeParts = scopes[i].split(':');
          if(scopeParts.length==2 && scopeParts[1]=='rw' && path.substring(0, scopeParts[0].length)==scopeParts[0]) {
            return true;
          }
        }
      }
    }
  }
  function writeHead(res, status, origin, timestamp, contentType) {
    var headers = {
      'Access-Control-Allow-Origin': (origin?origin:'*'),
      'Access-Control-Allow-Headers': 'Content-Type, Authorization, Origin',
      'Access-Control-Allow-Methods': 'GET, PUT, DELETE',
    };
    if(timestamp) {
      headers['last-modified']= new Date(timestamp).toString();
    }
    if(contentType) {
      headers['content-type']= contentType;
    }
    res.writeHead(status, headers);
  }
  function writeRaw(res, contentType, content, origin, timestamp) {
    console.log('access-control-allow-origin:'+ (origin?origin:'*'));
    console.log(contentType);
    console.log(content);
    writeHead(res, 200, origin, timestamp, contentType);
    res.write(content);
    res.end();
  }
  function writeJson(res, obj, origin, timestamp) {
    writeRaw(res, 'application/json', JSON.stringify(obj), origin, timestamp);
  }
  function writeHtml(res, html) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">'+html+'');
    res.end();
  }
  function give404(res, origin) {
    console.log('404');
    console.log(content);
    writeHead(res, 404, origin, 'now');
    res.end();
  }
  function computerSaysNo(res, origin) {
    console.log('COMPUTER_SAYS_NO');
    console.log(tokens);
    writeHead(res, 401, origin, 'now');
    res.end();
  }

  function toHtml(str) {
    return str
      .replace(/&/g, '&')
      .replace(/</g, '<')
      .replace(/>/g, '>')
      .replace(/"/g, '"');
  }
  function portal(urlObj, res) {
    res.writeHead(200, {
      'content-type': 'text/html'
    });
    res.write('<!DOCTYPE html lang="en"><title>'+config.host+'</title><meta charset="utf-8">
');
    var scopes = {
      'http://todomvc.michiel.5apps.com/': ['tasks:rw'],
      'http://litewrite.michiel.5apps.com/': ['documents:rw'],
      'http://docrastinate.michiel.5apps.com/': ['documents:rw'],
      'http://expenses.michiel.5apps.com/': ['money:rw'],
      'http://evanw-continuous-calendar.michiel.5apps.com/': ['calendar:rw'],
      'http://seven20.epic720.5apps.com/manage.htm': [':rw']
    };
    var outstanding = 0;
    for(var i in scopes) {
      outstanding++;
      (function(i) {
        createToken(config.defaultUserName, scopes[i], function(token) {
          res.write('
'+i+'
');
            +'&access_token='+token+'">'+i+'
');
          outstanding--;
          if(outstanding==0) {
            res.write('
');
            res.end();
          }
        });
      })(i);
    }
  }
  function webfinger(urlObj, res) {
    console.log('WEBFINGER');
    if(urlObj.query['resource']) {
      userAddress = urlObj.query['resource'].substring('acct:'.length);
      userName = userAddress.split('@')[0];
    }
    writeJson(res, {
      links:[{
        href: config.protocol+'://'+config.host+'/storage/'+userName,
        rel: "remoteStorage",
        type: "https://www.w3.org/community/rww/wiki/read-write-web-00#simple",
        properties: {
          'auth-method': "https://tools.ietf.org/html/draft-ietf-oauth-v2-26#section-4.2",
          'auth-endpoint': config.protocol+'://'+config.host+'/auth/'+userName
        }
      }]
    });
  }
  function oauth(urlObj, res) {
    console.log('OAUTH');
    var scopes = decodeURIComponent(urlObj.query['scope']).split(' '),
      clientId = decodeURIComponent(urlObj.query['client_id']),
      redirectUri = decodeURIComponent(urlObj.query['redirect_uri']),
      clientIdToMatch,
      userName;
    if(redirectUri.split('://').length<2) {
      clientIdToMatch=redirectUri;
    } else {
      clientIdToMatch = redirectUri.split('://')[1].split('/')[0];
    }
    if(clientId != clientIdToMatch) {
      writeHtml(res, 'we do not trust this combination of client_id and redirect_uri');
    } else {
      var userName = urlObj.pathname.substring('/auth/'.length);
      createToken(userName, scopes, function(token) {
        writeHtml(res, 'Allow');
      });
    }
  }
  function storage(req, urlObj, res) {
    var path=urlObj.pathname.substring('/storage/'.length);
    if(req.method=='OPTIONS') {
      console.log('OPTIONS ', req.headers);
      writeJson(res, null, req.headers.origin);
    } else if(req.method=='GET') {
      console.log('GET');
      if(mayRead(req.headers.authorization, path)) {
        if(content[path]) {
          if(path.substr(-1)=='/') {
            writeJson(res, content[path], req.headers.origin, 0);
          } else {
            writeRaw(res, contentType[path], content[path], req.headers.origin, lastModified[path]);
          }
        } else {
          if(path.substr(-1)=='/') {
            writeJson(res, {}, req.headers.origin, 0);
          } else {
            give404(res, req.headers.origin);
          }
        }
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='PUT') {
      console.log('PUT');
      if(mayWrite(req.headers.authorization, path) && path.substr(-1)!='/') {
        var dataStr = '';
        req.on('data', function(chunk) {
          dataStr+=chunk;
        });
        req.on('end', function(chunk) {
          var timestamp = new Date().getTime();
          content[path]=dataStr;
          contentType[path]=req.headers['content-type'];
          console.log('stored '+path, content[path], contentType[path]);
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var timestamp=new Date().getTime();
          console.log(pathParts);
          var fileItself=true;
          while(pathParts.length > 1) {
            var thisPart = pathParts.pop();
            if(fileItself) {
              fileItself=false;
            } else {
              thisPart += '/';
            }
            if(!content[pathParts.join('/')+'/']) {
              content[pathParts.join('/')+'/'] = {};
            }
            content[pathParts.join('/')+'/'][thisPart]=timestamp;
            console.log('stored parent '+pathParts.join('/')+'/ ['+thisPart+']='+timestamp, content[pathParts.join('/')+'/']);
          }
          console.log('content:', content);
          console.log('contentType:', contentType);
          console.log('lastModified:', lastModified);
          writeJson(res, null, req.headers.origin, timestamp);
        });
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else if(req.method=='DELETE') {
      console.log('DELETE');
      if(mayWrite(req.headers.authorization, path)) {
          var timestamp = new Date().getTime();
          delete content[path];
          delete contentType[path];
          lastModified[path]=timestamp;
          saveData();
          var pathParts=path.split('/');
          var thisPart = pathParts.pop();
          if(content[pathParts.join('/')+'/']) {
            console.log('delete content['+pathParts.join('/')+'/]['+thisPart+']');
            delete content[pathParts.join('/')+'/'][thisPart];
          }
          console.log(content);
          writeJson(res, null, req.headers.origin, timestamp);
      } else {
        computerSaysNo(res, req.headers.origin);
      }
    } else {
      console.log('ILLEGAL '+req.method);
      computerSaysNo(res, req.headers.origin);
    }
  }
  function serve(req, res, staticsMap) {
    var urlObj = url.parse(req.url, true), userAddress, userName;
    console.log(urlObj);
    if(urlObj.pathname == '/') {
      console.log('PORTAL');
      portal(urlObj, res);
    } else if(urlObj.pathname == '/.well-known/host-meta.json') {//TODO: implement rest of webfinger
      console.log('HOST-META');
      webfinger(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/auth/'.length) == '/auth/') {
      console.log('OAUTH');
      oauth(urlObj, res);
    } else if(urlObj.pathname.substring(0, '/storage/'.length) == '/storage/') {
      console.log('STORAGE');
      storage(req, urlObj, res);
    } else if(req.method == 'POST' && urlObj.pathname.substring(0, '/reset'.length) == '/reset') { // clear data; used in tests.
      resetState();
      writeJson(res, { forgot: 'everything' });
    } else {
      console.log('UNKNOWN');
      writeJson(res, urlObj.query);
    }
  }

  resetState();
  loadData();
  createInitialTokens();

  return {
    serve: serve
  };
})();

if(require.main==module) {//if this file is directly called from the CLI
  require('http').createServer(exports.handler.serve).listen(config.port);
  console.log("Example server started on 0.0.0.0:" + config.port);
}

<|endoftext|>


<commit_before>import { get, download } from '../src/getVideo';
import { loadConfig } from '../src/config';
import { NOT_FOUND } from '../src/errorsCode.json';

describe('getVideo.get()', () => {
  describe('Parâmetros inválidos', () => {
    // IDs inválidos
    const invalidIds = [
      { label: 'inteiros', value: [1, 2, 3] },
      {
        label: 'vídeo que não existe',
        value: 'abc',
        func: done => (code) => {
          if(code === NOT_FOUND) return done();
          return done(new Error(`o código de erro é ${code}`));
        }
      }
    ];

    invalidIds.forEach((invalidId) => {
      it(invalidId.label, (done) => {
        get(invalidId.value)
          .then(() => done(new Error('Parâmetro válido')))
          .catch(invalidId.func ? invalidId.func(done) : () => done());
      });
    });
  });

  describe('Baixar vídeo com ID válido', () => {
    const validId = 'l2wSlvWOvio';

    let video;

    it('carregando configurações', (done) => {
      loadConfig().then(() => done());
    });

    it('adicionar vídeo na lista de downloads', (done) => {
      video = get(validId)
        .then(() => done())
        .catch(() => done(new Error('Nâo foi possível adicionar o vídeo na lista de downloads')));
    });

    it('baixando...', (done) => {
      video.then(() => {
        download(validId)
          .then(([{ downloadPromise }]) => {
            downloadPromise
              .then(() => done());
          });
      });
    });
  });
});
<commit_msg>server: corrige erro nos testes<commit_after>import { get, download } from '../src/getVideo';
import { loadConfig } from '../src/config';
import { NOT_FOUND } from '../src/errorsCode.json';

describe('getVideo.get()', () => {
  describe('Parâmetros inválidos', () => {
    // IDs inválidos
    const invalidIds = [
      { label: 'inteiros', value: [1, 2, 3] },
      {
        label: 'vídeo que não existe',
        value: 'abc',
        func: done => (error) => {
          if(error.code === NOT_FOUND) return done();
          return done(new Error(`o código de erro é ${code}`));
        }
      }
    ];

    invalidIds.forEach((invalidId) => {
      it(invalidId.label, (done) => {
        get(invalidId.value)
          .then(() => done(new Error('Parâmetro válido')))
          .catch(invalidId.func ? invalidId.func(done) : () => done());
      });
    });
  });

  describe('Baixar vídeo com ID válido', () => {
    const validId = 'l2wSlvWOvio';

    let video;

    it('carregando configurações', (done) => {
      loadConfig().then(() => done());
    });

    it('adicionar vídeo na lista de downloads', (done) => {
      video = get(validId)
        .then(() => done())
        .catch(() => done(new Error('Nâo foi possível adicionar o vídeo na lista de downloads')));
    });

    it('baixando...', (done) => {
      video.then(() => {
        download(validId)
          .then(([{ downloadPromise }]) => {
            downloadPromise
              .then(() => done());
          });
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

var path = require('path');
var bufferEqual = require('buffer-equal');
var isJpg = require('is-jpg');
var isProgressive = require('is-progressive');
var read = require('vinyl-file').read;
var test = require('ava');
var vinylSmallestJpeg = require('vinyl-smallest-jpeg');
var file = vinylSmallestJpeg();
var jpegRecompress = require('../');

test('optimize a JPG', function (t) {
	t.plan(2);

	read(path.join(__dirname, 'fixtures/test.jpg'), function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress()();
		var size = file.contents.length;

		stream.on('data', function (data) {
			t.assert(data.contents.length < size, data.contents.length);
			t.assert(isJpg(data.contents));
			t.assert(isProgressive.buffer(data.contents));
		});

		stream.end(file);
	});
});

test('support jpeg-recompress options', function (t) {
	t.plan(2);

	read(path.join(__dirname, 'fixtures/test.jpg'), function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress({progressive: false})();

		stream.on('data', function (data) {
			t.assert(!isProgressive.buffer(data.contents));
		});

		stream.end(file);
	});
});

test('skip optimizing a non-JPG file', function (t) {
	t.plan(2);

	read(__filename, function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress()();
		var contents = file.contents;

		stream.on('data', function (data) {
			t.assert(bufferEqual(data.contents, contents));
		});

		stream.end(file);
	});
});

test('skip optimizing an already optimized JPG', function (t) {
	t.plan(1);

	var stream = jpegRecompress({method: 'ms-ssim'})();

	stream.on('data', function (data) {
		t.assert(bufferEqual(data.contents, file.contents));
	});

	stream.end(file);
});

test('throw error when a JPG is corrupt', function (t) {
	t.plan(4);

	read(path.join(__dirname, 'fixtures/test-corrupt.jpg'), function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress()();

		stream.on('error', function (err) {
			t.assert(err, err);
			t.assert(path.basename(err.fileName) === 'test-corrupt.jpg', err.fileName);
			t.assert(/Corrupt JPEG data/.test(err.message), err.message);
		});

		stream.end(file);
	});
});
<commit_msg>Fix assertion count<commit_after>'use strict';

var path = require('path');
var bufferEqual = require('buffer-equal');
var isJpg = require('is-jpg');
var isProgressive = require('is-progressive');
var read = require('vinyl-file').read;
var test = require('ava');
var vinylSmallestJpeg = require('vinyl-smallest-jpeg');
var file = vinylSmallestJpeg();
var jpegRecompress = require('../');

test('optimize a JPG', function (t) {
	t.plan(4);

	read(path.join(__dirname, 'fixtures/test.jpg'), function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress()();
		var size = file.contents.length;

		stream.on('data', function (data) {
			t.assert(data.contents.length < size, data.contents.length);
			t.assert(isJpg(data.contents));
			t.assert(isProgressive.buffer(data.contents));
		});

		stream.end(file);
	});
});

test('support jpeg-recompress options', function (t) {
	t.plan(2);

	read(path.join(__dirname, 'fixtures/test.jpg'), function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress({progressive: false})();

		stream.on('data', function (data) {
			t.assert(!isProgressive.buffer(data.contents));
		});

		stream.end(file);
	});
});

test('skip optimizing a non-JPG file', function (t) {
	t.plan(2);

	read(__filename, function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress()();
		var contents = file.contents;

		stream.on('data', function (data) {
			t.assert(bufferEqual(data.contents, contents));
		});

		stream.end(file);
	});
});

test('skip optimizing an already optimized JPG', function (t) {
	t.plan(1);

	var stream = jpegRecompress({method: 'ms-ssim'})();

	stream.on('data', function (data) {
		t.assert(bufferEqual(data.contents, file.contents));
	});

	stream.end(file);
});

test('throw error when a JPG is corrupt', function (t) {
	t.plan(4);

	read(path.join(__dirname, 'fixtures/test-corrupt.jpg'), function (err, file) {
		t.assert(!err, err);

		var stream = jpegRecompress()();

		stream.on('error', function (err) {
			t.assert(err, err);
			t.assert(path.basename(err.fileName) === 'test-corrupt.jpg', err.fileName);
			t.assert(/Corrupt JPEG data/.test(err.message), err.message);
		});

		stream.end(file);
	});
});
<|endoftext|>


<commit_before>/*!
**  bauer-cache -- Disk-cache library.
**  Copyright (c) 2015 Yuri Neves Silveira <http://yneves.com>
**  Licensed under The MIT License <http://opensource.org/licenses/MIT>
**  Distributed on <http://github.com/yneves/node-bauer-cache>
*/
// - -------------------------------------------------------------------- - //

"use strict";

var fs = require("fs");
var path = require("path");
var stream = require("stream");
var factory = require("bauer-factory");

// - -------------------------------------------------------------------- - //

var Cache = factory.createClass({

  // new Cache(options Object) :Cache
  constructor: {
    o: function(options) {
      if (factory.isDefined(options.file)) {
        this.setFile(options.file);
      }
      if (factory.isDefined(options.expires)) {
        this.setExpiration(options.expires);
      }
      if (factory.isDefined(options.json)) {
        this.setJSON(options.json);
      }
    }
  },

  setJSON: {

    // .setJSON(json Boolean) :void
    b: function(isJSON) {
      this.json = isJSON;
    }
  },

  // .isJSON() :Boolean
  isJSON: function() {
    return this.json;
  },

  parseJSON: {

    // .parseJSON(content String, callback Function) :void
    sf: function(content,callback) {
      var error;
      var object;
      try {
        object = JSON.parse(content);
      } catch(e) {
        error = e;
      }
      if (error) {
        callback(error);
      } else {
        callback(null,object);
      }
    }
  },

  setFile: {

    // .setFile(options Object) :void
    o: function(options) {
      if (!factory.isString(options.dir)) {
        throw new Error("invalid directory");
      }
      if (!factory.isString(options.name)) {
        throw new Error("invalid name");
      }
      if (!factory.isString(options.ext)) {
        throw new Error("invalid extension");
      }
      var filename = options.ext ? options.name + "." + options.ext : options.name;
      this.file = path.resolve(options.dir,filename);
    },

    // .setFile(file String) :void
    s: function(file) {
      this.file = path.resolve(file);
    }
  },

  // .getFile() :String
  getFile: function() {
    return this.file;
  },

  setExpiration: {

    // .setExpiration(time Number) :void
    n: function(time) {
      this.expires = time;
    },

    // .setExpiration(expires String) :void
    s: function(expires) {
      var period = parseInt(expires.substr(0,expires.length - 1));
      var suffix = expires.substr(-1,1);
      var multiplier = 0;
      if (suffix === "d") {
        multiplier = 24 * 60 * 60 * 1000;
      } else if (suffix === "w") {
        multiplier = 7 * 24 * 60 * 60 * 1000;
      } else if (suffix === "h") {
        multiplier = 60 * 60 * 1000;
      } else if (suffix === "m") {
        multiplier = 60 * 1000;
      }
      this.expires = period * multiplier;
    }
  },

  // .getExpiration() :Number
  getExpiration: function() {
    return this.expires;
  },

  read: {

    // .read() :Readable
    0: function() {
      var file = this.getFile();
      return fs.createReadStream(file);
    },

    // .read(callback Function) :void
    f: function(callback) {
      var file = this.getFile();
      fs.readFile(file,function(error,buffer) {
        if (error) {
          callback(error);
        } else {
          var content = buffer.toString();
          if (this.isJSON()) {
            this.parseJSON(content,callback);
          } else {
            callback(null,content);
          }
        }
      }.bind(this));
    }
  },

  write: {

    // .write(object Readable, callback Function) :void
    of: function(object,callback) {
      var error;
      var content;
      try {
        content = JSON.stringify(object,null,4);
      } catch(e) {
        error = e;
      }
      if (error) {
        callback(error);
      } else {
        this.write(content,callback);
      }
    },

    // .write(content String, callback Function) :void
    sf: function(content,callback) {
      var file = this.getFile();
      fs.writeFile(file,content,function(error) {
        if (error) {
          callback(error);
        } else {
          callback(null);
        }
      });
    }
  },

  // .exists(callback Function) :void
  exists: {
    f: function(callback) {
      var file = this.getFile();
      fs.exists(file,function(exists) {
        callback(null,exists);
      });
    }
  },

  // .expired(callback Function) :void
  expired: {
    f: function(callback) {
      var file = this.getFile();
      fs.stat(file,function(error,stats) {
        if (error) {
          callback(error);
        } else {
          var now = new Date().getTime();
          var mtime = stats.mtime.getTime();
          var expires = this.getExpiration();
          var expired = mtime < (now - expires);
          callback(null,expired);
        }
      }.bind(this));
    }
  },

  pipe: {

    // .pipe(stream Stream, callback Function) :Stream
    of: function(stream,callback) {
      return stream
        .on("error",function(error) {
          callback(error);
        })
        .pipe(fs.createWriteStream(this.getFile()))
        .on("close",function() {
          callback(null);
        });
    },

    // .pipe(stream Stream) :Stream
    o: function(stream) {
      return stream.pipe(fs.createWriteStream(this.getFile()));
    }

  }

});

// - -------------------------------------------------------------------- - //

module.exports = Cache;

// - -------------------------------------------------------------------- - //
<commit_msg>remove file on error<commit_after>/*!
**  bauer-cache -- Disk-cache library.
**  Copyright (c) 2015 Yuri Neves Silveira <http://yneves.com>
**  Licensed under The MIT License <http://opensource.org/licenses/MIT>
**  Distributed on <http://github.com/yneves/node-bauer-cache>
*/
// - -------------------------------------------------------------------- - //

"use strict";

var fs = require("fs");
var path = require("path");
var stream = require("stream");
var factory = require("bauer-factory");

// - -------------------------------------------------------------------- - //

var Cache = factory.createClass({

  // new Cache(options Object) :Cache
  constructor: {
    o: function(options) {
      if (factory.isDefined(options.file)) {
        this.setFile(options.file);
      }
      if (factory.isDefined(options.expires)) {
        this.setExpiration(options.expires);
      }
      if (factory.isDefined(options.json)) {
        this.setJSON(options.json);
      }
    }
  },

  setJSON: {

    // .setJSON(json Boolean) :void
    b: function(isJSON) {
      this.json = isJSON;
    }
  },

  // .isJSON() :Boolean
  isJSON: function() {
    return this.json;
  },

  parseJSON: {

    // .parseJSON(content String, callback Function) :void
    sf: function(content,callback) {
      var error;
      var object;
      try {
        object = JSON.parse(content);
      } catch(e) {
        error = e;
      }
      if (error) {
        callback(error);
      } else {
        callback(null,object);
      }
    }
  },

  setFile: {

    // .setFile(options Object) :void
    o: function(options) {
      if (!factory.isString(options.dir)) {
        throw new Error("invalid directory");
      }
      if (!factory.isString(options.name)) {
        throw new Error("invalid name");
      }
      if (!factory.isString(options.ext)) {
        throw new Error("invalid extension");
      }
      var filename = options.ext ? options.name + "." + options.ext : options.name;
      this.file = path.resolve(options.dir,filename);
    },

    // .setFile(file String) :void
    s: function(file) {
      this.file = path.resolve(file);
    }
  },

  // .getFile() :String
  getFile: function() {
    return this.file;
  },

  setExpiration: {

    // .setExpiration(time Number) :void
    n: function(time) {
      this.expires = time;
    },

    // .setExpiration(expires String) :void
    s: function(expires) {
      var period = parseInt(expires.substr(0,expires.length - 1));
      var suffix = expires.substr(-1,1);
      var multiplier = 0;
      if (suffix === "d") {
        multiplier = 24 * 60 * 60 * 1000;
      } else if (suffix === "w") {
        multiplier = 7 * 24 * 60 * 60 * 1000;
      } else if (suffix === "h") {
        multiplier = 60 * 60 * 1000;
      } else if (suffix === "m") {
        multiplier = 60 * 1000;
      }
      this.expires = period * multiplier;
    }
  },

  // .getExpiration() :Number
  getExpiration: function() {
    return this.expires;
  },

  read: {

    // .read() :Readable
    0: function() {
      var file = this.getFile();
      return fs.createReadStream(file);
    },

    // .read(callback Function) :void
    f: function(callback) {
      var file = this.getFile();
      fs.readFile(file,function(error,buffer) {
        if (error) {
          callback(error);
        } else {
          var content = buffer.toString();
          if (this.isJSON()) {
            this.parseJSON(content,callback);
          } else {
            callback(null,content);
          }
        }
      }.bind(this));
    }
  },

  write: {

    // .write(object Readable, callback Function) :void
    of: function(object,callback) {
      var error;
      var content;
      try {
        content = JSON.stringify(object,null,4);
      } catch(e) {
        error = e;
      }
      if (error) {
        callback(error);
      } else {
        this.write(content,callback);
      }
    },

    // .write(content String, callback Function) :void
    sf: function(content,callback) {
      var file = this.getFile();
      fs.writeFile(file,content,function(error) {
        if (error) {
          callback(error);
        } else {
          callback(null);
        }
      });
    }
  },

  // .exists(callback Function) :void
  exists: {
    f: function(callback) {
      var file = this.getFile();
      fs.exists(file,function(exists) {
        callback(null,exists);
      });
    }
  },

  // .expired(callback Function) :void
  expired: {
    f: function(callback) {
      var file = this.getFile();
      fs.stat(file,function(error,stats) {
        if (error) {
          callback(error);
        } else {
          var now = new Date().getTime();
          var mtime = stats.mtime.getTime();
          var expires = this.getExpiration();
          var expired = mtime < (now - expires);
          callback(null,expired);
        }
      }.bind(this));
    }
  },

  pipe: {

    // .pipe(stream Stream, callback Function) :Stream
    of: function(stream,callback) {
      var file = this.getFile();
      return stream
        .on("error",function(error) {
          fs.unlink(file,function() {
            callback(error);
          });
        })
        .pipe(fs.createWriteStream(file))
        .on("close",function() {
          callback(null);
        });
    },

    // .pipe(stream Stream) :Stream
    o: function(stream) {
      return stream.pipe(fs.createWriteStream(this.getFile()));
    }

  }

});

// - -------------------------------------------------------------------- - //

module.exports = Cache;

// - -------------------------------------------------------------------- - //
<|endoftext|>