<commit_before>'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _reactSwipeable = require('react-swipeable');

var _reactSwipeable2 = _interopRequireDefault(_reactSwipeable);

var ImageGallery = _react2['default'].createClass({

  displayName: 'ImageGallery',

  propTypes: {
    items: _react2['default'].PropTypes.array.isRequired,
    showThumbnails: _react2['default'].PropTypes.bool,
    showBullets: _react2['default'].PropTypes.bool,
    showNav: _react2['default'].PropTypes.bool,
    autoPlay: _react2['default'].PropTypes.bool,
    lazyLoad: _react2['default'].PropTypes.bool,
    slideInterval: _react2['default'].PropTypes.number,
    onSlide: _react2['default'].PropTypes.func,
    startIndex: _react2['default'].PropTypes.number
  },

  getDefaultProps: function getDefaultProps() {
    return {
      lazyLoad: true,
      showThumbnails: true,
      showBullets: false,
      showNav: true,
      autoPlay: false,
      slideInterval: 4000,
      startIndex: 0
    };
  },

  getInitialState: function getInitialState() {
    return {
      currentIndex: this.props.startIndex,
      thumbnailsTranslateX: 0,
      containerWidth: 0
    };
  },

  componentDidUpdate: function componentDidUpdate(prevProps, prevState) {
    if (prevState.containerWidth !== this.state.containerWidth || prevProps.showThumbnails !== this.props.showThumbnails) {

      // adjust thumbnail container when window width is adjusted
      // when the container resizes, thumbnailsTranslateX
      // should always be negative (moving right),
      // if container fits all thumbnails its set to 0

      this._setThumbnailsTranslateX(-this._getScrollX(this.state.currentIndex > 0 ? 1 : 0) * this.state.currentIndex);
    }

    if (prevState.currentIndex !== this.state.currentIndex) {

      // call back function if provided
      if (this.props.onSlide) {
        this.props.onSlide(this.state.currentIndex);
      }

      // calculates thumbnail container position
      if (this.state.currentIndex === 0) {
        this._setThumbnailsTranslateX(0);
      } else {
        var indexDifference = Math.abs(prevState.currentIndex - this.state.currentIndex);
        var _scrollX = this._getScrollX(indexDifference);
        if (_scrollX > 0) {
          if (prevState.currentIndex < this.state.currentIndex) {
            this._setThumbnailsTranslateX(this.state.thumbnailsTranslateX - _scrollX);
          } else if (prevState.currentIndex > this.state.currentIndex) {
            this._setThumbnailsTranslateX(this.state.thumbnailsTranslateX + _scrollX);
          }
        }
      }
    }
  },

  componentDidMount: function componentDidMount() {
    this._handleResize();
    if (this.props.autoPlay) {
      this.play();
    }
    if (window.addEventListener) {
      window.addEventListener('resize', this._handleResize);
    } else if (window.attachEvent) {
      window.attachEvent('onresize', this._handleResize);
    }
  },

  componentWillUnmount: function componentWillUnmount() {
    if (window.removeEventListener) {
      window.removeEventListener('resize', this._handleResize);
    } else if (window.detachEvent) {
      window.detachEvent('onresize', this._handleResize);
    }

    if (this._intervalId) {
      window.clearInterval(this._intervalId);
      this._intervalId = null;
    }
  },

  slideToIndex: function slideToIndex(index, event) {
    var slideCount = this.props.items.length - 1;

    if (index < 0) {
      this.setState({ currentIndex: slideCount });
    } else if (index > slideCount) {
      this.setState({ currentIndex: 0 });
    } else {
      this.setState({ currentIndex: index });
    }
    if (event) {
      if (this._intervalId) {
        // user event, reset interval
        this.pause();
        this.play();
      }
      event.preventDefault();
    }
  },

  play: function play() {
    var _this = this;

    if (this._intervalId) {
      return;
    }
    this._intervalId = window.setInterval((function () {
      if (!_this.state.hovering) {
        _this.slideToIndex(_this.state.currentIndex + 1);
      }
    }).bind(this), this.props.slideInterval);
  },

  pause: function pause() {
    if (this._intervalId) {
      window.clearInterval(this._intervalId);
      this._intervalId = null;
    }
  },

  _setThumbnailsTranslateX: function _setThumbnailsTranslateX(x) {
    this.setState({ thumbnailsTranslateX: x });
  },

  _handleResize: function _handleResize() {
    this.setState({ containerWidth: this.refs.imageGallery.offsetWidth });
  },

  _getScrollX: function _getScrollX(indexDifference) {
    if (this.refs.thumbnails) {
      var thumbNode = this.refs.thumbnails;
      if (thumbNode.scrollWidth <= this.state.containerWidth) {
        return 0;
      }
      var totalThumbnails = thumbNode.children.length;

      // total scroll-x required to see the last thumbnail
      var totalScrollX = thumbNode.scrollWidth - this.state.containerWidth;

      // scroll-x required per index change
      var perIndexScrollX = totalScrollX / (totalThumbnails - 1);

      return indexDifference * perIndexScrollX;
    }
  },

  _handleMouseOver: function _handleMouseOver() {
    this.setState({ hovering: true });
  },

  _handleMouseLeave: function _handleMouseLeave() {
    this.setState({ hovering: false });
  },

  _getAlignmentClassName: function _getAlignmentClassName(index) {
    var currentIndex = this.state.currentIndex;
    var alignment = '';
    switch (index) {
      case currentIndex - 1:
        alignment = ' left';
        break;
      case currentIndex:
        alignment = ' center';
        break;
      case currentIndex + 1:
        alignment = ' right';
        break;
    }

    if (this.props.items.length >= 3) {
      if (index === 0 && currentIndex === this.props.items.length - 1) {
        // set first slide as right slide if were sliding right from last slide
        alignment = ' right';
      } else if (index === this.props.items.length - 1 && currentIndex === 0) {
        // set last slide as left slide if were sliding left from first slide
        alignment = ' left';
      }
    }

    return alignment;
  },

  render: function render() {
    var _this2 = this;

    var currentIndex = this.state.currentIndex;
    var thumbnailStyle = {
      MozTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      WebkitTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      OTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      msTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      transform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)'
    };

    var slides = [];
    var thumbnails = [];
    var bullets = [];

    this.props.items.map(function (item, index) {
      var alignment = _this2._getAlignmentClassName(index);
      var originalClass = item.originalClass ? ' ' + item.originalClass : '';
      var thumbnailClass = item.thumbnailClass ? ' ' + item.thumbnailClass : '';

      var slide = _react2['default'].createElement(
        'div',
        {
          key: index,
          className: 'image-gallery-slide' + alignment + originalClass },
        _react2['default'].createElement('img', { src: item.original }),
        item.description
      );

      if (_this2.props.lazyLoad) {
        if (alignment) {
          slides.push(slide);
        }
      } else {
        slides.push(slide);
      }

      if (_this2.props.showThumbnails) {
        thumbnails.push(_react2['default'].createElement(
          'a',
          {
            key: index,
            className: 'image-gallery-thumbnail' + (currentIndex === index ? ' active' : '') + thumbnailClass,

            onTouchStart: _this2.slideToIndex.bind(_this2, index),
            onClick: _this2.slideToIndex.bind(_this2, index) },
          _react2['default'].createElement('img', { src: item.thumbnail })
        ));
      }

      if (_this2.props.showBullets) {
        bullets.push(_react2['default'].createElement('li', {
          key: index,
          className: 'image-gallery-bullet ' + (currentIndex === index ? 'active' : ''),

          onTouchStart: _this2.slideToIndex.bind(_this2, index),
          onClick: _this2.slideToIndex.bind(_this2, index) }));
      }
    });

    var swipePrev = this.slideToIndex.bind(this, currentIndex - 1);
    var swipeNext = this.slideToIndex.bind(this, currentIndex + 1);

    return _react2['default'].createElement(
      'section',
      { ref: 'imageGallery', className: 'image-gallery' },
      _react2['default'].createElement(
        'div',
        {
          onMouseOver: this._handleMouseOver,
          onMouseLeave: this._handleMouseLeave,
          className: 'image-gallery-content' },
        this.props.items.length >= 2 ? [this.props.showNav && [_react2['default'].createElement('a', {
          key: 'leftNav',
          className: 'image-gallery-left-nav',
          onTouchStart: swipePrev,
          onClick: swipePrev }), _react2['default'].createElement('a', {
          key: 'rightNav',
          className: 'image-gallery-right-nav',
          onTouchStart: swipeNext,
          onClick: swipeNext })], _react2['default'].createElement(
          _reactSwipeable2['default'],
          {
            key: 'swipeable',
            onSwipedLeft: swipeNext,
            onSwipedRight: swipePrev },
          _react2['default'].createElement(
            'div',
            { className: 'image-gallery-slides' },
            slides
          )
        )] : _react2['default'].createElement(
          'div',
          { className: 'image-gallery-slides' },
          slides
        ),
        this.props.showBullets && _react2['default'].createElement(
          'div',
          { className: 'image-gallery-bullets' },
          _react2['default'].createElement(
            'ul',
            { className: 'image-gallery-bullets-container' },
            bullets
          )
        )
      ),
      this.props.showThumbnails && _react2['default'].createElement(
        'div',
        { className: 'image-gallery-thumbnails' },
        _react2['default'].createElement(
          'div',
          {
            ref: 'thumbnails',
            className: 'image-gallery-thumbnails-container',
            style: thumbnailStyle },
          thumbnails
        )
      )
    );
  }

});

exports['default'] = ImageGallery;
module.exports = exports['default'];<commit_msg>added props to node element reference<commit_after>'use strict';

