<commit_before>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import {connect, Provider} from 'react-redux';
import {bindActionCreators} from 'redux';
import {autobind} from 'core-decorators';
import {fromJS} from 'immutable';
import {addLocaleData, IntlProvider, intlShape, injectIntl} from 'react-intl';
import {injectSpark} from '@ciscospark/react-redux-spark';
import {injectSparkMetrics, default as metricsReducer} from '@ciscospark/react-redux-spark-metrics';

import createRootReducer from './root-reducer';
import initStore from './store';
import {removeWidget} from './actions';
import registerWidget from './browser-globals';
import setupDataAPI from './data-api';

function getDisplayName(C) {
  return C.displayName || C.name || `C`;
}

const defaultOptions = fromJS({
  intl: {
    messages: null,
    locale: `en`
  },
  browserGlobals: true,
  dataAPI: true,
  metrics: false,
  reducers: {},
  propTypes: {
    accessToken: PropTypes.string,
    injectProvider: PropTypes.bool,
    logLevel: PropTypes.oneOf([
      `silent`,
      `error`,
      `warn`,
      `log`,
      `info`,
      `debug`,
      `trace`
    ]),
    metrics: PropTypes.object,
    onEvent: PropTypes.func,
    spark: PropTypes.object,
    store: PropTypes.object
  }
});

/**
 * Constructs a connected component with necessary redux modules
 * @param {String} widgetName Unique name for widget, camel cased
 * @param  {Object} Widget React container for the widget
 * @param  {Object} [options={}] Options matching format of defaultOptions above
 * @returns {Object} React Component
 */
// eslint-disable-next-line max-statements
export default function constructSparkWidget(widgetName, Widget, options = {}) {
  let widgetEl;
  let WidgetComponent = Widget;
  let widgetOptions = defaultOptions.mergeDeep(options);

  if (widgetOptions.get(`metrics`)) {
    widgetOptions = widgetOptions.setIn([`reducers`, `metricsStore`], metricsReducer);
  }

  const reducers = widgetOptions.get(`reducers`);
  const allowedPropTypes = widgetOptions.get(`propTypes`).toJS();
  // Setup Reducers
  const rootReducer = createRootReducer(reducers.toJS());

  /**
   * Injects widget into DOM
   * @param  {DOMNode} element DOM Element or Node where widget will inject
   * @param  {Object} props Any props that should be passed to widget
   * @returns {DOMNode} DOM Element where widget is rendered
   */
  function initSparkWidget(element, props) {
    // Filter props based on options passed to constructor
    const filteredProps = {};
    const allowedProps = Object.keys(allowedPropTypes);

    Object.keys(props).forEach((k) => {
      if (allowedProps.includes(k)) {
        filteredProps[k] = props[k];
      }
    });

    ReactDOM.render(
      <SparkWidget {...filteredProps} />, element
    );

    widgetEl = element;
    return element;
  }

  function removeSparkWidget(el) {
    ReactDOM.unmountComponentAtNode(el);
  }

  const registerTuple = [widgetName, initSparkWidget, removeSparkWidget];

  // Setup Browser globals for widget
  if (widgetOptions.get(`browserGlobals`)) {
    registerWidget(...registerTuple);
  }

  // Setup Data API
  if (widgetOptions.get(`dataAPI`)) {
    setupDataAPI(...registerTuple);
  }

  // Setup React-intl
  const intlMessages = widgetOptions.getIn([`intl`, `messages`]).toJS();
  const intlLocale = widgetOptions.getIn([`intl`, `locale`]);
  if (intlMessages) {
    // eslint-disable-next-line global-require
    const localeData = require(`react-intl/locale-data/${intlLocale}`);
    addLocaleData(localeData);
    allowedPropTypes.intl = intlShape;
  }

  WidgetComponent = injectSpark(WidgetComponent);

  if (intlMessages) {
    WidgetComponent = injectIntl(WidgetComponent);
  }

  if (widgetOptions.get(`metrics`)) {
    WidgetComponent = injectSparkMetrics(WidgetComponent, widgetName);
  }

  // Connect widget to Redux store
  // Will prefer to return passed props than what's stored in state
  WidgetComponent = connect(
    (state, ownProps) => Object.assign({}, state, ownProps),
    (dispatch) => bindActionCreators({removeWidget}, dispatch)
  )(WidgetComponent);

  /**
   * Spark Widget HOC
   * @type {class}
   * @extends {Component}
   */
  class SparkWidget extends Component {

    constructor(props) {
      super(props);
      this.el = widgetEl;
      this.handleEvent = this.handleEvent.bind(this);
    }

    shouldComponentUpdate() {
      return true;
    }

    @autobind
    handleEvent(name, data) {
      const {
        onEvent
      } = this.props;
      if (typeof onEvent === `function`) {
        this.props.onEvent(name, data);
      }
    }

    render() {
      const {
        store,
        injectProvider
      } = this.props;
      let widgetStore = store;
      if (injectProvider && !widgetStore) {
        widgetStore = initStore(rootReducer);
      }

      let widget = <WidgetComponent {...this.props} onEvent={this.handleEvent} />;

      if (widgetStore && injectProvider) {
        widget = <Provider store={widgetStore}>{widget}</Provider>;
      }

      if (intlMessages) {
        widget = ( // eslint-disable-line no-extra-parens
          <IntlProvider locale={intlLocale} messages={intlMessages} >
            {widget}
          </IntlProvider>
        );
      }

      return widget;
    }
  }

  const injectedProps = {
    removeWidget: PropTypes.func
  };

  SparkWidget.propTypes = {
    ...injectedProps,
    ...allowedPropTypes
  };

  SparkWidget.defaultProps = {
    injectProvider: true
  };

  SparkWidget.displayName = `SparkWidget(${getDisplayName(Widget)})`;
  SparkWidget.Widget = Widget;

  return SparkWidget;
}
<commit_msg>fix(widget-base): return unmount status during .remove()<commit_after>import React, {Component} from 'react';
import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import {connect, Provider} from 'react-redux';
import {bindActionCreators} from 'redux';
import {autobind} from 'core-decorators';
import {fromJS} from 'immutable';
import {addLocaleData, IntlProvider, intlShape, injectIntl} from 'react-intl';
import {injectSpark} from '@ciscospark/react-redux-spark';
import {injectSparkMetrics, default as metricsReducer} from '@ciscospark/react-redux-spark-metrics';

import createRootReducer from './root-reducer';
import initStore from './store';
import {removeWidget} from './actions';
import registerWidget from './browser-globals';
import setupDataAPI from './data-api';

function getDisplayName(C) {
  return C.displayName || C.name || `C`;
}

const defaultOptions = fromJS({
  intl: {
    messages: null,
    locale: `en`
  },
  browserGlobals: true,
  dataAPI: true,
  metrics: false,
  reducers: {},
  propTypes: {
    accessToken: PropTypes.string,
    injectProvider: PropTypes.bool,
    logLevel: PropTypes.oneOf([
      `silent`,
      `error`,
      `warn`,
      `log`,
      `info`,
      `debug`,
      `trace`
    ]),
    metrics: PropTypes.object,
    onEvent: PropTypes.func,
    spark: PropTypes.object,
    store: PropTypes.object
  }
});

/**
 * Constructs a connected component with necessary redux modules
 * @param {String} widgetName Unique name for widget, camel cased
 * @param  {Object} Widget React container for the widget
 * @param  {Object} [options={}] Options matching format of defaultOptions above
 * @returns {Object} React Component
 */
// eslint-disable-next-line max-statements
export default function constructSparkWidget(widgetName, Widget, options = {}) {
  let widgetEl;
  let WidgetComponent = Widget;
  let widgetOptions = defaultOptions.mergeDeep(options);

  if (widgetOptions.get(`metrics`)) {
    widgetOptions = widgetOptions.setIn([`reducers`, `metricsStore`], metricsReducer);
  }

  const reducers = widgetOptions.get(`reducers`);
  const allowedPropTypes = widgetOptions.get(`propTypes`).toJS();
  // Setup Reducers
  const rootReducer = createRootReducer(reducers.toJS());

  /**
   * Injects widget into DOM
   * @param  {DOMNode} element DOM Element or Node where widget will inject
   * @param  {Object} props Any props that should be passed to widget
   * @returns {DOMNode} DOM Element where widget is rendered
   */
  function initSparkWidget(element, props) {
    // Filter props based on options passed to constructor
    const filteredProps = {};
    const allowedProps = Object.keys(allowedPropTypes);

    Object.keys(props).forEach((k) => {
      if (allowedProps.includes(k)) {
        filteredProps[k] = props[k];
      }
    });

    ReactDOM.render(
      <SparkWidget {...filteredProps} />, element
    );

    widgetEl = element;
    return element;
  }

  function removeSparkWidget(el) {
    return ReactDOM.unmountComponentAtNode(el);
  }

  const registerTuple = [widgetName, initSparkWidget, removeSparkWidget];

  // Setup Browser globals for widget
  if (widgetOptions.get(`browserGlobals`)) {
    registerWidget(...registerTuple);
  }

  // Setup Data API
  if (widgetOptions.get(`dataAPI`)) {
    setupDataAPI(...registerTuple);
  }

  // Setup React-intl
  const intlMessages = widgetOptions.getIn([`intl`, `messages`]).toJS();
  const intlLocale = widgetOptions.getIn([`intl`, `locale`]);
  if (intlMessages) {
    // eslint-disable-next-line global-require
    const localeData = require(`react-intl/locale-data/${intlLocale}`);
    addLocaleData(localeData);
    allowedPropTypes.intl = intlShape;
  }

  WidgetComponent = injectSpark(WidgetComponent);

  if (intlMessages) {
    WidgetComponent = injectIntl(WidgetComponent);
  }

  if (widgetOptions.get(`metrics`)) {
    WidgetComponent = injectSparkMetrics(WidgetComponent, widgetName);
  }

  // Connect widget to Redux store
  // Will prefer to return passed props than what's stored in state
  WidgetComponent = connect(
    (state, ownProps) => Object.assign({}, state, ownProps),
    (dispatch) => bindActionCreators({removeWidget}, dispatch)
  )(WidgetComponent);

  /**
   * Spark Widget HOC
   * @type {class}
   * @extends {Component}
   */
  class SparkWidget extends Component {

    constructor(props) {
      super(props);
      this.el = widgetEl;
      this.handleEvent = this.handleEvent.bind(this);
    }

    shouldComponentUpdate() {
      return true;
    }

    @autobind
    handleEvent(name, data) {
      const {
        onEvent
      } = this.props;
      if (typeof onEvent === `function`) {
        this.props.onEvent(name, data);
      }
    }

    render() {
      const {
        store,
        injectProvider
      } = this.props;
      let widgetStore = store;
      if (injectProvider && !widgetStore) {
        widgetStore = initStore(rootReducer);
      }

      let widget = <WidgetComponent {...this.props} onEvent={this.handleEvent} />;

      if (widgetStore && injectProvider) {
        widget = <Provider store={widgetStore}>{widget}</Provider>;
      }

      if (intlMessages) {
        widget = ( // eslint-disable-line no-extra-parens
          <IntlProvider locale={intlLocale} messages={intlMessages} >
            {widget}
          </IntlProvider>
        );
      }

      return widget;
    }
  }

  const injectedProps = {
    removeWidget: PropTypes.func
  };

  SparkWidget.propTypes = {
    ...injectedProps,
    ...allowedPropTypes
  };

  SparkWidget.defaultProps = {
    injectProvider: true
  };

  SparkWidget.displayName = `SparkWidget(${getDisplayName(Widget)})`;
  SparkWidget.Widget = Widget;

  return SparkWidget;
}
<|endoftext|>


