<commit_before>(function() {
  'use strict';

  var app = angular.module('radar.marmoset', []);

  app.directive('checkboxes', function() {
    return {
      require: '?ngModel',
      scope: {
        options: '='
      },
      template: function(element, attrs) {
        if (attrs.inline === 'true') {
          return '<input type="checkbox" ng-model="states[$index]" ng-change="update($index)" /> {{option.label}}';
        } else {
          return '
<input type="checkbox" ng-model="states[$index]" ng-change="update($index)" /> {{option.label}}
';
        }
      },
      link: function(scope, element, attr, ngModel) {
        if (!ngModel) {
          return;
        }

        function updateState() {
          var value = ngModel.$viewValue;
          var options = scope.options || [];

          for (var i = 0; i < options.length; i++) {
            scope.states[i] = options[i].value === value;
          }
        }

        scope.update = function(index) {
          var value = scope.states[index] ? scope.options[index].value : null;
          ngModel.$setViewValue(value);
          updateState();
        };

        scope.states = {};

        scope.$watch('options', updateState);
        ngModel.$render = updateState;
      }
    }
  });

  function intWidget(scope, field) {
    var element = angular.element('<input />');
    element.attr('class', 'form-control');
    element.attr('integer-validator', '');
    element.attr('name', field.name);
    element.attr('type', 'text');
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function floatWidget(scope, field) {
    var element = angular.element('<input />');
    element.attr('class', 'form-control');
    element.attr('number-validator', '');
    element.attr('name', field.name);
    element.attr('type', 'text');
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function stringWidget(scope, field) {
    return angular.element('<input class="form-control" type="text" ng-model="field.data[field.name]" />');
  }

  function dateWidget(scope, field) {
    var element = angular.element('<input />');
    element.attr('class', 'form-control');
    element.attr('date-validator', '');
    element.attr('frm-date-picker', '');
    element.attr('name', field.name);
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function selectWidget(scope, field) {
    // TODO add option for null

    var element = angular.element('<select></select>');
    element.attr('class', 'form-control');
    element.attr('name', field.name);
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-options', 'x.value as x.label for x in field.options');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function radioWidget(scope, field) {
    // TODO add radio for null

    var divElement = angular.element('

');
    divElement.attr('class', 'radio');
    divElement.attr('ng-repeat', 'option in field.options');

    var labelElement = angular.element('');
    divElement.append(labelElement);

    var inputElement = angular.element('<input />');
    inputElement.attr('name', field.name);
    inputElement.attr('type', 'radio');
    inputElement.attr('ng-value', 'option.value');
    inputElement.attr('ng-model', 'field.data[field.name]');
    inputElement.attr('ng-required', 'field.required()');
    labelElement.append(inputElement);

    labelElement.append('{{option.label}}');

    return divElement;
  }

  function yesNoRadioWidget(scope, field) {
    scope.options = [
      {value: true, label: 'Yes'},
      {value: false, label: 'No'},
      {value: null, label: 'Not Answered'}
    ];

    var divElement = angular.element('

');
    divElement.attr('class', 'radio');
    divElement.attr('ng-repeat', 'option in options');

    var labelElement = angular.element('');
    divElement.append(labelElement);

    var inputElement = angular.element('<input />');
    inputElement.attr('name', field.name);
    inputElement.attr('type', 'radio');
    inputElement.attr('ng-value', 'option.value');
    inputElement.attr('ng-model', 'field.data[field.name]');
    inputElement.attr('ng-required', 'field.required()');
    labelElement.append(inputElement);

    labelElement.append('{{option.label}}');

    return divElement;
  }

  function checkboxesWidget(scope, field, data) {
    var element = angular.element('

')
    element.attr('checkboxes', '');
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    element.attr('options', 'field.options');
    element.attr('inline', data.inline ? 'true' : 'false');
    return element;
  }

  function formulaWidget(scope, field) {
    if (field.type === 'date') {
      return angular.element('
{{field.data[field.name] | dateFormat}}
');
    } else {
      return angular.element('
{{field.data[field.name] | missing}}
');
    }
  }

  function Registry() {
    this.widgets = {};
    this.defaultWidgets = {};
    this.defaultSelectWidget = null;
    this.required = {};
    this.visible = {};
  }

  /** Register a widget factory. */
  Registry.prototype.addWidget = function(name, factory) {
    this.widgets[name] = factory;
  };

  /** Return the widget factory for the given field. */
  Registry.prototype.getWidget = function(field) {
    var name = field.widget === undefined ? null : field.widget.name;

    // No explicit widget in the field definition so we'll use
    // the default widget for this type of field.
    if (!name) {
      var type = field.type;
      var select = field.options !== undefined;

      if (select) {
        name = this.defaultSelectWidget || this.defaultWidgets[type];
      } else {
        name = this.defaultWidgets[type];
      }
    }

    var factory = this.widgets[name];

    if (factory === undefined) {
      throw 'No widget factory for: ' + name;
    }

    return factory;
  };

  /** Set the default widget for a type. */
  Registry.prototype.setDefaultWidget = function(type, name) {
    this.defaultWidgets[type] = name;
  };

  /** Set the default select widget (for fields with options). */
  Registry.prototype.setDefaultSelectWidget = function(name) {
    this.defaultSelectWidget = name;
  };

  /** Register a required function. */
  Registry.prototype.addRequired = function(type, f) {
    this.required[type] = f;
  };

  /** Get a required function. */
  Registry.prototype.getRequired = function(type) {
    var f = this.required[type];

    if (f === undefined) {
      throw 'No required function for: ' + type;
    }

    return f;
  };

  /** Register a visible function. */
  Registry.prototype.addVisible = function(type, f) {
    this.visible[type] = f;
  };

  /** Get a visible function. */
  Registry.prototype.getVisible = function(type) {
    var f = this.visible[type];

    if (f === undefined) {
      throw 'No visible function for: ' + type;
    }

    return f;
  };

  function jsHandler(field, data) {
    /*jshint evil:true */
    var f = new Function('form', 'path', data.value);

    return function(wrapped) {
      return f(wrapped.data, [wrapped.name]);
    };
  }

  var registry = new Registry();

  registry.addWidget('string', stringWidget);
  registry.addWidget('date', dateWidget);
  registry.addWidget('int', intWidget);
  registry.addWidget('float', floatWidget);
  registry.addWidget('select', selectWidget);
  registry.addWidget('radio', radioWidget);
  registry.addWidget('yesNoRadio', yesNoRadioWidget);
  registry.addWidget('checkboxes', checkboxesWidget);

  registry.setDefaultWidget('int', 'int');
  registry.setDefaultWidget('float', 'float');
  registry.setDefaultWidget('string', 'string');
  registry.setDefaultWidget('boolean', 'yesNoRadio');
  registry.setDefaultWidget('date', 'date');
  registry.setDefaultWidget('datetime', 'string');

  registry.setDefaultSelectWidget('select');

  registry.addRequired('js', jsHandler);
  registry.addVisible('js', jsHandler);

  /** Wrap a value in a function. */
  function wrap(value) {
    return function() {
      return value;
    };
  }

  /** A form schema. */
  function Schema(registry, data) {
    this.registry = registry;
    this.fields = [];

    for (var i = 0; i < data.length; i++) {
      var field = new Field(this, data[i]);
      this.fields.push(field);
    }
  }

  /** A field in the form schema. */
  function Field(schema, data) {
    this.schema = schema;
    this.name = data.name;
    this.type = data.type;
    this.label = data.label;
    this.help = data.help;

    // Default to required
    var required = data.required === undefined ? true : data.required;

    if (required === true) {
      this.required = wrap(true);
    } else if (required === false) {
      this.required = wrap(false);
    } else {
      this.required = this.schema.registry.getRequired(required.type)(this, required);
    }

    /// Default to visible
    var visible = data.visible === undefined ? true : data.visible;

    if (visible === true) {
      this.visible = wrap(true);
    } else if (visible === false) {
      this.visible = wrap(false);
    } else {
      this.visible = this.schema.registry.getVisible(visible.type)(this, visible);
    }

    this.options = data.options;

    // Return a widget element
    this.widget = function(scope) {
      return this.schema.registry.getWidget(this)(scope, this, data.widget);
    };
  }

  /** Wrap a field so it has access to it's value and any errors. */
  function wrapField(field, data, errors) {
    var wrapped = Object.create(field);

    wrapped.data = data;
    wrapped.errors = errors;

    wrapped.required = function() {
      return field.required(wrapped);
    };

    wrapped.visible = function() {
      return field.visible(wrapped);
    };

    wrapped.valid = function() {
      // Valid if there aren't any errors
      return !errors[field.name];
    };

    return wrapped;
  }

  // Directive to render a schema
  app.directive('marmosetSchema', ['$compile', function($compile) {
    return {
      scope: {
        schema: '=marmosetSchema',
        data: '=',
        errors: '='
      },
      link: function(scope, element) {
        // TODO
        if (scope.errors === undefined) {
          scope.errors = {};
        }

        // Note: add the container to the DOM here so we can $compile in the for loop
        var container = angular.element('

');
        element.append(container);

        // TODO formula fields

        // Loop through the fields in the schema
        for (var i = 0; i < scope.schema.fields.length; i++) {
          var field = scope.schema.fields[i];
          var wrapped = wrapField(field, scope.data, scope.errors);

          var fieldScope = scope.$new();
          fieldScope.field = wrapped;

          // Note: important that the element is compiled after being added to the DOM
          var fieldElement = angular.element('

');
          container.append(fieldElement);
          $compile(fieldElement)(fieldScope);
        }
      }
    };
  }]);

  // Directive to render a field
  app.directive('marmosetField', ['$compile', '$templateCache', function($compile, $templateCache) {
    return {
      require: '?^form',
      scope: {
        field: '=marmosetField'
      },
      templateUrl: 'app/marmoset/field.html',
      link: function(scope, element, attr, formCtrl) {
        scope.form = formCtrl;

        var targetElement = element.find('[widget]');
        targetElement.removeAttr('widget');

        // Create a new child scope for the widget
        var widgetScope = scope.$new();
        var widgetElement = scope.field.widget(widgetScope);

        // Anything that uses ng-model must be compiled *after* being added
        // to the DOM otherwise it won't be able to find the form controller.
        targetElement.append(widgetElement);
        $compile(widgetElement)(widgetScope);

        // Set the field to null when it is hidden
        scope.$watch(function() {
          return scope.field.visible();
        }, function(visible) {
          // Hidden
          if (!visible) {
            scope.field.data[scope.field.name] = null;
          }
        });

        function getModelCtrl() {
          // frmCtrl (FormController) will be null if this directive isn't inside a form
          // modelCtrl (NgModelController) will be undefined on load
          return formCtrl ? (formCtrl[scope.field.name] || null) : null;
        }

        scope.error = function(name) {
          var modelCtrl = getModelCtrl();

          if (!modelCtrl) {
            return false;
          }

          return !!modelCtrl.$error[name];
        };

        // Return true if the field is valid (no client-side or server-side errors)
        scope.valid = function() {
          var modelCtrl = getModelCtrl();

          // If modelCtrl isn't availble yet treat the field as valid and pristine
          var valid = modelCtrl ? modelCtrl.$valid : true;
          var pristine = modelCtrl ? modelCtrl.$pristine : false;

          // Note: we ignore client-side errors if the field is pristine (not-edited)
          return (valid || pristine) && scope.field.valid();
        };
      }
    };
  }]);

  // TODO
  app.factory('createSchema', function() {
    return function(data) {
      return new Schema(registry, data);
    };
  });
})();
<commit_msg>Add inline radio<commit_after>(function() {
  'use strict';

  var app = angular.module('radar.marmoset', []);

  app.directive('checkboxes', function() {
    return {
      require: '?ngModel',
      scope: {
        options: '='
      },
      template: function(element, attrs) {
        if (attrs.inline === 'true') {
          return '<input type="checkbox" ng-model="states[$index]" ng-change="update($index)" /> {{option.label}}';
        } else {
          return '
<input type="checkbox" ng-model="states[$index]" ng-change="update($index)" /> {{option.label}}
';
        }
      },
      link: function(scope, element, attr, ngModel) {
        if (!ngModel) {
          return;
        }

        function updateState() {
          var value = ngModel.$viewValue;
          var options = scope.options || [];

          for (var i = 0; i < options.length; i++) {
            scope.states[i] = options[i].value === value;
          }
        }

        scope.update = function(index) {
          var value = scope.states[index] ? scope.options[index].value : null;
          ngModel.$setViewValue(value);
          updateState();
        };

        scope.states = {};

        scope.$watch('options', updateState);
        ngModel.$render = updateState;
      }
    }
  });

  function intWidget(scope, field) {
    var element = angular.element('<input />');
    element.attr('class', 'form-control');
    element.attr('integer-validator', '');
    element.attr('name', field.name);
    element.attr('type', 'text');
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function floatWidget(scope, field) {
    var element = angular.element('<input />');
    element.attr('class', 'form-control');
    element.attr('number-validator', '');
    element.attr('name', field.name);
    element.attr('type', 'text');
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function stringWidget(scope, field) {
    return angular.element('<input class="form-control" type="text" ng-model="field.data[field.name]" />');
  }

  function dateWidget(scope, field) {
    var element = angular.element('<input />');
    element.attr('class', 'form-control');
    element.attr('date-validator', '');
    element.attr('frm-date-picker', '');
    element.attr('name', field.name);
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function selectWidget(scope, field) {
    // TODO add option for null

    var element = angular.element('<select></select>');
    element.attr('class', 'form-control');
    element.attr('name', field.name);
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-options', 'x.value as x.label for x in field.options');
    element.attr('ng-required', 'field.required()');
    return element;
  }

  function radioWidget(scope, field, data) {
    // TODO add radio for null

    var labelElement = angular.element('');

    var inputElement = angular.element('<input />');
    inputElement.attr('name', field.name);
    inputElement.attr('type', 'radio');
    inputElement.attr('ng-value', 'option.value');
    inputElement.attr('ng-model', 'field.data[field.name]');
    inputElement.attr('ng-required', 'field.required()');
    labelElement.append(inputElement);

    labelElement.append('{{option.label}}');

    var rootElement;

    if (data.inline) {
      labelElement.attr('class', 'radio-inline');
      labelElement.attr('ng-repeat', 'option in field.options');
      rootElement = labelElement;
    } else {
      var divElement = angular.element('

');
      divElement.attr('class', 'radio');
      divElement.attr('ng-repeat', 'option in field.options');
      divElement.append(labelElement);
      rootElement = divElement;
    }

    return rootElement;
  }

  function yesNoRadioWidget(scope, field) {
    scope.options = [
      {value: true, label: 'Yes'},
      {value: false, label: 'No'},
      {value: null, label: 'Not Answered'}
    ];

    var divElement = angular.element('

');
    divElement.attr('class', 'radio');
    divElement.attr('ng-repeat', 'option in options');

    var labelElement = angular.element('');
    divElement.append(labelElement);

    var inputElement = angular.element('<input />');
    inputElement.attr('name', field.name);
    inputElement.attr('type', 'radio');
    inputElement.attr('ng-value', 'option.value');
    inputElement.attr('ng-model', 'field.data[field.name]');
    inputElement.attr('ng-required', 'field.required()');
    labelElement.append(inputElement);

    labelElement.append('{{option.label}}');

    return divElement;
  }

  function checkboxesWidget(scope, field, data) {
    var element = angular.element('

')
    element.attr('checkboxes', '');
    element.attr('ng-model', 'field.data[field.name]');
    element.attr('ng-required', 'field.required()');
    element.attr('options', 'field.options');
    element.attr('inline', data.inline ? 'true' : 'false');
    return element;
  }

  function formulaWidget(scope, field) {
    if (field.type === 'date') {
      return angular.element('
{{field.data[field.name] | dateFormat}}
');
    } else {
      return angular.element('
{{field.data[field.name] | missing}}
');
    }
  }

  function Registry() {
    this.widgets = {};
    this.defaultWidgets = {};
    this.defaultSelectWidget = null;
    this.required = {};
    this.visible = {};
  }

  /** Register a widget factory. */
  Registry.prototype.addWidget = function(name, factory) {
    this.widgets[name] = factory;
  };

  /** Return the widget factory for the given field. */
  Registry.prototype.getWidget = function(field) {
    var name = field.widget === undefined ? null : field.widget.name;

    // No explicit widget in the field definition so we'll use
    // the default widget for this type of field.
    if (!name) {
      var type = field.type;
      var select = field.options !== undefined;

      if (select) {
        name = this.defaultSelectWidget || this.defaultWidgets[type];
      } else {
        name = this.defaultWidgets[type];
      }
    }

    var factory = this.widgets[name];

    if (factory === undefined) {
      throw 'No widget factory for: ' + name;
    }

    return factory;
  };

  /** Set the default widget for a type. */
  Registry.prototype.setDefaultWidget = function(type, name) {
    this.defaultWidgets[type] = name;
  };

  /** Set the default select widget (for fields with options). */
  Registry.prototype.setDefaultSelectWidget = function(name) {
    this.defaultSelectWidget = name;
  };

  /** Register a required function. */
  Registry.prototype.addRequired = function(type, f) {
    this.required[type] = f;
  };

  /** Get a required function. */
  Registry.prototype.getRequired = function(type) {
    var f = this.required[type];

    if (f === undefined) {
      throw 'No required function for: ' + type;
    }

    return f;
  };

  /** Register a visible function. */
  Registry.prototype.addVisible = function(type, f) {
    this.visible[type] = f;
  };

  /** Get a visible function. */
  Registry.prototype.getVisible = function(type) {
    var f = this.visible[type];

    if (f === undefined) {
      throw 'No visible function for: ' + type;
    }

    return f;
  };

  function jsHandler(field, data) {
    /*jshint evil:true */
    var f = new Function('form', 'path', data.value);

    return function(wrapped) {
      return f(wrapped.data, [wrapped.name]);
    };
  }

  var registry = new Registry();

  registry.addWidget('string', stringWidget);
  registry.addWidget('date', dateWidget);
  registry.addWidget('int', intWidget);
  registry.addWidget('float', floatWidget);
  registry.addWidget('select', selectWidget);
  registry.addWidget('radio', radioWidget);
  registry.addWidget('yesNoRadio', yesNoRadioWidget);
  registry.addWidget('checkboxes', checkboxesWidget);

  registry.setDefaultWidget('int', 'int');
  registry.setDefaultWidget('float', 'float');
  registry.setDefaultWidget('string', 'string');
  registry.setDefaultWidget('boolean', 'yesNoRadio');
  registry.setDefaultWidget('date', 'date');
  registry.setDefaultWidget('datetime', 'string');

  registry.setDefaultSelectWidget('select');

  registry.addRequired('js', jsHandler);
  registry.addVisible('js', jsHandler);

  /** Wrap a value in a function. */
  function wrap(value) {
    return function() {
      return value;
    };
  }

  /** A form schema. */
  function Schema(registry, data) {
    this.registry = registry;
    this.fields = [];

    for (var i = 0; i < data.length; i++) {
      var field = new Field(this, data[i]);
      this.fields.push(field);
    }
  }

  /** A field in the form schema. */
  function Field(schema, data) {
    this.schema = schema;
    this.name = data.name;
    this.type = data.type;
    this.label = data.label;
    this.help = data.help;

    // Default to required
    var required = data.required === undefined ? true : data.required;

    if (required === true) {
      this.required = wrap(true);
    } else if (required === false) {
      this.required = wrap(false);
    } else {
      this.required = this.schema.registry.getRequired(required.type)(this, required);
    }

    /// Default to visible
    var visible = data.visible === undefined ? true : data.visible;

    if (visible === true) {
      this.visible = wrap(true);
    } else if (visible === false) {
      this.visible = wrap(false);
    } else {
      this.visible = this.schema.registry.getVisible(visible.type)(this, visible);
    }

    this.options = data.options;

    // Return a widget element
    this.widget = function(scope) {
      return this.schema.registry.getWidget(this)(scope, this, data.widget);
    };
  }

  /** Wrap a field so it has access to it's value and any errors. */
  function wrapField(field, data, errors) {
    var wrapped = Object.create(field);

    wrapped.data = data;
    wrapped.errors = errors;

    wrapped.required = function() {
      return field.required(wrapped);
    };

    wrapped.visible = function() {
      return field.visible(wrapped);
    };

    wrapped.valid = function() {
      // Valid if there aren't any errors
      return !errors[field.name];
    };

    return wrapped;
  }

  // Directive to render a schema
  app.directive('marmosetSchema', ['$compile', function($compile) {
    return {
      scope: {
        schema: '=marmosetSchema',
        data: '=',
        errors: '='
      },
      link: function(scope, element) {
        // TODO
        if (scope.errors === undefined) {
          scope.errors = {};
        }

        // Note: add the container to the DOM here so we can $compile in the for loop
        var container = angular.element('

');
        element.append(container);

        // TODO formula fields

        // Loop through the fields in the schema
        for (var i = 0; i < scope.schema.fields.length; i++) {
          var field = scope.schema.fields[i];
          var wrapped = wrapField(field, scope.data, scope.errors);

          var fieldScope = scope.$new();
          fieldScope.field = wrapped;

          // Note: important that the element is compiled after being added to the DOM
          var fieldElement = angular.element('

');
          container.append(fieldElement);
          $compile(fieldElement)(fieldScope);
        }
      }
    };
  }]);

  // Directive to render a field
  app.directive('marmosetField', ['$compile', '$templateCache', function($compile, $templateCache) {
    return {
      require: '?^form',
      scope: {
        field: '=marmosetField'
      },
      templateUrl: 'app/marmoset/field.html',
      link: function(scope, element, attr, formCtrl) {
        scope.form = formCtrl;

        var targetElement = element.find('[widget]');
        targetElement.removeAttr('widget');

        // Create a new child scope for the widget
        var widgetScope = scope.$new();
        var widgetElement = scope.field.widget(widgetScope);

        // Anything that uses ng-model must be compiled *after* being added
        // to the DOM otherwise it won't be able to find the form controller.
        targetElement.append(widgetElement);
        $compile(widgetElement)(widgetScope);

        // Set the field to null when it is hidden
        scope.$watch(function() {
          return scope.field.visible();
        }, function(visible) {
          // Hidden
          if (!visible) {
            scope.field.data[scope.field.name] = null;
          }
        });

        function getModelCtrl() {
          // frmCtrl (FormController) will be null if this directive isn't inside a form
          // modelCtrl (NgModelController) will be undefined on load
          return formCtrl ? (formCtrl[scope.field.name] || null) : null;
        }

        scope.error = function(name) {
          var modelCtrl = getModelCtrl();

          if (!modelCtrl) {
            return false;
          }

          return !!modelCtrl.$error[name];
        };

        // Return true if the field is valid (no client-side or server-side errors)
        scope.valid = function() {
          var modelCtrl = getModelCtrl();

          // If modelCtrl isn't availble yet treat the field as valid and pristine
          var valid = modelCtrl ? modelCtrl.$valid : true;
          var pristine = modelCtrl ? modelCtrl.$pristine : false;

          // Note: we ignore client-side errors if the field is pristine (not-edited)
          return (valid || pristine) && scope.field.valid();
        };
      }
    };
  }]);

  // TODO
  app.factory('createSchema', function() {
    return function(data) {
      return new Schema(registry, data);
    };
  });
})();
<|endoftext|>


<commit_before>// 3rd party modules
const _ = require('lodash');
const sinon = require('sinon');
const {expect} = require('chai');
const Promise = require('bluebird');
// module under test
const {Onkyo, OnkyoError} = require('../lib');

const {stub, spy} = sinon;

describe('Onkyo', function () {
  describe('eiscpPacketExtract', function () {
    it('raises', function () {
      expect(() => Onkyo.eiscpPacketExtract('')).to.throw(OnkyoError);
      expect(() => Onkyo.eiscpPacketExtract('asd')).to.throw(OnkyoError);
      expect(() => Onkyo.eiscpPacketExtract('000000')).to.throw(OnkyoError);
    });
    it('pass', function () {
      expect(Onkyo.eiscpPacketExtract('abc\x0d')).to.be.eql('abc');
      expect(Onkyo.eiscpPacketExtract('abc\x1a')).to.be.eql('abc');
      expect(Onkyo.eiscpPacketExtract('abc\x1a\x0d')).to.be.eql('abc');
      expect(Onkyo.eiscpPacketExtract('abc\x1a\x0d\x0a')).to.be.eql('abc');
    });
  });
  describe('constructor', function () {
    const validate = (onkyo, obj) => {
      expect(onkyo).to.be.instanceof(Onkyo);
      if (obj) {
        if (obj.name) {
          expect(onkyo.name).to.be.equal(obj.name);
        }
        if (obj.address) {
          expect(onkyo.address).to.be.equal(obj.address);
        }
        if (obj.port) {
          expect(onkyo.port).to.be.equal(obj.port);
        }
      }
    };
    const test = (obj) => {
      const onkyo = new Onkyo(obj);
      validate(onkyo);
    };
    const options = {
      'with address': {address: '1.2.3.4'},
      'with logger': {
        logger: new Proxy({}, {get: () => () => {}}),
        address: '1.2.3.4'
      },
      'with name': {name: 'test', address: '1.2.3.4'},
      'with port': {port: 1, address: '1.2.3.4'}
    };
    it('raises', function () {
      expect(() => new Onkyo()).to.throw(Error);
      expect(() => new Onkyo({})).to.throw(Error);
      expect(() => new Onkyo({logger: {}})).to.throw(Error);
      expect(() => new Onkyo({port: 123})).to.throw(Error);
      expect(() => new Onkyo({port: 123})).to.throw(Error);
      expect(() => Onkyo.init()).to.throw(Error);
    });
    _.each(options, (opt, key) => {
      it(key, function () {
        test(opt);
      });
    });
    it('init', function () {
      const onkyo = Onkyo.init({address: 'localhost'});
      validate(onkyo);
    });
  });
  let socket;
  let onEvents;
  const customConnect = (host, cb) => {
    cb();
    return socket;
  };
  beforeEach(function () {
    onEvents = {};
    socket = {
      on: stub(),
      end: stub(),
      write: stub()
    };
    socket.on.callsFake((event, cb) => {
      onEvents[event] = cb;
    });
  });
  it('connect', function () {
    const onkyo = new Onkyo({address: 'localhost'});
    const myConnect = (host, cb) => {
      expect(host).to.be.deep.eql({
        host: 'localhost',
        port: Onkyo.DEFAULT_PORT
      });
      return customConnect(host, cb);
    };
    return onkyo.connect(myConnect)
      .then(() => {
        expect(socket.on.callCount).to.be.equal(3);
      });
  });
  describe('receive', function () {
    let onkyo;
    beforeEach(function () {
      onkyo = new Onkyo({address: 'localhost'});
      return onkyo
        .connect(customConnect);
    });
    const tests = [
      {
        rx: '!1PWR00',
        event: 'PWR',
        payload: {PWR: false}
      },
      {
        rx: '!1PWR01',
        event: 'PWR',
        payload: {PWR: true}
      },
      {
        rx: '!1AMT01',
        event: 'AMT',
        payload: {AMT: true}
      },
      {
        rx: '!1AMT00',
        event: 'AMT',
        payload: {AMT: false}
      },
      {
        rx: '!1CEC00',
        event: 'CEC',
        payload: {CEC: 'off'}
      },
      {
        rx: '!1NDN00',
        event: 'NDN',
        payload: {NDN: '00'}
      }
    ];
    _.each(tests, (obj) => {
      it(obj.rx, function () {
        return new Promise((resolve) => {
          onkyo.on(obj.event, resolve);
          onEvents.data(Onkyo.createEiscpBuffer(`${obj.rx}\x1a`));
        })
          .then((data) => {
            expect(data).to.be.deep.eql(obj.payload);
          });
      });
    });
    it('unrecognize', function (done) {
      spy(onkyo, '_parseMsg');
      onEvents.data(Onkyo.createEiscpBuffer('abc01\x1a'));
      onkyo.once('error', () => {
        expect(onkyo._parseMsg.calledOnce).to.be.eql(true);
        onkyo._parseMsg.restore();
        done();
      });
    });
  });
  it('sendCommand', function () {
    const onkyo = new Onkyo({address: 'localhost'});
    return onkyo
      .connect(customConnect)
      .then(() => {
        const fakeData = onEvents.data;
        const response = Onkyo.createEiscpBuffer('!1PWR01\x1a');
        return Promise.all([
          onkyo.sendCommand('POWER', 'ON'),
          Promise.delay(1).then(() => fakeData(response))
        ]);
      })
      .then(() => {
        expect(socket.write.callCount).to.be.equal(1);
      });
  });
  describe('api', function () {
    let onkyo;
    const pypass = x => x;
    beforeEach(function () {
      onkyo = new Onkyo({address: 'localhost'});
      stub(onkyo, '_client');
      stub(onkyo, '_sendISCPpacket');
      return onkyo
        .connect(customConnect);
    });
    describe('setVolume', function () {
      it('throws when invalid input', function () {
        expect(() => onkyo.setVolume('')).to.throw(Error);
        expect(() => onkyo.setVolume(0.1)).to.throw(Error);
        expect(() => onkyo.setVolume(-1)).to.throw(Error);
      });
      it('pass', function () {
        const vol = 50;
        const callFakes = [
          () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
          () => onkyo.emit('MVL', vol.toString(16)) // VOL 1
        ];
        _.each(callFakes, (callFake, index) => {
          onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
        });
        return onkyo.setVolume(vol)
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    describe('getVolume', function () {
      it('pass', function () {
        const vol = 10;
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket(`MVL${vol.toString(16)}`, pypass);
        });
        return onkyo.getVolume()
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    describe('getMute', function () {
      it('pass', function () {
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket('AMT01', pypass);
        });
        return onkyo.getMute()
          .then((volume) => {
            expect(volume).to.be.eql(true);
          });
      });
    });
    describe('getSource', function () {
      it('pass', function () {
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket('SLI2B', pypass);
        });
        return onkyo.getSource()
          .then((volume) => {
            expect(volume).to.be.eql('NET');
          });
      });
    });
    describe('setSource', function () {
      it('pass', function () {
        const src = 'NET'; // SOURCE NET
        const callFakes = [
          () => onkyo.emit('SLI', src)
        ];
        _.each(callFakes, (callFake, index) => {
          onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
        });
        return onkyo.setSource(src)
          .then((source) => {
            expect(source).to.be.eql(src);
          });
      });
    });
    describe('getSoundMode', function () {
      it('pass', function () {
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket('LMD02', pypass);
        });
        return onkyo.getSoundMode()
          .then((volume) => {
            expect(volume).to.be.eql('SURROUND');
          });
      });
    });
    describe('setSoundMode', function () {
      it('pass', function () {
        const mode = 'SURROUND';
        const callFakes = [
          () => onkyo.emit('LMD', mode)
        ];
        _.each(callFakes, (callFake, index) => {
          onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
        });
        return onkyo.setSoundMode(mode)
          .then((soundMode) => {
            expect(soundMode).to.be.eql(mode);
          });
      });
    });
    describe('setCenterVolume', function () {
      it('throws when invalid input', function () {
        expect(() => onkyo.setCenterVolume('')).to.throw(Error);
        expect(() => onkyo.setCenterVolume(0.1)).to.throw(Error);
        expect(() => onkyo.setCenterVolume(-13)).to.throw(Error);
        expect(() => onkyo.setCenterVolume(13)).to.throw(Error);
      });
      it('pass', function () {
        const vol = 6;
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket(`CTL+${vol.toString(16)}`, pypass);
        });
        return onkyo.setCenterVolume(vol)
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    describe('getCenterVolume', function () {
      it('pass', function () {
        const vol = 10;
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket(`CTL+${vol.toString(16)}`, pypass);
        });
        return onkyo.getCenterVolume()
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    it('getDeviceState', function () {
      const callFakes = [
        () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
        () => onkyo._parseClientPacket('SLI02', pypass), // Game
        () => onkyo._parseClientPacket('MVL01', pypass), // VOL 1
        () => onkyo._parseClientPacket('AMT00', pypass), // unmute
        () => onkyo._parseClientPacket('LMD01', pypass), // sound mode direct
        () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
        () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
        () => onkyo._parseClientPacket('PWR00', pypass), // POWER off
        () => onkyo._parseClientPacket('PWR00', pypass) // POWER off
      ];
      _.each(callFakes, (callFake, index) => {
        onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
      });
      return onkyo.getDeviceState()
        .then((states) => {
          const shouldBe = {
            PWR: true, SLI: 'GAME', MVL: 1, AMT: false, LMD: 'DIRECT'
          };
          expect(states).to.be.deep.equal(shouldBe);
          return onkyo.isOn();
        })
        .then((on) => {
          expect(on).to.be.true;
          return onkyo.isOff();
        })
        .then((off) => {
          expect(off).to.be.false;
          return onkyo.isOn();
        })
        .then((on) => {
          expect(on).to.be.false;
          return onkyo.isOff();
        })
        .then((off) => {
          expect(off).to.be.true;
        });
    });
    // @TODO more test..
  });
});
<commit_msg>fix test (#90)<commit_after>// 3rd party modules
const _ = require('lodash');
const sinon = require('sinon');
const {expect} = require('chai');
const Promise = require('bluebird');
// module under test
const {Onkyo, OnkyoError} = require('../lib');

const {stub, spy} = sinon;

describe('Onkyo', function () {
  describe('eiscpPacketExtract', function () {
    it('raises', function () {
      expect(() => Onkyo.eiscpPacketExtract('')).to.throw(OnkyoError);
      expect(() => Onkyo.eiscpPacketExtract('asd')).to.throw(OnkyoError);
      expect(() => Onkyo.eiscpPacketExtract('000000')).to.throw(OnkyoError);
    });
    it('pass', function () {
      expect(Onkyo.eiscpPacketExtract('abc\x0d')).to.be.eql('abc');
      expect(Onkyo.eiscpPacketExtract('abc\x1a')).to.be.eql('abc');
      expect(Onkyo.eiscpPacketExtract('abc\x1a\x0d')).to.be.eql('abc');
      expect(Onkyo.eiscpPacketExtract('abc\x1a\x0d\x0a')).to.be.eql('abc');
    });
  });
  describe('constructor', function () {
    const validate = (onkyo, obj) => {
      expect(onkyo).to.be.instanceof(Onkyo);
      if (obj) {
        if (obj.name) {
          expect(onkyo.name).to.be.equal(obj.name);
        }
        if (obj.address) {
          expect(onkyo.address).to.be.equal(obj.address);
        }
        if (obj.port) {
          expect(onkyo.port).to.be.equal(obj.port);
        }
      }
    };
    const test = (obj) => {
      const onkyo = new Onkyo(obj);
      validate(onkyo);
    };
    const options = {
      'with address': {address: '1.2.3.4'},
      'with logger': {
        logger: new Proxy({}, {get: () => () => {}}),
        address: '1.2.3.4'
      },
      'with name': {name: 'test', address: '1.2.3.4'},
      'with port': {port: 1, address: '1.2.3.4'}
    };
    it('raises', function () {
      expect(() => new Onkyo()).to.throw(Error);
      expect(() => new Onkyo({})).to.throw(Error);
      expect(() => new Onkyo({logger: {}})).to.throw(Error);
      expect(() => new Onkyo({port: 123})).to.throw(Error);
      expect(() => new Onkyo({port: 123})).to.throw(Error);
      expect(() => Onkyo.init()).to.throw(Error);
    });
    _.each(options, (opt, key) => {
      it(key, function () {
        test(opt);
      });
    });
    it('init', function () {
      const onkyo = Onkyo.init({address: 'localhost'});
      validate(onkyo);
    });
  });
  let socket;
  let onEvents;
  const customConnect = (host, cb) => {
    cb();
    return socket;
  };
  beforeEach(function () {
    onEvents = {};
    socket = {
      on: stub(),
      end: stub(),
      write: stub()
    };
    socket.on.callsFake((event, cb) => {
      onEvents[event] = cb;
    });
  });
  it('connect', function () {
    const onkyo = new Onkyo({address: 'localhost'});
    const myConnect = (host, cb) => {
      expect(host).to.be.deep.eql({
        host: 'localhost',
        port: Onkyo.DEFAULT_PORT
      });
      return customConnect(host, cb);
    };
    return onkyo.connect(myConnect)
      .then(() => {
        expect(socket.on.callCount).to.be.equal(4);
      });
  });
  describe('receive', function () {
    let onkyo;
    beforeEach(function () {
      onkyo = new Onkyo({address: 'localhost'});
      return onkyo
        .connect(customConnect);
    });
    const tests = [
      {
        rx: '!1PWR00',
        event: 'PWR',
        payload: {PWR: false}
      },
      {
        rx: '!1PWR01',
        event: 'PWR',
        payload: {PWR: true}
      },
      {
        rx: '!1AMT01',
        event: 'AMT',
        payload: {AMT: true}
      },
      {
        rx: '!1AMT00',
        event: 'AMT',
        payload: {AMT: false}
      },
      {
        rx: '!1CEC00',
        event: 'CEC',
        payload: {CEC: 'off'}
      },
      {
        rx: '!1NDN00',
        event: 'NDN',
        payload: {NDN: '00'}
      }
    ];
    _.each(tests, (obj) => {
      it(obj.rx, function () {
        return new Promise((resolve) => {
          onkyo.on(obj.event, resolve);
          onEvents.data(Onkyo.createEiscpBuffer(`${obj.rx}\x1a`));
        })
          .then((data) => {
            expect(data).to.be.deep.eql(obj.payload);
          });
      });
    });
    it('unrecognize', function (done) {
      spy(onkyo, '_parseMsg');
      onEvents.data(Onkyo.createEiscpBuffer('abc01\x1a'));
      onkyo.once('error', () => {
        expect(onkyo._parseMsg.calledOnce).to.be.eql(true);
        onkyo._parseMsg.restore();
        done();
      });
    });
  });
  it('sendCommand', function () {
    const onkyo = new Onkyo({address: 'localhost'});
    return onkyo
      .connect(customConnect)
      .then(() => {
        const fakeData = onEvents.data;
        const response = Onkyo.createEiscpBuffer('!1PWR01\x1a');
        return Promise.all([
          onkyo.sendCommand('POWER', 'ON'),
          Promise.delay(1).then(() => fakeData(response))
        ]);
      })
      .then(() => {
        expect(socket.write.callCount).to.be.equal(1);
      });
  });
  describe('api', function () {
    let onkyo;
    const pypass = x => x;
    beforeEach(function () {
      onkyo = new Onkyo({address: 'localhost'});
      stub(onkyo, '_client');
      stub(onkyo, '_sendISCPpacket');
      return onkyo
        .connect(customConnect);
    });
    describe('setVolume', function () {
      it('throws when invalid input', function () {
        expect(() => onkyo.setVolume('')).to.throw(Error);
        expect(() => onkyo.setVolume(0.1)).to.throw(Error);
        expect(() => onkyo.setVolume(-1)).to.throw(Error);
      });
      it('pass', function () {
        const vol = 50;
        const callFakes = [
          () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
          () => onkyo.emit('MVL', vol.toString(16)) // VOL 1
        ];
        _.each(callFakes, (callFake, index) => {
          onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
        });
        return onkyo.setVolume(vol)
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    describe('getVolume', function () {
      it('pass', function () {
        const vol = 10;
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket(`MVL${vol.toString(16)}`, pypass);
        });
        return onkyo.getVolume()
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    describe('getMute', function () {
      it('pass', function () {
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket('AMT01', pypass);
        });
        return onkyo.getMute()
          .then((volume) => {
            expect(volume).to.be.eql(true);
          });
      });
    });
    describe('getSource', function () {
      it('pass', function () {
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket('SLI2B', pypass);
        });
        return onkyo.getSource()
          .then((volume) => {
            expect(volume).to.be.eql('NET');
          });
      });
    });
    describe('setSource', function () {
      it('pass', function () {
        const src = 'NET'; // SOURCE NET
        const callFakes = [
          () => onkyo.emit('SLI', src)
        ];
        _.each(callFakes, (callFake, index) => {
          onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
        });
        return onkyo.setSource(src)
          .then((source) => {
            expect(source).to.be.eql(src);
          });
      });
    });
    describe('getSoundMode', function () {
      it('pass', function () {
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket('LMD02', pypass);
        });
        return onkyo.getSoundMode()
          .then((volume) => {
            expect(volume).to.be.eql('SURROUND');
          });
      });
    });
    describe('setSoundMode', function () {
      it('pass', function () {
        const mode = 'SURROUND';
        const callFakes = [
          () => onkyo.emit('LMD', mode)
        ];
        _.each(callFakes, (callFake, index) => {
          onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
        });
        return onkyo.setSoundMode(mode)
          .then((soundMode) => {
            expect(soundMode).to.be.eql(mode);
          });
      });
    });
    describe('setCenterVolume', function () {
      it('throws when invalid input', function () {
        expect(() => onkyo.setCenterVolume('')).to.throw(Error);
        expect(() => onkyo.setCenterVolume(0.1)).to.throw(Error);
        expect(() => onkyo.setCenterVolume(-13)).to.throw(Error);
        expect(() => onkyo.setCenterVolume(13)).to.throw(Error);
      });
      it('pass', function () {
        const vol = 6;
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket(`CTL+${vol.toString(16)}`, pypass);
        });
        return onkyo.setCenterVolume(vol)
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    describe('getCenterVolume', function () {
      it('pass', function () {
        const vol = 10;
        onkyo._sendISCPpacket.callsFake(() => {
          onkyo._parseClientPacket(`CTL+${vol.toString(16)}`, pypass);
        });
        return onkyo.getCenterVolume()
          .then((volume) => {
            expect(volume).to.be.eql(vol);
          });
      });
    });
    it('getDeviceState', function () {
      const callFakes = [
        () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
        () => onkyo._parseClientPacket('SLI02', pypass), // Game
        () => onkyo._parseClientPacket('MVL01', pypass), // VOL 1
        () => onkyo._parseClientPacket('AMT00', pypass), // unmute
        () => onkyo._parseClientPacket('LMD01', pypass), // sound mode direct
        () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
        () => onkyo._parseClientPacket('PWR01', pypass), // POWER on
        () => onkyo._parseClientPacket('PWR00', pypass), // POWER off
        () => onkyo._parseClientPacket('PWR00', pypass) // POWER off
      ];
      _.each(callFakes, (callFake, index) => {
        onkyo._sendISCPpacket.onCall(index).callsFake(callFake);
      });
      return onkyo.getDeviceState()
        .then((states) => {
          const shouldBe = {
            PWR: true, SLI: 'GAME', MVL: 1, AMT: false, LMD: 'DIRECT'
          };
          expect(states).to.be.deep.equal(shouldBe);
          return onkyo.isOn();
        })
        .then((on) => {
          expect(on).to.be.true;
          return onkyo.isOff();
        })
        .then((off) => {
          expect(off).to.be.false;
          return onkyo.isOn();
        })
        .then((on) => {
          expect(on).to.be.false;
          return onkyo.isOff();
        })
        .then((off) => {
          expect(off).to.be.true;
        });
    });
    // @TODO more test..
  });
});
<|endoftext|>


<commit_before>class ResultController {
  constructor(_, $state, $log, $window, $scope, feedbackURL, SocketService, toastr, CreditsFactory) {
    'ngInject';

    this._ = _;
    this.$log = $log;
    this.$state = $state;
    this.$window = $window;
    this.SocketService = SocketService;
    this.feedbackURL = feedbackURL;

    this.places = [];
    this.canCreate = true;
    this.current = 'result.credits';
    this.showTab();

    $scope.$on('owner_disconnect', function(event, args) {
      this.canCreate = false;

      toastr.info('Computer disconnected. Cannot create new game.');
    });

    $scope.$on('server_disconnect', function(event, args) {
      this.canCreate = false;

      toastr.info('Server disconnected. Cannot create new game.');
    });

    this.SocketService.extendedHandler = (message) => {
      if(message.type === 'player_create') {
        this.SocketService.disconnect();
        this.canCreate = false;

        toastr.info('A player is creating a new game at this computer. Join with the' + " 'join another game' button.");
      }
    };

    CreditsFactory.getList(399).success( (result) => {
      this.$log.debug(result);
      this.places = this._.sortBy(result, (d) => d.name);
      this.$log.debug(this.places);
    });
  }

  changeTab(value) {
    this.current = value;
    this.showTab();
  }

  showTab() {
    this.$state.transitionTo(this.current);
  }

  openFeedback() {
    this.$window.open(this.feedbackURL, '_blank');
  }

  createNewGame() {
    this.SocketService.send({
      type: 'player_create'
    });

    this.$state.go('select');
  }

  joinAnotherGame() {
    this.$state.go('main');
    this.SocketService.disconnect();
  }
}

export default ResultController;
<commit_msg>Get images from the api<commit_after>class ResultController {
  constructor(_, $q, $state, $log, $window, $scope, feedbackURL, SocketService, toastr, CreditsFactory, ImageFactory) {
    'ngInject';

    this._ = _;
    this.$q = $q;
    this.$log = $log;
    this.$state = $state;
    this.$window = $window;
    this.SocketService = SocketService;
    this.CreditsFactory = CreditsFactory;
    this.ImageFactory = ImageFactory;
    this.feedbackURL = feedbackURL;

    this.places = [];
    this.canCreate = true;
    this.current = 'result.credits';
    this.showTab();

    $scope.$on('owner_disconnect', function(event, args) {
      this.canCreate = false;

      toastr.info('Computer disconnected. Cannot create new game.');
    });

    $scope.$on('server_disconnect', function(event, args) {
      this.canCreate = false;

      toastr.info('Server disconnected. Cannot create new game.');
    });

    this.SocketService.extendedHandler = (message) => {
      if (message.type === 'player_create') {
        this.SocketService.disconnect();
        this.canCreate = false;

        toastr.info('A player is creating a new game at this computer. Join with the' + " 'join another game' button.");
      }
    };

    this.prepareCredits();
  }

  changeTab(value) {
    this.current = value;
    this.showTab();
  }

  showTab() {
    this.$state.transitionTo(this.current);
  }

  prepareCredits() {
    this.CreditsFactory.getList(399).success(result => {
      this.places = this._.sortBy(result, (d) => d.name);

      const promises = this._.chain(this.places)
        .map(p => p.photo_id)
        .map(id => this.ImageFactory.getImage(id))
        .value();

      this.$q.all(promises).then(results => {
        this.places = this._.each(this.places, p => {
          p.photo = results.shift().data;
          p.photo.thumb = p.photo.url.replace('b.jpg', 'q.jpg');
        });

        this.$log.debug(this.places);
      });
    });
  }

  openFeedback() {
    this.$window.open(this.feedbackURL, '_blank');
  }

  createNewGame() {
    this.SocketService.send({
      type: 'player_create'
    });

    this.$state.go('select');
  }

  joinAnotherGame() {
    this.$state.go('main');
    this.SocketService.disconnect();
  }
}

export default ResultController;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2015 node-coap contributors.
 *
 * node-coap is licensed under an MIT +no-false-attribs license.
 * All rights not explicitly granted in the MIT license are reserved.
 * See the included LICENSE file for more details.
 */

var coap      = require('../')
  , parse     = require('coap-packet').parse
  , generate  = require('coap-packet').generate
  , dgram     = require('dgram')
  , bl        = require('bl')
  , request   = coap.request
  , tk        = require('timekeeper')
  , sinon     = require('sinon')
  , params    = require('../lib/parameters')

describe('proxy', function() {
  var server
    , port
    , clientPort
    , client
    , target
    , targetPort
    , clock

  beforeEach(function(done) {
    clock = sinon.useFakeTimers()
    port = nextPort()
    server = coap.createServer({
      proxy: true
    })
    server.listen(port, function() {
      clientPort = nextPort()
      client = dgram.createSocket('udp4')
      targetPort = nextPort()
      target = coap.createServer()

      client.bind(clientPort, function() {
        target.listen(targetPort, done)
      })
    })
  })

  afterEach(function(done) {
    function closeSocket(socketcToClose, callback) {
      try {
        socketToClose.on('close', callback)
        socketcToClose.close()
      } catch (ignored) {
        callback()
      }
    }

    clock.restore()

    closeSocket(client, function() {
      closeSocket(server, function() {
        closeSocket(target, function() {
          tk.reset()
          done()
        })
      })
    });
  })

  function send(message) {
    client.send(message, 0, message.length, port, '127.0.0.1')
  }

  function ssend(rsinfo, packet) {
    var toSend = generate(packet)
    target.send(toSend, 0, toSend.length, rsinfo.port, rsinfo.address)
  }

  function fastForward(increase, max) {
    clock.tick(increase)
    if (increase < max)
      setImmediate(fastForward.bind(null, increase, max - increase))
  }

  it('should resend the message to its destination specified in the Proxy-Uri option', function(done) {
    send(generate({
      options: [{
        name: 'Proxy-Uri'
        , value: new Buffer('coap://localhost:' + targetPort + '/the/path')
      }]
    }))

    target.on('request', function(req, res) {
      done()
    })
  })

  it('should resend notifications in an observe connection', function(done) {
    var counter = 0,
        req;

    clock.restore()

    function sendObservation(message) {
      target.on('request', function(req, res) {
        res.setOption('Observe', 1);
        res.write('Pruebas');

        setTimeout(function() {
          res.write('Pruebas2');
          res.end('Last msg');
        }, 500)
      })

      return request({
        port: port
        , observe: true
        ,  proxyUri: 'coap://localhost:' + targetPort + '/the/path'
      }).end()
    }

    req = sendObservation()

    req.on('response', function(res) {
      res.on('data', function(msg) {
        if (counter === 2)
          done()
        else
          counter++;

        clock.tick(600);
      })
    })
  })

  it('should not process the request as a standard server request', function(done) {
    target.on('request', function(req, res) {
      done()
    })

    server.on('request', function(req, res) {
    })

    send(generate({
      options: [{
        name: 'Proxy-Uri'
        , value: new Buffer('coap://localhost:' + targetPort + '/the/path')
      }]
    }))
  })

  it('should return the target response to the original requestor', function(done) {
    send(generate({
      options: [{
        name: 'Proxy-Uri'
        , value: new Buffer('coap://localhost:' + targetPort + '/the/path')
      }]
    }))

    target.on('request', function(req, res) {
      res.end('The response')
    })

    client.on('message', function(msg) {
      var package = parse(msg)
      expect(package.payload.toString()).to.eql('The response')
      done()
    })
  })

  describe('with a proxied request initiated by an agent', function() {
    it('should forward the request to the URI specified in proxyUri ', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        proxyUri: 'coap://localhost:' + targetPort,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        done()
      })

      request.end()
    })
    it('should forward the response to the request back to the agent', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        proxyUri: 'coap://localhost:' + targetPort,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        res.end('This is the response')
      })

      request.on('response', function(res) {
        expect(res.payload.toString()).to.eql('This is the response');
        done()
      })

      request.end()
    })
  })

  describe('with a proxied request with a wrong destination', function() {
    it('should return an error to the caller', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        proxyUri: 'coap://unexistentCOAPUri:7968',
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('error', function(req, res) {
      })

      request
          .on('response', function(res) {
            expect(res.code).to.eql('5.00');
            expect(res.payload.toString()).to.contain('ENOTFOUND');
            done()
          })
          .end()
    })
  })

  describe('with a non-proxied request', function() {
    it('should call the handler as usual', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('request', function(req, res) {
        res.end('Standard response')
      })

      request
          .on('response', function(res) {
            expect(res.payload.toString()).to.contain('Standard response');
            done()
          })
          .end()
    })
  })

  describe('with an observe request to a proxied server', function() {
    it('should call the handler as usual', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        observe: true,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('request', function(req, res) {
        res.end('Standard response')
      })

      request
        .on('response', function(res) {
          expect(res.payload.toString()).to.contain('Standard response');
          done()
        })
        .end()
    })
    it('should allow all the responses', function(done) {
      var request = coap.request({
          host: 'localhost',
          port: port,
          observe: true,
          query: 'a=b'
        }),
        count = 0;

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('request', function(req, res) {
        res.setOption('Observe', 1);
        res.write('This is the first response');

        setTimeout(function() {
          res.setOption('Observe', 1);
          res.write('And this is the second');
        }, 200);
      })

      request
        .on('response', function(res) {
          res.on('data', function(chunk) {
            count++;

            if (count === 1) {
              expect(chunk.toString('utf8')).to.contain('This is the first response');
              clock.tick(300);
            } else if (count === 2) {
              expect(chunk.toString('utf8')).to.contain('And this is the second');
              done()
            }
          })
        })
        .end()
    })
  })
})
<commit_msg>Accept EAI_AGAIN as DNS failure<commit_after>/*
 * Copyright (c) 2013-2015 node-coap contributors.
 *
 * node-coap is licensed under an MIT +no-false-attribs license.
 * All rights not explicitly granted in the MIT license are reserved.
 * See the included LICENSE file for more details.
 */

var coap      = require('../')
  , parse     = require('coap-packet').parse
  , generate  = require('coap-packet').generate
  , dgram     = require('dgram')
  , bl        = require('bl')
  , request   = coap.request
  , tk        = require('timekeeper')
  , sinon     = require('sinon')
  , params    = require('../lib/parameters')

describe('proxy', function() {
  var server
    , port
    , clientPort
    , client
    , target
    , targetPort
    , clock

  beforeEach(function(done) {
    clock = sinon.useFakeTimers()
    port = nextPort()
    server = coap.createServer({
      proxy: true
    })
    server.listen(port, function() {
      clientPort = nextPort()
      client = dgram.createSocket('udp4')
      targetPort = nextPort()
      target = coap.createServer()

      client.bind(clientPort, function() {
        target.listen(targetPort, done)
      })
    })
  })

  afterEach(function(done) {
    function closeSocket(socketcToClose, callback) {
      try {
        socketToClose.on('close', callback)
        socketcToClose.close()
      } catch (ignored) {
        callback()
      }
    }

    clock.restore()

    closeSocket(client, function() {
      closeSocket(server, function() {
        closeSocket(target, function() {
          tk.reset()
          done()
        })
      })
    });
  })

  function send(message) {
    client.send(message, 0, message.length, port, '127.0.0.1')
  }

  function ssend(rsinfo, packet) {
    var toSend = generate(packet)
    target.send(toSend, 0, toSend.length, rsinfo.port, rsinfo.address)
  }

  function fastForward(increase, max) {
    clock.tick(increase)
    if (increase < max)
      setImmediate(fastForward.bind(null, increase, max - increase))
  }

  it('should resend the message to its destination specified in the Proxy-Uri option', function(done) {
    send(generate({
      options: [{
        name: 'Proxy-Uri'
        , value: new Buffer('coap://localhost:' + targetPort + '/the/path')
      }]
    }))

    target.on('request', function(req, res) {
      done()
    })
  })

  it('should resend notifications in an observe connection', function(done) {
    var counter = 0,
        req;

    clock.restore()

    function sendObservation(message) {
      target.on('request', function(req, res) {
        res.setOption('Observe', 1);
        res.write('Pruebas');

        setTimeout(function() {
          res.write('Pruebas2');
          res.end('Last msg');
        }, 500)
      })

      return request({
        port: port
        , observe: true
        ,  proxyUri: 'coap://localhost:' + targetPort + '/the/path'
      }).end()
    }

    req = sendObservation()

    req.on('response', function(res) {
      res.on('data', function(msg) {
        if (counter === 2)
          done()
        else
          counter++;

        clock.tick(600);
      })
    })
  })

  it('should not process the request as a standard server request', function(done) {
    target.on('request', function(req, res) {
      done()
    })

    server.on('request', function(req, res) {
    })

    send(generate({
      options: [{
        name: 'Proxy-Uri'
        , value: new Buffer('coap://localhost:' + targetPort + '/the/path')
      }]
    }))
  })

  it('should return the target response to the original requestor', function(done) {
    send(generate({
      options: [{
        name: 'Proxy-Uri'
        , value: new Buffer('coap://localhost:' + targetPort + '/the/path')
      }]
    }))

    target.on('request', function(req, res) {
      res.end('The response')
    })

    client.on('message', function(msg) {
      var package = parse(msg)
      expect(package.payload.toString()).to.eql('The response')
      done()
    })
  })

  describe('with a proxied request initiated by an agent', function() {
    it('should forward the request to the URI specified in proxyUri ', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        proxyUri: 'coap://localhost:' + targetPort,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        done()
      })

      request.end()
    })
    it('should forward the response to the request back to the agent', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        proxyUri: 'coap://localhost:' + targetPort,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        res.end('This is the response')
      })

      request.on('response', function(res) {
        expect(res.payload.toString()).to.eql('This is the response');
        done()
      })

      request.end()
    })
  })

  describe('with a proxied request with a wrong destination', function() {
    it('should return an error to the caller', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        proxyUri: 'coap://unexistentCOAPUri:7968',
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('error', function(req, res) {
      })

      request
          .on('response', function(res) {
            expect(res.code).to.eql('5.00');
            expect(res.payload.toString()).to.match(/ENOTFOUND|EAI_AGAIN/);
            done()
          })
          .end()
    })
  })

  describe('with a non-proxied request', function() {
    it('should call the handler as usual', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('request', function(req, res) {
        res.end('Standard response')
      })

      request
          .on('response', function(res) {
            expect(res.payload.toString()).to.contain('Standard response');
            done()
          })
          .end()
    })
  })

  describe('with an observe request to a proxied server', function() {
    it('should call the handler as usual', function(done) {
      var request = coap.request({
        host: 'localhost',
        port: port,
        observe: true,
        query: 'a=b'
      })

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('request', function(req, res) {
        res.end('Standard response')
      })

      request
        .on('response', function(res) {
          expect(res.payload.toString()).to.contain('Standard response');
          done()
        })
        .end()
    })
    it('should allow all the responses', function(done) {
      var request = coap.request({
          host: 'localhost',
          port: port,
          observe: true,
          query: 'a=b'
        }),
        count = 0;

      target.on('request', function(req, res) {
        console.log('should not get here')
      })

      server.on('request', function(req, res) {
        res.setOption('Observe', 1);
        res.write('This is the first response');

        setTimeout(function() {
          res.setOption('Observe', 1);
          res.write('And this is the second');
        }, 200);
      })

      request
        .on('response', function(res) {
          res.on('data', function(chunk) {
            count++;

            if (count === 1) {
              expect(chunk.toString('utf8')).to.contain('This is the first response');
              clock.tick(300);
            } else if (count === 2) {
              expect(chunk.toString('utf8')).to.contain('And this is the second');
              done()
            }
          })
        })
        .end()
    })
  })
})
<|endoftext|>


<commit_before>/*
 * Tests for Route
 */

var paths = {
  explicit: '/posts',
  required: '/posts/:param',
  multi: '/posts/:paramOne/:paramTwo',
  optional: '/posts/:paramOne/:paramTwo?',
  simpleOptional: '/:param?',
  wildcard: '/posts/*',
  namedWildcard: '/posts/:file(*)',
  regex: /^\/commits\/(\d+)\.\.(\d+)/
};

Tinytest.add('Route - matching', function (test) {
  var route = new Route(Router, 'explicit', {
    path: paths.explicit
  });
  test.isTrue(route.test('/posts'));
  test.isTrue(route.exec('/posts'));
  test.isTrue(route.test('/posts/'));
  test.isFalse(route.test('/posts/1'));
  test.isNull(route.exec('/posts/1'));

  route = new Route(Router, 'required', {
    path: paths.required
  });
  test.isTrue(route.test('/posts/1'));
  test.isTrue(route.exec('/posts/1'));
  test.isFalse(route.test('/posts/1/2'));
  test.isNull(route.exec('/posts/1/2'));

  route = new Route(Router, 'multi', {
    path: paths.multi
  });
  test.isTrue(route.test('/posts/1/2'));
  test.isTrue(route.exec('/posts/1/2'));
  test.isFalse(route.test('/posts/1/2/3'));
  test.isNull(route.exec('/posts/1/2/3'));

  route = new Route(Router, 'optional', {
    path: paths.optional
  });
  test.isTrue(route.test('/posts/1'));
  test.isTrue(route.exec('/posts/1'));
  test.isTrue(route.test('/posts/1/2'));
  test.isTrue(route.exec('/posts/1/2'));

  route = new Route(Router, 'simpleOptional', {
    path: paths.simpleOptional
  });
  test.isTrue(route.test('/'));
  test.isTrue(route.exec('/'));
  test.isTrue(route.test('/1'));
  test.isTrue(route.exec('/1'));

  route = new Route(Router, 'wildcard', {
    path: paths.wildcard
  });
  test.isTrue(route.test('/posts/1/2'));
  test.isTrue(route.exec('/posts/1/2'));
  test.isTrue(route.test('/posts/1/2/3'));
  test.isTrue(route.exec('/posts/1/2/3'));
  test.isTrue(route.test('/posts/1/2/3/4'));
  test.isTrue(route.exec('/posts/1/2/3/4'));

  route = new Route(Router, 'namedWildcard', {
    path: paths.namedWildcard
  });
  test.isTrue(route.test('/posts/path/to/file'));
  test.isTrue(route.exec('/posts/path/to/file'));

  route = new Route(Router, 'regex', {
    path: paths.regex
  });
  test.isTrue(route.test('/commits/123..456'));
  test.isTrue(route.exec('/commits/123..456'));
});

Tinytest.add('Route - params', function (test) {
  var route = new Route(Router, 'explicit', {
    path: paths.explicit
  });

  test.isNull(route.params());
  test.isTrue(route.params('/posts') instanceof Array);

  route = new Route(Router, 'required', {
    path: paths.required
  });

  var params = route.params('/posts/1');
  test.equal(params.param, "1");

  route = new Route(Router, 'multi', {
    path: paths.multi
  });
  params = route.params('/posts/1/2');
  test.equal(params.paramOne, '1');
  test.equal(params.paramTwo, '2');

  route = new Route(Router, 'optional', {
    path: paths.optional
  });
  params = route.params('/posts/1');
  test.equal(params.paramOne, '1');
  test.isUndefined(params.paramTwo);

  params = route.params('/posts/1/2');
  test.equal(params.paramOne, '1');
  test.equal(params.paramTwo, '2');

  route = new Route(Router, 'simpleOptional', {
    path: paths.simpleOptional
  });

  params = route.params('/');
  test.isUndefined(params.param);

  params = route.params('/1');
  test.equal(params.param, '1');

  route = new Route(Router, 'wildcard', {
    path: paths.wildcard
  });
  params = route.params('/posts/some/wildcard/path');
  test.equal(params[0], 'some/wildcard/path');

  route = new Route(Router, 'namedWildcard', {
    path: paths.namedWildcard
  });
  params = route.params('/posts/some/file/path');
  test.equal(params.file, 'some/file/path');

  route = new Route(Router, 'regex', {
    path: paths.regex
  });
  params = route.params('/commits/123..456');
  test.equal(params[0], '123');
  test.equal(params[1], '456');
});

Tinytest.add('Route - params with query and hash', function (test) {
  var route = new Route(Router, 'optional', {
    path: paths.optional
  });

  var params;

  params = route.params('/posts/1?q=s#anchorTag');
  test.equal(params.paramOne, '1');
  test.isUndefined(params.paramTwo);
  test.equal(params.q, 's');
  test.equal(params.hash, 'anchorTag');

  params = route.params('/posts/1/2?q=s#anchorTag');
  test.equal(params.paramTwo, '2');
});

Tinytest.add('Route - resolve', function (test) {
  var route = new Route(Router, 'required', {
    path: paths.required
  });

  var params;
  var options;

  params = {
    param: '1'
  };
  test.equal(route.resolve(params), '/posts/1');

  params = {
    param: '1'
  };
  options = {
    query: {
      q: 's'
    },
    hash: 'anchorTag'
  };
  test.equal(route.resolve(params, options), '/posts/1/?q=s#anchorTag');

  route = new Route(Router, 'simpleOptional', {
    path: paths.simpleOptional
  });
  test.equal(route.resolve(), '/');
  test.equal(route.resolve({param: '1'}), '/1');

  route = new Route(Router, 'wildcard', {
    path: paths.wildcard
  });
  params = ['some/file/path'];
  test.equal(route.resolve(params), '/posts/some/file/path');

  route = new Route(Router, 'namedWildcard', {
    path: paths.namedWildcard
  });
});

Tinytest.add('Route - normalizePath', function (test) {
  var route = new Route(Router, 'explicit', {
    path: paths.explicit
  });

  test.equal(route.normalizePath('/posts'), '/posts');
  test.equal(route.normalizePath('posts'), '/posts');
  test.equal(route.normalizePath(Meteor.absoluteUrl('posts')), '/posts');
  test.equal(route.normalizePath('/posts?q=s'), '/posts');
  test.equal(route.normalizePath('/posts#anchorTag'), '/posts');
});

Tinytest.add('Route - getController', function (test) {
  var route;
  var root = Utils.global();

  root.TestController = function (options)  {
    if (arguments.length < 1)
      throw new Error('Argument length check');

    this.options = options;
  };

  var testGetController = function (route) {
    var controller = route.getController('/test', {option: true});
    test.isTrue(controller instanceof TestController);
    test.equal(controller.options.route, route);
    test.equal(controller.options.template, 'template');
    test.isTrue(controller.options.option);
  };

  // case 1: controller option
  var route = new Route(Router, 'test', {
    controller: root.TestController,
    template: 'template'
  });
  testGetController(route);

  // case 1a: controller option as string
  var route = new Route(Router, 'test', {
    controller: 'TestController',
    template: 'template'
  });
  testGetController(route);

  root.App = {};
  root.App.TestController = root.TestController;
  // case 1b: controller option as namespaced string
  var route = new Route(Router, 'test', {
    controller: 'App.TestController',
    template: 'template'
  });
  testGetController(route);

  // case 2: resolve controller intelligently
  var route = new Route(Router, 'test', {
    template: 'template'
  });
  testGetController(route);

  // case 3: anonymous controller
  // case 2: resolve controller intelligently
  var route = new Route(Router, 'anon', {
    template: 'template'
  });
  var controller = route.getController('/anon', {option: true});
  test.isTrue(controller instanceof RouteController);
  test.equal(controller.options.route, route);
  test.equal(controller.options.template, 'template');
  test.isTrue(controller.options.option);
});
<commit_msg>Basic test for integer params. For #330<commit_after>/*
 * Tests for Route
 */

var paths = {
  explicit: '/posts',
  required: '/posts/:param',
  multi: '/posts/:paramOne/:paramTwo',
  optional: '/posts/:paramOne/:paramTwo?',
  simpleOptional: '/:param?',
  wildcard: '/posts/*',
  namedWildcard: '/posts/:file(*)',
  regex: /^\/commits\/(\d+)\.\.(\d+)/
};

Tinytest.add('Route - matching', function (test) {
  var route = new Route(Router, 'explicit', {
    path: paths.explicit
  });
  test.isTrue(route.test('/posts'));
  test.isTrue(route.exec('/posts'));
  test.isTrue(route.test('/posts/'));
  test.isFalse(route.test('/posts/1'));
  test.isNull(route.exec('/posts/1'));

  route = new Route(Router, 'required', {
    path: paths.required
  });
  test.isTrue(route.test('/posts/1'));
  test.isTrue(route.exec('/posts/1'));
  test.isFalse(route.test('/posts/1/2'));
  test.isNull(route.exec('/posts/1/2'));

  route = new Route(Router, 'multi', {
    path: paths.multi
  });
  test.isTrue(route.test('/posts/1/2'));
  test.isTrue(route.exec('/posts/1/2'));
  test.isFalse(route.test('/posts/1/2/3'));
  test.isNull(route.exec('/posts/1/2/3'));

  route = new Route(Router, 'optional', {
    path: paths.optional
  });
  test.isTrue(route.test('/posts/1'));
  test.isTrue(route.exec('/posts/1'));
  test.isTrue(route.test('/posts/1/2'));
  test.isTrue(route.exec('/posts/1/2'));

  route = new Route(Router, 'simpleOptional', {
    path: paths.simpleOptional
  });
  test.isTrue(route.test('/'));
  test.isTrue(route.exec('/'));
  test.isTrue(route.test('/1'));
  test.isTrue(route.exec('/1'));

  route = new Route(Router, 'wildcard', {
    path: paths.wildcard
  });
  test.isTrue(route.test('/posts/1/2'));
  test.isTrue(route.exec('/posts/1/2'));
  test.isTrue(route.test('/posts/1/2/3'));
  test.isTrue(route.exec('/posts/1/2/3'));
  test.isTrue(route.test('/posts/1/2/3/4'));
  test.isTrue(route.exec('/posts/1/2/3/4'));

  route = new Route(Router, 'namedWildcard', {
    path: paths.namedWildcard
  });
  test.isTrue(route.test('/posts/path/to/file'));
  test.isTrue(route.exec('/posts/path/to/file'));

  route = new Route(Router, 'regex', {
    path: paths.regex
  });
  test.isTrue(route.test('/commits/123..456'));
  test.isTrue(route.exec('/commits/123..456'));
});

Tinytest.add('Route - params', function (test) {
  var route = new Route(Router, 'explicit', {
    path: paths.explicit
  });

  test.isNull(route.params());
  test.isTrue(route.params('/posts') instanceof Array);

  route = new Route(Router, 'required', {
    path: paths.required
  });

  var params = route.params('/posts/1');
  test.equal(params.param, "1");

  route = new Route(Router, 'multi', {
    path: paths.multi
  });
  params = route.params('/posts/1/2');
  test.equal(params.paramOne, '1');
  test.equal(params.paramTwo, '2');

  route = new Route(Router, 'optional', {
    path: paths.optional
  });
  params = route.params('/posts/1');
  test.equal(params.paramOne, '1');
  test.isUndefined(params.paramTwo);

  params = route.params('/posts/1/2');
  test.equal(params.paramOne, '1');
  test.equal(params.paramTwo, '2');

  route = new Route(Router, 'simpleOptional', {
    path: paths.simpleOptional
  });

  params = route.params('/');
  test.isUndefined(params.param);

  params = route.params('/1');
  test.equal(params.param, '1');

  route = new Route(Router, 'wildcard', {
    path: paths.wildcard
  });
  params = route.params('/posts/some/wildcard/path');
  test.equal(params[0], 'some/wildcard/path');

  route = new Route(Router, 'namedWildcard', {
    path: paths.namedWildcard
  });
  params = route.params('/posts/some/file/path');
  test.equal(params.file, 'some/file/path');

  route = new Route(Router, 'regex', {
    path: paths.regex
  });
  params = route.params('/commits/123..456');
  test.equal(params[0], '123');
  test.equal(params[1], '456');
});

Tinytest.add('Route - params with query and hash', function (test) {
  var route = new Route(Router, 'optional', {
    path: paths.optional
  });

  var params;

  params = route.params('/posts/1?q=s#anchorTag');
  test.equal(params.paramOne, '1');
  test.isUndefined(params.paramTwo);
  test.equal(params.q, 's');
  test.equal(params.hash, 'anchorTag');

  params = route.params('/posts/1/2?q=s#anchorTag');
  test.equal(params.paramTwo, '2');
});

Tinytest.add('Route - resolve', function (test) {
  var route = new Route(Router, 'required', {
    path: paths.required
  });

  var params;
  var options;

  params = {
    param: '1'
  };
  test.equal(route.resolve(params), '/posts/1');

  params = {
    param: 1
  };
  test.equal(route.resolve(params), '/posts/1');

  params = {
    param: '1'
  };
  options = {
    query: {
      q: 's'
    },
    hash: 'anchorTag'
  };
  test.equal(route.resolve(params, options), '/posts/1/?q=s#anchorTag');

  route = new Route(Router, 'simpleOptional', {
    path: paths.simpleOptional
  });
  test.equal(route.resolve(), '/');
  test.equal(route.resolve({param: '1'}), '/1');

  route = new Route(Router, 'wildcard', {
    path: paths.wildcard
  });
  params = ['some/file/path'];
  test.equal(route.resolve(params), '/posts/some/file/path');

  route = new Route(Router, 'namedWildcard', {
    path: paths.namedWildcard
  });
});

Tinytest.add('Route - normalizePath', function (test) {
  var route = new Route(Router, 'explicit', {
    path: paths.explicit
  });

  test.equal(route.normalizePath('/posts'), '/posts');
  test.equal(route.normalizePath('posts'), '/posts');
  test.equal(route.normalizePath(Meteor.absoluteUrl('posts')), '/posts');
  test.equal(route.normalizePath('/posts?q=s'), '/posts');
  test.equal(route.normalizePath('/posts#anchorTag'), '/posts');
});

Tinytest.add('Route - getController', function (test) {
  var route;
  var root = Utils.global();

  root.TestController = function (options)  {
    if (arguments.length < 1)
      throw new Error('Argument length check');

    this.options = options;
  };

  var testGetController = function (route) {
    var controller = route.getController('/test', {option: true});
    test.isTrue(controller instanceof TestController);
    test.equal(controller.options.route, route);
    test.equal(controller.options.template, 'template');
    test.isTrue(controller.options.option);
  };

  // case 1: controller option
  var route = new Route(Router, 'test', {
    controller: root.TestController,
    template: 'template'
  });
  testGetController(route);

  // case 1a: controller option as string
  var route = new Route(Router, 'test', {
    controller: 'TestController',
    template: 'template'
  });
  testGetController(route);

  root.App = {};
  root.App.TestController = root.TestController;
  // case 1b: controller option as namespaced string
  var route = new Route(Router, 'test', {
    controller: 'App.TestController',
    template: 'template'
  });
  testGetController(route);

  // case 2: resolve controller intelligently
  var route = new Route(Router, 'test', {
    template: 'template'
  });
  testGetController(route);

  // case 3: anonymous controller
  // case 2: resolve controller intelligently
  var route = new Route(Router, 'anon', {
    template: 'template'
  });
  var controller = route.getController('/anon', {option: true});
  test.isTrue(controller instanceof RouteController);
  test.equal(controller.options.route, route);
  test.equal(controller.options.template, 'template');
  test.isTrue(controller.options.option);
});
<|endoftext|>