Object.defineProperty(exports, '__esModule', {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }

var _react = require('react');

var _react2 = _interopRequireDefault(_react);

var _reactSwipeable = require('react-swipeable');

var _reactSwipeable2 = _interopRequireDefault(_reactSwipeable);

var ImageGallery = _react2['default'].createClass({

  displayName: 'ImageGallery',

  propTypes: {
    items: _react2['default'].PropTypes.array.isRequired,
    showThumbnails: _react2['default'].PropTypes.bool,
    showBullets: _react2['default'].PropTypes.bool,
    showNav: _react2['default'].PropTypes.bool,
    autoPlay: _react2['default'].PropTypes.bool,
    lazyLoad: _react2['default'].PropTypes.bool,
    slideInterval: _react2['default'].PropTypes.number,
    onSlide: _react2['default'].PropTypes.func,
    startIndex: _react2['default'].PropTypes.number
  },

  getDefaultProps: function getDefaultProps() {
    return {
      lazyLoad: true,
      showThumbnails: true,
      showBullets: false,
      showNav: true,
      autoPlay: false,
      slideInterval: 4000,
      startIndex: 0
    };
  },

  getInitialState: function getInitialState() {
    return {
      currentIndex: this.props.startIndex,
      thumbnailsTranslateX: 0,
      containerWidth: 0
    };
  },

  componentDidUpdate: function componentDidUpdate(prevProps, prevState) {
    if (prevState.containerWidth !== this.state.containerWidth || prevProps.showThumbnails !== this.props.showThumbnails) {

      // adjust thumbnail container when window width is adjusted
      // when the container resizes, thumbnailsTranslateX
      // should always be negative (moving right),
      // if container fits all thumbnails its set to 0

      this._setThumbnailsTranslateX(-this._getScrollX(this.state.currentIndex > 0 ? 1 : 0) * this.state.currentIndex);
    }

    if (prevState.currentIndex !== this.state.currentIndex) {

      // call back function if provided
      if (this.props.onSlide) {
        this.props.onSlide(this.state.currentIndex);
      }

      // calculates thumbnail container position
      if (this.state.currentIndex === 0) {
        this._setThumbnailsTranslateX(0);
      } else {
        var indexDifference = Math.abs(prevState.currentIndex - this.state.currentIndex);
        var _scrollX = this._getScrollX(indexDifference);
        if (_scrollX > 0) {
          if (prevState.currentIndex < this.state.currentIndex) {
            this._setThumbnailsTranslateX(this.state.thumbnailsTranslateX - _scrollX);
          } else if (prevState.currentIndex > this.state.currentIndex) {
            this._setThumbnailsTranslateX(this.state.thumbnailsTranslateX + _scrollX);
          }
        }
      }
    }
  },

  componentDidMount: function componentDidMount() {
    this._handleResize();
    if (this.props.autoPlay) {
      this.play();
    }
    if (window.addEventListener) {
      window.addEventListener('resize', this._handleResize);
    } else if (window.attachEvent) {
      window.attachEvent('onresize', this._handleResize);
    }
  },

  componentWillUnmount: function componentWillUnmount() {
    if (window.removeEventListener) {
      window.removeEventListener('resize', this._handleResize);
    } else if (window.detachEvent) {
      window.detachEvent('onresize', this._handleResize);
    }

    if (this._intervalId) {
      window.clearInterval(this._intervalId);
      this._intervalId = null;
    }
  },

  slideToIndex: function slideToIndex(index, event) {
    var slideCount = this.props.items.length - 1;

    if (index < 0) {
      this.setState({ currentIndex: slideCount });
    } else if (index > slideCount) {
      this.setState({ currentIndex: 0 });
    } else {
      this.setState({ currentIndex: index });
    }
    if (event) {
      if (this._intervalId) {
        // user event, reset interval
        this.pause();
        this.play();
      }
      event.preventDefault();
    }
  },

  play: function play() {
    var _this = this;

    if (this._intervalId) {
      return;
    }
    this._intervalId = window.setInterval((function () {
      if (!_this.state.hovering) {
        _this.slideToIndex(_this.state.currentIndex + 1);
      }
    }).bind(this), this.props.slideInterval);
  },

  pause: function pause() {
    if (this._intervalId) {
      window.clearInterval(this._intervalId);
      this._intervalId = null;
    }
  },

  _setThumbnailsTranslateX: function _setThumbnailsTranslateX(x) {
    this.setState({ thumbnailsTranslateX: x });
  },

  _handleResize: function _handleResize() {
    this.setState({ containerWidth: this.refs.imageGallery.offsetWidth });
  },

  _getScrollX: function _getScrollX(indexDifference) {
    if (this.refs.thumbnails) {
      var thumbNode = this.refs.thumbnails;
      if (thumbNode.scrollWidth <= this.state.containerWidth) {
        return 0;
      }
      var totalThumbnails = thumbNode.props.children.length;

      // total scroll-x required to see the last thumbnail
      var totalScrollX = thumbNode.scrollWidth - this.state.containerWidth;

      // scroll-x required per index change
      var perIndexScrollX = totalScrollX / (totalThumbnails - 1);

      return indexDifference * perIndexScrollX;
    }
  },

  _handleMouseOver: function _handleMouseOver() {
    this.setState({ hovering: true });
  },

  _handleMouseLeave: function _handleMouseLeave() {
    this.setState({ hovering: false });
  },

  _getAlignmentClassName: function _getAlignmentClassName(index) {
    var currentIndex = this.state.currentIndex;
    var alignment = '';
    switch (index) {
      case currentIndex - 1:
        alignment = ' left';
        break;
      case currentIndex:
        alignment = ' center';
        break;
      case currentIndex + 1:
        alignment = ' right';
        break;
    }

    if (this.props.items.length >= 3) {
      if (index === 0 && currentIndex === this.props.items.length - 1) {
        // set first slide as right slide if were sliding right from last slide
        alignment = ' right';
      } else if (index === this.props.items.length - 1 && currentIndex === 0) {
        // set last slide as left slide if were sliding left from first slide
        alignment = ' left';
      }
    }

    return alignment;
  },

  render: function render() {
    var _this2 = this;

    var currentIndex = this.state.currentIndex;
    var thumbnailStyle = {
      MozTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      WebkitTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      OTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      msTransform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)',
      transform: 'translate3d(' + this.state.thumbnailsTranslateX + 'px, 0, 0)'
    };

    var slides = [];
    var thumbnails = [];
    var bullets = [];

    this.props.items.map(function (item, index) {
      var alignment = _this2._getAlignmentClassName(index);
      var originalClass = item.originalClass ? ' ' + item.originalClass : '';
      var thumbnailClass = item.thumbnailClass ? ' ' + item.thumbnailClass : '';

      var slide = _react2['default'].createElement(
        'div',
        {
          key: index,
          className: 'image-gallery-slide' + alignment + originalClass },
        _react2['default'].createElement('img', { src: item.original }),
        item.description
      );

      if (_this2.props.lazyLoad) {
        if (alignment) {
          slides.push(slide);
        }
      } else {
        slides.push(slide);
      }

      if (_this2.props.showThumbnails) {
        thumbnails.push(_react2['default'].createElement(
          'a',
          {
            key: index,
            className: 'image-gallery-thumbnail' + (currentIndex === index ? ' active' : '') + thumbnailClass,

            onTouchStart: _this2.slideToIndex.bind(_this2, index),
            onClick: _this2.slideToIndex.bind(_this2, index) },
          _react2['default'].createElement('img', { src: item.thumbnail })
        ));
      }

      if (_this2.props.showBullets) {
        bullets.push(_react2['default'].createElement('li', {
          key: index,
          className: 'image-gallery-bullet ' + (currentIndex === index ? 'active' : ''),

          onTouchStart: _this2.slideToIndex.bind(_this2, index),
          onClick: _this2.slideToIndex.bind(_this2, index) }));
      }
    });

    var swipePrev = this.slideToIndex.bind(this, currentIndex - 1);
    var swipeNext = this.slideToIndex.bind(this, currentIndex + 1);

    return _react2['default'].createElement(
      'section',
      { ref: 'imageGallery', className: 'image-gallery' },
      _react2['default'].createElement(
        'div',
        {
          onMouseOver: this._handleMouseOver,
          onMouseLeave: this._handleMouseLeave,
          className: 'image-gallery-content' },
        this.props.items.length >= 2 ? [this.props.showNav && [_react2['default'].createElement('a', {
          key: 'leftNav',
          className: 'image-gallery-left-nav',
          onTouchStart: swipePrev,
          onClick: swipePrev }), _react2['default'].createElement('a', {
          key: 'rightNav',
          className: 'image-gallery-right-nav',
          onTouchStart: swipeNext,
          onClick: swipeNext })], _react2['default'].createElement(
          _reactSwipeable2['default'],
          {
            key: 'swipeable',
            onSwipedLeft: swipeNext,
            onSwipedRight: swipePrev },
          _react2['default'].createElement(
            'div',
            { className: 'image-gallery-slides' },
            slides
          )
        )] : _react2['default'].createElement(
          'div',
          { className: 'image-gallery-slides' },
          slides
        ),
        this.props.showBullets && _react2['default'].createElement(
          'div',
          { className: 'image-gallery-bullets' },
          _react2['default'].createElement(
            'ul',
            { className: 'image-gallery-bullets-container' },
            bullets
          )
        )
      ),
      this.props.showThumbnails && _react2['default'].createElement(
        'div',
        { className: 'image-gallery-thumbnails' },
        _react2['default'].createElement(
          'div',
          {
            ref: 'thumbnails',
            className: 'image-gallery-thumbnails-container',
            style: thumbnailStyle },
          thumbnails
        )
      )
    );
  }

});

exports['default'] = ImageGallery;
module.exports = exports['default'];
<|endoftext|>


<commit_before>// existingImages.js
// =================

/* Copyright  2014 Yahoo Inc.
 * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
 */

define([
  'core/utils',
  'core/AnimatedGIF',
  'core/getBase64GIF',
  'core/error'
], function(utils, AnimatedGIF, getBase64GIF, error) {
  return function(obj) {
    var images = obj.images,
      imagesLength = obj.imagesLength,
      callback = obj.callback,
      options = obj.options,
      skipObj = {
        'getUserMedia': true,
        'window.URL': true
      },
      errorObj = error.validate(skipObj),
      loadedImages = 0,
      tempImage,
      ag;

    if (errorObj.error) {
      return callback(errorObj);
    }

    // change workerPath to point to where Animated_GIF.worker.js is
    ag = new AnimatedGIF(options);

    utils.each(images, function(index, currentImage) {
      if (utils.isElement(currentImage)) {
        if (options.setCrossOrigin) {
          currentImage.crossOrigin = options.setCrossOrigin
        }
        ag.addFrame(currentImage, options);
        loadedImages += 1;
        if (loadedImages === imagesLength) {
          getBase64GIF(ag, callback);
        }
      } else if (utils.isString(currentImage)) {
        tempImage = document.createElement('img');
        if (options.setCrossOrigin) {
          currentImage.crossOrigin = options.setCrossOrigin;
        }
        tempImage.onerror = function(e) {
          // If there is an error, ignore the image
          if (imagesLength > 0) {
            imagesLength -= 1;
          }
        }

        tempImage.src = currentImage;

        (function(tempImage) {
          tempImage.onload = function() {
            ag.addFrame(tempImage, options);
            utils.removeElement(tempImage);
            loadedImages += 1;
            if (loadedImages === imagesLength) {
              getBase64GIF(ag, callback);
            }
          };
        }(tempImage));

        utils.setCSSAttr(tempImage, {
          'position': 'fixed',
          'opacity': '0'
        });

        document.body.appendChild(tempImage);
      }
    });
  };
});
<commit_msg>semicolin;<commit_after>// existingImages.js
// =================

/* Copyright  2014 Yahoo Inc.
 * Copyrights licensed under the MIT License. See the accompanying LICENSE file for terms.
 */