<commit_before>/**
Object which contains context information.
@typedef {Object} context
@prop {String} to_confirm - Array of parameter names to confirm.
@prop {Sting} confirming - Parameter name which Bot is now confirming.
@prop {Object} confirmed - Object which contains confirmed value of the parameters as properties. If you want to retrieve confirmed value of "date" parameter, access confirmed.date.
@prop {Object} previous - Object which contains conversation history in the current context.
@prop {Array.<Object>} previous.confirmed - Previously confirmed parameter.
@prop {Array.<Object>} previous.message - Array of message object exchanged so far.
@prop {String} previous.message[].from - "bot" or "user"
@prop {MessageObject} previous.message[].message - Message object sent or received.
@prop {Object} previous.event - Previous event
@prop {Object} intent - Intent object which contains various information about current intent based on response from NLP.
@prop {String} intent.name - Intent name detected by NLP
@prop {String} intent.text_response - Text response provided by NLP.
@prop {Array.<Object>} intent.parameters - Entities identified by NLP
@prop {Skill} skill - Skill object currelty applied.
@prop {Array.<Object>} param_change_history - Change log to revive skill instance in the next event.
@prop {String} sender_language - Automatically detected ISO-639-1 based code of the senders language.
@prop {String} translation - Translated text of current message.
@prop {Boolean} _in_progress - Flag to indicate if bot is currenty processing an event from this user.
@prop {String} _flow - Flow applied to current event.
*/

/**
Object which contains intent and related information.
@typedef {Object} intent
@prop {String} id - Intent id.
@prop {String} name - Intent name.
@prop {Object} parameters - Parameters found in the sentence.
@prop {String} text_response - Text response to the sentence.
@prop {Object} fulfillment - Object to fulfill the action.
*/

/**
Custom event to start conversation from Bot.
@typedef {Object} push_event
@prop {String} type - Event type. This is always "bot-express:push".
@prop {Object} to - Object which contains destination information.
@prop {String} to.type - Type of reciever. Supported values are "user", "room", "group".
@prop {String} [to.userId] - User id of reciever. Required if to.type is "user".
@prop {String} [to.roomId] - Room id of reciever. Required if to.type is "room".
@prop {String} [to.groupId] - Group id of reciever. Required if to.type is "group".
@prop {intent} intent - Intent object to be applied in this conversation.
@prop {String} language - ISO-639-1 based code of the sender's language.
*/
<commit_msg>Fix bug in doc<commit_after>/**
Object which contains context information.
@typedef {Object} context
@prop {Array.<Object>} to_confirm - Array of parameter names to confirm.
@prop {Sting} confirming - Parameter name which Bot is now confirming.
@prop {Object} confirmed - Object which contains confirmed value of the parameters as properties. If you want to retrieve confirmed value of "date" parameter, access confirmed.date.
@prop {Object} previous - Object which contains conversation history in the current context.
@prop {Array.<Object>} previous.confirmed - Previously confirmed parameter.
@prop {Array.<Object>} previous.message - Array of message object exchanged so far.
@prop {String} previous.message[].from - "bot" or "user"
@prop {MessageObject} previous.message[].message - Message object sent or received.
@prop {Object} previous.event - Previous event
@prop {Object} intent - Intent object which contains various information about current intent based on response from NLP.
@prop {String} intent.name - Intent name detected by NLP
@prop {String} intent.text_response - Text response provided by NLP.
@prop {Array.<Object>} intent.parameters - Entities identified by NLP
@prop {Skill} skill - Skill object currelty applied.
@prop {Array.<Object>} param_change_history - Change log to revive skill instance in the next event.
@prop {String} sender_language - Automatically detected ISO-639-1 based code of the senders language.
@prop {String} translation - Translated text of current message.
@prop {Boolean} _in_progress - Flag to indicate if bot is currenty processing an event from this user.
@prop {String} _flow - Flow applied to current event.
*/

/**
Object which contains intent and related information.
@typedef {Object} intent
@prop {String} id - Intent id.
@prop {String} name - Intent name.
@prop {Object} parameters - Parameters found in the sentence.
@prop {String} text_response - Text response to the sentence.
@prop {Object} fulfillment - Object to fulfill the action.
*/

/**
Custom event to start conversation from Bot.
@typedef {Object} push_event
@prop {String} type - Event type. This is always "bot-express:push".
@prop {Object} to - Object which contains destination information.
@prop {String} to.type - Type of reciever. Supported values are "user", "room", "group".
@prop {String} [to.userId] - User id of reciever. Required if to.type is "user".
@prop {String} [to.roomId] - Room id of reciever. Required if to.type is "room".
@prop {String} [to.groupId] - Group id of reciever. Required if to.type is "group".
@prop {intent} intent - Intent object to be applied in this conversation.
@prop {String} language - ISO-639-1 based code of the sender's language.
*/
<|endoftext|>


<commit_before>"use strict";

var Anye = require( "../lib/anye.js" );

module.exports = {
    setUp: function( fDone ) {
        Anye.clear();
        fDone();
    },

    "Anye Tests": function( oTest ) {
        // Setting
        oTest.equal( Anye.set( "simple", "/url/" ), "/url/", "Anye.set should return the given URL." );
        oTest.equal( Anye.set( "one-param", "/url/:id" ), "/url/:id", "Anye.set should return the given URL." );

        // Counting
        oTest.equal( Anye.length, 2, "Anye.length should be 2." );
        Anye.set( "multi-params", "/url/:id/:module" );
        oTest.equal( Anye.length, 3, "Anye.length should be 3." );

        // Clearing
        Anye.clear();
        oTest.equal( Anye.length, 0, "Anye.length should be 0." );

        Anye.set( "simple", "/url/" );
        Anye.set( "one-param", "/url/:id" );
        Anye.set( "multi-params", "/url/:id/:module" );

        // Retrieving
        oTest.throws( function() { Anye.get( "unknown" ); }, "Anye.get should throws when calling an url that is not in the store." );

        oTest.equal( Anye.get( "simple" ), "/url/", "Anye.get should return the good url." );

        oTest.throws( function() { Anye.get( "one-param" ); }, "Anye.get should throws when calling an url expecting with no params." );
        oTest.throws( function() { Anye.get( "one-param", { foo: "bar" } ); }, "Anye.get should throws when calling an url expecting with not the attended params." );
        oTest.equal( Anye.get( "one-param", { id: 2 } ), "/url/2", "Anye.get should return the good url, with the given params." );
        oTest.equal( Anye.get( "one-param", { id: "bar" } ), "/url/bar", "Anye.get should return the good url, with the given params." );

        oTest.throws( function() { Anye.get( "multi-params", { id: 2 } ); }, "Anye.get should throws when calling an url expecting with not the attended params." );
        oTest.equal( Anye.get( "multi-params", { id: 2, module: "foo" } ), "/url/2/foo", "Anye.get should return the good url, with the given params." );
        oTest.equal( Anye.get( "multi-params", { module: 2, id: "foo" } ), "/url/foo/2", "Anye.get should return the good url, with the given params." );

        // All
        oTest.deepEqual( Anye.all(), { "simple": "/url/", "one-param": "/url/:id", "multi-params": "/url/:id/:module" }, "Anye.all should return all the DataStore" );

        // Raw url
        oTest.throws( function() { Anye.raw( "unknown" ); }, "Anye.raw should throws when calling an url that is not in the store." );
        oTest.equal( Anye.raw( "simple" ), "/url/", "Anye.raw should return the good url." );
        oTest.equal( Anye.raw( "one-param" ), "/url/:id", "Anye.raw should return the good url." );
        oTest.equal( Anye.url( "multi-params" ), "/url/:id/:module", "Anye.raw url return the good url." );

            // URL Decoding
        oTest.equal( Anye.get( "one-param", { id: "bar^" } ), "/url/bar%5E", "Anye.get should return the good url, with the given params, and URL-encoded by default." );
        oTest.equal( Anye.get( "one-param", { id: "bar^" }, true ), "/url/bar^", "Anye.get should return the good url, with the given params, and URL-decoded." );

            // Additional parameters
        oTest.equal( Anye.get( "simple", { id: 2, foo: "bar" } ), "/url/?id=2&foo=bar", "Anye.get should return the good url and add additional params to the query string." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar" } ), "/url/2?foo=bar", "Anye.get should return the good url, with the given params, and add additional params to the query string." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: "baz" } ), "/url/2?foo=bar&bar=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar^", bar: "baz" }, true ), "/url/2?foo=bar^&bar=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string, and URL-decoded." );

            // Complex & nested additional parameters
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: [ 1, 2, 3 ] } ), "/url/2?foo=bar&bar%5B0%5D=1&bar%5B1%5D=2&bar%5B2%5D=3", "Anye.get should return the good url, with the given params, and add additional params to the query string, event the complex ones, like Arrays." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: { baz: "bar", zab: "baz" } } ), "/url/2?foo=bar&bar%5Bbaz%5D=bar&bar%5Bzab%5D=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string, event the complex ones, like Objects." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: { baz: "bar", zab: "baz" } }, true ), "/url/2?foo=bar&bar[baz]=bar&bar[zab]=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string, event the complex ones, like Objects, and URL-decoded." );

        // Generating
        oTest.equal( Anye.generate( "/url/" ), "/url/", "Anye.generate should return the given url." );
        oTest.throws( function() { Anye.generate( "/url/:id" ); }, "Anye.generate should throws when giving an url with params, and no giving params." );
        oTest.equal( Anye.generate( "/url/:id", { id: 2 } ), "/url/2", "Anye.generate should return the given url, with the given params." );
        oTest.equal( Anye.generate( "/url/:id/:module", { id: 2, module: "foo" } ), "/url/2/foo", "Anye.generate should return the given url, with the given params." );
        oTest.equal( Anye.generate( "/url/:id", { id: "bar^" } ), "/url/bar%5E", "Anye.generate should return the given url, with the given params, and URL-encoded by default." );
        oTest.equal( Anye.generate( "/url/:id", { id: "bar^" }, true ), "/url/bar^", "Anye.generate should return the given url, with the given params, and URL-decoded." );
        oTest.equal( Anye.generate( "/url/:id", { id: 2, foo: "bar" } ), "/url/2?foo=bar", "Anye.generate should return the given url, with the given params, and add additional params to the query string." );
        oTest.equal( Anye.generate( "/url/:id", { id: 2, foo: "bar", bar: { baz: "bar", zab: "baz" } } ), "/url/2?foo=bar&bar%5Bbaz%5D=bar&bar%5Bzab%5D=baz", "Anye.generate should return the given url, with the given params, and add additional params to the query string, event the complex ones, like Objects." );

        // Aliases
        oTest.equal( Anye.store( "simple", "/url/" ), "/url/", "Anye.store() should behave like Anye.set()." );
        oTest.equal( Anye.retrieve( "simple" ), "/url/", "Anye.retrieve() should behave like Anye.get()." );
        oTest.equal( Anye.build( "simple" ), "/url/", "Anye.build() should behave like Anye.get()." );

        oTest.done();
    }
};
<commit_msg>Refactor some tests<commit_after>"use strict";