define([
  'core/utils',
  'core/AnimatedGIF',
  'core/getBase64GIF',
  'core/error'
], function(utils, AnimatedGIF, getBase64GIF, error) {
  return function(obj) {
    var images = obj.images,
      imagesLength = obj.imagesLength,
      callback = obj.callback,
      options = obj.options,
      skipObj = {
        'getUserMedia': true,
        'window.URL': true
      },
      errorObj = error.validate(skipObj),
      loadedImages = 0,
      tempImage,
      ag;

    if (errorObj.error) {
      return callback(errorObj);
    }

    // change workerPath to point to where Animated_GIF.worker.js is
    ag = new AnimatedGIF(options);

    utils.each(images, function(index, currentImage) {
      if (utils.isElement(currentImage)) {
        if (options.setCrossOrigin) {
          currentImage.crossOrigin = options.setCrossOrigin;
        }
        ag.addFrame(currentImage, options);
        loadedImages += 1;
        if (loadedImages === imagesLength) {
          getBase64GIF(ag, callback);
        }
      } else if (utils.isString(currentImage)) {
        tempImage = document.createElement('img');
        if (options.setCrossOrigin) {
          currentImage.crossOrigin = options.setCrossOrigin;
        }
        tempImage.onerror = function(e) {
          // If there is an error, ignore the image
          if (imagesLength > 0) {
            imagesLength -= 1;
          }
        }

        tempImage.src = currentImage;

        (function(tempImage) {
          tempImage.onload = function() {
            ag.addFrame(tempImage, options);
            utils.removeElement(tempImage);
            loadedImages += 1;
            if (loadedImages === imagesLength) {
              getBase64GIF(ag, callback);
            }
          };
        }(tempImage));

        utils.setCSSAttr(tempImage, {
          'position': 'fixed',
          'opacity': '0'
        });

        document.body.appendChild(tempImage);
      }
    });
  };
});
<|endoftext|>


<commit_before>const babel = require('rollup-plugin-babel')
const resolve = require('rollup-plugin-node-resolve')
const resolveCommonJS = require('rollup-plugin-commonjs')

/**
 * Rollup config template.
 * @type {{format: string, plugins: [*]}}
 */
const config = {
  format: 'cjs',
  plugins: [
    resolveCommonJS(),
    resolve(),
    babel({
      exclude: 'node_modules/**',
    }),
  ],
}

module.exports = config
<commit_msg>Add "events" module to Rollup's external dependencies<commit_after>const babel = require('rollup-plugin-babel')
const resolve = require('rollup-plugin-node-resolve')
const resolveCommonJS = require('rollup-plugin-commonjs')

/**
 * Rollup config template.
 * @type {{format: string, plugins: [*]}}
 */
const config = {
  format: 'cjs',
  external: ['events'],
  plugins: [
    resolveCommonJS(),
    resolve(),
    babel({
      exclude: 'node_modules/**',
    }),
  ],
}

module.exports = config
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

'use strict';

var fs = require('fs');
var path = require('path');
var _ = require('lodash');
var grpc = require('..');
var testProto = grpc.load({
  root: __dirname + '/../../..',
  file: 'test/proto/test.proto'}).grpc.testing;

var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial';
var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin';

var incompressible_data = fs.readFileSync(
    __dirname + '/../../../test/cpp/interop/rnd.dat');

/**
 * Create a buffer filled with size zeroes
 * @param {number} size The length of the buffer
 * @return {Buffer} The new buffer
 */
function zeroBuffer(size) {
  var zeros = new Buffer(size);
  zeros.fill(0);
  return zeros;
}

/**
 * Echos a header metadata item as specified in the interop spec.
 * @param {Call} call The call to echo metadata on
 */
function echoHeader(call) {
  var echo_initial = call.metadata.get(ECHO_INITIAL_KEY);
  if (echo_initial.length > 0) {
    var response_metadata = new grpc.Metadata();
    response_metadata.set(ECHO_INITIAL_KEY, echo_initial[0]);
    call.sendMetadata(response_metadata);
  }
}

/**
 * Gets the trailer metadata that should be echoed when the call is done,
 * as specified in the interop spec.
 * @param {Call} call The call to get metadata from
 * @return {grpc.Metadata} The metadata to send as a trailer
 */
function getEchoTrailer(call) {
  var echo_trailer = call.metadata.get(ECHO_TRAILING_KEY);
  var response_trailer = new grpc.Metadata();
  if (echo_trailer.length > 0) {
    response_trailer.set(ECHO_TRAILING_KEY, echo_trailer[0]);
  }
  return response_trailer;
}