var Anye = require( "../lib/anye.js" );

module.exports = {
    setUp: function( fDone ) {
        Anye.clear();
        fDone();
    },

    "Anye Tests": function( oTest ) {
        // Setting
        oTest.equal( Anye.set( "simple", "/url/" ), "/url/", "Anye.set should return the given URL." );
        oTest.equal( Anye.set( "one-param", "/url/:id" ), "/url/:id", "Anye.set should return the given URL." );

        // Counting
        oTest.equal( Anye.count(), 2, "Anye.count should return 2." );
        Anye.set( "multi-params", "/url/:id/:module" );
        oTest.equal( Anye.count(), 3, "Anye.count should return 3." );

        // Clearing
        Anye.clear();
        oTest.equal( Anye.count(), 0, "Anye.count() should return 0." );

        Anye.set( "simple", "/url/" );
        Anye.set( "one-param", "/url/:id" );
        Anye.set( "multi-params", "/url/:id/:module" );

        // Retrieving
        oTest.throws( function() { Anye.get( "unknown" ); }, "Anye.get should throws when calling an url that is not in the store." );

        oTest.equal( Anye.get( "simple" ), "/url/", "Anye.get should return the good url." );

        oTest.throws( function() { Anye.get( "one-param" ); }, "Anye.get should throws when calling an url expecting with no params." );
        oTest.throws( function() { Anye.get( "one-param", { foo: "bar" } ); }, "Anye.get should throws when calling an url expecting with not the attended params." );
        oTest.equal( Anye.get( "one-param", { id: 2 } ), "/url/2", "Anye.get should return the good url, with the given params." );
        oTest.equal( Anye.get( "one-param", { id: "bar" } ), "/url/bar", "Anye.get should return the good url, with the given params." );

        oTest.throws( function() { Anye.get( "multi-params", { id: 2 } ); }, "Anye.get should throws when calling an url expecting with not the attended params." );
        oTest.equal( Anye.get( "multi-params", { id: 2, module: "foo" } ), "/url/2/foo", "Anye.get should return the good url, with the given params." );
        oTest.equal( Anye.get( "multi-params", { module: 2, id: "foo" } ), "/url/foo/2", "Anye.get should return the good url, with the given params." );

        // All
        oTest.deepEqual( Anye.all(), { "simple": "/url/", "one-param": "/url/:id", "multi-params": "/url/:id/:module" }, "Anye.all should return all the DataStore" );

        // Raw url
        oTest.throws( function() { Anye.raw( "unknown" ); }, "Anye.raw should throws when calling an url that is not in the store." );
        oTest.equal( Anye.raw( "simple" ), "/url/", "Anye.raw should return the good url." );
        oTest.equal( Anye.raw( "one-param" ), "/url/:id", "Anye.raw should return the good url." );
        oTest.equal( Anye.url( "multi-params" ), "/url/:id/:module", "Anye.raw url return the good url." );

            // URL Decoding
        oTest.equal( Anye.get( "one-param", { id: "bar^" } ), "/url/bar%5E", "Anye.get should return the good url, with the given params, and URL-encoded by default." );
        oTest.equal( Anye.get( "one-param", { id: "bar^" }, true ), "/url/bar^", "Anye.get should return the good url, with the given params, and URL-decoded." );

            // Additional parameters
        oTest.equal( Anye.get( "simple", { id: 2, foo: "bar" } ), "/url/?id=2&foo=bar", "Anye.get should return the good url and add additional params to the query string." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar" } ), "/url/2?foo=bar", "Anye.get should return the good url, with the given params, and add additional params to the query string." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: "baz" } ), "/url/2?foo=bar&bar=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar^", bar: "baz" }, true ), "/url/2?foo=bar^&bar=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string, and URL-decoded." );

            // Complex & nested additional parameters
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: [ 1, 2, 3 ] } ), "/url/2?foo=bar&bar%5B0%5D=1&bar%5B1%5D=2&bar%5B2%5D=3", "Anye.get should return the good url, with the given params, and add additional params to the query string, event the complex ones, like Arrays." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: { baz: "bar", zab: "baz" } } ), "/url/2?foo=bar&bar%5Bbaz%5D=bar&bar%5Bzab%5D=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string, event the complex ones, like Objects." );
        oTest.equal( Anye.get( "one-param", { id: 2, foo: "bar", bar: { baz: "bar", zab: "baz" } }, true ), "/url/2?foo=bar&bar[baz]=bar&bar[zab]=baz", "Anye.get should return the good url, with the given params, and add additional params to the query string, event the complex ones, like Objects, and URL-decoded." );

        // Generating
        oTest.equal( Anye.generate( "/url/" ), "/url/", "Anye.generate should return the given url." );
        oTest.throws( function() { Anye.generate( "/url/:id" ); }, "Anye.generate should throws when giving an url with params, and no giving params." );
        oTest.equal( Anye.generate( "/url/:id", { id: 2 } ), "/url/2", "Anye.generate should return the given url, with the given params." );
        oTest.equal( Anye.generate( "/url/:id/:module", { id: 2, module: "foo" } ), "/url/2/foo", "Anye.generate should return the given url, with the given params." );
        oTest.equal( Anye.generate( "/url/:id", { id: "bar^" } ), "/url/bar%5E", "Anye.generate should return the given url, with the given params, and URL-encoded by default." );
        oTest.equal( Anye.generate( "/url/:id", { id: "bar^" }, true ), "/url/bar^", "Anye.generate should return the given url, with the given params, and URL-decoded." );
        oTest.equal( Anye.generate( "/url/:id", { id: 2, foo: "bar" } ), "/url/2?foo=bar", "Anye.generate should return the given url, with the given params, and add additional params to the query string." );
        oTest.equal( Anye.generate( "/url/:id", { id: 2, foo: "bar", bar: { baz: "bar", zab: "baz" } } ), "/url/2?foo=bar&bar%5Bbaz%5D=bar&bar%5Bzab%5D=baz", "Anye.generate should return the given url, with the given params, and add additional params to the query string, event the complex ones, like Objects." );

        // Aliases
        oTest.equal( Anye.store( "simple", "/url/" ), "/url/", "Anye.store() should behave like Anye.set()." );
        oTest.equal( Anye.retrieve( "simple" ), "/url/", "Anye.retrieve() should behave like Anye.get()." );
        oTest.equal( Anye.build( "simple" ), "/url/", "Anye.build() should behave like Anye.get()." );

        oTest.done();
    }
};
<|endoftext|>


<commit_before>/*
 * Leaves breadcrumbs when navigation methods are called or events are emitted
 */
exports.init = (client, win = window) => {
  if (!('addEventListener' in win)) return

  // returns a function that will drop a breadcrumb with a given name
  const drop = name => () => client.leaveBreadcrumb(name, {}, 'navigation')

  // simple drops – just names, no meta
  win.addEventListener('pagehide', drop('Page hidden'), true)
  win.addEventListener('pageshow', drop('Page shown'), true)
  win.addEventListener('load', drop('Page loaded'), true)
  win.document.addEventListener('DOMContentLoaded', drop('DOMContentLoaded'), true)
  // some browsers like to emit popstate when the page loads, so only add the popstate listener after that
  win.addEventListener('load', () => win.addEventListener('popstate', drop('Navigated back'), true))

  // hashchange has some metaData that we care about
  win.addEventListener('hashchange', event => {
    const metaData = event.oldURL
      ? { from: relativeLocation(event.oldURL), to: relativeLocation(event.newURL), state: getCurrentState() }
      : { to: relativeLocation(win.location.href) }
    client.leaveBreadcrumb('Hash changed', metaData, 'navigation')
  }, true)

  // the only way to know about replaceState/pushState is to wrap them… >_<

  if (win.history.replaceState) wrapHistoryFn(client, win.history, 'replaceState', win)
  if (win.history.pushState) wrapHistoryFn(client, win.history, 'pushState', win)

  client.leaveBreadcrumb('Bugsnag loaded', {}, 'navigation')
}

exports.configSchema = {
  navigationBreadcrumbsEnabled: {
    defaultValue: () => undefined,
    validate: (value) => value === true || value === false || value === undefined,
    message: 'should be true|false'
  }
}

if (process.env.NODE_ENV !== 'production') {
  exports.destroy = (win = window) => {
    win.history.replaceState._restore()
    win.history.pushState._restore()
  }
}

// takes a full url like http://foo.com:1234/pages/01.html?yes=no#section-2 and returns
// just the path and hash parts, e.g. /pages/01.html?yes=no#section-2
const relativeLocation = (url, win) => {
  const a = win.document.createElement('A')
  a.href = url
  return `${a.pathname}${a.search}${a.hash}`
}

const stateChangeToMetaData = (win, state, title, url) => {
  const currentPath = relativeLocation(win.location.href, win)
  return { title, state, prevState: getCurrentState(win), to: url || currentPath, from: currentPath }
}

const wrapHistoryFn = (client, target, fn, win) => {
  const orig = target[fn]
  target[fn] = (state, title, url) => {
    client.leaveBreadcrumb(`History ${fn}`, stateChangeToMetaData(win, state, title, url), 'navigation')
    // if throttle plugin is in use, refresh the event sent count
    if (typeof client.refresh === 'function') client.refresh()
    // if the client is operating in session-mode, a new route should trigger a new session
    if (client.session) client.startSession()
    // Internet Explorer will convert `undefined` to a string when passed, causing an unintended redirect
    // to '/undefined'. therefore we only pass the url if it's not undefined.
    orig.apply(target, [ state, title ].concat(url !== undefined ? url : []))
  }
  target[fn]._restore = () => { target[fn] = orig }
}

const getCurrentState = (win) => {
  try {
    return win.history.state
  } catch (e) {}
}
<commit_msg>fix(navigation-breadcrumbs): Ensure window reference is passed where necessary<commit_after>/*
 * Leaves breadcrumbs when navigation methods are called or events are emitted
 */
exports.init = (client, win = window) => {
  if (!('addEventListener' in win)) return

  // returns a function that will drop a breadcrumb with a given name
  const drop = name => () => client.leaveBreadcrumb(name, {}, 'navigation')

  // simple drops – just names, no meta
  win.addEventListener('pagehide', drop('Page hidden'), true)
  win.addEventListener('pageshow', drop('Page shown'), true)
  win.addEventListener('load', drop('Page loaded'), true)
  win.document.addEventListener('DOMContentLoaded', drop('DOMContentLoaded'), true)
  // some browsers like to emit popstate when the page loads, so only add the popstate listener after that
  win.addEventListener('load', () => win.addEventListener('popstate', drop('Navigated back'), true))

  // hashchange has some metaData that we care about
  win.addEventListener('hashchange', event => {
    const metaData = event.oldURL
      ? { from: relativeLocation(event.oldURL, win), to: relativeLocation(event.newURL, win), state: getCurrentState(win) }
      : { to: relativeLocation(win.location.href, win) }
    client.leaveBreadcrumb('Hash changed', metaData, 'navigation')
  }, true)

  // the only way to know about replaceState/pushState is to wrap them… >_<

  if (win.history.replaceState) wrapHistoryFn(client, win.history, 'replaceState', win)
  if (win.history.pushState) wrapHistoryFn(client, win.history, 'pushState', win)

  client.leaveBreadcrumb('Bugsnag loaded', {}, 'navigation')
}

exports.configSchema = {
  navigationBreadcrumbsEnabled: {
    defaultValue: () => undefined,
    validate: (value) => value === true || value === false || value === undefined,
    message: 'should be true|false'
  }
}

if (process.env.NODE_ENV !== 'production') {
  exports.destroy = (win = window) => {
    win.history.replaceState._restore()
    win.history.pushState._restore()
  }
}

// takes a full url like http://foo.com:1234/pages/01.html?yes=no#section-2 and returns
// just the path and hash parts, e.g. /pages/01.html?yes=no#section-2
const relativeLocation = (url, win) => {
  const a = win.document.createElement('A')
  a.href = url
  return `${a.pathname}${a.search}${a.hash}`
}

const stateChangeToMetaData = (win, state, title, url) => {
  const currentPath = relativeLocation(win.location.href, win)
  return { title, state, prevState: getCurrentState(win), to: url || currentPath, from: currentPath }
}

const wrapHistoryFn = (client, target, fn, win) => {
  const orig = target[fn]
  target[fn] = (state, title, url) => {
    client.leaveBreadcrumb(`History ${fn}`, stateChangeToMetaData(win, state, title, url), 'navigation')
    // if throttle plugin is in use, refresh the event sent count
    if (typeof client.refresh === 'function') client.refresh()
    // if the client is operating in session-mode, a new route should trigger a new session
    if (client.session) client.startSession()
    // Internet Explorer will convert `undefined` to a string when passed, causing an unintended redirect
    // to '/undefined'. therefore we only pass the url if it's not undefined.
    orig.apply(target, [ state, title ].concat(url !== undefined ? url : []))
  }
  target[fn]._restore = () => { target[fn] = orig }
}

const getCurrentState = (win) => {
  try {
    return win.history.state
  } catch (e) {}
}
<|endoftext|>


<commit_before>/*global describe, it, after*/
/*
 * mochify.js
 *
 * Copyright (c) 2014 Maximilian Antoni <mail@maxantoni.de>
 *
 * @license MIT
 */
'use strict';

var assert = require('assert');
var run    = require('./fixture/run');

describe('args', function () {

  it('quits with usage', function (done) {
    run('passes', ['--unknown'], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout, 'Unknown argument: --unknown\n'
                   + 'Run `mochify --help` for usage.\n\n');
      done();
    });
  });

  it('requires single glob to match', function (done) {
    run('passes', ['./unknown/*'], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout, 'Error: Nothing found for "./unknown/*".\n\n');
      done();
    });
  });

  it('requires multiple globs to match', function (done) {
    run('passes', ['./thing-a/*', './thing-b/*'], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout,
          'Error: Nothing found for "./thing-a/*" or "./thing-b/*".\n\n');
      done();
    });
  });

  it('fails with meaningful message if file is missing', function (done) {
    run('passes', ['./unknown-file.js'], function (code, stdout) {
      assert.equal(code, 1);
      assert(stdout.indexOf('./unknown-file.js') !== -1);
      done();
    });
  });

  it('fails with meaningful message for defaults', function (done) {
    run('.', [], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout, 'Error: Nothing found for "./test/*.js".\n\n');
      done();
    });
  });

});
<commit_msg>Add args tests<commit_after>/*global describe, it, after*/
/*
 * mochify.js
 *
 * Copyright (c) 2014 Maximilian Antoni <mail@maxantoni.de>
 *
 * @license MIT
 */
'use strict';

var assert = require('assert');
var args   = require('../lib/args');
var run    = require('./fixture/run');

describe('args', function () {

  it('sets defaults', function () {
    var opts = args.parse([]);

    assert.equal(opts.watch, false);
    assert.equal(opts.cover, false);
    assert.equal(opts.node, false);
    assert.equal(opts.wd, false);
    assert.equal(opts.reporter, 'dot');
    assert.equal(opts.port, 0);
    assert.equal(opts.yields, 0);
  });

  it('parses --reporter', function () {
    var opts = args.parse(['--reporter', 'tap']);

    assert.equal(opts.reporter, 'tap');
  });

  it('parses -R', function () {
    var opts = args.parse(['-R', 'tap']);

    assert.equal(opts.reporter, 'tap');
  });

  it('parses --watch', function () {
    var opts = args.parse(['--watch']);

    assert(opts.watch);
  });

  it('parses -w', function () {
    var opts = args.parse(['-w']);

    assert(opts.watch);
  });

  it('parses --node', function () {
    var opts = args.parse(['--node']);

    assert(opts.node);
  });

  it('parses --wd', function () {
    var opts = args.parse(['--wd']);

    assert(opts.wd);
  });

  it('parses --port', function () {
    var opts = args.parse(['--port', '8765']);

    assert.equal(opts.port, 8765);
  });

  it('parses --yields', function () {
    var opts = args.parse(['--yields', '123']);

    assert.equal(opts.yields, 123);
  });

  it('parses -y', function () {
    var opts = args.parse(['-y', '123']);

    assert.equal(opts.yields, 123);
  });

  it('quits with usage', function (done) {
    run('passes', ['--unknown'], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout, 'Unknown argument: --unknown\n'
                   + 'Run `mochify --help` for usage.\n\n');
      done();
    });
  });

  it('requires single glob to match', function (done) {
    run('passes', ['./unknown/*'], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout, 'Error: Nothing found for "./unknown/*".\n\n');
      done();
    });
  });

  it('requires multiple globs to match', function (done) {
    run('passes', ['./thing-a/*', './thing-b/*'], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout,
          'Error: Nothing found for "./thing-a/*" or "./thing-b/*".\n\n');
      done();
    });
  });

  it('fails with meaningful message if file is missing', function (done) {
    run('passes', ['./unknown-file.js'], function (code, stdout) {
      assert.equal(code, 1);
      assert(stdout.indexOf('./unknown-file.js') !== -1);
      done();
    });
  });

  it('fails with meaningful message for defaults', function (done) {
    run('.', [], function (code, stdout) {
      assert.equal(code, 1);
      assert.equal(stdout, 'Error: Nothing found for "./test/*.js".\n\n');
      done();
    });
  });

});
<|endoftext|>