function getPayload(payload_type, size) {
  if (payload_type === 'RANDOM') {
    payload_type = ['COMPRESSABLE',
                    'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1];
  }
  var body;
  switch (payload_type) {
    case 'COMPRESSABLE': body = zeroBuffer(size); break;
    case 'UNCOMPRESSABLE': incompressible_data.slice(size); break;
  }
  return {type: payload_type, body: body};
}

/**
 * Respond to an empty parameter with an empty response.
 * NOTE: this currently does not work due to issue #137
 * @param {Call} call Call to handle
 * @param {function(Error, Object)} callback Callback to call with result
 *     or error
 */
function handleEmpty(call, callback) {
  echoHeader(call);
  callback(null, {}, getEchoTrailer(call));
}

/**
 * Handle a unary request by sending the requested payload
 * @param {Call} call Call to handle
 * @param {function(Error, Object)} callback Callback to call with result or
 *     error
 */
function handleUnary(call, callback) {
  echoHeader(call);
  var req = call.request;
  if (req.response_status) {
    var status = req.response_status;
    status.metadata = getEchoTrailer(call);
    callback(status);
    return;
  }
  var payload = getPayload(req.response_type, req.response_size);
  callback(null, {payload: payload},
           getEchoTrailer(call));
}

/**
 * Respond to a streaming call with the total size of all payloads
 * @param {Call} call Call to handle
 * @param {function(Error, Object)} callback Callback to call with result or
 *     error
 */
function handleStreamingInput(call, callback) {
  echoHeader(call);
  var aggregate_size = 0;
  call.on('data', function(value) {
    aggregate_size += value.payload.body.length;
  });
  call.on('end', function() {
    callback(null, {aggregated_payload_size: aggregate_size},
             getEchoTrailer(call));
  });
}

/**
 * Respond to a payload request with a stream of the requested payloads
 * @param {Call} call Call to handle
 */
function handleStreamingOutput(call) {
  echoHeader(call);
  var req = call.request;
  if (req.response_status) {
    var status = req.response_status;
    status.metadata = getEchoTrailer(call);
    call.emit('error', status);
    return;
  }
  _.each(req.response_parameters, function(resp_param) {
    call.write({payload: getPayload(req.response_type, resp_param.size)});
  });
  call.end(getEchoTrailer(call));
}

/**
 * Respond to a stream of payload requests with a stream of payload responses as
 * they arrive.
 * @param {Call} call Call to handle
 */
function handleFullDuplex(call) {
  echoHeader(call);
  call.on('data', function(value) {
    if (value.response_status) {
      var status = value.response_status;
      status.metadata = getEchoTrailer(call);
      call.emit('error', status);
      return;
    }
    _.each(value.response_parameters, function(resp_param) {
      call.write({payload: getPayload(value.response_type, resp_param.size)});
    });
  });
  call.on('end', function() {
    call.end(getEchoTrailer(call));
  });
}

/**
 * Respond to a stream of payload requests with a stream of payload responses
 * after all requests have arrived
 * @param {Call} call Call to handle
 */
function handleHalfDuplex(call) {
  call.emit('error', Error('HalfDuplexCall not yet implemented'));
}

/**
 * Get a server object bound to the given port
 * @param {string} port Port to which to bind
 * @param {boolean} tls Indicates that the bound port should use TLS
 * @return {{server: Server, port: number}} Server object bound to the support,
 *     and port number that the server is bound to
 */
function getServer(port, tls) {
  // TODO(mlumish): enable TLS functionality
  var options = {};
  var server_creds;
  if (tls) {
    var key_path = path.join(__dirname, '../test/data/server1.key');
    var pem_path = path.join(__dirname, '../test/data/server1.pem');

    var key_data = fs.readFileSync(key_path);
    var pem_data = fs.readFileSync(pem_path);
    server_creds = grpc.ServerCredentials.createSsl(null,
                                                    [{private_key: key_data,
                                                      cert_chain: pem_data}]);
  } else {
    server_creds = grpc.ServerCredentials.createInsecure();
  }
  var server = new grpc.Server(options);
  server.addProtoService(testProto.TestService.service, {
    emptyCall: handleEmpty,
    unaryCall: handleUnary,
    streamingOutputCall: handleStreamingOutput,
    streamingInputCall: handleStreamingInput,
    fullDuplexCall: handleFullDuplex,
    halfDuplexCall: handleHalfDuplex
  });
  var port_num = server.bind('0.0.0.0:' + port, server_creds);
  return {server: server, port: port_num};
}

if (require.main === module) {
  var parseArgs = require('minimist');
  var argv = parseArgs(process.argv, {
    string: ['port', 'use_tls']
  });
  var server_obj = getServer(argv.port, argv.use_tls === 'true');
  console.log('Server attaching to port ' + argv.port);
  server_obj.server.start();
}

/**
 * See docs for getServer
 */
exports.getServer = getServer;
<commit_msg>Added interval_us delay in Node interop server<commit_after>/*
 *
 * Copyright 2015, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

'use strict';

var fs = require('fs');
var path = require('path');
var async = require('async');
var _ = require('lodash');
var grpc = require('..');
var testProto = grpc.load({
  root: __dirname + '/../../..',
  file: 'test/proto/test.proto'}).grpc.testing;

var ECHO_INITIAL_KEY = 'x-grpc-test-echo-initial';
var ECHO_TRAILING_KEY = 'x-grpc-test-echo-trailing-bin';

var incompressible_data = fs.readFileSync(
    __dirname + '/../../../test/cpp/interop/rnd.dat');

/**
 * Create a buffer filled with size zeroes
 * @param {number} size The length of the buffer
 * @return {Buffer} The new buffer
 */
function zeroBuffer(size) {
  var zeros = new Buffer(size);
  zeros.fill(0);
  return zeros;
}

/**
 * Echos a header metadata item as specified in the interop spec.
 * @param {Call} call The call to echo metadata on
 */
function echoHeader(call) {
  var echo_initial = call.metadata.get(ECHO_INITIAL_KEY);
  if (echo_initial.length > 0) {
    var response_metadata = new grpc.Metadata();
    response_metadata.set(ECHO_INITIAL_KEY, echo_initial[0]);
    call.sendMetadata(response_metadata);
  }
}

/**
 * Gets the trailer metadata that should be echoed when the call is done,
 * as specified in the interop spec.
 * @param {Call} call The call to get metadata from
 * @return {grpc.Metadata} The metadata to send as a trailer
 */
function getEchoTrailer(call) {
  var echo_trailer = call.metadata.get(ECHO_TRAILING_KEY);
  var response_trailer = new grpc.Metadata();
  if (echo_trailer.length > 0) {
    response_trailer.set(ECHO_TRAILING_KEY, echo_trailer[0]);
  }
  return response_trailer;
}

/**
 * @typedef Payload
 * @type {object}
 * @property {string} payload_type The payload type
 * @property {Buffer} body The payload body
 */

/**
 * Get a payload of the specified type and size. If the requested payload is
 * COMPRESSABLE, it returns a zero buffer. If the type is UNCOMRESSABLE, it
 * returns a slice of pre-loaded uncompressable data. If the type is RANDOM,
 * it returns one of the other choices, chosen at random.
 * @param {string} payload_type The type of payload to return
 * @param {Number} size The size of the payload body
 * @return {Payload} The requested payload
 */
function getPayload(payload_type, size) {
  if (payload_type === 'RANDOM') {
    payload_type = ['COMPRESSABLE',
                    'UNCOMPRESSABLE'][Math.random() < 0.5 ? 0 : 1];
  }
  var body;
  switch (payload_type) {
    case 'COMPRESSABLE': body = zeroBuffer(size); break;
    case 'UNCOMPRESSABLE': incompressible_data.slice(size); break;
  }
  return {type: payload_type, body: body};
}

function respondWithStream(call, request, callback) {
  async.eachSeries(request.response_parameters, function(resp_param, callback) {
    setTimeout(function() {
      call.write({payload: getPayload(request.response_type, resp_param.size)});
      callback();
    }, resp_param.interval_us/1000);
  }, callback);
}

/**
 * Respond to an empty parameter with an empty response.
 * NOTE: this currently does not work due to issue #137
 * @param {Call} call Call to handle
 * @param {function(Error, Object)} callback Callback to call with result
 *     or error
 */
function handleEmpty(call, callback) {
  echoHeader(call);
  callback(null, {}, getEchoTrailer(call));
}

/**
 * Handle a unary request by sending the requested payload
 * @param {Call} call Call to handle
 * @param {function(Error, Object)} callback Callback to call with result or
 *     error
 */
function handleUnary(call, callback) {
  echoHeader(call);
  var req = call.request;
  if (req.response_status) {
    var status = req.response_status;
    status.metadata = getEchoTrailer(call);
    callback(status);
    return;
  }
  var payload = getPayload(req.response_type, req.response_size);
  callback(null, {payload: payload},
           getEchoTrailer(call));
}

/**
 * Respond to a streaming call with the total size of all payloads
 * @param {Call} call Call to handle
 * @param {function(Error, Object)} callback Callback to call with result or
 *     error
 */
function handleStreamingInput(call, callback) {
  echoHeader(call);
  var aggregate_size = 0;
  call.on('data', function(value) {
    aggregate_size += value.payload.body.length;
  });
  call.on('end', function() {
    callback(null, {aggregated_payload_size: aggregate_size},
             getEchoTrailer(call));
  });
}

/**
 * Respond to a payload request with a stream of the requested payloads
 * @param {Call} call Call to handle
 */
function handleStreamingOutput(call) {
  echoHeader(call);
  var req = call.request;
  if (req.response_status) {
    var status = req.response_status;
    status.metadata = getEchoTrailer(call);
    call.emit('error', status);
    return;
  }
  respondWithStream(call, req, function(err) {
    if (err) {
      call.emit(err);
    } else {
      call.end(getEchoTrailer(call));
    }
  });
}

/**
 * Respond to a stream of payload requests with a stream of payload responses as
 * they arrive.
 * @param {Call} call Call to handle
 */
function handleFullDuplex(call) {
  echoHeader(call);
  var call_ended;
  call.on('data', function(value) {
    if (value.response_status) {
      var status = value.response_status;
      status.metadata = getEchoTrailer(call);
      call.emit('error', status);
      return;
    }
    call.pause();
    respondWithStream(call, value, function(err) {
      call.resume();
      if (call_ended) {
        call.end(getEchoTrailer(call));
      }
    });
  });
  call.on('end', function() {
    call_ended = true;

  });
}

/**
 * Respond to a stream of payload requests with a stream of payload responses
 * after all requests have arrived
 * @param {Call} call Call to handle
 */
function handleHalfDuplex(call) {
  call.emit('error', Error('HalfDuplexCall not yet implemented'));
}

/**
 * Get a server object bound to the given port
 * @param {string} port Port to which to bind
 * @param {boolean} tls Indicates that the bound port should use TLS
 * @return {{server: Server, port: number}} Server object bound to the support,
 *     and port number that the server is bound to
 */
function getServer(port, tls) {
  // TODO(mlumish): enable TLS functionality
  var options = {};
  var server_creds;
  if (tls) {
    var key_path = path.join(__dirname, '../test/data/server1.key');
    var pem_path = path.join(__dirname, '../test/data/server1.pem');

    var key_data = fs.readFileSync(key_path);
    var pem_data = fs.readFileSync(pem_path);
    server_creds = grpc.ServerCredentials.createSsl(null,
                                                    [{private_key: key_data,
                                                      cert_chain: pem_data}]);
  } else {
    server_creds = grpc.ServerCredentials.createInsecure();
  }
  var server = new grpc.Server(options);
  server.addProtoService(testProto.TestService.service, {
    emptyCall: handleEmpty,
    unaryCall: handleUnary,
    streamingOutputCall: handleStreamingOutput,
    streamingInputCall: handleStreamingInput,
    fullDuplexCall: handleFullDuplex,
    halfDuplexCall: handleHalfDuplex
  });
  var port_num = server.bind('0.0.0.0:' + port, server_creds);
  return {server: server, port: port_num};
}

if (require.main === module) {
  var parseArgs = require('minimist');
  var argv = parseArgs(process.argv, {
    string: ['port', 'use_tls']
  });
  var server_obj = getServer(argv.port, argv.use_tls === 'true');
  console.log('Server attaching to port ' + argv.port);
  server_obj.server.start();
}

/**
 * See docs for getServer
 */
exports.getServer = getServer;
<|endoftext|>


<commit_before>import { expect } from 'chai';
import proxyquire from 'proxyquire';
import sinon from 'sinon';
import _ from 'underscore';
import * as Helpers from '../../../imports/helpers/date';
import * as EmailHelpers from '../../../imports/helpers/email';
import * as SubElements from '../../../imports/helpers/subElements';

let MinutesSchema = {
    find: sinon.stub(),
    findOne: sinon.stub()
};

MinutesSchema.getCollection = _ => MinutesSchema;

class MeteorError {}

let Meteor = {
    call: sinon.stub(),
    callPromise: sinon.stub().resolves(true),
    Error: MeteorError
};

let PromisedMethods = {};

let isCurrentUserModeratorStub = sinon.stub();
let updateLastMinutesFieldsStub = sinon.stub();
let updateLastMinutesFieldsAsyncStub = sinon.stub().resolves(true);
let MeetingSeries = function(seriesId) {
    this._id = seriesId;
    this.isCurrentUserModerator = isCurrentUserModeratorStub;
    this.updateLastMinutesFields = updateLastMinutesFieldsStub;
    this.updateLastMinutesFieldsAsync = updateLastMinutesFieldsAsyncStub;
};

let topicGetOpenActionItemsStub = sinon.stub().returns([]);
let Topic = function () {
    this.getOpenActionItems = topicGetOpenActionItemsStub;
};
Topic.hasOpenActionItem = () => { return false; };

let ActionItem = function (topic, doc) {
    this._parentTopic = topic;
    this._infoItemDoc = doc;
};

SubElements['@noCallThru'] = true;
EmailHelpers['@noCallThru'] = true;
const Random = {id: () => {}};
const {
    Minutes
    } = proxyquire('../../../imports/minutes', {
    'meteor/meteor': { Meteor, '@noCallThru': true},
    'meteor/random': { Random, '@noCallThru': true},
    './collections/minutes_private': { MinutesSchema, '@noCallThru': true},
    './collections/minutes.schema': { MinutesSchema, '@noCallThru': true},
    './collections/workflow_private': { "null": null, '@noCallThru': true},
    './helpers/promisedMethods': { PromisedMethods, '@noCallThru': true},
    './meetingseries': { MeetingSeries, '@noCallThru': true},
    './topic': { Topic, '@noCallThru': true},
    '/imports/user': { "null": null, '@noCallThru': true},
    './actionitem': { ActionItem, '@noCallThru': true},
    '/imports/helpers/email': EmailHelpers,
    '/imports/helpers/subElements': SubElements,
    'meteor/underscore': { _, '@noCallThru': true}
});

describe('Minutes', function () {

    let minutesDoc, minute;

    beforeEach(function () {
        minutesDoc = {
            meetingSeries_id: 'AaBbCc01',
            _id: 'AaBbCc02',
            date: '2016-05-06',
            createdAt: new Date(),
            topics: [],
            isFinalized: false,
            participants: '',
            agenda: ''
        };

        minute = new Minutes(minutesDoc);
    });

    afterEach(function () {
        MinutesSchema.find.resetHistory();
        MinutesSchema.findOne.resetHistory();
        Meteor.call.resetHistory();
        Meteor.callPromise.resetHistory();
        isCurrentUserModeratorStub.resetHistory();
        updateLastMinutesFieldsStub.resetHistory();
        topicGetOpenActionItemsStub.resetHistory();
    });

    describe('#constructor', function () {

        it('sets the properties correctly', function () {
            expect(JSON.stringify(minute)).to.equal(JSON.stringify(minutesDoc));
        });

        it('fetches the minute from the database if the id was given', function() {

            new Minutes(minutesDoc._id);
            expect(MinutesSchema.findOne.calledOnce, "findOne should be called once").to.be.true;
            expect(MinutesSchema.findOne.calledWith(minutesDoc._id), "findOne should be called with the id").to.be.true;
        });

        it('throws exception if constructor will be called without any arguments', function () {
            let exceptionThrown;
            try {
                new Minutes();
                exceptionThrown = false;
            } catch (e) {
                exceptionThrown = (e instanceof MeteorError);
            }

            expect(exceptionThrown).to.be.true;
        });

    });

    describe('find', function () {

        it('#find', function () {
            Minutes.find("myArg");
            expect(MinutesSchema.find.calledOnce, "find-Method should be called once").to.be.true;
            expect(MinutesSchema.find.calledWithExactly("myArg"), "arguments should be passed").to.be.true;
        });

        it('#findOne', function () {
            Minutes.findOne("myArg");
            expect(MinutesSchema.findOne.calledOnce, "findOne-Method should be called once").to.be.true;
            expect(MinutesSchema.findOne.calledWithExactly("myArg"), "arguments should be passed").to.be.true;
        });

        describe('#findAllIn', function () {

            let minIdArray;
            let limit;

            beforeEach(function () {
                minIdArray = ['1', '2'];
                limit = 3;
            });

            it('calls the find-Method of the Collection', function () {
                Minutes.findAllIn(minIdArray, limit);
                expect(MinutesSchema.find.calledOnce, "find-Method should be called once").to.be.true;
            });

            it('sets the id selector correctly', function () {
                Minutes.findAllIn(minIdArray, limit);
                let selector = MinutesSchema.find.getCall(0).args[0];
                expect(selector, "Selector has the property _id").to.have.ownProperty('_id');
                expect(selector._id, '_id-selector has propery $in').to.have.ownProperty('$in');
                expect(selector._id.$in, 'idArray should be passed').to.deep.equal(minIdArray);
            });

            it('sets the option correctly (sort, no limit)', function () {
                let expectedOption = { sort: {date: -1} };
                Minutes.findAllIn(minIdArray);
                let options = MinutesSchema.find.getCall(0).args[1];
                expect(options).to.deep.equal(expectedOption);
            });

            it('sets the option correctly (sort and limit)', function () {
                let expectedOption = { sort: {date: -1}, limit: limit };
                Minutes.findAllIn(minIdArray, limit);
                let options = MinutesSchema.find.getCall(0).args[1];
                expect(options).to.deep.equal(expectedOption);
            });

        });

    });

    describe('#remove', function () {

        it('calls the meteor method minutes.remove', function () {
            Minutes.remove(minute._id);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the minutes id to the meteor method minutes.remove', function () {
            Minutes.remove(minute._id);
            expect(Meteor.callPromise.calledWithExactly('workflow.removeMinute', minute._id)).to.be.true;
        });

    });

    describe('#syncVisibilityAndParticipants', function () {

        let visibleForArray, parentSeriesId;

        beforeEach(function () {
            visibleForArray = [
                '1',
                '2'
            ];
            parentSeriesId = minute.meetingSeries_id;
        });

        it('calls the meteor method minutes.syncVisibilityAndParticipants', function () {
            Minutes.syncVisibility(parentSeriesId, visibleForArray);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the parentSeriesId and the visibleFor-array to the meteor method minutes.syncVisibilityAndParticipants', function () {
            Minutes.syncVisibility(parentSeriesId, visibleForArray);
            expect(Meteor.callPromise.calledWithExactly('minutes.syncVisibilityAndParticipants', parentSeriesId, visibleForArray)).to.be.true;
        });

    });

    describe('#update', function () {

        let updateDocPart;

        beforeEach(function () {
            updateDocPart = {
                date: '2016-05-07'
            }
        });

        it('calls the meteor method minutes.update', function () {
            minute.update(updateDocPart);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the doc part and the minutes id to the meteor method minutes.update', function () {
            minute.update(updateDocPart);
            let sentObj = JSON.parse(JSON.stringify(updateDocPart));
            sentObj._id = minute._id;
            expect(Meteor.callPromise.calledWithExactly('minutes.update', sentObj, undefined)).to.be.true;
        });

        it('updates the changed property of the minute object', async function () {
            await minute.update(updateDocPart);
            expect(minute.date).to.equal(updateDocPart.date);
        });

    });

    describe('#save', function () {

        it('calls the meteor method minutes.insert if a new minute will be saved', function () {
            delete minute._id;
            minute.save();
            expect(Meteor.call.calledOnce).to.be.true;
        });

        it('uses the workflow.addMinutes method to save a new minutes document', function () {
            delete minute._id;
            minute.save();
            expect(Meteor.call.calledWithExactly('workflow.addMinutes', minute, undefined, undefined)).to.be.true;
        });

        it('sets the createdAt-property if it is not set', function () {
            delete minute._id;
            delete minute.createdAt;
            minute.save();
            expect(minute).to.have.ownProperty('createdAt');
        });

        it('calls the meteor method minutes.update if a existing minute will be saved', function () {
            minute.save();
            expect(Meteor.call.calledOnce).to.be.true;
        });

        it('sends the minutes object to the meteor method minutes.update', function () {
            minute.save();
            expect(Meteor.call.calledWithExactly('minutes.update', minute)).to.be.true;
        });

    });

    it('#parentMeetingSeries', function () {
        let parentSeries = minute.parentMeetingSeries();
        expect(parentSeries instanceof MeetingSeries, "result should be an instance of MeetingSeries").to.be.true;
        expect(parentSeries._id, "created meeting series object should have the correct series id").to.equal(minute.meetingSeries_id);
    });

    it('#parentMeetingSeriesID', function () {
        expect(minute.parentMeetingSeriesID()).to.equal(minute.meetingSeries_id);
    });

    describe('topic related methods', function () {

        let topic1, topic2, topic3, topic4;

        beforeEach(function () {
            topic1 = {
                _id: "01",
                subject: "firstTopic",
                isNew: true,
                isOpen: true
            };
            topic2 = {
                _id: "02",
                subject: "2ndTopic",
                isNew: true,
                isOpen: false
            };
            topic3 = {
                _id: "03",
                subject: "3rdTopic",
                isNew: false,
                isOpen: true
            };
            topic4 = {
                _id: "04",
                subject: "4thTopic",
                isNew: false,
                isOpen: false
            };
            minute.topics.push(topic1);
            minute.topics.push(topic2);
            minute.topics.push(topic3);
            minute.topics.push(topic4);
        });

        describe('#findTopic', function () {

            it('finds the correct topic identified by its id', function () {
                expect(minute.findTopic(topic1._id)).to.deep.equal(topic1);
            });

            it('returns undefined if topic was not found', function () {
                expect(minute.findTopic('unknownId')).to.be.undefined;
            });

        });

        describe('#removeTopic', function () {

            it('removes the topic from the topics array', function () {
                let oldLength = minute.topics.length;
                minute.removeTopic(topic1._id);
                expect(minute.topics).to.have.length(oldLength-1);
            });

            it('calls the meteor method minutes.update', function () {
                minute.removeTopic(topic1._id);
                expect(Meteor.callPromise.calledOnce).to.be.true;
            });

        });

        describe('#getNewTopics', function () {

            it('returns the correct amount of topics', function () {
                expect(minute.getNewTopics()).to.have.length(2);
            });

            it('returns only new topics', function () {
                let newTopics = minute.getNewTopics();
                newTopics.forEach(topic => {
                    expect(topic.isNew, "isNew-flag should be set").to.be.true;
                });
            });

        });

        describe('#getOldClosedTopics', function () {

            it('returns the correct amount of topics', function () {
                expect(minute.getOldClosedTopics()).to.have.length(1);
            });

            it('returns only old and closed topics', function () {
                let oldClosedTopics = minute.getOldClosedTopics();
                oldClosedTopics.forEach(topic => {
                    expect(
                        topic.isNew && topic.isOpen,
                        "isNew and isOpen flag should both not set"
                    ).to.be.false;
                });
            });

        });

        describe('#getOpenActionItems', function () {

            it('calls the getOpenActionItems method for each topic', function () {
                minute.getOpenActionItems();
                expect(topicGetOpenActionItemsStub.callCount).to.equal(minute.topics.length);
            });

            it('concatenates all results of each getOpenActionItems-call', function () {
                topicGetOpenActionItemsStub.returns([5,7]);
                expect(minute.getOpenActionItems()).to.have.length(minute.topics.length * 2);

            });

        });

    });

    describe('#upsertTopic', function () {

        let topicDoc;

        beforeEach(function () {
            topicDoc = {
                subject: "myTopic"
            }
        });

        it('adds a new topic to the topic array', function () {
            minute.upsertTopic(topicDoc);
            expect(Meteor.callPromise.calledOnce).to.be.true;
            expect(Meteor.callPromise.calledWithExactly('minutes.addTopic', sinon.match.string, topicDoc));
        });

        it('adds a new topic which already has an id', function () {
            topicDoc._id = "myId";
            minute.upsertTopic(topicDoc);
            expect(Meteor.callPromise.calledOnce).to.be.true;
            expect(Meteor.callPromise.calledWithExactly('minutes.addTopic', topicDoc._id, topicDoc));
        });

        it('updates an existing topic correctly', function () {
            topicDoc._id = "myId";
            minute.topics.unshift(topicDoc);
            topicDoc.subject = "changedSubject";
            minute.upsertTopic(topicDoc);
            expect(minute.topics, "update an existing topic should not change the size of the topics array").to.have.length(1);
            expect(minute.topics[0].subject, "the subject should have been updated").to.equal(topicDoc.subject);
        });

        it('calls the meteor method minutes.update', function () {
            minute.upsertTopic(topicDoc);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the minutes id and the topic doc to the meteor method minutes.addTopic', function () {
            minute.upsertTopic(topicDoc);
            let callArgs = Meteor.callPromise.getCall(0).args;
            expect(callArgs[0], "first argument should be the name of the meteor method", 'minutes.addTopic');
            let sentDoc = callArgs[1];
            expect(callArgs[1], 'minutes id should be sent to the meteor method').to.equal(minutesDoc._id);
            expect(callArgs[2], 'topic-doc should be sent to the meteor method').to.equal(topicDoc);
        });

    });

    it('#isCurrentUserModerator', function () {
        minute.isCurrentUserModerator();

        expect(isCurrentUserModeratorStub.calledOnce).to.be.true;
    });

});
<commit_msg>Fix unit tests<commit_after>import { expect } from 'chai';
import proxyquire from 'proxyquire';
import sinon from 'sinon';
import _ from 'underscore';
import * as Helpers from '../../../imports/helpers/date';
import * as EmailHelpers from '../../../imports/helpers/email';
import * as SubElements from '../../../imports/helpers/subElements';

let MinutesSchema = {
    find: sinon.stub(),
    findOne: sinon.stub()
};

MinutesSchema.getCollection = _ => MinutesSchema;

class MeteorError {}

let Meteor = {
    call: sinon.stub(),
    callPromise: sinon.stub().resolves(true),
    Error: MeteorError
};

let PromisedMethods = {};

let isCurrentUserModeratorStub = sinon.stub();
let updateLastMinutesFieldsStub = sinon.stub();
let updateLastMinutesFieldsAsyncStub = sinon.stub().resolves(true);
let MeetingSeries = function(seriesId) {
    this._id = seriesId;
    this.isCurrentUserModerator = isCurrentUserModeratorStub;
    this.updateLastMinutesFields = updateLastMinutesFieldsStub;
    this.updateLastMinutesFieldsAsync = updateLastMinutesFieldsAsyncStub;
};

let topicGetOpenActionItemsStub = sinon.stub().returns([]);
let Topic = function () {
    this.getOpenActionItems = topicGetOpenActionItemsStub;
};
Topic.hasOpenActionItem = () => { return false; };

let ActionItem = function (topic, doc) {
    this._parentTopic = topic;
    this._infoItemDoc = doc;
};

SubElements['@noCallThru'] = true;
EmailHelpers['@noCallThru'] = true;
const Random = {id: () => {}};
const {
    Minutes
    } = proxyquire('../../../imports/minutes', {
    'meteor/meteor': { Meteor, '@noCallThru': true},
    'meteor/universe:i18n': { Meteor, '@noCallThru': true},
    'meteor/random': { Random, '@noCallThru': true},
    './collections/minutes_private': { MinutesSchema, '@noCallThru': true},
    './collections/minutes.schema': { MinutesSchema, '@noCallThru': true},
    './collections/workflow_private': { "null": null, '@noCallThru': true},
    './helpers/promisedMethods': { PromisedMethods, '@noCallThru': true},
    './meetingseries': { MeetingSeries, '@noCallThru': true},
    './topic': { Topic, '@noCallThru': true},
    '/imports/user': { "null": null, '@noCallThru': true},
    './actionitem': { ActionItem, '@noCallThru': true},
    '/imports/helpers/email': EmailHelpers,
    '/imports/helpers/subElements': SubElements,
    'meteor/underscore': { _, '@noCallThru': true}
});

describe('Minutes', function () {

    let minutesDoc, minute;

    beforeEach(function () {
        minutesDoc = {
            meetingSeries_id: 'AaBbCc01',
            _id: 'AaBbCc02',
            date: '2016-05-06',
            createdAt: new Date(),
            topics: [],
            isFinalized: false,
            participants: '',
            agenda: ''
        };

        minute = new Minutes(minutesDoc);
    });

    afterEach(function () {
        MinutesSchema.find.resetHistory();
        MinutesSchema.findOne.resetHistory();
        Meteor.call.resetHistory();
        Meteor.callPromise.resetHistory();
        isCurrentUserModeratorStub.resetHistory();
        updateLastMinutesFieldsStub.resetHistory();
        topicGetOpenActionItemsStub.resetHistory();
    });

    describe('#constructor', function () {

        it('sets the properties correctly', function () {
            expect(JSON.stringify(minute)).to.equal(JSON.stringify(minutesDoc));
        });

        it('fetches the minute from the database if the id was given', function() {

            new Minutes(minutesDoc._id);
            expect(MinutesSchema.findOne.calledOnce, "findOne should be called once").to.be.true;
            expect(MinutesSchema.findOne.calledWith(minutesDoc._id), "findOne should be called with the id").to.be.true;
        });

        it('throws exception if constructor will be called without any arguments', function () {
            let exceptionThrown;
            try {
                new Minutes();
                exceptionThrown = false;
            } catch (e) {
                exceptionThrown = (e instanceof MeteorError);
            }

            expect(exceptionThrown).to.be.true;
        });

    });

    describe('find', function () {

        it('#find', function () {
            Minutes.find("myArg");
            expect(MinutesSchema.find.calledOnce, "find-Method should be called once").to.be.true;
            expect(MinutesSchema.find.calledWithExactly("myArg"), "arguments should be passed").to.be.true;
        });

        it('#findOne', function () {
            Minutes.findOne("myArg");
            expect(MinutesSchema.findOne.calledOnce, "findOne-Method should be called once").to.be.true;
            expect(MinutesSchema.findOne.calledWithExactly("myArg"), "arguments should be passed").to.be.true;
        });

        describe('#findAllIn', function () {

            let minIdArray;
            let limit;

            beforeEach(function () {
                minIdArray = ['1', '2'];
                limit = 3;
            });

            it('calls the find-Method of the Collection', function () {
                Minutes.findAllIn(minIdArray, limit);
                expect(MinutesSchema.find.calledOnce, "find-Method should be called once").to.be.true;
            });

            it('sets the id selector correctly', function () {
                Minutes.findAllIn(minIdArray, limit);
                let selector = MinutesSchema.find.getCall(0).args[0];
                expect(selector, "Selector has the property _id").to.have.ownProperty('_id');
                expect(selector._id, '_id-selector has propery $in').to.have.ownProperty('$in');
                expect(selector._id.$in, 'idArray should be passed').to.deep.equal(minIdArray);
            });

            it('sets the option correctly (sort, no limit)', function () {
                let expectedOption = { sort: {date: -1} };
                Minutes.findAllIn(minIdArray);
                let options = MinutesSchema.find.getCall(0).args[1];
                expect(options).to.deep.equal(expectedOption);
            });

            it('sets the option correctly (sort and limit)', function () {
                let expectedOption = { sort: {date: -1}, limit: limit };
                Minutes.findAllIn(minIdArray, limit);
                let options = MinutesSchema.find.getCall(0).args[1];
                expect(options).to.deep.equal(expectedOption);
            });

        });

    });

    describe('#remove', function () {

        it('calls the meteor method minutes.remove', function () {
            Minutes.remove(minute._id);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the minutes id to the meteor method minutes.remove', function () {
            Minutes.remove(minute._id);
            expect(Meteor.callPromise.calledWithExactly('workflow.removeMinute', minute._id)).to.be.true;
        });

    });

    describe('#syncVisibilityAndParticipants', function () {

        let visibleForArray, parentSeriesId;

        beforeEach(function () {
            visibleForArray = [
                '1',
                '2'
            ];
            parentSeriesId = minute.meetingSeries_id;
        });

        it('calls the meteor method minutes.syncVisibilityAndParticipants', function () {
            Minutes.syncVisibility(parentSeriesId, visibleForArray);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the parentSeriesId and the visibleFor-array to the meteor method minutes.syncVisibilityAndParticipants', function () {
            Minutes.syncVisibility(parentSeriesId, visibleForArray);
            expect(Meteor.callPromise.calledWithExactly('minutes.syncVisibilityAndParticipants', parentSeriesId, visibleForArray)).to.be.true;
        });

    });

    describe('#update', function () {

        let updateDocPart;

        beforeEach(function () {
            updateDocPart = {
                date: '2016-05-07'
            }
        });

        it('calls the meteor method minutes.update', function () {
            minute.update(updateDocPart);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the doc part and the minutes id to the meteor method minutes.update', function () {
            minute.update(updateDocPart);
            let sentObj = JSON.parse(JSON.stringify(updateDocPart));
            sentObj._id = minute._id;
            expect(Meteor.callPromise.calledWithExactly('minutes.update', sentObj, undefined)).to.be.true;
        });

        it('updates the changed property of the minute object', async function () {
            await minute.update(updateDocPart);
            expect(minute.date).to.equal(updateDocPart.date);
        });

    });

    describe('#save', function () {

        it('calls the meteor method minutes.insert if a new minute will be saved', function () {
            delete minute._id;
            minute.save();
            expect(Meteor.call.calledOnce).to.be.true;
        });

        it('uses the workflow.addMinutes method to save a new minutes document', function () {
            delete minute._id;
            minute.save();
            expect(Meteor.call.calledWithExactly('workflow.addMinutes', minute, undefined, undefined)).to.be.true;
        });

        it('sets the createdAt-property if it is not set', function () {
            delete minute._id;
            delete minute.createdAt;
            minute.save();
            expect(minute).to.have.ownProperty('createdAt');
        });

        it('calls the meteor method minutes.update if a existing minute will be saved', function () {
            minute.save();
            expect(Meteor.call.calledOnce).to.be.true;
        });

        it('sends the minutes object to the meteor method minutes.update', function () {
            minute.save();
            expect(Meteor.call.calledWithExactly('minutes.update', minute)).to.be.true;
        });

    });

    it('#parentMeetingSeries', function () {
        let parentSeries = minute.parentMeetingSeries();
        expect(parentSeries instanceof MeetingSeries, "result should be an instance of MeetingSeries").to.be.true;
        expect(parentSeries._id, "created meeting series object should have the correct series id").to.equal(minute.meetingSeries_id);
    });

    it('#parentMeetingSeriesID', function () {
        expect(minute.parentMeetingSeriesID()).to.equal(minute.meetingSeries_id);
    });

    describe('topic related methods', function () {

        let topic1, topic2, topic3, topic4;

        beforeEach(function () {
            topic1 = {
                _id: "01",
                subject: "firstTopic",
                isNew: true,
                isOpen: true
            };
            topic2 = {
                _id: "02",
                subject: "2ndTopic",
                isNew: true,
                isOpen: false
            };
            topic3 = {
                _id: "03",
                subject: "3rdTopic",
                isNew: false,
                isOpen: true
            };
            topic4 = {
                _id: "04",
                subject: "4thTopic",
                isNew: false,
                isOpen: false
            };
            minute.topics.push(topic1);
            minute.topics.push(topic2);
            minute.topics.push(topic3);
            minute.topics.push(topic4);
        });

        describe('#findTopic', function () {

            it('finds the correct topic identified by its id', function () {
                expect(minute.findTopic(topic1._id)).to.deep.equal(topic1);
            });

            it('returns undefined if topic was not found', function () {
                expect(minute.findTopic('unknownId')).to.be.undefined;
            });

        });

        describe('#removeTopic', function () {

            it('removes the topic from the topics array', function () {
                let oldLength = minute.topics.length;
                minute.removeTopic(topic1._id);
                expect(minute.topics).to.have.length(oldLength-1);
            });

            it('calls the meteor method minutes.update', function () {
                minute.removeTopic(topic1._id);
                expect(Meteor.callPromise.calledOnce).to.be.true;
            });

        });

        describe('#getNewTopics', function () {

            it('returns the correct amount of topics', function () {
                expect(minute.getNewTopics()).to.have.length(2);
            });

            it('returns only new topics', function () {
                let newTopics = minute.getNewTopics();
                newTopics.forEach(topic => {
                    expect(topic.isNew, "isNew-flag should be set").to.be.true;
                });
            });

        });

        describe('#getOldClosedTopics', function () {

            it('returns the correct amount of topics', function () {
                expect(minute.getOldClosedTopics()).to.have.length(1);
            });

            it('returns only old and closed topics', function () {
                let oldClosedTopics = minute.getOldClosedTopics();
                oldClosedTopics.forEach(topic => {
                    expect(
                        topic.isNew && topic.isOpen,
                        "isNew and isOpen flag should both not set"
                    ).to.be.false;
                });
            });

        });

        describe('#getOpenActionItems', function () {

            it('calls the getOpenActionItems method for each topic', function () {
                minute.getOpenActionItems();
                expect(topicGetOpenActionItemsStub.callCount).to.equal(minute.topics.length);
            });

            it('concatenates all results of each getOpenActionItems-call', function () {
                topicGetOpenActionItemsStub.returns([5,7]);
                expect(minute.getOpenActionItems()).to.have.length(minute.topics.length * 2);

            });

        });

    });

    describe('#upsertTopic', function () {

        let topicDoc;

        beforeEach(function () {
            topicDoc = {
                subject: "myTopic"
            }
        });

        it('adds a new topic to the topic array', function () {
            minute.upsertTopic(topicDoc);
            expect(Meteor.callPromise.calledOnce).to.be.true;
            expect(Meteor.callPromise.calledWithExactly('minutes.addTopic', sinon.match.string, topicDoc));
        });

        it('adds a new topic which already has an id', function () {
            topicDoc._id = "myId";
            minute.upsertTopic(topicDoc);
            expect(Meteor.callPromise.calledOnce).to.be.true;
            expect(Meteor.callPromise.calledWithExactly('minutes.addTopic', topicDoc._id, topicDoc));
        });

        it('updates an existing topic correctly', function () {
            topicDoc._id = "myId";
            minute.topics.unshift(topicDoc);
            topicDoc.subject = "changedSubject";
            minute.upsertTopic(topicDoc);
            expect(minute.topics, "update an existing topic should not change the size of the topics array").to.have.length(1);
            expect(minute.topics[0].subject, "the subject should have been updated").to.equal(topicDoc.subject);
        });

        it('calls the meteor method minutes.update', function () {
            minute.upsertTopic(topicDoc);
            expect(Meteor.callPromise.calledOnce).to.be.true;
        });

        it('sends the minutes id and the topic doc to the meteor method minutes.addTopic', function () {
            minute.upsertTopic(topicDoc);
            let callArgs = Meteor.callPromise.getCall(0).args;
            expect(callArgs[0], "first argument should be the name of the meteor method", 'minutes.addTopic');
            let sentDoc = callArgs[1];
            expect(callArgs[1], 'minutes id should be sent to the meteor method').to.equal(minutesDoc._id);
            expect(callArgs[2], 'topic-doc should be sent to the meteor method').to.equal(topicDoc);
        });

    });

    it('#isCurrentUserModerator', function () {
        minute.isCurrentUserModerator();

        expect(isCurrentUserModeratorStub.calledOnce).to.be.true;
    });

});
<|endoftext|>


<commit_before>describe('opentok-angular', function () {
  var OTSession, session, $rootScope, subscriber;
  beforeEach(function () {
    module('opentok', function ($provide) {
      OT.initSession = function () {
        session = {
          isConnected: jasmine.createSpy('isConnected').and.returnValue(true),
          signal: jasmine.createSpy('signal'),
          connect: jasmine.createSpy('connect'),
          publish: jasmine.createSpy('publish'),
          unpublish: jasmine.createSpy('unpublish'),
          subscribe: function () {
            subscriber = {};
            OT.$.eventing(subscriber);
            return subscriber;
          },
          unsubscribe: jasmine.createSpy('unsubscribe'),
          connection: {
            connectionId: 'sessionConnectionId'
          }
        };
        OT.$.eventing(session);
        spyOn(session, 'subscribe').and.callThrough();
        spyOn(session, 'on').and.callThrough();
        return session;
      };
      spyOn(OT, 'initSession').and.callThrough();
    });
    inject(function (_OTSession_, _$rootScope_) {
      OTSession = _OTSession_;
      spyOn(OTSession, 'trigger');
      $rootScope = _$rootScope_;
    });
  });

  describe('OTSession', function () {
    it('defines the right api', function () {
      expect(OTSession.init).toEqual(jasmine.any(Function));
      expect(OTSession.streams).toEqual(jasmine.any(Array));
      expect(OTSession.publishers).toEqual(jasmine.any(Array));
    });

    describe('init called', function () {
      var apiKey = 'mockAPIKey',
        sessionId = 'mockSessionId',
        token = 'mockToken',
        cb = jasmine.createSpy('cb');
      beforeEach(function () {
        OTSession.init(apiKey, sessionId, token, cb);
      });

      it('calls OT.initSession, session.on and session.connect when you call init', function () {
        expect(OT.initSession).toHaveBeenCalledWith(sessionId);
        expect(OTSession.session).toBeDefined();
        expect(OTSession.session.on).toHaveBeenCalledWith(jasmine.objectContaining({
          streamCreated: jasmine.any(Function),
          streamDestroyed: jasmine.any(Function)
        }));
        expect(OTSession.session.connect).toHaveBeenCalledWith(apiKey, token, jasmine.any(Function));
      });

      it('triggers init on init', function () {
        expect(OTSession.trigger).toHaveBeenCalledWith('init');
      });

      it('adds streams on streamCreated', function (done) {
        var stream = {};
        session.trigger('streamCreated', {stream: stream});
        setTimeout(function () {
          expect(OTSession.streams).toContain(stream);
          done();
        }, 10);
      });

      it('removes streams on streamDestroyed', function (done) {
        var stream = {};
        session.trigger('streamCreated', {stream: stream});
        session.trigger('streamDestroyed', {stream: stream});
        setTimeout(function () {
          expect(OTSession.streams).not.toContain(stream);
          done();
        }, 10);
      });

      it('empties the streams array on sessionDisconnected', function (done) {
        session.trigger('streamCreated', {stream: {}});
        session.trigger('streamCreated', {stream: {}});
        session.trigger('streamCreated', {stream: {}});
        session.trigger('sessionDisconnected', {stream: {}});
        setTimeout(function () {
          expect(OTSession.streams.length).toBe(0);
          done();
        }, 10);
      });

      it('publishes publishers on sessionConnected', function (done) {
        var publisher = {};
        OTSession.publishers.push(publisher);
        session.trigger('sessionConnected');
        setTimeout(function () {
          expect(session.publish).toHaveBeenCalledWith(publisher);
          done();
        }, 10);
      });
    });
  });
  describe('directives:', function () {
    beforeEach(function () {
      var apiKey = 'mockAPIKey',
        sessionId = 'mockSessionId',
        token = 'mockToken',
        cb = jasmine.createSpy('cb');
      OTSession.init(apiKey, sessionId, token, cb);
    });

    describe('ot-layout', function () {
      var element, scope, layout, $window;
      beforeEach(inject(function ($rootScope, $compile, _$window_) {
        $window = _$window_;
        var oldInitLayoutContainer = OT.initLayoutContainer;
        OT.initLayoutContainer = function () {
          var container = oldInitLayoutContainer.apply(this, arguments);
          layout = container.layout = jasmine.createSpy('layout').and.callFake(container.layout);
          return container;
        };
        spyOn(OT, 'initLayoutContainer').and.callThrough();
        scope = $rootScope.$new();
        element = '<ot-layout props="{animate:true}"></ot-layout>';
        element = $compile(element)(scope);
        scope.$digest();
      }));

      it('calls OT.initLayoutContainer with the right props', function () {
        expect(OT.initLayoutContainer).toHaveBeenCalledWith(element[0], jasmine.objectContaining({
          animate: true
        }));
      });

      it('calls layout if you add a child', function () {
        var numCalls = layout.calls.count();
        element.append($('

'));
        scope.$digest();
        expect(layout.calls.count()).toBeGreaterThan(numCalls);
      });

      it('calls layout if you get a streamPropertyChanged for videoDimentions', function (done) {
        var numCalls = layout.calls.count();
        session.trigger('streamPropertyChanged', {changedProperty: 'videoDimensions'});
        setTimeout(function () {
          expect(layout.calls.count()).toBeGreaterThan(numCalls);
          done();
        }, 10);
      });

      it ('calls layout if the window resizes', function (done) {
        var numCalls = layout.calls.count();
        $window.dispatchEvent(new Event('resize'));
        setTimeout(function () {
          expect(layout.calls.count()).toBeGreaterThan(numCalls);
          done();
        }, 10);
      });
    });

    describe('ot-publisher', function () {
      var element, scope, publisher;
      beforeEach(inject(function ($rootScope, $compile) {
        OTSession.init('mockAPIKey', 'mockSessionId', 'mockToken');
        spyOn(OT, 'initPublisher').and.callFake(function () {
          var publisher = {
            destroy: jasmine.createSpy('destroy')
          };
          OT.$.eventing(publisher);
          return publisher;
        });
        scope = $rootScope.$new();
        element = '<ot-publisher apiKey="mockAPIKey" props="{name:\'mockName\'}" style="width:200px;height:300px;">' +
          '

</ot-publisher>';
        element = $compile(element)(scope);
        scope.$digest();
        publisher = element.isolateScope().publisher;
      }));

      it('calls initPublisher and defines scope.publisher', function () {
        expect(OT.initPublisher).toHaveBeenCalledWith('mockAPIKey', element[0],
          jasmine.objectContaining({name: 'mockName'}), jasmine.any(Function));
        expect(publisher).toBeDefined();
      });

      it('maintains the contents of <ot-publisher>', function () {
        expect(element.find('#contents').length).toBe(1);
      });

      it('emits otLayout when the publisher is loaded', function (done) {
        scope.$on('otLayout', done);
        publisher.trigger('loaded');
      });

      it('adds an allowed class', function (done) {
        publisher.trigger('accessAllowed');
        setTimeout(function () {
          expect(element[0].className).toContain('allowed');
          done();
        }, 10);
      });

      it('adds the publisher to the OTSession.publishers', function () {
        expect(OTSession.publishers).toContain(publisher);
      });

      it('calls session.publish if there is a session', function () {
        expect(session.publish).toHaveBeenCalledWith(publisher, jasmine.any(Function));
      });

      it('cleans up when its destroyed', function (done) {
        element.isolateScope().$emit('$destroy');
        setTimeout(function () {
          expect(session.unpublish).toHaveBeenCalledWith(publisher);
          expect(OTSession.publishers.length).toBe(0);
          expect(element.isolateScope().publisher).toBeNull();
          done();
        }, 10);
      });

      it('emits otPublisherError if there is an error on initPublisher', function (done) {
        scope.$on('otPublisherError', done);
        OT.initPublisher.calls.argsFor(0)[3]({});
      });

      it('emits otPublisherError if there is an error on session.publish', function (done) {
        scope.$on('otPublisherError', done);
        session.publish.calls.argsFor(0)[1]({});
      });

      it('uses the right width and height', function () {
        expect(OT.initPublisher.calls.argsFor(0)[2].width).toBe(200);
        expect(OT.initPublisher.calls.argsFor(0)[2].height).toBe(300);
      });
    });

    describe('ot-subscriber', function () {
      var element, scope;
      beforeEach(inject(function ($rootScope, $compile) {
        OTSession.init('mockAPIKey', 'mockSessionId', 'mockToken');
        scope = $rootScope.$new();
        scope.stream = {};
        element = '<ot-subscriber stream="stream" props="{subscribeToVideo:false}" style="width:200px;height:300px;">' +
          '

</ot-publisher>';
        element = $compile(element)(scope);
        scope.$digest();
      }));

      it('calls session.subscribe', function () {
        expect(session.subscribe).toHaveBeenCalledWith(scope.stream, element[0], jasmine.objectContaining({
          subscribeToVideo: false
        }), jasmine.any(Function));
      });

      it('maintains the contents of <ot-subscriber>', function () {
        expect(element.find('#contents').length).toBe(1);
      });

      it('cleans up when its destroyed', function (done) {
        element.isolateScope().$emit('$destroy');
        setTimeout(function () {
          expect(session.unsubscribe).toHaveBeenCalled();
          done();
        }, 10);
      });

      it('emits an otLayout when loaded', function (done) {
        scope.$on('otLayout', done);
        subscriber.trigger('loaded');
      });

      it('emits an otSubscriberError if there is an error on subscribe', function (done) {
        scope.$on('otSubscriberError', done);
        session.subscribe.calls.argsFor(0)[3]({});
      });

      it('uses the right width and height', function () {
        expect(session.subscribe.calls.argsFor(0)[2].width).toBe(200);
        expect(session.subscribe.calls.argsFor(0)[2].height).toBe(300);
      });
    });
  });

});<commit_msg>Adding unit tests for publisher access events<commit_after>describe('opentok-angular', function () {
  var OTSession, session, $rootScope, subscriber;
  beforeEach(function () {
    module('opentok', function ($provide) {
      OT.initSession = function () {
        session = {
          isConnected: jasmine.createSpy('isConnected').and.returnValue(true),
          signal: jasmine.createSpy('signal'),
          connect: jasmine.createSpy('connect'),
          publish: jasmine.createSpy('publish'),
          unpublish: jasmine.createSpy('unpublish'),
          subscribe: function () {
            subscriber = {};
            OT.$.eventing(subscriber);
            return subscriber;
          },
          unsubscribe: jasmine.createSpy('unsubscribe'),
          connection: {
            connectionId: 'sessionConnectionId'
          }
        };
        OT.$.eventing(session);
        spyOn(session, 'subscribe').and.callThrough();
        spyOn(session, 'on').and.callThrough();
        return session;
      };
      spyOn(OT, 'initSession').and.callThrough();
    });
    inject(function (_OTSession_, _$rootScope_) {
      OTSession = _OTSession_;
      spyOn(OTSession, 'trigger');
      $rootScope = _$rootScope_;
    });
  });

  describe('OTSession', function () {
    it('defines the right api', function () {
      expect(OTSession.init).toEqual(jasmine.any(Function));
      expect(OTSession.streams).toEqual(jasmine.any(Array));
      expect(OTSession.publishers).toEqual(jasmine.any(Array));
    });

    describe('init called', function () {
      var apiKey = 'mockAPIKey',
        sessionId = 'mockSessionId',
        token = 'mockToken',
        cb = jasmine.createSpy('cb');
      beforeEach(function () {
        OTSession.init(apiKey, sessionId, token, cb);
      });

      it('calls OT.initSession, session.on and session.connect when you call init', function () {
        expect(OT.initSession).toHaveBeenCalledWith(sessionId);
        expect(OTSession.session).toBeDefined();
        expect(OTSession.session.on).toHaveBeenCalledWith(jasmine.objectContaining({
          streamCreated: jasmine.any(Function),
          streamDestroyed: jasmine.any(Function)
        }));
        expect(OTSession.session.connect).toHaveBeenCalledWith(apiKey, token, jasmine.any(Function));
      });

      it('triggers init on init', function () {
        expect(OTSession.trigger).toHaveBeenCalledWith('init');
      });

      it('adds streams on streamCreated', function (done) {
        var stream = {};
        session.trigger('streamCreated', {stream: stream});
        setTimeout(function () {
          expect(OTSession.streams).toContain(stream);
          done();
        }, 10);
      });

      it('removes streams on streamDestroyed', function (done) {
        var stream = {};
        session.trigger('streamCreated', {stream: stream});
        session.trigger('streamDestroyed', {stream: stream});
        setTimeout(function () {
          expect(OTSession.streams).not.toContain(stream);
          done();
        }, 10);
      });

      it('empties the streams array on sessionDisconnected', function (done) {
        session.trigger('streamCreated', {stream: {}});
        session.trigger('streamCreated', {stream: {}});
        session.trigger('streamCreated', {stream: {}});
        session.trigger('sessionDisconnected', {stream: {}});
        setTimeout(function () {
          expect(OTSession.streams.length).toBe(0);
          done();
        }, 10);
      });

      it('publishes publishers on sessionConnected', function (done) {
        var publisher = {};
        OTSession.publishers.push(publisher);
        session.trigger('sessionConnected');
        setTimeout(function () {
          expect(session.publish).toHaveBeenCalledWith(publisher);
          done();
        }, 10);
      });
    });
  });
  describe('directives:', function () {
    beforeEach(function () {
      var apiKey = 'mockAPIKey',
        sessionId = 'mockSessionId',
        token = 'mockToken',
        cb = jasmine.createSpy('cb');
      OTSession.init(apiKey, sessionId, token, cb);
    });

    describe('ot-layout', function () {
      var element, scope, layout, $window;
      beforeEach(inject(function ($rootScope, $compile, _$window_) {
        $window = _$window_;
        var oldInitLayoutContainer = OT.initLayoutContainer;
        OT.initLayoutContainer = function () {
          var container = oldInitLayoutContainer.apply(this, arguments);
          layout = container.layout = jasmine.createSpy('layout').and.callFake(container.layout);
          return container;
        };
        spyOn(OT, 'initLayoutContainer').and.callThrough();
        scope = $rootScope.$new();
        element = '<ot-layout props="{animate:true}"></ot-layout>';
        element = $compile(element)(scope);
        scope.$digest();
      }));

      it('calls OT.initLayoutContainer with the right props', function () {
        expect(OT.initLayoutContainer).toHaveBeenCalledWith(element[0], jasmine.objectContaining({
          animate: true
        }));
      });

      it('calls layout if you add a child', function () {
        var numCalls = layout.calls.count();
        element.append($('

'));
        scope.$digest();
        expect(layout.calls.count()).toBeGreaterThan(numCalls);
      });

      it('calls layout if you get a streamPropertyChanged for videoDimentions', function (done) {
        var numCalls = layout.calls.count();
        session.trigger('streamPropertyChanged', {changedProperty: 'videoDimensions'});
        setTimeout(function () {
          expect(layout.calls.count()).toBeGreaterThan(numCalls);
          done();
        }, 10);
      });

      it ('calls layout if the window resizes', function (done) {
        var numCalls = layout.calls.count();
        $window.dispatchEvent(new Event('resize'));
        setTimeout(function () {
          expect(layout.calls.count()).toBeGreaterThan(numCalls);
          done();
        }, 10);
      });
    });

    describe('ot-publisher', function () {
      var element, scope, publisher;
      beforeEach(inject(function ($rootScope, $compile) {
        OTSession.init('mockAPIKey', 'mockSessionId', 'mockToken');
        spyOn(OT, 'initPublisher').and.callFake(function () {
          var publisher = {
            destroy: jasmine.createSpy('destroy')
          };
          OT.$.eventing(publisher);
          return publisher;
        });
        scope = $rootScope.$new();
        element = '<ot-publisher apiKey="mockAPIKey" props="{name:\'mockName\'}" style="width:200px;height:300px;">' +
          '

</ot-publisher>';
        element = $compile(element)(scope);
        scope.$digest();
        publisher = element.isolateScope().publisher;
      }));

      it('calls initPublisher and defines scope.publisher', function () {
        expect(OT.initPublisher).toHaveBeenCalledWith('mockAPIKey', element[0],
          jasmine.objectContaining({name: 'mockName'}), jasmine.any(Function));
        expect(publisher).toBeDefined();
      });

      it('maintains the contents of <ot-publisher>', function () {
        expect(element.find('#contents').length).toBe(1);
      });

      it('emits otLayout when the publisher is loaded', function (done) {
        scope.$on('otLayout', done);
        publisher.trigger('loaded');
      });

      it('adds an allowed class', function (done) {
        publisher.trigger('accessAllowed');
        setTimeout(function () {
          expect(element[0].className).toContain('allowed');
          done();
        }, 10);
      });

      it('emits otAccessAllowed on publisher accessAllowed', function (done) {
        scope.$on('otAccessAllowed', done);
        publisher.trigger('accessAllowed');
      });

      it('emits otAccessDenied on publisher accessDenied', function (done) {
        scope.$on('otAccessDenied', done);
        publisher.trigger('accessDenied');
      });

      it('emits otAccessDialogOpened on publisher accessDialogOpened', function (done) {
        scope.$on('otAccessDialogOpened', done);
        publisher.trigger('accessDialogOpened');
      });

      it('emits otAccessDialogClosed on publisher accessDialogClosed', function (done) {
        scope.$on('otAccessDialogClosed', done);
        publisher.trigger('accessDialogClosed');
      });

      it('adds the publisher to the OTSession.publishers', function () {
        expect(OTSession.publishers).toContain(publisher);
      });

      it('calls session.publish if there is a session', function () {
        expect(session.publish).toHaveBeenCalledWith(publisher, jasmine.any(Function));
      });

      it('cleans up when its destroyed', function (done) {
        element.isolateScope().$emit('$destroy');
        setTimeout(function () {
          expect(session.unpublish).toHaveBeenCalledWith(publisher);
          expect(OTSession.publishers.length).toBe(0);
          expect(element.isolateScope().publisher).toBeNull();
          done();
        }, 10);
      });

      it('emits otPublisherError if there is an error on initPublisher', function (done) {
        scope.$on('otPublisherError', done);
        OT.initPublisher.calls.argsFor(0)[3]({});
      });

      it('emits otPublisherError if there is an error on session.publish', function (done) {
        scope.$on('otPublisherError', done);
        session.publish.calls.argsFor(0)[1]({});
      });

      it('uses the right width and height', function () {
        expect(OT.initPublisher.calls.argsFor(0)[2].width).toBe(200);
        expect(OT.initPublisher.calls.argsFor(0)[2].height).toBe(300);
      });
    });

    describe('ot-subscriber', function () {
      var element, scope;
      beforeEach(inject(function ($rootScope, $compile) {
        OTSession.init('mockAPIKey', 'mockSessionId', 'mockToken');
        scope = $rootScope.$new();
        scope.stream = {};
        element = '<ot-subscriber stream="stream" props="{subscribeToVideo:false}" style="width:200px;height:300px;">' +
          '

</ot-publisher>';
        element = $compile(element)(scope);
        scope.$digest();
      }));

      it('calls session.subscribe', function () {
        expect(session.subscribe).toHaveBeenCalledWith(scope.stream, element[0], jasmine.objectContaining({
          subscribeToVideo: false
        }), jasmine.any(Function));
      });

      it('maintains the contents of <ot-subscriber>', function () {
        expect(element.find('#contents').length).toBe(1);
      });

      it('cleans up when its destroyed', function (done) {
        element.isolateScope().$emit('$destroy');
        setTimeout(function () {
          expect(session.unsubscribe).toHaveBeenCalled();
          done();
        }, 10);
      });

      it('emits an otLayout when loaded', function (done) {
        scope.$on('otLayout', done);
        subscriber.trigger('loaded');
      });

      it('emits an otSubscriberError if there is an error on subscribe', function (done) {
        scope.$on('otSubscriberError', done);
        session.subscribe.calls.argsFor(0)[3]({});
      });

      it('uses the right width and height', function () {
        expect(session.subscribe.calls.argsFor(0)[2].width).toBe(200);
        expect(session.subscribe.calls.argsFor(0)[2].height).toBe(300);
      });
    });
  });

});<|endoftext|>