<commit_before>describe('Complex example', function() {
    it('runs complex example', function() {
        var flag;
        var waitInit = function() {
            flag = false;
            setTimeout(function() { flag = true; }, 500);
        };
        var wait = function() {
            waitsFor(function() { return flag; });
        };

        window.TEMPLATES = {
            StagesBefore: '
before
',
            Stages: '
1

2

3
',
            StagesAfter: 'after',
            StagesSuccess: '
success
'
        };

        var functionCalls = [];

        $CR
            .add('/stages', {
                title: 'Stages',
                data: '/api/data',
                render: {
                    before: 'StagesBefore',
                    success: 'Stages',
                    error: 'StagesError',
                    after: [{template: 'StagesAfter', replace: false}, 'StagesAfter']
                },
                frames: {
                    '/sub1': {
                        title: 'Stages Sub1',
                        data: '/api/sub1',
                        parent: '.sub',
                        render: {
                            before: ['StagesBefore', function() { return document.createTextNode('bebebe'); }, {parent: '.sub2', template: 'StagesBefore'}, function() { return false; }, 'StagesBefore'],
                            success: ['StagesSuccess', function() { functionCalls.push('sub-success1'); }, 'StagesSuccess'],
                            error: 'StagesError',
                            after: 'StagesAfter'
                        }
                    },
                    'sub2': {
                        title: 'Stages Sub2',
                        data: '/api/sub2',
                        render: {
                            before: [function() { return 'StagesBefore'; }, {template: function() { return 'StagesBefore'; }}, {parent: '.sub2', template: function() { return document.createTextNode('bebe'); }}],
                            error: 'StagesError',
                            success: function() { return 'SomeSuccess'}
                        }
                    },
                    'sub3': {
                        title: 'Stages Sub3',
                        data: '/none/sub2',
                        parent: '.sub2',
                        render: {
                            before: 'StagesBefore',
                            success: 'StagesSuccess',
                            error: 'StagesError'
                        }
                    },
                    '/:sub4': {
                        params: {sub4: function(val) { return val === 'sub4' ? val : undefined; }},
                        title: 'Stages Sub3',
                        data: ['/api/data1', function() { return new Promise(function(resolve) { setTimeout(function() { resolve('oooo'); }, 100); }); }, '/api/data2'],
                        render: {
                            success: function(data1, data2, data3, params) {
                                return HTML2DOM('
' + JSON.stringify(data1) + '
' +
                                                '
' + JSON.stringify(data2) + '
' +
                                                '
' + JSON.stringify(data3) + '
' +
                                                '
' + JSON.stringify(params) + '
');
                            }
                        }
                    }

                }
            })
            .run({callTemplate: testCallTemplate});

        $CR.set('/stages');
        expect(objectifyBody()).toEqual([
            {name: 'div', value: ['before']}
        ]);

        waitInit();
        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);

            $CR.set('/stages/sub1');
            waitInit();

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [
                        {name: 'div', value: ['before']},
                        'bebebe'
                    ], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['before']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: ['after'], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);

            $CR.set('/stages/sub2');
            waitInit();

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        'bebe'
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after',
                {name: 'div', value: ['before']},
                {name: 'div', value: ['before']}
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after',
                {name: 'div', value: ['SomeSuccess']},
                {name: 'p', value: ['/stages/sub2']}
            ]);

            $CR.set('/stages/sub3');
            waitInit();

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['before']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['StagesError']},
                        {name: 'p', value: ['/stages/sub3']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);

            $CR.set('/stages/sub4');
            waitInit();

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['StagesError']},
                        {name: 'p', value: ['/stages/sub3']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after',
                {name: 'h1', value: ['{"url":"/api/data1","method":"GET"}']},
                {name: 'h2', value: ['"oooo"']},
                {name: 'h3', value: ['{"url":"/api/data2","method":"GET"}']},
                {name: 'h4', value: ['{"sub4":"sub4"}']}
            ]);
        });
    });
});
<commit_msg>Checking titles.<commit_after>describe('Complex example', function() {
    it('runs complex example', function() {
        var flag;
        var waitInit = function() {
            flag = false;
            setTimeout(function() { flag = true; }, 500);
        };
        var wait = function() {
            waitsFor(function() { return flag; });
        };

        window.TEMPLATES = {
            StagesBefore: '
before
',
            Stages: '
1

2

3
',
            StagesAfter: 'after',
            StagesSuccess: '
success
'
        };

        var functionCalls = [];

        $CR
            .add('/stages', {
                title: 'Stages',
                data: '/api/data',
                render: {
                    before: 'StagesBefore',
                    success: 'Stages',
                    error: 'StagesError',
                    after: [{template: 'StagesAfter', replace: false}, 'StagesAfter']
                },
                frames: {
                    '/sub1': {
                        title: 'Stages Sub1',
                        data: '/api/sub1',
                        parent: '.sub',
                        render: {
                            before: ['StagesBefore', function() { return document.createTextNode('bebebe'); }, {parent: '.sub2', template: 'StagesBefore'}, function() { return false; }, 'StagesBefore'],
                            success: ['StagesSuccess', function() { functionCalls.push('sub-success1'); }, 'StagesSuccess'],
                            error: 'StagesError',
                            after: 'StagesAfter'
                        }
                    },
                    'sub2': {
                        title: 'Stages Sub2',
                        data: '/api/sub2',
                        render: {
                            before: [function() { return 'StagesBefore'; }, {template: function() { return 'StagesBefore'; }}, {parent: '.sub2', template: function() { return document.createTextNode('bebe'); }}],
                            error: 'StagesError',
                            success: function() { return 'SomeSuccess'}
                        }
                    },
                    'sub3': {
                        title: 'Stages Sub3',
                        data: '/none/sub2',
                        parent: '.sub2',
                        render: {
                            before: 'StagesBefore',
                            success: 'StagesSuccess',
                            error: 'StagesError'
                        }
                    },
                    '/:sub4': {
                        params: {sub4: function(val) { return val === 'sub4' ? val : undefined; }},
                        title: 'Stages Sub4',
                        data: ['/api/data1', function() { return new Promise(function(resolve) { setTimeout(function() { resolve('oooo'); }, 100); }); }, '/api/data2'],
                        render: {
                            success: function(data1, data2, data3, params) {
                                return HTML2DOM('
' + JSON.stringify(data1) + '
' +
                                                '
' + JSON.stringify(data2) + '
' +
                                                '
' + JSON.stringify(data3) + '
' +
                                                '
' + JSON.stringify(params) + '
');
                            }
                        }
                    }

                }
            })
            .run({callTemplate: testCallTemplate});

        $CR.set('/stages');
        expect(objectifyBody()).toEqual([
            {name: 'div', value: ['before']}
        ]);

        expect(document.title).toEqual('Stages');

        waitInit();
        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);

            $CR.set('/stages/sub1');
            waitInit();

            expect(document.title).toEqual('Stages Sub1');

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [
                        {name: 'div', value: ['before']},
                        'bebebe'
                    ], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['before']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: ['after'], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);

            $CR.set('/stages/sub2');
            waitInit();

            expect(document.title).toEqual('Stages Sub2');

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        'bebe'
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after',
                {name: 'div', value: ['before']},
                {name: 'div', value: ['before']}
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after',
                {name: 'div', value: ['SomeSuccess']},
                {name: 'p', value: ['/stages/sub2']}
            ]);

            $CR.set('/stages/sub3');
            waitInit();

            expect(document.title).toEqual('Stages Sub3');

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['before']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['StagesError']},
                        {name: 'p', value: ['/stages/sub3']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);

            $CR.set('/stages/sub4');
            waitInit();

            expect(document.title).toEqual('Stages Sub4');

            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [
                        {name: 'div', value: ['StagesError']},
                        {name: 'p', value: ['/stages/sub3']}
                    ], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after'
            ]);
        });

        wait();

        runs(function() {
            expect(objectifyBody()).toEqual([
                {name: 'div', value: [
                    '1',
                    {name: 'div', value: [], attr: {class: 'sub'}},
                    '2',
                    {name: 'div', value: [], attr: {class: 'sub2'}},
                    '3'
                ]},
                'after',
                'after',
                {name: 'h1', value: ['{"url":"/api/data1","method":"GET"}']},
                {name: 'h2', value: ['"oooo"']},
                {name: 'h3', value: ['{"url":"/api/data2","method":"GET"}']},
                {name: 'h4', value: ['{"sub4":"sub4"}']}
            ]);
        });
    });
});
<|endoftext|>


<commit_before>/// <vs BeforeBuild='watch' />
// Include plug-ins
var gulp = require('gulp'),
	jshint = require('gulp-jshint'),
	concat = require('gulp-concat'),
	minifyCSS = require('gulp-minify-css'),
	autoprefixer = require('gulp-autoprefixer'),
	uglify = require('gulp-uglify'),
	rename = require('gulp-rename'),
	inject = require('gulp-inject'),
	minifyHTML = require('gulp-minify-html'),
	del = require('del'),
	angularFilesort = require('gulp-angular-filesort');

 // File paths
var config = {
    // Include all js files but exclude any min.js files
	appJsSrc : ['app/**/*.js', '!app/**/*.min.js'],
	appCssSrc: ['content/toastr.css', 'content/main.css'],
	appIndexHtml: 'index-template.html'
}

// For browser caching
var getStamp = function() {
  var myDate = new Date();

  var myYear = myDate.getFullYear().toString();
  var myMonth = ('0' + (myDate.getMonth() + 1)).slice(-2);
  var myDay = ('0' + myDate.getDate()).slice(-2);
  var mySeconds = myDate.getSeconds().toString();

  var myFullDate = myYear + myMonth + myDay + mySeconds;

  return myFullDate;
};

 // Minify, prefix and contat CSS
gulp.task('css', function(){
	del.sync(['app/build/allcss*']);

    return gulp.src(config.appCssSrc)
		// .pipe(minifyCSS())
		.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9'))
		// .pipe(concat('allcss' + getStamp() + '.min.css', {newLine: ''}))
		.pipe(concat('allcss.min.css', {newLine: ''}))
		.pipe(gulp.dest('app/build'))
});

// Lint Task
gulp.task('lint', function() {
    return gulp.src(config.appJsSrc)
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});

// Combine and minify all JS files from the app folder
gulp.task('scripts', function() {
	del.sync(['app/build/alljs*']);

	return gulp.src(config.appJsSrc)
		.pipe(angularFilesort())
		// .pipe(uglify())
		// .pipe(concat('alljs' + getStamp() + '.min.js', {newLine: ''}))
		.pipe(concat('alljs.min.js', {newLine: ''}))
		.pipe(gulp.dest('app/build'));
});

// Inject minified files into new HTML
gulp.task('html', ['css', 'scripts'], function () {
    del.sync(['index.html']);
	var target = gulp.src(config.appIndexHtml);
	var sources = gulp.src(['app/build/alljs*', 'app/build/allcss*']);

	return target.pipe(inject(sources))
		// .pipe(minifyHTML({ conditionals: true }))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('./'));
});

// Watch for changes
gulp.task('watch', ['lint', 'css', 'scripts', 'html'], function () {
	gulp.watch(config.appCssSrc, ['css', 'html']);
	gulp.watch(config.appJsSrc, ['lint', 'scripts', 'html']);
	gulp.watch(config.appIndexHtml, ['html']);
});

// Set  default tasks
gulp.task('default', ['lint', 'css', 'scripts', 'html'], function(){});<commit_msg>Gulpfile - added time stamp<commit_after>/// <vs BeforeBuild='watch' />
// Include plug-ins
var gulp = require('gulp'),
	jshint = require('gulp-jshint'),
	concat = require('gulp-concat'),
	minifyCSS = require('gulp-minify-css'),
	autoprefixer = require('gulp-autoprefixer'),
	uglify = require('gulp-uglify'),
	rename = require('gulp-rename'),
	inject = require('gulp-inject'),
	minifyHTML = require('gulp-minify-html'),
	del = require('del'),
	angularFilesort = require('gulp-angular-filesort');

 // File paths
var config = {
    // Include all js files but exclude any min.js files
	appJsSrc : ['app/**/*.js', '!app/**/*.min.js'],
	appCssSrc: ['content/toastr.css', 'content/main.css'],
	appIndexHtml: 'index-template.html'
}

// For browser caching
var getStamp = function() {
  var myDate = new Date();

  var myYear = myDate.getFullYear().toString();
  var myMonth = ('0' + (myDate.getMonth() + 1)).slice(-2);
  var myDay = ('0' + myDate.getDate()).slice(-2);
  var mySeconds = myDate.getSeconds().toString();

  var myFullDate = myYear + myMonth + myDay + mySeconds;

  return myFullDate;
};

 // Minify, prefix and contat CSS
gulp.task('css', function(){
	del.sync(['app/build/allcss*']);

    return gulp.src(config.appCssSrc)
		// .pipe(minifyCSS())
		.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9'))
		.pipe(concat('allcss' + getStamp() + '.min.css', {newLine: ''}))
		.pipe(concat('allcss.min.css', {newLine: ''}))
		.pipe(gulp.dest('app/build'))
});

// Lint Task
gulp.task('lint', function() {
    return gulp.src(config.appJsSrc)
        .pipe(jshint())
        .pipe(jshint.reporter('default'));
});

// Combine and minify all JS files from the app folder
gulp.task('scripts', function() {
	del.sync(['app/build/alljs*']);

	return gulp.src(config.appJsSrc)
		.pipe(angularFilesort())
		// .pipe(uglify())
		.pipe(concat('alljs' + getStamp() + '.min.js', {newLine: ''}))
		.pipe(concat('alljs.min.js', {newLine: ''}))
		.pipe(gulp.dest('app/build'));
});

// Inject minified files into new HTML
gulp.task('html', ['css', 'scripts'], function () {
    del.sync(['index.html']);
	var target = gulp.src(config.appIndexHtml);
	var sources = gulp.src(['app/build/alljs*', 'app/build/allcss*']);

	return target.pipe(inject(sources))
		.pipe(minifyHTML({ conditionals: true }))
		.pipe(rename('index.html'))
		.pipe(gulp.dest('./'));
});

// Watch for changes
gulp.task('watch', ['lint', 'css', 'scripts', 'html'], function () {
	gulp.watch(config.appCssSrc, ['css', 'html']);
	gulp.watch(config.appJsSrc, ['lint', 'scripts', 'html']);
	gulp.watch(config.appIndexHtml, ['html']);
});

// Set  default tasks
gulp.task('default', ['lint', 'css', 'scripts', 'html'], function(){});<|endoftext|>


<commit_before>var LFSR = require('../index.js'),
    expect = require('chai').expect,
    utils = require('./utils.js'),
    bin = utils.bin,
    str = utils.str;

context('statistical', function() {
    utils.everyLength(28, function(n) {
        (function(n) {
            it('max length for n = ' + n + ' is ' + (Math.pow(2, n) - 1), function() {
                this.timeout(20000);

                var lfsr = new LFSR(n),
                    count = 0,
                    initial = lfsr.register;
                do {
                    lfsr.shift();
                    count++;
                } while (lfsr.register != initial);
                expect(count).to.equal(Math.pow(2, n) - 1);
            });
        })(n);
    });

    describe('distribution', function() {
        utils.everyLength(function(n) {
            (function(n) {
                it('evenly distributes bits for n = ' + n, function() {
                    var lfsr = new LFSR(),
                        occur = {'0': 0, '1': 0},
                        bit;
                    for (var i = 0; i < 100000; i++) {
                        bit = lfsr.shift();
                        occur[bit]++;
                    }
                    var ratio = occur['0'] / occur['1'];
                    expect(ratio).to.be.closeTo(1, 0.1);
                });
            })(n);
        });
    });
});
<commit_msg>test entire range<commit_after>var LFSR = require('../index.js'),
    expect = require('chai').expect,
    utils = require('./utils.js'),
    bin = utils.bin,
    str = utils.str;

context('statistical', function() {
    utils.everyLength(function(n) {
        (function(n) {
            it('max length for n = ' + n + ' is ' + (Math.pow(2, n) - 1), function() {
                this.timeout(20000);

                var lfsr = new LFSR(n),
                    count = 0,
                    initial = lfsr.register;
                do {
                    lfsr.shift();
                    count++;
                } while (lfsr.register != initial);
                expect(count).to.equal(Math.pow(2, n) - 1);
            });
        })(n);
    });

    describe('distribution', function() {
        utils.everyLength(function(n) {
            (function(n) {
                it('evenly distributes bits for n = ' + n, function() {
                    var lfsr = new LFSR(),
                        occur = {'0': 0, '1': 0},
                        bit;
                    for (var i = 0; i < 100000; i++) {
                        bit = lfsr.shift();
                        occur[bit]++;
                    }
                    var ratio = occur['0'] / occur['1'];
                    expect(ratio).to.be.closeTo(1, 0.1);
                });
            })(n);
        });
    });
});
<|endoftext|>


<commit_before>import { withComponent, shadow } from 'skatejs';
import withPreact from '@skatejs/renderer-preact';
import { store } from '../store.js';
import { extend, supportsShadowDom } from '../utils/index.js';
import { h } from 'preact';

export class BaseComponent extends withComponent(withPreact()) {
  get renderRoot() {
    // if (this.useShadow === true && supportsShadowDom) {
    //   return super.renderRoot || shadow(this);
    // } else {
    return this;
    // }
  }

  disconnectedCallback() {
    this.__storeUnsubscribe();

    if (super.disconnectedCallback) {
      super.disconnectedCallback();
    }
  }

  connectedCallback() {
    this.__storeUnsubscribe = store.subscribe(() =>
      this._stateChanged(store.getState())
    );
    this._stateChanged(store.getState());
    if (super.connectedCallback) {
      super.connectedCallback();
    }
  }

  _stateChanged(state) {
    // throw new Error('_stateChanged() not implemented', this);
    this.triggerUpdate();
  }

  /**
   * Update component state and schedule a re-render.
   * @param {object} state A dict of state properties to be shallowly merged
   * 	into the current state, or a function that will produce such a dict. The
   * 	function is called with the current state and props.
   * @param {() => void} callback A function to be called once component state is
   * 	updated
   */
  setState(state, callback) {
    if (!this._prevState) {
      this._prevState = this.state;
    }

    this.state = extend(
      extend({}, this.state),
      typeof state === 'function' ? state(this.state, this.props) : state
    );

    if (callback) {
      this._renderCallbacks.push(callback);
    }

    this.triggerUpdate();
  }

  _renderStyles(stylesheets) {
    let styles = Array.from(stylesheets);
    styles = styles.join(' ');

    if (styles) {
      return this.useShadow && <style>{styles}</style>;
    } else {
      return null;
    }
  }
}
<commit_msg>refactor: simplify state updates in base component<commit_after>import { withComponent, shadow } from 'skatejs';
import withPreact from '@skatejs/renderer-preact';
import { store } from '../store.js';
import { extend, supportsShadowDom } from '../utils/index.js';
import { h } from 'preact';

export class BaseComponent extends withComponent(withPreact()) {
  get renderRoot() {
    // if (this.useShadow === true && supportsShadowDom) {
    //   return super.renderRoot || shadow(this);
    // } else {
    return this;
    // }
  }

  disconnectedCallback() {
    this.__storeUnsubscribe();

    if (super.disconnectedCallback) {
      super.disconnectedCallback();
    }
  }

  connectedCallback() {
    this.__storeUnsubscribe = store.subscribe(() =>
      this._stateChanged(store.getState())
    );
    this._stateChanged(store.getState());
    if (super.connectedCallback) {
      super.connectedCallback();
    }
  }

  _stateChanged(state) {
    // throw new Error('_stateChanged() not implemented', this);
    this.triggerUpdate();
  }

  /**
   * Update component state and schedule a re-render.
   * @param {object} state A dict of state properties to be shallowly merged
   * 	into the current state, or a function that will produce such a dict. The
   * 	function is called with the current state and props.
   * @param {() => void} callback A function to be called once component state is
   * 	updated
   */
  setState(state, callback) {

    this.state = Object.assign({}, this.state, state);

  }

  _renderStyles(stylesheets) {
    let styles = Array.from(stylesheets);
    styles = styles.join(' ');

    if (styles) {
      return this.useShadow && <style>{styles}</style>;
    } else {
      return null;
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2018 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */
"use strict";

const Node = require("./node");
const KIND = "defaultkeyword";

/**
 * Represents the null keyword
 * @constructor NullKeyword
 * @extends {Node}
 */
module.exports = Node.extends(KIND, function DefaultKeyword(
  raw,
  docs,
  location
) {
  Node.apply(this, [KIND, docs, location]);
  this.raw = raw;
});
<commit_msg>Fix JSDoc references to class name<commit_after>/**
 * Copyright (C) 2018 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */
"use strict";

const Node = require("./node");
const KIND = "defaultkeyword";

/**
 * Represents the default keyword, specifically for match statement
 * @constructor DefaultKeyword
 * @extends {Node}
 */
module.exports = Node.extends(KIND, function DefaultKeyword(
  raw,
  docs,
  location
) {
  Node.apply(this, [KIND, docs, location]);
  this.raw = raw;
});
<|endoftext|>


<commit_before>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var should = require('should')
  , sinon = require('sinon')
  , util = require('util');

var io = require('socket.io')
  , SbStore = require('../lib/sbstore');

describe('Service Bus Store objects', function() {
  var sandbox;

  before(function () {
    sandbox = sinon.sandbox.create();
    sandbox.stub(SbStore.prototype, 'createServiceBusConnector', function (options) {
      return {
        start: sandbox.stub().callsArgAsync(0),
        send: sandbox.stub(),
        on: sandbox.stub()
      }
    });
  });

  after(function () {
    sandbox.restore();
  });

  describe('when creating', function () {
    var store;
    var startCallback = sinon.spy();
    before(function (done) {
      store = new SbStore();
      store.on('started', function (err) {
        startCallback(err);
        done();
      });
    });

    it('should start the service bus polling', function () {
      store.sb.start.called.should.be.true;
    });

    it('should invoke the start callback', function () {
      startCallback.calledOnce.should.be.true;
    });
  });

  describe('when publishing', function () {
    var store;
    var eventsEmitted = [];

    before(function () {
      store = new SbStore({ nodeId: 'my-node-id' });

      store.on('publish', function (name) {
        var args = Array.prototype.slice.call(arguments, 1);
        eventsEmitted.push({event: 'publish', name: name, args: args });
        });
      store.publish('message', 1, 2, 3);
    });

    it('should send message to servicebus topic', function () {
      store.sb.send.calledOnce.should.be.true;
      var call = store.sb.send.getCall(0);
      call.args[0].should.equal('message');
      call.args[1].should.have.length(3);
      call.args[1][0].should.equal(1);
      call.args[1][1].should.equal(2);
      call.args[1][2].should.equal(3);
    });

    it('should emit local publish event', function () {
      eventsEmitted.should.have.length(1);
      eventsEmitted[0].event.should.equal('publish');
      eventsEmitted[0].name.should.equal('message');
      var args = eventsEmitted[0].args;
      args.should.have.length(3);
      args[0].should.equal(1);
      args[1].should.equal(2);
      args[2].should.equal(3);
    });
  });

  describe('when receiving', function () {
    var store;

    var subscriber1 = sinon.spy();
    var subscriber2 = sinon.spy();
    var subscriber2a = sinon.spy();
    var subscriber3 = sinon.spy();

    var listeners = {
      onreceived: sinon.spy(),
      onsubscribe: sinon.spy(),
      onunsubscribe: sinon.spy()
    };

    before(function () {
      store = new SbStore();
      store.on('subscribe', listeners.onsubscribe);
      store.on('received', listeners.onreceived);
      store.on('unsubscribe', listeners.onunsubscribe);

      store.subscribe('message1', subscriber1);
      store.subscribe('message2', subscriber2);
      store.subscribe('message2', subscriber2a);
      store.subscribe('message3', subscriber3);

      store.receiveMessage('some-other-node', 'message2', [6, 7, 'eight']);
    });

    it('should emit subscribe events', function () {
      listeners.onsubscribe.callCount.should.equal(4);
      listeners.onsubscribe.calledWith('message1', subscriber1).should.be.true;
      listeners.onsubscribe.calledWith('message2', subscriber2).should.be.true;
      listeners.onsubscribe.calledWith('message2', subscriber2a).should.be.true;
      listeners.onsubscribe.calledWith('message3', subscriber3).should.be.true;
    });

    it('should call all subscribers when message received', function () {
      subscriber2.calledOnce.should.be.true;
      subscriber2a.calledOnce.should.be.true;
    });

    it('should not call subscribers for other messages', function () {
      subscriber1.called.should.be.false;
      subscriber3.called.should.be.false;
    });

    it('should emit received event when message received', function () {
      listeners.onreceived.calledOnce.should.be.true;
    });

    it('should not call subscribers for messages from itself', function () {
      store.receiveMessage(store.nodeId, 'message3', ['a message']);

      subscriber3.called.should.be.false;
    });
  });
});
<commit_msg>Passing error details out through started event<commit_after>/**
* Copyright (c) Microsoft.  All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

var should = require('should')
  , sinon = require('sinon')
  , util = require('util');

var io = require('socket.io')
  , SbStore = require('../lib/sbstore');

describe('Service Bus Store objects', function() {
  var sandbox;
  var startupErr;

  before(function () {
    sandbox = sinon.sandbox.create();
    sandbox.stub(SbStore.prototype, 'createServiceBusConnector', function (options) {
      return {
        start: sandbox.stub().callsArgWithAsync(0, startupErr),
        send: sandbox.stub(),
        on: sandbox.stub()
      }
    });
  });

  after(function () {
    sandbox.restore();
  });

  describe('when creating', function () {
    var store;
    var startCallback = sinon.spy();
    before(function (done) {
      store = new SbStore();
      store.on('started', function (err) {
        startCallback(err);
        done();
      });
    });

    it('should start the service bus polling', function () {
      store.sb.start.called.should.be.true;
    });

    it('should invoke the start callback', function () {
      startCallback.calledOnce.should.be.true;
      should.not.exist(startCallback.firstCall.args[0]);
    });
  });

  describe('when creating and there is a startup error', function () {
    var store;
    var startCallback = sinon.spy();
    var errCode = '500';
    var errDetail = 'Internal Server Error';

    beforeEach(function (done) {
      startupErr = new Error('Error: 500 - Internal Server Error');
      startupErr.code = errCode;
      startupErr.detail = errDetail;

      store = new SbStore();
      store.on('started', function (err) {
        startCallback(err);
        done();
      });
    });

    afterEach(function () {
      startupErr = undefined;
    });

    it('should emit the started event', function () {
      startCallback.calledOnce.should.be.true;
    });

    it('should pass error info to the event', function () {
      startCallback.firstCall.args[0].should.exist;
      startCallback.firstCall.args[0].code.should.equal(errCode);
      startCallback.firstCall.args[0].detail.should.equal(errDetail);
    });
  });

  describe('when publishing', function () {
    var store;
    var eventsEmitted = [];

    before(function () {
      store = new SbStore({ nodeId: 'my-node-id' });

      store.on('publish', function (name) {
        var args = Array.prototype.slice.call(arguments, 1);
        eventsEmitted.push({event: 'publish', name: name, args: args });
        });
      store.publish('message', 1, 2, 3);
    });

    it('should send message to servicebus topic', function () {
      store.sb.send.calledOnce.should.be.true;
      var call = store.sb.send.getCall(0);
      call.args[0].should.equal('message');
      call.args[1].should.have.length(3);
      call.args[1][0].should.equal(1);
      call.args[1][1].should.equal(2);
      call.args[1][2].should.equal(3);
    });

    it('should emit local publish event', function () {
      eventsEmitted.should.have.length(1);
      eventsEmitted[0].event.should.equal('publish');
      eventsEmitted[0].name.should.equal('message');
      var args = eventsEmitted[0].args;
      args.should.have.length(3);
      args[0].should.equal(1);
      args[1].should.equal(2);
      args[2].should.equal(3);
    });
  });

  describe('when receiving', function () {
    var store;

    var subscriber1 = sinon.spy();
    var subscriber2 = sinon.spy();
    var subscriber2a = sinon.spy();
    var subscriber3 = sinon.spy();

    var listeners = {
      onreceived: sinon.spy(),
      onsubscribe: sinon.spy(),
      onunsubscribe: sinon.spy()
    };

    before(function () {
      store = new SbStore();
      store.on('subscribe', listeners.onsubscribe);
      store.on('received', listeners.onreceived);
      store.on('unsubscribe', listeners.onunsubscribe);

      store.subscribe('message1', subscriber1);
      store.subscribe('message2', subscriber2);
      store.subscribe('message2', subscriber2a);
      store.subscribe('message3', subscriber3);

      store.receiveMessage('some-other-node', 'message2', [6, 7, 'eight']);
    });

    it('should emit subscribe events', function () {
      listeners.onsubscribe.callCount.should.equal(4);
      listeners.onsubscribe.calledWith('message1', subscriber1).should.be.true;
      listeners.onsubscribe.calledWith('message2', subscriber2).should.be.true;
      listeners.onsubscribe.calledWith('message2', subscriber2a).should.be.true;
      listeners.onsubscribe.calledWith('message3', subscriber3).should.be.true;
    });

    it('should call all subscribers when message received', function () {
      subscriber2.calledOnce.should.be.true;
      subscriber2a.calledOnce.should.be.true;
    });

    it('should not call subscribers for other messages', function () {
      subscriber1.called.should.be.false;
      subscriber3.called.should.be.false;
    });

    it('should emit received event when message received', function () {
      listeners.onreceived.calledOnce.should.be.true;
    });

    it('should not call subscribers for messages from itself', function () {
      store.receiveMessage(store.nodeId, 'message3', ['a message']);

      subscriber3.called.should.be.false;
    });
  });
});
<|endoftext|>


<commit_before>var validation = require("../");
var expect = require("chai").expect;

var types = ["a","b","c"];
var schema = {
  singleMatch: { type: String, match: /^abc/ },
  multiMatch: { type: [String], match: /^abc/ },
  singleMatchCustom: { type: String, match: [/^abc/, "Woops! {TYPE} {VALUE} {PATH}"] },
  multiMatchCustom: { type: [String], match: [/^abc/, "Woops! {TYPE} {VALUE} {PATH}"] },
  singleEnum: { type: String, enum: types },
  multiEnum: { type: [String], enum: types },
  singleEnumCustom: { type: String, enum: { values: types, message: "Woops! {TYPE} {VALUE} {PATH}"}},
  multiEnumCustom: { type: [String], enum: { values: types, message: "Woops! {TYPE} {VALUE} {PATH}"} }
};

var validFn = validation(schema);

describe("StringSchema.match", function () {
  describe("Passes", function () {
    it("String passes when match.test(value)", function () {
      var res = validFn({ singleMatch: "abcdef" });
      expect(res.singleMatch).to.be.equal("abcdef");
    });

    it("[String] passes when all values match", function () {
      var res = validFn({ multiMatch: ["abc", "abcde", "abcdef"] });
      expect(res.multiMatch[0]).to.be.equal("abc");
      expect(res.multiMatch[1]).to.be.equal("abcde");
      expect(res.multiMatch[2]).to.be.equal("abcdef");
    });

    it("String passes when match.test(val) using custom message", function () {
      var res = validFn({ singleMatchCustom: "abc" });
      expect(res.singleMatchCustom).to.be.equal("abc");
    });

    it("[String] passes when all values match using custom message", function () {
      var res = validFn({ multiMatchCustom: ["abc", "abcde", "abcdef"] });
      expect(res.multiMatchCustom[0]).to.be.equal("abc");
      expect(res.multiMatchCustom[1]).to.be.equal("abcde");
      expect(res.multiMatchCustom[2]).to.be.equal("abcdef");
    });

  });

  describe("Fails", function () {
    it("String fails with message when !match.test(value)", function () {
      var res = validFn({ singleMatch: "def" });
      expect(res).to.be.equal("Path `singleMatch` is invalid (def).");
    });

    it("[String] fails when some values don't match", function () {
      var res = validFn({ multiMatch: ["abc", "def", "abcdef"] });
      expect(res).to.be.equal("Path `multiMatch` is invalid (def).");
    });

    it("String fails when !match.test(value) using custom message", function () {
      var res = validFn({ singleMatchCustom: "def" });
      expect(res).to.be.equal("Woops! match def singleMatchCustom");
    });

    it("[String] fails when some values don't match using custom message", function () {
      var res = validFn({ multiMatchCustom: ["abc", "def", "abcdef"] });
      expect(res).to.be.equal("Woops! match def multiMatchCustom");
    });
  });
});

describe("StringSchema.enum", function () {
  describe("Passes", function () {
    it("String passes when value in enum", function () {
      var res = validFn({ singleEnum: "a" });
      expect(res.singleEnum).to.be.equal("a");
    });

    it("[String] passes when all values in enum", function () {
      var res = validFn({ multiEnum: ["a", "a", "b"] });
      expect(res.multiEnum[0]).to.be.equal("a");
      expect(res.multiEnum[1]).to.be.equal("a");
      expect(res.multiEnum[2]).to.be.equal("b");
    });

    it("String passes when value in enum using custom message", function () {
      var res = validFn({ singleEnumCustom: "a" });
      expect(res.singleEnumCustom).to.be.equal("a");
    });

    it("[String] passes when all values <= enum using custom message", function () {
      var res = validFn({ multiEnumCustom: ["a", "a", "c"] });
      expect(res.multiEnumCustom[0]).to.be.equal("a");
      expect(res.multiEnumCustom[1]).to.be.equal("a");
      expect(res.multiEnumCustom[2]).to.be.equal("c");
    });
  });

  describe("Fails", function () {
    it("String fails with message when value not in enum", function () {
      var res = validFn({ singleEnum: "e" });
      expect(res).to.be.equal("`e` is not a valid enum value for path `singleEnum`.");
    });

    it("[String] fails when some values not in enum", function () {
      var res = validFn({ multiEnum: ["a", "d", "b"] });
      expect(res).to.be.equal("`d` is not a valid enum value for path `multiEnum`.");
    });

    it("String fails when value not in enum using custom message", function () {
      var res = validFn({ singleEnumCustom: "d" });
      expect(res).to.be.equal("Woops! enum d singleEnumCustom");
    });

    it("[String] fails when some values < match using custom message", function () {
      var res = validFn({ multiEnumCustom: ["a", "d"] });
      expect(res).to.be.equal("Woops! enum d multiEnumCustom");
    });
  });
});
<commit_msg>Tests for String formatters<commit_after>var validation = require("../");
var expect = require("chai").expect;

var types = ["a","b","c"];
var schema = {
  singleMatch: { type: String, match: /^abc/ },
  multiMatch: { type: [String], match: /^abc/ },
  singleMatchCustom: { type: String, match: [/^abc/, "Woops! {TYPE} {VALUE} {PATH}"] },
  multiMatchCustom: { type: [String], match: [/^abc/, "Woops! {TYPE} {VALUE} {PATH}"] },
  singleEnum: { type: String, enum: types },
  multiEnum: { type: [String], enum: types },
  singleEnumCustom: { type: String, enum: { values: types, message: "Woops! {TYPE} {VALUE} {PATH}"}},
  multiEnumCustom: { type: [String], enum: { values: types, message: "Woops! {TYPE} {VALUE} {PATH}"} },
  singleLowercase: { type: String, lowercase: true },
  multiLowercase: { type: [String], lowercase: true },
  singleUppercase: { type: String, uppercase: true },
  multiUppercase: { type: [String], uppercase: true },
  singleTrim: { type: String, trim: true },
  multiTrim: { type: [String], trim: true }
};

var validFn = validation(schema);

describe("StringSchema.match", function () {
  describe("Passes", function () {
    it("String passes when match.test(value)", function () {
      var res = validFn({ singleMatch: "abcdef" });
      expect(res.singleMatch).to.be.equal("abcdef");
    });

    it("[String] passes when all values match", function () {
      var res = validFn({ multiMatch: ["abc", "abcde", "abcdef"] });
      expect(res.multiMatch[0]).to.be.equal("abc");
      expect(res.multiMatch[1]).to.be.equal("abcde");
      expect(res.multiMatch[2]).to.be.equal("abcdef");
    });

    it("String passes when match.test(val) using custom message", function () {
      var res = validFn({ singleMatchCustom: "abc" });
      expect(res.singleMatchCustom).to.be.equal("abc");
    });

    it("[String] passes when all values match using custom message", function () {
      var res = validFn({ multiMatchCustom: ["abc", "abcde", "abcdef"] });
      expect(res.multiMatchCustom[0]).to.be.equal("abc");
      expect(res.multiMatchCustom[1]).to.be.equal("abcde");
      expect(res.multiMatchCustom[2]).to.be.equal("abcdef");
    });

  });

  describe("Fails", function () {
    it("String fails with message when !match.test(value)", function () {
      var res = validFn({ singleMatch: "def" });
      expect(res).to.be.equal("Path `singleMatch` is invalid (def).");
    });

    it("[String] fails when some values don't match", function () {
      var res = validFn({ multiMatch: ["abc", "def", "abcdef"] });
      expect(res).to.be.equal("Path `multiMatch` is invalid (def).");
    });

    it("String fails when !match.test(value) using custom message", function () {
      var res = validFn({ singleMatchCustom: "def" });
      expect(res).to.be.equal("Woops! match def singleMatchCustom");
    });

    it("[String] fails when some values don't match using custom message", function () {
      var res = validFn({ multiMatchCustom: ["abc", "def", "abcdef"] });
      expect(res).to.be.equal("Woops! match def multiMatchCustom");
    });
  });
});

describe("StringSchema.enum", function () {
  describe("Passes", function () {
    it("String passes when value in enum", function () {
      var res = validFn({ singleEnum: "a" });
      expect(res.singleEnum).to.be.equal("a");
    });

    it("[String] passes when all values in enum", function () {
      var res = validFn({ multiEnum: ["a", "a", "b"] });
      expect(res.multiEnum[0]).to.be.equal("a");
      expect(res.multiEnum[1]).to.be.equal("a");
      expect(res.multiEnum[2]).to.be.equal("b");
    });

    it("String passes when value in enum using custom message", function () {
      var res = validFn({ singleEnumCustom: "a" });
      expect(res.singleEnumCustom).to.be.equal("a");
    });

    it("[String] passes when all values <= enum using custom message", function () {
      var res = validFn({ multiEnumCustom: ["a", "a", "c"] });
      expect(res.multiEnumCustom[0]).to.be.equal("a");
      expect(res.multiEnumCustom[1]).to.be.equal("a");
      expect(res.multiEnumCustom[2]).to.be.equal("c");
    });
  });

  describe("Fails", function () {
    it("String fails with message when value not in enum", function () {
      var res = validFn({ singleEnum: "e" });
      expect(res).to.be.equal("`e` is not a valid enum value for path `singleEnum`.");
    });

    it("[String] fails when some values not in enum", function () {
      var res = validFn({ multiEnum: ["a", "d", "b"] });
      expect(res).to.be.equal("`d` is not a valid enum value for path `multiEnum`.");
    });

    it("String fails when value not in enum using custom message", function () {
      var res = validFn({ singleEnumCustom: "d" });
      expect(res).to.be.equal("Woops! enum d singleEnumCustom");
    });

    it("[String] fails when some values < match using custom message", function () {
      var res = validFn({ multiEnumCustom: ["a", "d"] });
      expect(res).to.be.equal("Woops! enum d multiEnumCustom");
    });
  });
});

describe("StringSchema.lowercase", function () {
  it("casts String to lower case", function () {
    var res = validFn({ singleLowercase: "AbCdEfG" });
    expect(res.singleLowercase).to.be.equal("abcdefg");
  });

  it("casts [String] to lower case", function () {
    var res = validFn({ multiLowercase: ["AAA", "BBB", "CCC"] });
    expect(res.multiLowercase[0]).to.be.equal("aaa");
    expect(res.multiLowercase[1]).to.be.equal("bbb");
    expect(res.multiLowercase[2]).to.be.equal("ccc");
  });
});

describe("StringSchema.uppercase", function () {
  it("casts String to upper case", function () {
    var res = validFn({ singleUppercase: "AbCdEfG" });
    expect(res.singleUppercase).to.be.equal("ABCDEFG");
  });

  it("casts [String] to uppercase", function () {
    var res = validFn({ multiUppercase: ["aaa", "bbb", "ccc"] });
    expect(res.multiUppercase[0]).to.be.equal("AAA");
    expect(res.multiUppercase[1]).to.be.equal("BBB");
    expect(res.multiUppercase[2]).to.be.equal("CCC");
  });
});

describe("StringSchema.trim", function () {
  it("trims String", function () {
    var res = validFn({ singleTrim: "   aaa " });
    expect(res.singleTrim).to.be.equal("aaa");
  });

  it("trims [String]", function () {
    var res = validFn({ multiTrim: ["aaa ", " bbb", "ccc"] });
    expect(res.multiTrim[0]).to.be.equal("aaa");
    expect(res.multiTrim[1]).to.be.equal("bbb");
    expect(res.multiTrim[2]).to.be.equal("ccc");
  });
});
<|endoftext|>