<commit_before>    /** @const */ var OAUTH_VERSION = '1.0';

    /**
     * OAuth
     * @constructor
     */
    function OAuth(options) {
	if (window === this) {
	    return new OAuth(options);
	}

	return this.init(options);
    }

    OAuth.prototype = {
	init: function (options) {
	    var oauth = {
		debug: options.debug || false,
		consumerKey: options.consumerKey,
		consumerSecret: options.consumerSecret,
		accessTokenSecret: options.accessTokenSecret || '',
		signatureMethod: options.signatureMethod || 'HMAC-SHA1'
	    };

	    this.request = function (options) {
		var method, url, data, headers, success, failure, xhr, i,
		    headerParams, signatureMethod, signatureString, signature,
		    query;

		method = options.method || 'GET';
		url = options.url;
		data = options.data || {};
		headers = options.headers || {};
		success = options.success || function (data) {};
		failure = options.failure || function () {};

		if (oauth.debug) {
		    netscape.security.PrivilegeManager
			    .enablePrivilege("UniversalBrowserRead UniversalBrowserWrite");
		}

		xhr = new XMLHttpRequest();
		xhr.onreadystatechange = function () {
		    if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
			success(xhr.responseText);
		    } else if(xhr.readyState == 4 && xhr.status != 200 && xhr.status != 0) {
			failure();
		    }
		};

		for(i in data) {
		    query.push(i + '=' + data[i]);
		}

		headerParams = {
		    'oauth_callback': 'oob',
		    'oauth_consumer_key': oauth.consumerKey,
		    'oauth_token': oauth.accessTokenSecret,
		    'oauth_signature_method': oauth.signatureMethod,
		    'oauth_timestamp': getTimestamp(),
		    'oauth_nonce': getNonce(),
		    'oauth_verifier': oauth.verifier || '',
		    'oauth_version': OAUTH_VERSION
		};

		signatureMethod = oauth.signatureMethod;
		signatureString = toSignatureBaseString(method, url, headerParams, query);
		signature = OAuth['signatureMethod'][signatureMethod](oauth.consumerSecret, oauth.accessTokenSecret, signatureString);

		headerParams.oauth_signature = signature;

		query = query.join('&');
		if(method == 'GET') {
		    if (query) {
			url += '?' + query;
		    }
		    query = null;
		} else {
		    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		}

		xhr.open(method, url, true);

		xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams));
		xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
		for (i in headers) {
		    xhr.setRequestHeader(i, headers[i]);
		}

		xhr.send(query);
	    }
	},

	authenticate: function (options) {},
	deauthenticate: function (options) {},

	request: '',

	get: function (url, success, failure) {
	    this.request({'url': url, 'success': success, 'failure': failure});
	},
	post: function (url, success, failure) {
	    this.request({'method': 'POST', 'url': url, 'success': success, 'failure': failure});
	},
	getJSON: function (url, success, failure) {
	    this.get(url, function (json) {
		success(JSON.parse(json));
	    } , failure);
	},
	getXML: function (url, success, failure) {/* noop */}
    };

    OAuth.signatureMethod = {
	'HMAC-SHA1': function (consumer_secret, token_secret, signature_base) {
	    var passphrase, signature;

	    consumer_secret = OAuth.urlEncode(consumer_secret);
	    token_secret = OAuth.urlEncode(token_secret || '');

	    passphrase = consumer_secret + '&' + token_secret;
	    signature = HMAC(SHA1(), passphrase, signature_base);

	    return btoa(signature);
	}
    };

    function toHeaderString(params) {
	var arr = [], i;
	for (i in params) {
            if (params[i] && params[i] != undefined && params[i] != '') {
		arr.push(i + '="' + OAuth.urlEncode(params[i]) + '"');
            }
	}

	return arr.join(', ');
    }

    function toSignatureBaseString(method, url, header_parms, query_params) {
	var arr = [], i;

	for (i in header_parms) {
	    if (header_parms[i] && header_parms[i] != undefined) {
		arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(header_parms[i]+''));
	    }
	}
	for (i in query_params) {
	    if (query_params[i] && query_params[i] != undefined) {
		if (!oauth_header_params[i]) {
		    arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(query_params[i]+''));
		}
	    }
	}

        return [
            method,
            OAuth.urlEncode(url),
            OAuth.urlEncode(arr.sort().join('&'))
        ].join('&');
    }

    function getTimestamp() {
        return parseInt(+new Date / 1000, 10); // use short form of getting a timestamp
    };

    function getNonce(key_length) {
        key_length = key_length || 64;

        var key_bytes = key_length / 8, value = '', key_iter = key_bytes / 4,
	    key_remainder = key_bytes % 4, i,
	    chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
                     '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
                     '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
                     '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
                     '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
                     '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
                     '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
                     '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
                     '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
                     '7A', '7B', '7C', '7D', '7E'];

        for (i = 0; i < key_iter; i++) {
            value += chars[rand()] + chars[rand()] + chars[rand()]+ chars[rand()];
        }

        // handle remaing bytes
        for (i = 0; i < key_remainder; i++) {
            value += chars[rand()];
        }

	function rand() {
	    return Math.floor(Math.random() * chars.length);
	}

	return value;
    };

    OAuth.urlEncode = function (string) {
	if (!string) return '';

	string = string + '';
	var reserved_chars = / |!|\*|"|'|\(|\)|;|:|@|&|=|\+|\$|,|\/|\?|%|#|\[|\]|<|>|{|}|\||\\|`|\^/,
        str_len = string.length, i, string_arr = string.split('');

	for (i = 0; i < str_len; i++) {
	    if (string_arr[i].match(reserved_chars)) {
		string_arr[i] = '%' + (string_arr[i].charCodeAt(0)).toString(16).toUpperCase();
	    }
	}

	return string_arr.join('');
    };

    OAuth.urlDecode = function (string){
	if (!string) return '';

	return string.replace(/%[a-fA-F0-9]{2}/ig, function (match) {
	    return String.fromCharCode(parseInt(match.replace('%', ''), 16));
	});
    };<commit_msg>updated documentation comments<commit_after>    /** @const */ var OAUTH_VERSION = '1.0';

    /**
     * OAuth
     *
     * @constructor
     */
    function OAuth(options) {
        if (window === this) {
            return new OAuth(options);
        }

        return this.init(options);
    }

    OAuth.prototype = {
        init: function (options) {
            var oauth = {
                debug: options.debug || false,
                consumerKey: options.consumerKey,
                consumerSecret: options.consumerSecret,
                accessTokenSecret: options.accessTokenSecret || '',
                signatureMethod: options.signatureMethod || 'HMAC-SHA1'
            };

            /**
             * Makes an authenticated http request
             *
             * @param options {object}
             *      method {string} ['GET', 'POST', 'PUT', ...]
             *      url {string} A valid http(s) url
             *      data {object} A key value paired object of data
             *                      example: {'q':'foobar'}
             *                      for GET this will append a query string
             *      headers {object} A key value paired object of additional headers
             *      success {function} callback for a sucessful request
             *      failure {function} callback for a failed request
             */
            this.request = function (options) {
                var method, url, data, headers, success, failure, xhr, i,
                    headerParams, signatureMethod, signatureString, signature,
                    query = [];

                method = options.method || 'GET';
                url = options.url;
                data = options.data || {};
                headers = options.headers || {};
                success = options.success || function (data) {};
                failure = options.failure || function () {};

                if (oauth.debug) {
                    netscape.security.PrivilegeManager
                        .enablePrivilege("UniversalBrowserRead UniversalBrowserWrite");
                }

                xhr = new XMLHttpRequest();
                xhr.onreadystatechange = function () {
                    if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 0)) {
                        success({text: xhr.responseText, xml: xhr.responseXML});
                    } else if(xhr.readyState == 4 && xhr.status != 200 && xhr.status != 0) {
                        failure({text: xhr.responseText, xml: xhr.responseXML});
                    }
                };

                for(i in data) {
                    query.push(i + '=' + data[i]);
                }

                headerParams = {
                    'oauth_callback': 'oob',
                    'oauth_consumer_key': oauth.consumerKey,
                    'oauth_token': oauth.accessTokenSecret,
                    'oauth_signature_method': oauth.signatureMethod,
                    'oauth_timestamp': getTimestamp(),
                    'oauth_nonce': getNonce(),
                    'oauth_verifier': oauth.verifier || '',
                    'oauth_version': OAUTH_VERSION
                };

                signatureMethod = oauth.signatureMethod;

                signatureString = toSignatureBaseString(method, url, headerParams, query);
                signature = OAuth['signatureMethod'][signatureMethod](oauth.consumerSecret, oauth.accessTokenSecret, signatureString);

                headerParams.oauth_signature = signature;

                query = query.sort().join('&');
                if(method == 'GET') {
                    if (query) {
                        url += '?' + query;
                    }
                    query = null;
                } else {
                    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
                }

                xhr.open(method, url, true);

                xhr.setRequestHeader('Authorization', 'OAuth ' + toHeaderString(headerParams));
                xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
                for (i in headers) {
                    xhr.setRequestHeader(i, headers[i]);
                }

                xhr.send(query);
            }
        },

        authenticate: function (options) {},
        deauthenticate: function (options) {},

        request: '',

        /**
         * Wrapper for GET OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        get: function (url, success, failure) {
            this.request({'url': url, 'success': success, 'failure': failure});
        },

        /**
         * Wrapper for POST OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param data {object} A key value paired object of data
         *                      example: {'q':'foobar'}
         *                      for GET this will append a query string
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        post: function (url, data, success, failure) {
            this.request({'method': 'POST', 'url': url, 'data': data, 'success': success, 'failure': failure});
        },

        /**
         * Wrapper to parse a JSON string and pass it to the callback
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        getJSON: function (url, success, failure) {
            this.get(url, function (data) {
                success(JSON.parse(data.text));
            } , failure);
        },

        /**
         * Wrapper for JSON pasrsed GET OAuth.request
         *
         * @param url {string} vaild http(s) url
         * @param success {function} callback for a successful request
         * @param failure {function} callback for a failed request
         */
        getXML: function (url, success, failure) {
            this.get(url, function (data) {
                success(data.xml);
            } , failure);
        }
    };

    OAuth.signatureMethod = {
        /**
         * Sign the request
         *
         * @param consumer_secret {string} the consumer secret
         * @param token_secret {string}  the token secret
         * @param signature_base {string}  the signature base string
         */
        'HMAC-SHA1': function (consumer_secret, token_secret, signature_base) {
            var passphrase, signature;

            consumer_secret = OAuth.urlEncode(consumer_secret);
            token_secret = OAuth.urlEncode(token_secret || '');

            passphrase = consumer_secret + '&' + token_secret;
            signature = HMAC(SHA1(), passphrase, signature_base);

            return OAuth.btoa(signature);
        }
    };

    /**
     * Get a string of the parameters for the OAuth Authorization header
     *
     * @param params {object} A key value paired object of data
     *                           example: {'q':'foobar'}
     *                           for GET this will append a query string
     */
    function toHeaderString(params) {
        var arr = [], i;

        for (i in params) {
            if (params[i] && params[i] != undefined && params[i] != '') {
                arr.push(i + '="' + OAuth.urlEncode(params[i]+'') + '"');
            }
        }

        return arr.sort().join(', ');
    }

    /**
     * Generate a signature base string for the request
     *
     * @param method {string} ['GET', 'POST', 'PUT', ...]
     * @param url {string} A valid http(s) url
     * @param header_params A key value paired object of additional headers
     * @param query_params {object} A key value paired object of data
     *                               example: {'q':'foobar'}
     *                               for GET this will append a query string
     */
    function toSignatureBaseString(method, url, header_parms, query_params) {
        var arr = [], i;

        for (i in header_parms) {
            if (header_parms[i] && header_parms[i] != undefined && header_parms[i] != '') {
                arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(header_parms[i]+''));
            }
        }

        for (i in query_params) {
            if (query_params[i] && query_params[i] != undefined && query_params[i] != '') {
                if (!oauth_header_params[i]) {
                    arr.push(OAuth.urlEncode(i) + '=' + OAuth.urlEncode(query_params[i]+''));
                }
            }
        }

        return [
            method,
            OAuth.urlEncode(url),
            OAuth.urlEncode(arr.sort().join('&'))
        ].join('&');
    }

    /**
     * Generate a timestamp for the request
     */
    function getTimestamp() {
        return parseInt(+new Date / 1000, 10); // use short form of getting a timestamp
    };

    /**
     * Generate a nonce for the request
     *
     * @param key_length {number} Optional nonce length
     */
    function getNonce(key_length) {
        key_length = key_length || 64;

        var key_bytes = key_length / 8, value = '', key_iter = key_bytes / 4,
        key_remainder = key_bytes % 4, i,
        chars = ['20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
                     '2A', '2B', '2C', '2D', '2E', '2F', '30', '31', '32', '33',
                     '34', '35', '36', '37', '38', '39', '3A', '3B', '3C', '3D',
                     '3E', '3F', '40', '41', '42', '43', '44', '45', '46', '47',
                     '48', '49', '4A', '4B', '4C', '4D', '4E', '4F', '50', '51',
                     '52', '53', '54', '55', '56', '57', '58', '59', '5A', '5B',
                     '5C', '5D', '5E', '5F', '60', '61', '62', '63', '64', '65',
                     '66', '67', '68', '69', '6A', '6B', '6C', '6D', '6E', '6F',
                     '70', '71', '72', '73', '74', '75', '76', '77', '78', '79',
                     '7A', '7B', '7C', '7D', '7E'];

        for (i = 0; i < key_iter; i++) {
            value += chars[rand()] + chars[rand()] + chars[rand()]+ chars[rand()];
        }

        // handle remaing bytes
        for (i = 0; i < key_remainder; i++) {
            value += chars[rand()];
        }

        function rand() {
            return Math.floor(Math.random() * chars.length);
        }

        return value;
    };

    /**
     * Url encode a string
     *
     * @param string {string} string to be url encoded
     */
    OAuth.urlEncode = function (string) {
        if (!string) return '';

        string = string + '';
        var reserved_chars = / |!|\*|"|'|\(|\)|;|:|@|&|=|\+|\$|,|\/|\?|%|#|\[|\]|<|>|{|}|\||\\|`|\^/,
            str_len = string.length, i, string_arr = string.split('');

        for (i = 0; i < str_len; i++) {
            if (string_arr[i].match(reserved_chars)) {
            string_arr[i] = '%' + (string_arr[i].charCodeAt(0)).toString(16).toUpperCase();
            }
        }

        return string_arr.join('');
    };

    /**
     * Url decode a string
     *
     * @param string {string} string to be url decoded
     */
    OAuth.urlDecode = function (string){
        if (!string) return '';

        return string.replace(/%[a-fA-F0-9]{2}/ig, function (match) {
            return String.fromCharCode(parseInt(match.replace('%', ''), 16));
        });
    };
<|endoftext|>


<commit_before>/* eslint-disable no-console */
/*
 0.OFF
 1.INFO
 2.WARN
 3.ERROR
 4.TRACE
 5.DEBUG
 6.ALL
 */

export default class Log {
    constructor(prefix) {
        this.prefix = prefix;
    }

    static level() {
        return process.env.MD_LOG_LEVEL || 'INFO,WARN,ERROR';
    }

    static slice(args) {
        return Array.prototype.slice.call(args, 0);
    }

    log(type, args) {
        console.log.apply(null, [`${type}  ${this.prefix}: `].concat(Log.slice(args)));
    }

    info(...args) {
        if (/INFO|ALL/i.test(Log.level())) {
            this.log('INFO', args);
        }
    }

    warn(...args) {
        if (/WARN|ALL/i.test(Log.level())) {
            this.log('WARN', args);
        }
    }

    error(...args) {
        if (/ERROR|ALL/i.test(Log.level())) {
            this.log('ERROR', args);
        }
    }

    debug(...args) {
        if (/DEBUG|ALL/i.test(Log.level())) {
            this.log('DEBUG', args);
        }
    }

    verbose(...args) {
        if (/VERBOSE|ALL/i.test(Log.level())) {
            this.log('VERBOSE', args);
        }
    }

    trace(...args) {
        if (/TRACE|ALL/i.test(Log.level())) {
            this.log('TRACE', args);
        }
    }
}
<commit_msg>Respect MD_LOG_LEVEL<commit_after>/* eslint-disable no-console */
/*
 0.OFF
 1.INFO
 2.WARN
 3.ERROR
 4.TRACE
 5.DEBUG
 6.ALL
 */

export default class Log {
    constructor(prefix) {
        this.prefix = prefix;
    }

    static level() {
        return process.env.MD_LOG_LEVEL || 'ALL';
    }

    static slice(args) {
        return Array.prototype.slice.call(args, 0);
    }

    log(type, args) {
        console.log.apply(null, [`${type}  ${this.prefix}: `].concat(Log.slice(args)));
    }

    info(...args) {
        if (/INFO|ALL/i.test(Log.level())) {
            this.log('INFO', args);
        }
    }

    warn(...args) {
        if (/WARN|ALL/i.test(Log.level())) {
            this.log('WARN', args);
        }
    }

    error(...args) {
        if (/ERROR|ALL/i.test(Log.level())) {
            this.log('ERROR', args);
        }
    }

    debug(...args) {
        if (/DEBUG|ALL/i.test(Log.level())) {
            this.log('DEBUG', args);
        }
    }

    verbose(...args) {
        if (/VERBOSE|ALL/i.test(Log.level())) {
            this.log('VERBOSE', args);
        }
    }

    trace(...args) {
        if (/TRACE|ALL/i.test(Log.level())) {
            this.log('TRACE', args);
        }
    }
}
<|endoftext|>


<commit_before>Package.describe({
  name: 'zodern:pure-admin',
  version: '0.7.0',
  summary: 'Isolated, customizable admin panel for Meteor',
  git: 'https://github.com/zodern/meteor-pure-admin.git',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.8.0.2');
  api.use(['ecmascript', 'dynamic-import']);
  api.use('svelte:compiler@3.6.7_1||4.0.0-beta.1');

  api.mainModule('./src/main.js', 'client');
  api.mainModule('./src/server.js', 'server');
});

Package.onTest(function(api) {
  api.use('tinytest');
  api.addFiles('pureadmin-tests.js');
});
<commit_msg>Release 0.7.1<commit_after>Package.describe({
  name: 'zodern:pure-admin',
  version: '0.7.1',
  summary: 'Isolated, customizable admin panel for Meteor',
  git: 'https://github.com/zodern/meteor-pure-admin.git',
  documentation: 'README.md'
});

Package.onUse(function(api) {
  api.versionsFrom('1.8.0.2');
  api.use(['ecmascript', 'dynamic-import']);
  api.use('svelte:compiler@3.6.7_1||4.0.0-beta.1');

  api.mainModule('./src/main.js', 'client');
  api.mainModule('./src/server.js', 'server');
});

Package.onTest(function(api) {
  api.use('tinytest');
  api.addFiles('pureadmin-tests.js');
});
<|endoftext|>


<commit_before>'use strict';

var Path = require('path');
var expect = require('unexpected')
  .clone()
  .installPlugin(require('unexpected-express'))
  .installPlugin(require('unexpected-sinon'));
var connect = require('connect');
var sinon = require('sinon');
var pitm = require('../lib/piggy-in-the-middle');
var babyTolk = require('baby-tolk');
var fs = require('fs');

function getPath(path) {
  return Path.join(process.cwd(), 'examples/site', path || '');
}

describe('middleware', function () {
  var app;
  var compileCount = 0;

  before(function() {
    app = connect().use(pitm(getPath()));

    // babyTolk.read is called on compile so spy on this to see if
    // compilation has taken place
    babyTolk.read = sinon.spy(babyTolk, 'read');
  });

  function expectCompiled() {
    compileCount = compileCount + 1;
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  function expectCached() {
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  describe('first requests', function() {

    it('should compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCompiled());
    });

    it('should compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/styles/main.styl'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

    it('should compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCompiled());
    });

    it('should have coffee sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/scripts/main.coffee'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

  });

  describe('second (cached) requests', function() {

    it('should *not* compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCached());
    });

  });

  describe('third request (after editing a watched file)', function() {
    var pathToCSS = getPath('styles/main.styl');
    var fileContents;

    before(function(done) {
      fs.readFile(pathToCSS, function(err, contents){
        fileContents = contents;
        fs.writeFile(pathToCSS, contents+' ', function() {
          setTimeout(done, 1000); // Allow 1 second for chokidar to notice the change
        });
      });
    });

    after(function(done) {
      // Revert file back to original state
      fs.writeFile(pathToCSS, fileContents, done);
    });

    it('should recompile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });
  });

  describe('fourth (cached) requests', function() {
    // Probably bit pointless doing this again but just in case

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

  });

});
<commit_msg>Tests: forgot that reverting the file back will invalidate the cache<commit_after>'use strict';

var Path = require('path');
var expect = require('unexpected')
  .clone()
  .installPlugin(require('unexpected-express'))
  .installPlugin(require('unexpected-sinon'));
var connect = require('connect');
var sinon = require('sinon');
var pitm = require('../lib/piggy-in-the-middle');
var babyTolk = require('baby-tolk');
var fs = require('fs');

function getPath(path) {
  return Path.join(process.cwd(), 'examples/site', path || '');
}

describe('middleware', function () {
  var app;
  var compileCount = 0;

  before(function() {
    app = connect().use(pitm(getPath()));

    // babyTolk.read is called on compile so spy on this to see if
    // compilation has taken place
    babyTolk.read = sinon.spy(babyTolk, 'read');
  });

  function expectCompiled() {
    compileCount = compileCount + 1;
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  function expectCached() {
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  describe('first requests', function() {

    it('should compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCompiled());
    });

    it('should compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/styles/main.styl'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

    it('should compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCompiled());
    });

    it('should have coffee sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/scripts/main.coffee'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

  });

  describe('second (cached) requests', function() {

    it('should *not* compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCached());
    });

  });

  describe('third requests (after editing a watched file)', function() {
    var pathToCSS = getPath('styles/main.styl');
    var fileContents;

    before(function(done) {
      fs.readFile(pathToCSS, function(err, contents){
        fileContents = contents;
        // Add space to end of file
        // Allow 250ms for chokidar to notice the change
        fs.writeFile(pathToCSS, contents+' ', () => setTimeout(done, 250));
      });
    });

    after(function(done) {
      // Revert file back to original contents
      // Allow 250ms for chokidar to notice the change
      fs.writeFile(pathToCSS, fileContents, () => setTimeout(done, 250));
    });

    it('should recompile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });
  });

  describe('fourth requests', function() {
    // Probably bit pointless doing this again but just in case
    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });
  });

  describe('fifth (cached) requests', function() {
    // Probably bit pointless doing this again but just in case
    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });
  });

});
<|endoftext|>


<commit_before>var Table = require('cli-table');

var data,
    log = console.log,
    fileColWidth = 50;

data = {
    assertions: [],
    tests: [],
    summaries: [],
    coverages: []
};

/**
 * Add data to the log report.
 *
 * @param {String} type
 * @param {Object} obj
 * @return {Array}
 */
exports.add = function(type, obj) {
    if (obj) {
        data[type].push(obj);
    }
    return data[type];
};

/**
 * Get global tests stats in unified format
 */
exports.stats = function() {
    var stats = {
        files: 0,
        assertions: 0,
        failed: 0,
        passed: 0,
        runtime: 0
    };

    data.summaries.forEach(function(file) {
        stats.files++;
        stats.assertions += file.total;
        stats.failed += file.failed;
        stats.passed += file.passed;
        stats.runtime += file.runtime;
    });

    stats.tests = data.tests.length;

    stats.coverage = {
        files: 0,
        statements: { covered: 0, total: 0 },
        branches: { covered: 0, total: 0 },
        functions: { covered: 0, total: 0 },
        lines: { covered: 0, total: 0 }
    };

    data.coverages.forEach(function(file) {
        stats.coverage.files++;
        stats.coverage.statements.covered += file.statements.covered;
        stats.coverage.statements.total += file.statements.total;
        stats.coverage.branches.covered += file.branches.covered;
        stats.coverage.branches.total += file.branches.total;
        stats.coverage.functions.covered += file.functions.covered;
        stats.coverage.functions.total += file.functions.total;
        stats.coverage.lines.covered += file.lines.covered;
        stats.coverage.lines.total += file.lines.total;
    });

    return stats;
};

/**
 * Reset global stats data
 */
exports.reset = function() {
    data = {
        assertions: [],
        tests: [],
        summaries: [],
        coverages: []
    };
};

var print = exports.print = {};

print.assertions = function() {
    var table,
        currentModule, module,
        currentTest, test;

    table = new Table({
        head: ['Module', 'Test', 'Assertion', 'Result']
    });

    data.assertions.forEach(function(data) {
        // just easier to read the table
        if (data.module === currentModule) {
            module = '';
        } else {
            module = currentModule = data.module;
        }

        // just easier to read the table
        if (data.test === currentTest) {
            test = '';
        } else {
            test = currentTest = data.test;
        }

        table.push([module, test, data.message || '', data.result ? 'ok' : 'fail']);
    });

    log('\nAssertions:\n' + table.toString());
};

print.errors = function() {
    var errors = [];

    data.assertions.forEach(function(data) {
        if (!data.result) {
            errors.push(data);
        }
    });

    if (errors.length) {
        log('\n\nErrors:');
        errors.forEach(function(data) {
            log('\nModule: ' + data.module + ' Test: ' + data.test);
            if (data.message) {
                log(data.message);
            }

            if (data.source) {
                log(data.source);
            }

            if (data.expected != null || data.actual != null) {
                //it will be an error if data.expected !== data.actual, but if they're
                //both undefined, it means that they were just not filled out because
                //no assertions were hit (likely due to code error that would have been logged as source or message).
                log('Actual value:');
                log(data.actual);
                log('Expected value:');
                log(data.expected);
            }
        });
    }
};

print.tests = function() {
    var table,
        currentModule, module;

    table = new Table({
        head: ['Module', 'Test', 'Failed', 'Passed', 'Total']
    });

    data.tests.forEach(function(data) {
        // just easier to read the table
        if (data.module === currentModule) {
            module = '';
        } else {
            module = currentModule = data.module;
        }

        table.push([module, data.name, data.failed, data.passed, data.total]);
    });

    log('\nTests:\n' + table.toString());
};

// truncate file name
function truncFile(code) {
    if (code && code.length > fileColWidth) {
        code = '...' + code.slice(code.length - fileColWidth + 3);
    }
    return code;
}

print.summary = function() {
    var table;

    table = new Table({
        head: ['File', 'Failed', 'Passed', 'Total', 'Runtime']
    });

    data.summaries.forEach(function(data) {
        table.push([truncFile(data.code), data.failed, data.passed, data.total, data.runtime]);
    });

    log('\nSummary:\n' + table.toString());
};

print.globalSummary = function() {
    var table,
        data = exports.stats();

    table = new Table({
        head: ['Files', 'Tests', 'Assertions', 'Failed', 'Passed', 'Runtime']
    });

    table.push([data.files, data.tests, data.assertions, data.failed,
        data.passed, data.runtime]);

    log('\nGlobal summary:\n' + table.toString());
};

function getMet(metric) {
    function percent(covered, total) {
        var tmp;
        if (total > 0) {
            tmp = 1000 * 100 * covered / total + 5;
            return Math.floor(tmp / 10) / 100;
        } else {
            return 100.00;
        }
    }
    if (!metric.pct) metric.pct = percent(metric.covered, metric.total);
    return metric.pct + '% (' + metric.covered + '/' + metric.total + ')';
}

print.coverage = function() {
    var table;

    if (!data.coverages.length) return;

    table = new Table({
        head: ['File', 'Statements', 'Branches', 'Functions', 'Lines']
    });

    data.coverages.forEach(function(coverage) {
        table.push([
            truncFile(coverage.code),
            getMet(coverage.statements),
            getMet(coverage.branches),
            getMet(coverage.functions),
            getMet(coverage.lines)]);
    });

    log('\nCoverage:\n' + table.toString());
};

print.globalCoverage = function() {
    var coverage, table;

    if (!data.coverages.length) return;

    coverage = exports.stats().coverage;
    table = new Table({
        head: ['Files', 'Statements', 'Branches', 'Functions', 'Lines']
    });

    table.push([
        coverage.files,
        getMet(coverage.statements),
        getMet(coverage.branches),
        getMet(coverage.functions),
        getMet(coverage.lines)
    ]);

    log('\nGlobal coverage:\n' + table.toString());
};
<commit_msg>Sort by module before logging the assertions and the tests<commit_after>var Table = require('cli-table');

var data,
    log = console.log,
    fileColWidth = 50;

data = {
    assertions: [],
    tests: [],
    summaries: [],
    coverages: []
};

/**
 * Add data to the log report.
 *
 * @param {String} type
 * @param {Object} obj
 * @return {Array}
 */
exports.add = function(type, obj) {
    if (obj) {
        data[type].push(obj);
    }
    return data[type];
};

/**
 * Get global tests stats in unified format
 */
exports.stats = function(complete) {
    var stats = {
        files: 0,
        assertions: 0,
        failed: 0,
        passed: 0,
        runtime: 0
    };

    data.summaries.forEach(function(file) {
        stats.files++;
        stats.assertions += file.total;
        stats.failed += file.failed;
        stats.passed += file.passed;
        stats.runtime += file.runtime;
    });

    if(complete){
        stats.tests = data.tests;
    } else{
        stats.tests = data.tests.length;
    }

    stats.coverage = {
        files: 0,
        statements: { covered: 0, total: 0 },
        branches: { covered: 0, total: 0 },
        functions: { covered: 0, total: 0 },
        lines: { covered: 0, total: 0 }
    };

    data.coverages.forEach(function(file) {
        stats.coverage.files++;
        stats.coverage.statements.covered += file.statements.covered;
        stats.coverage.statements.total += file.statements.total;
        stats.coverage.branches.covered += file.branches.covered;
        stats.coverage.branches.total += file.branches.total;
        stats.coverage.functions.covered += file.functions.covered;
        stats.coverage.functions.total += file.functions.total;
        stats.coverage.lines.covered += file.lines.covered;
        stats.coverage.lines.total += file.lines.total;
    });

    return stats;
};

/**
 * Reset global stats data
 */
exports.reset = function() {
    data = {
        assertions: [],
        tests: [],
        summaries: [],
        coverages: []
    };
};

var print = exports.print = {};

print.assertions = function() {
    var table,
        currentModule, module,
        currentTest, test;

    table = new Table({
        head: ['Module', 'Test', 'Assertion', 'Result']
    });

    data.assertions.sort(function(a,b){if(a.module > b.module) return 1; if(a.module < b.module) return -1; return 0});

    data.assertions.forEach(function(data) {
        // just easier to read the table
        if (data.module === currentModule) {
            module = '';
        } else {
            module = currentModule = data.module;
        }

        // just easier to read the table
        if (data.test === currentTest) {
            test = '';
        } else {
            test = currentTest = data.test;
        }

        table.push([module, test, data.message || '', data.result ? 'ok' : 'fail']);
    });

    log('\nAssertions:\n' + table.toString());
};

print.errors = function() {
    var errors = [];

    data.assertions.forEach(function(data) {
        if (!data.result) {
            errors.push(data);
        }
    });

    if (errors.length) {
        log('\n\nErrors:');
        errors.forEach(function(data) {
            log('\nModule: ' + data.module + ' Test: ' + data.test);
            if (data.message) {
                log(data.message);
            }

            if (data.source) {
                log(data.source);
            }

            if (data.expected != null || data.actual != null) {
                //it will be an error if data.expected !== data.actual, but if they're
                //both undefined, it means that they were just not filled out because
                //no assertions were hit (likely due to code error that would have been logged as source or message).
                log('Actual value:');
                log(data.actual);
                log('Expected value:');
                log(data.expected);
            }
        });
    }
};

print.tests = function() {
    var table,
        currentModule, module;

    table = new Table({
        head: ['Module', 'Test', 'Failed', 'Passed', 'Total']
    });

    data.tests.sort(function(a,b){if(a.module > b.module) return 1; if(a.module < b.module) return -1; return 0});

    data.tests.forEach(function(data) {
        // just easier to read the table
        if (data.module === currentModule) {
            module = '';
        } else {
            module = currentModule = data.module;
        }

        table.push([module, data.name, data.failed, data.passed, data.total]);
    });

    log('\nTests:\n' + table.toString());
};

// truncate file name
function truncFile(code) {
    if (code && code.length > fileColWidth) {
        code = '...' + code.slice(code.length - fileColWidth + 3);
    }
    return code;
}

print.summary = function() {
    var table;

    table = new Table({
        head: ['File', 'Failed', 'Passed', 'Total', 'Runtime']
    });

    data.summaries.forEach(function(data) {
        table.push([truncFile(data.code), data.failed, data.passed, data.total, data.runtime]);
    });

    log('\nSummary:\n' + table.toString());
};

print.globalSummary = function() {
    var table,
        data = exports.stats();

    table = new Table({
        head: ['Files', 'Tests', 'Assertions', 'Failed', 'Passed', 'Runtime']
    });

    table.push([data.files, data.tests, data.assertions, data.failed,
        data.passed, data.runtime]);

    log('\nGlobal summary:\n' + table.toString());
};

function getMet(metric) {
    function percent(covered, total) {
        var tmp;
        if (total > 0) {
            tmp = 1000 * 100 * covered / total + 5;
            return Math.floor(tmp / 10) / 100;
        } else {
            return 100.00;
        }
    }
    if (!metric.pct) metric.pct = percent(metric.covered, metric.total);
    return metric.pct + '% (' + metric.covered + '/' + metric.total + ')';
}

print.coverage = function() {
    var table;

    if (!data.coverages.length) return;

    table = new Table({
        head: ['File', 'Statements', 'Branches', 'Functions', 'Lines']
    });

    data.coverages.forEach(function(coverage) {
        table.push([
            truncFile(coverage.code),
            getMet(coverage.statements),
            getMet(coverage.branches),
            getMet(coverage.functions),
            getMet(coverage.lines)]);
    });

    log('\nCoverage:\n' + table.toString());
};

print.globalCoverage = function() {
    var coverage, table;

    if (!data.coverages.length) return;

    coverage = exports.stats().coverage;
    table = new Table({
        head: ['Files', 'Statements', 'Branches', 'Functions', 'Lines']
    });

    table.push([
        coverage.files,
        getMet(coverage.statements),
        getMet(coverage.branches),
        getMet(coverage.functions),
        getMet(coverage.lines)
    ]);

    log('\nGlobal coverage:\n' + table.toString());
};
<|endoftext|>


<commit_before>Package.describe({
  summary: 'BDD testing APIs for the space:event-sourcing package',
  name: 'space:testing-event-sourcing',
  version: '3.0.0',
  git: 'https://github.com/meteor-space/testing-event-sourcing.git',
  debugOnly: true
});

Package.onUse(function(api) {

  api.versionsFrom('1.2.0.1');

  api.use([
    'coffeescript',
    'ecmascript',
    'underscore',
    'space:base@4.0.0',
    'practicalmeteor:munit@2.1.5'
  ]);

  api.addFiles([
    'source/allow-to-ignore-some-struct-types.js',
    'source/aggregates-bdd-api.coffee'
  ], 'server');

});

Package.onTest(function(api) {

  api.use([
    'coffeescript',
    'ecmascript',
    'check',
    'space:base@4.0.0',
    'space:testing@3.0.1',
    'practicalmeteor:munit@2.1.5'
  ]);

  api.addFiles([
  ], 'server');

});
<commit_msg>Fix package configuration<commit_after>Package.describe({
  summary: 'BDD testing APIs for the space:event-sourcing package',
  name: 'space:testing-event-sourcing',
  version: '3.0.0',
  git: 'https://github.com/meteor-space/testing-event-sourcing.git',
  debugOnly: true
});

Package.onUse(function(api) {

  api.versionsFrom('1.2.0.1');

  api.use([
    'coffeescript',
    'ecmascript',
    'check',
    'underscore',
    'space:base@4.0.0',
    'practicalmeteor:munit@2.1.5'
  ]);

  api.addFiles([
    'source/allow-to-ignore-some-struct-types.js',
    'source/aggregates-bdd-api.coffee'
  ], 'server');

});

Package.onTest(function(api) {

  api.use([
    'coffeescript',
    'ecmascript',
    'check',
    'space:base@4.0.0',
    'space:testing@3.0.1',
    'practicalmeteor:munit@2.1.5'
  ]);

  api.addFiles([
  ], 'server');

});
<|endoftext|>


<commit_before>'use strict';

var Path = require('path');
var expect = require('unexpected')
  .clone()
  .installPlugin(require('unexpected-express'))
  .installPlugin(require('unexpected-sinon'));
var connect = require('connect');
var sinon = require('sinon');
var pitm = require('../lib/piggy-in-the-middle');
var babyTolk = require('baby-tolk');
var fs = require('fs');

function getPath(path) {
  return Path.join(process.cwd(), 'examples/site', path || '');
}

describe('middleware', function () {
  var app;
  var compileCount = 0;

  before(function() {
    app = connect().use(pitm(getPath()));

    // babyTolk.read is called on compile so spy on this to see if
    // compilation has taken place
    babyTolk.read = sinon.spy(babyTolk, 'read');
  });

  function expectCompiled() {
    compileCount = compileCount + 1;
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  function expectCached() {
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  describe('first requests', function() {

    it('should compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCompiled());
    });

    it('should compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/styles/main.styl'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

    it('should compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCompiled());
    });

    it('should have coffee sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/scripts/main.coffee'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

  });

  describe('second (cached) requests', function() {

    it('should *not* compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCached());
    });

  });

  describe('third request (after editing a watched file)', function() {
    var pathToCSS = getPath('styles/main.styl');
    var fileContents;

    before(function(done) {
      fs.readFile(pathToCSS, function(err, contents){
        fileContents = contents;
        fs.writeFile(pathToCSS, contents+' ', function() {
          setTimeout(done, 250); // Allow 250ms for chokidar to notice the change
        });
      });
    });

    after(function(done) {
      // Revert file back to original state
      fs.writeFile(pathToCSS, fileContents, done);
    });

    it('should recompile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });
  });

  describe('fourth (cached) requests', function() {
    // Probably bit pointless doing this again but just in case

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

  });

});
<commit_msg>Seems like travis-ci needs more than 250ms for chokidar to notice changes sometimes, so bumping it to 1 second<commit_after>'use strict';

var Path = require('path');
var expect = require('unexpected')
  .clone()
  .installPlugin(require('unexpected-express'))
  .installPlugin(require('unexpected-sinon'));
var connect = require('connect');
var sinon = require('sinon');
var pitm = require('../lib/piggy-in-the-middle');
var babyTolk = require('baby-tolk');
var fs = require('fs');

function getPath(path) {
  return Path.join(process.cwd(), 'examples/site', path || '');
}

describe('middleware', function () {
  var app;
  var compileCount = 0;

  before(function() {
    app = connect().use(pitm(getPath()));

    // babyTolk.read is called on compile so spy on this to see if
    // compilation has taken place
    babyTolk.read = sinon.spy(babyTolk, 'read');
  });

  function expectCompiled() {
    compileCount = compileCount + 1;
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  function expectCached() {
    return expect(babyTolk.read, 'was called times', compileCount);
  }

  describe('first requests', function() {

    it('should compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCompiled());
    });

    it('should compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });

    it('should have styl sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/styles/main.styl'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

    it('should compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCompiled());
    });

    it('should have coffee sourcemap', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js.map' },
        response: {
          statusCode: 200,
          headers: {
            'Content-Type': 'application/json'
          },
          body: {
            sources: ['/scripts/main.coffee'],
            mappings: expect.it('to be non-empty')
          }
        }
      }).then(() => expectCached()); // Loading source files also caches source map
    });

  });

  describe('second (cached) requests', function() {

    it('should *not* compile jade file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/' },
        response: {
          headers: {
            'Content-Type': 'text/html; charset=UTF-8'
          },
          body: expect.it('to contain', '
Piggy In The Middle
')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

    it('should *not* compile coffee file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/scripts/main.js' },
        response: {
          headers: {
            'Content-Type': 'application/javascript',
            'X-SourceMap': 'main.js.map'
          },
          body: expect.it('to contain', 'console.log(')
        }
      }).then(() => expectCached());
    });

  });

  describe('third request (after editing a watched file)', function() {
    var pathToCSS = getPath('styles/main.styl');
    var fileContents;

    before(function(done) {
      fs.readFile(pathToCSS, function(err, contents){
        fileContents = contents;
        fs.writeFile(pathToCSS, contents+' ', function() {
          setTimeout(done, 1000); // Allow 1 second for chokidar to notice the change
        });
      });
    });

    after(function(done) {
      // Revert file back to original state
      fs.writeFile(pathToCSS, fileContents, done);
    });

    it('should recompile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCompiled());
    });
  });

  describe('fourth (cached) requests', function() {
    // Probably bit pointless doing this again but just in case

    it('should *not* compile styl file', function() {
      return expect(app, 'to yield exchange', {
        request: { url: '/styles/main.css' },
        response: {
          headers: {
            'Content-Type': 'text/css; charset=UTF-8',
            'X-SourceMap': 'main.css.map'
          },
          body: expect.it('to contain', 'body {')
        }
      }).then(() => expectCached());
    });

  });

});
<|endoftext|>


<commit_before>import eyes from 'eyes'

const styles = Object.assign({}, eyes.defaults.styles, {
  all:      'grey',
  string:   'red',
  label:    'yellow',
  key :     'cyan',
  bool:     'green',
  regexp:   'magenta',
})

const inspect = eyes.inspector({ styles })

export default function log(...args) {
  inspect(...args.reverse())
}
<commit_msg>remove mutation on logs arguments<commit_after>import eyes from 'eyes'

const styles = Object.assign({}, eyes.defaults.styles, {
  all:      'grey',
  string:   'red',
  label:    'yellow',
  key :     'cyan',
  bool:     'green',
  regexp:   'magenta',
})

const inspect = eyes.inspector({ styles })

export default function log(...args) {
  inspect(...args.slice().reverse())
}
<|endoftext|>


<commit_before>Package.describe({
  name: 'ndemoreau:azimulti-core',
  summary: 'Azimuth multilangual CMS core code',
  version: '0.4.1',
  //git: 'https://github.com/ndemoreau/azimuth-core/ '
});

Package.on_use(function (api) {
  // Include some core meteor smart packages
  api.use([ 'service-configuration@1.0.2', 'accounts-ui@1.1.2', 'accounts-base@1.1.2', 'accounts-password@1.0.4', 'underscore@1.0.1', 'jquery@1.0.1',
    'less@1.0.11', 'spiderable@1.0.5', 'standard-app-packages@1.0.3'], 'client');

  // Use the meteorite smart packages specified in smart.json
  api.use(['alanning:roles@1.2.13', 'iron:router@1.0.1', 'multiply:iron-router-progress@1.0.0', 'mrt:numeral@0.0.1', 'cfs:standard-packages@0.0.2', 'cfs:filesystem@0.0.0'], 'client');

  // And ensure we have the packages we need server-side available to the server
  api.use(['accounts-base', 'accounts-password', 'spiderable', 'alanning:roles@1.2.13', 'cfs:standard-packages', 'cfs:filesystem'], 'server');

  api.addFiles('server/server_init.js', 'server');

  api.add_files('client/collections.js', 'client');
  api.add_files('client/metadata_fields.js', 'client');
  api.add_files('client/lib/vendor/jquery.nestable.js', 'client');
  api.add_files('client/lib/vendor/noty/jquery.noty.js', 'client');
  api.add_files('client/lib/vendor/noty/layouts/inline.js', 'client');
  api.add_files('client/lib/vendor/noty/layouts/bottomRight.js', 'client');
  api.add_files('client/lib/vendor/noty/themes/default.js', 'client');
  api.add_files('client/css/vendor/jquery.cleditor.css', 'client');
  api.add_files('client/lib/vendor/jquery.cleditor.js', 'client');
  api.add_files('client/lib/vendor/jquery.cleditor.image.js', 'client');
  api.add_files('client/lib/vendor/jquery.cleditor.file.js', 'client');
  api.add_files('client/lib/vendor/jquery.selectize.js', 'client');
  api.add_files('client/css/vendor/selectize.css', 'client');
  api.add_files('client/css/vendor/selectize.default.css', 'client');
  api.add_files('client/lib/helpers.js', 'client');
  api.add_files('client/lib/registry.js', 'client');
  api.add_files('client/lib/utils.js', 'client');
  api.add_files('client/lib/admin_panel.js', 'client');
  api.add_files('client/router.js', 'client');
  api.add_files('client/css/vendor/nestable.css', 'client');
  api.add_files('client/css/main.less', 'client');
  api.add_files('client/css/admin_panel.less', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.eot', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.svg', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.ttf', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.woff', 'client');
  api.add_files('client/css/dripicons.css', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.eot', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.svg', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.ttf', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.woff', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.eot', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.svg', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.ttf', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.woff', 'client');
  api.add_files('client/blocks/ten_column/ten_column.js', 'client');
  api.add_files('client/blocks/eight_column/eight_column.js', 'client');
  api.add_files('client/blocks/four_column/four_column.js', 'client');
  api.add_files('client/blocks/six_column/six_column.js', 'client');
  api.add_files('client/blocks/three_column/three_column.js', 'client');
  api.add_files('client/blocks/block_with_title/block_with_title.js', 'client');
  api.add_files('client/blocks/two_column/two_column.js', 'client');
  api.add_files('client/pages/home_page/home_page.js', 'client');
  api.add_files('client/pages/page_default/page_default.js', 'client');
  api.add_files('client/pages/sidebar_left/sidebar_left.js', 'client');
  api.add_files('client/pages/sidebar_right/sidebar_right.js', 'client');
  api.add_files('client/views/accounts/account_buttons.js', 'client');
  api.add_files('client/views/accounts/error.js', 'client');
  api.add_files('client/views/accounts/forgot_password.js', 'client');
  api.add_files('client/views/accounts/login.js', 'client');
  api.add_files('client/views/accounts/sign_up.js', 'client');
  api.add_files('client/views/accounts/social.js', 'client');
  api.add_files('client/views/admin/admin_users.html', 'client');
  api.add_files('client/views/admin/admin_users.js', 'client');
  api.add_files('client/views/admin/navigation.html', 'client');
  api.add_files('client/views/admin/navigation.js', 'client');
  api.add_files('client/views/admin/site_settings.html', 'client');
  api.add_files('client/views/admin/site_settings.js', 'client');
  api.add_files('client/views/admin/pages/page_settings.html', 'client');
  api.add_files('client/views/admin/pages/page_settings.js', 'client');
  api.add_files('client/views/admin/pages/new_page.html', 'client');
  api.add_files('client/views/admin/pages/new_page.js', 'client');
  api.add_files('client/views/admin/pages/page_template_selector.html', 'client');
  api.add_files('client/views/admin/pages/page_template_selector.js', 'client');
  api.add_files('client/views/admin/assets.html', 'client');
  api.add_files('client/views/admin/assets.js', 'client');
  api.add_files('client/views/admin/admin_panel.html', 'client');
  api.add_files('client/views/admin/admin_panel.js', 'client');
  api.add_files('client/views/admin/blocks/block_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_edit.js', 'client');
  api.add_files('client/views/admin/blocks/block_tag_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_tag_edit.js', 'client');
  api.add_files('client/views/admin/blocks/block_type_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_type_edit.js', 'client');
  api.add_files('client/views/admin/blocks/block_zone_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_zone_edit.js', 'client');
  api.add_files('client/views/block_set.html', 'client');
  api.add_files('client/views/block_set.js', 'client');
  api.add_files('client/views/block_display.js', 'client');
  api.add_files('client/views/footer.js', 'client');
  api.add_files('client/views/form/wysiwyg.html', 'client');
  api.add_files('client/views/form/wysiwyg.js', 'client');
  api.add_files('client/views/form/tag.html', 'client');
  api.add_files('client/views/form/tag.js', 'client');
  api.add_files('client/views/form/metadata.html', 'client');
  api.add_files('client/views/form/metadata.js', 'client');
  api.add_files('client/views/form/text.html', 'client');
  api.add_files('client/views/form/textarea.html', 'client');
  api.add_files('client/views/header.js', 'client');
  api.add_files('client/views/layout.js', 'client');
  api.add_files('client/index.html', 'client');
  api.add_files('img/azimuth-logo.png', 'client');
  api.add_files('img/buttons.png', 'client');
  api.add_files('img/toolbar.gif', 'client');
  api.add_files('img/file.gif', 'client');
  api.add_files('img/image.gif', 'client');
  api.add_files('img/file-large.png', 'client');

});<commit_msg>change github address<commit_after>Package.describe({
  name: 'ndemoreau:azimulti-core',
  summary: 'Azimuth multilangual CMS core code',
  version: '0.4.1',
  git: 'https://github.com/ndemoreau/azimulti-core/ '
});

Package.on_use(function (api) {
  // Include some core meteor smart packages
  api.use([ 'service-configuration@1.0.2', 'accounts-ui@1.1.2', 'accounts-base@1.1.2', 'accounts-password@1.0.4', 'underscore@1.0.1', 'jquery@1.0.1',
    'less@1.0.11', 'spiderable@1.0.5', 'standard-app-packages@1.0.3'], 'client');

  // Use the meteorite smart packages specified in smart.json
  api.use(['alanning:roles@1.2.13', 'iron:router@1.0.1', 'multiply:iron-router-progress@1.0.0', 'mrt:numeral@0.0.1', 'cfs:standard-packages@0.0.2', 'cfs:filesystem@0.0.0'], 'client');

  // And ensure we have the packages we need server-side available to the server
  api.use(['accounts-base', 'accounts-password', 'spiderable', 'alanning:roles@1.2.13', 'cfs:standard-packages', 'cfs:filesystem'], 'server');

  api.addFiles('server/server_init.js', 'server');

  api.add_files('client/collections.js', 'client');
  api.add_files('client/metadata_fields.js', 'client');
  api.add_files('client/lib/vendor/jquery.nestable.js', 'client');
  api.add_files('client/lib/vendor/noty/jquery.noty.js', 'client');
  api.add_files('client/lib/vendor/noty/layouts/inline.js', 'client');
  api.add_files('client/lib/vendor/noty/layouts/bottomRight.js', 'client');
  api.add_files('client/lib/vendor/noty/themes/default.js', 'client');
  api.add_files('client/css/vendor/jquery.cleditor.css', 'client');
  api.add_files('client/lib/vendor/jquery.cleditor.js', 'client');
  api.add_files('client/lib/vendor/jquery.cleditor.image.js', 'client');
  api.add_files('client/lib/vendor/jquery.cleditor.file.js', 'client');
  api.add_files('client/lib/vendor/jquery.selectize.js', 'client');
  api.add_files('client/css/vendor/selectize.css', 'client');
  api.add_files('client/css/vendor/selectize.default.css', 'client');
  api.add_files('client/lib/helpers.js', 'client');
  api.add_files('client/lib/registry.js', 'client');
  api.add_files('client/lib/utils.js', 'client');
  api.add_files('client/lib/admin_panel.js', 'client');
  api.add_files('client/router.js', 'client');
  api.add_files('client/css/vendor/nestable.css', 'client');
  api.add_files('client/css/main.less', 'client');
  api.add_files('client/css/admin_panel.less', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.eot', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.svg', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.ttf', 'client');
  api.add_files('client/css/fonts/dripicons/dripicons.woff', 'client');
  api.add_files('client/css/dripicons.css', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.eot', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.svg', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.ttf', 'client');
  api.add_files('client/css/fonts/raleway/raleway-regular.woff', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.eot', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.svg', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.ttf', 'client');
  api.add_files('client/css/fonts/raleway/raleway-bold.woff', 'client');
  api.add_files('client/blocks/ten_column/ten_column.js', 'client');
  api.add_files('client/blocks/eight_column/eight_column.js', 'client');
  api.add_files('client/blocks/four_column/four_column.js', 'client');
  api.add_files('client/blocks/six_column/six_column.js', 'client');
  api.add_files('client/blocks/three_column/three_column.js', 'client');
  api.add_files('client/blocks/block_with_title/block_with_title.js', 'client');
  api.add_files('client/blocks/two_column/two_column.js', 'client');
  api.add_files('client/pages/home_page/home_page.js', 'client');
  api.add_files('client/pages/page_default/page_default.js', 'client');
  api.add_files('client/pages/sidebar_left/sidebar_left.js', 'client');
  api.add_files('client/pages/sidebar_right/sidebar_right.js', 'client');
  api.add_files('client/views/accounts/account_buttons.js', 'client');
  api.add_files('client/views/accounts/error.js', 'client');
  api.add_files('client/views/accounts/forgot_password.js', 'client');
  api.add_files('client/views/accounts/login.js', 'client');
  api.add_files('client/views/accounts/sign_up.js', 'client');
  api.add_files('client/views/accounts/social.js', 'client');
  api.add_files('client/views/admin/admin_users.html', 'client');
  api.add_files('client/views/admin/admin_users.js', 'client');
  api.add_files('client/views/admin/navigation.html', 'client');
  api.add_files('client/views/admin/navigation.js', 'client');
  api.add_files('client/views/admin/site_settings.html', 'client');
  api.add_files('client/views/admin/site_settings.js', 'client');
  api.add_files('client/views/admin/pages/page_settings.html', 'client');
  api.add_files('client/views/admin/pages/page_settings.js', 'client');
  api.add_files('client/views/admin/pages/new_page.html', 'client');
  api.add_files('client/views/admin/pages/new_page.js', 'client');
  api.add_files('client/views/admin/pages/page_template_selector.html', 'client');
  api.add_files('client/views/admin/pages/page_template_selector.js', 'client');
  api.add_files('client/views/admin/assets.html', 'client');
  api.add_files('client/views/admin/assets.js', 'client');
  api.add_files('client/views/admin/admin_panel.html', 'client');
  api.add_files('client/views/admin/admin_panel.js', 'client');
  api.add_files('client/views/admin/blocks/block_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_edit.js', 'client');
  api.add_files('client/views/admin/blocks/block_tag_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_tag_edit.js', 'client');
  api.add_files('client/views/admin/blocks/block_type_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_type_edit.js', 'client');
  api.add_files('client/views/admin/blocks/block_zone_edit.html', 'client');
  api.add_files('client/views/admin/blocks/block_zone_edit.js', 'client');
  api.add_files('client/views/block_set.html', 'client');
  api.add_files('client/views/block_set.js', 'client');
  api.add_files('client/views/block_display.js', 'client');
  api.add_files('client/views/footer.js', 'client');
  api.add_files('client/views/form/wysiwyg.html', 'client');
  api.add_files('client/views/form/wysiwyg.js', 'client');
  api.add_files('client/views/form/tag.html', 'client');
  api.add_files('client/views/form/tag.js', 'client');
  api.add_files('client/views/form/metadata.html', 'client');
  api.add_files('client/views/form/metadata.js', 'client');
  api.add_files('client/views/form/text.html', 'client');
  api.add_files('client/views/form/textarea.html', 'client');
  api.add_files('client/views/header.js', 'client');
  api.add_files('client/views/layout.js', 'client');
  api.add_files('client/index.html', 'client');
  api.add_files('img/azimuth-logo.png', 'client');
  api.add_files('img/buttons.png', 'client');
  api.add_files('img/toolbar.gif', 'client');
  api.add_files('img/file.gif', 'client');
  api.add_files('img/image.gif', 'client');
  api.add_files('img/file-large.png', 'client');

});<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { getAvatarUrlFromUsername } from 'meteor/rocketchat:utils';

const recursiveRemove = (msg, deep = 1) => {
	if (!msg) {
		return;
	}

	if (deep > RocketChat.settings.get('Message_QuoteChainLimit')) {
		delete msg.attachments;
		return msg;
	}

	msg.attachments = Array.isArray(msg.attachments) ? msg.attachments.map(
		(nestedMsg) => recursiveRemove(nestedMsg, deep + 1)
	) : null;

	return msg;
};

const shouldAdd = (attachments, attachment) => !attachments.some(({ message_link }) => message_link && message_link === attachment.message_link);

Meteor.methods({
	pinMessage(message, pinnedAt) {
		const userId = Meteor.userId();
		if (!userId) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'pinMessage',
			});
		}

		if (!RocketChat.settings.get('Message_AllowPinning')) {
			throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', {
				method: 'pinMessage',
				action: 'Message_pinning',
			});
		}

		if (!RocketChat.authz.hasPermission(Meteor.userId(), 'pin-message')) {
			throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'pinMessage' });
		}

		const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } });
		if (!subscription) {
			return false;
		}

		let originalMessage = RocketChat.models.Messages.findOneById(message._id);
		if (originalMessage == null || originalMessage._id == null) {
			throw new Meteor.Error('error-invalid-message', 'Message you are pinning was not found', {
				method: 'pinMessage',
				action: 'Message_pinning',
			});
		}

		// If we keep history of edits, insert a new message to store history information
		if (RocketChat.settings.get('Message_KeepHistory')) {
			RocketChat.models.Messages.cloneAndSaveAsHistoryById(message._id);
		}
		const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
		const me = RocketChat.models.Users.findOneById(userId);

		originalMessage.pinned = true;
		originalMessage.pinnedAt = pinnedAt || Date.now;
		originalMessage.pinnedBy = {
			_id: userId,
			username: me.username,
		};

		originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage);

		RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned);
		if (RocketChat.isTheLastMessage(room, message)) {
			RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned);
		}

		const attachments = [];

		if (Array.isArray(originalMessage.attachments)) {
			originalMessage.attachments.forEach((attachment) => {
				if (!attachment.message_link || shouldAdd(attachments, attachment)) {
					attachments.push(attachment);
				}
			});
		}

		return RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser(
			'message_pinned',
			originalMessage.rid,
			'',
			me,
			{
				attachments: [
					{
						text: originalMessage.msg,
						author_name: originalMessage.u.username,
						author_icon: getAvatarUrlFromUsername(
							originalMessage.u.username
						),
						ts: originalMessage.ts,
						attachments: recursiveRemove(attachments),
					},
				],
			}
		);
	},
	unpinMessage(message) {
		if (!Meteor.userId()) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'unpinMessage',
			});
		}

		if (!RocketChat.settings.get('Message_AllowPinning')) {
			throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', {
				method: 'unpinMessage',
				action: 'Message_pinning',
			});
		}

		if (!RocketChat.authz.hasPermission(Meteor.userId(), 'pin-message')) {
			throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'pinMessage' });
		}

		const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } });
		if (!subscription) {
			return false;
		}

		let originalMessage = RocketChat.models.Messages.findOneById(message._id);

		if (originalMessage == null || originalMessage._id == null) {
			throw new Meteor.Error('error-invalid-message', 'Message you are unpinning was not found', {
				method: 'unpinMessage',
				action: 'Message_pinning',
			});
		}

		// If we keep history of edits, insert a new message to store history information
		if (RocketChat.settings.get('Message_KeepHistory')) {
			RocketChat.models.Messages.cloneAndSaveAsHistoryById(originalMessage._id);
		}

		const me = RocketChat.models.Users.findOneById(Meteor.userId());
		originalMessage.pinned = false;
		originalMessage.pinnedBy = {
			_id: Meteor.userId(),
			username: me.username,
		};
		originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage);
		const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
		if (RocketChat.isTheLastMessage(room, message)) {
			RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned);
		}

		return RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned);
	},
});
<commit_msg>Regression: Fix message pinning (#13213)<commit_after>import { Meteor } from 'meteor/meteor';
import { RocketChat } from 'meteor/rocketchat:lib';
import { getAvatarUrlFromUsername } from 'meteor/rocketchat:utils';

const recursiveRemove = (msg, deep = 1) => {
	if (!msg) {
		return;
	}

	if (deep > RocketChat.settings.get('Message_QuoteChainLimit')) {
		delete msg.attachments;
		return msg;
	}

	msg.attachments = Array.isArray(msg.attachments) ? msg.attachments.map(
		(nestedMsg) => recursiveRemove(nestedMsg, deep + 1)
	) : null;

	return msg;
};

const shouldAdd = (attachments, attachment) => !attachments.some(({ message_link }) => message_link && message_link === attachment.message_link);

Meteor.methods({
	pinMessage(message, pinnedAt) {
		const userId = Meteor.userId();
		if (!userId) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'pinMessage',
			});
		}

		if (!RocketChat.settings.get('Message_AllowPinning')) {
			throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', {
				method: 'pinMessage',
				action: 'Message_pinning',
			});
		}

		if (!RocketChat.authz.hasPermission(Meteor.userId(), 'pin-message', message.rid)) {
			throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'pinMessage' });
		}

		const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } });
		if (!subscription) {
			return false;
		}

		let originalMessage = RocketChat.models.Messages.findOneById(message._id);
		if (originalMessage == null || originalMessage._id == null) {
			throw new Meteor.Error('error-invalid-message', 'Message you are pinning was not found', {
				method: 'pinMessage',
				action: 'Message_pinning',
			});
		}

		// If we keep history of edits, insert a new message to store history information
		if (RocketChat.settings.get('Message_KeepHistory')) {
			RocketChat.models.Messages.cloneAndSaveAsHistoryById(message._id);
		}
		const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
		const me = RocketChat.models.Users.findOneById(userId);

		originalMessage.pinned = true;
		originalMessage.pinnedAt = pinnedAt || Date.now;
		originalMessage.pinnedBy = {
			_id: userId,
			username: me.username,
		};

		originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage);

		RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned);
		if (RocketChat.isTheLastMessage(room, message)) {
			RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned);
		}

		const attachments = [];

		if (Array.isArray(originalMessage.attachments)) {
			originalMessage.attachments.forEach((attachment) => {
				if (!attachment.message_link || shouldAdd(attachments, attachment)) {
					attachments.push(attachment);
				}
			});
		}

		return RocketChat.models.Messages.createWithTypeRoomIdMessageAndUser(
			'message_pinned',
			originalMessage.rid,
			'',
			me,
			{
				attachments: [
					{
						text: originalMessage.msg,
						author_name: originalMessage.u.username,
						author_icon: getAvatarUrlFromUsername(
							originalMessage.u.username
						),
						ts: originalMessage.ts,
						attachments: recursiveRemove(attachments),
					},
				],
			}
		);
	},
	unpinMessage(message) {
		if (!Meteor.userId()) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'unpinMessage',
			});
		}

		if (!RocketChat.settings.get('Message_AllowPinning')) {
			throw new Meteor.Error('error-action-not-allowed', 'Message pinning not allowed', {
				method: 'unpinMessage',
				action: 'Message_pinning',
			});
		}

		if (!RocketChat.authz.hasPermission(Meteor.userId(), 'pin-message', message.rid)) {
			throw new Meteor.Error('not-authorized', 'Not Authorized', { method: 'pinMessage' });
		}

		const subscription = RocketChat.models.Subscriptions.findOneByRoomIdAndUserId(message.rid, Meteor.userId(), { fields: { _id: 1 } });
		if (!subscription) {
			return false;
		}

		let originalMessage = RocketChat.models.Messages.findOneById(message._id);

		if (originalMessage == null || originalMessage._id == null) {
			throw new Meteor.Error('error-invalid-message', 'Message you are unpinning was not found', {
				method: 'unpinMessage',
				action: 'Message_pinning',
			});
		}

		// If we keep history of edits, insert a new message to store history information
		if (RocketChat.settings.get('Message_KeepHistory')) {
			RocketChat.models.Messages.cloneAndSaveAsHistoryById(originalMessage._id);
		}

		const me = RocketChat.models.Users.findOneById(Meteor.userId());
		originalMessage.pinned = false;
		originalMessage.pinnedBy = {
			_id: Meteor.userId(),
			username: me.username,
		};
		originalMessage = RocketChat.callbacks.run('beforeSaveMessage', originalMessage);
		const room = Meteor.call('canAccessRoom', message.rid, Meteor.userId());
		if (RocketChat.isTheLastMessage(room, message)) {
			RocketChat.models.Rooms.setLastMessagePinned(room._id, originalMessage.pinnedBy, originalMessage.pinned);
		}

		return RocketChat.models.Messages.setPinnedByIdAndUserId(originalMessage._id, originalMessage.pinnedBy, originalMessage.pinned);
	},
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'SignInView',
  extends: 'foam.u2.View',

  documentation: 'Sign In View',

  implements: [
    'foam.mlang.Expressions'
  ],

  imports: [
    'auth',
    'loginSuccess',
    'signUpEnabled',
    'stack',
    'user'
  ],

  exports: [ 'as data' ],

  requires: [
    'foam.comics.DAOCreateControllerView',
    'foam.nanos.auth.resetPassword.EmailView',
    'foam.nanos.auth.User',
    'foam.u2.dialog.NotificationMessage'
  ],

  css: `
    ^{
      width: 490px;
      margin: auto;
    }
    ^ .sign-in-container{
      padding-top: 20px;
      width: 490px;
      height: 230px;
      border-radius: 2px;
      background-color: #ffffff;
    }
    ^ p{
      display: inline-block;
    }
    ^ .full-width-button{
      width: 90%;
      height: 40px;
      border-radius: 2px;
      border: solid 1px #59a5d5;
      margin: 0 auto;
      background-color: #59aadd;
      text-align: center;
      line-height: 40px;
      cursor: pointer;
      color: #ffffff;
      margin-top: 10px;
    }
    ^ .full-width-input{
      width: 90%;
      height: 40px;
      margin-left: 5%;
      margin-bottom: 15px;
      outline: none;
      padding: 10px;
    }
    ^ .full-width-input-password {
      /* Required for password input field */
      width: 90%;
      height: 40px;
      margin-left: 5%;
      margin-bottom: 15px;
      outline: none;
      padding: 10px;
    }
    ^ .label{
      height: 16px;
      font-family: Roboto;
      font-size: 14px;
      font-weight: 300;
      text-align: left;
      color: #093649;
      margin-bottom: 8px;
      margin-left: 25px;
    }
    ^ .foam-u2-ActionView-signIn{
      width: 90%;
      margin-left: 25px;
    }
    ^ .foam-u2-ActionView-signIn > span{
      position: relative;
      top: -5px;
    }
    ^ .link{
      margin-left: 2px;
      color: #59a5d5;
      cursor: pointer;
    }
    ^ .forgot-link{
      margin-left: 2px;
      color: #59a5d5;
      cursor: pointer;
      float: right;
    }
  `,

  properties: [
    {
      class: 'String',
      name: 'email'
    },
    {
      class: 'Password',
      name: 'password',
      view: { class: 'foam.u2.view.PasswordView' }
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      var self = this;

      this.addClass(this.myClass())
      .start()
        .start('h1').add('Sign In').end()
        .start('form').addClass('sign-in-container')
          .start().addClass('label').add('Email Address').end()
          .start(this.EMAIL).addClass('full-width-input').end()
          .start().addClass('label').add('Password').end()
          .add(this.PASSWORD)
          .start(this.SIGN_IN).addClass('full-width-button').end()
        .end()
        .start('div')
          .callIf(this.signUpEnabled, function() {
            this.start('p').add('Don\'t have an account?').end()
            .start('p').style({ 'margin-left': '2px' }).addClass('link')
              .add('Sign up.')
              .on('click', self.signUp)
            .end();
          })
          .start('p').style({ 'margin-left': '150px' }).addClass('forgot-link')
            .add('Forgot Password?')
            .on('click', function() {
              self.stack.push({ class: 'foam.nanos.auth.resetPassword.EmailView' })
            })
          .end()
        .end()
      .end();
    }
  ],

  listeners: [
    function signUp() {
      var self = this;
      var view = foam.u2.ListCreateController.CreateController.create(
        null,
        this.__context__.createSubContext({
          detailView: foam.nanos.auth.SignUpView,
          back: this.stack.back.bind(this.stack),
          dao: this.userDAO,
          factory: function() {
            return self.User.create();
          },
          showActions: false
        }));
      this.stack.push(view);
    }
  ],

  actions: [
    {
      name: 'signIn',
      label: 'Sign In',
      code: function(X) {
        var self = this;

        if ( ! this.email ) {
          this.add(this.NotificationMessage.create({ message: 'Please enter an email address', type: 'error' }));
          return;
        }

        if ( ! this.password ) {
          this.add(this.NotificationMessage.create({ message: 'Please enter a password', type: 'error' }));
          return;
        }

        this.auth.loginByEmail(null, this.email, this.password).then(function(user) {
          if ( user && user.twoFactorEnabled ) {
            self.loginSuccess = false;
            self.user.copyFrom(user);
            self.stack.push({ class: 'foam.nanos.auth.twofactor.TwoFactorSignInView' });
          } else {
            self.loginSuccess = user ? true : false;
            self.user.copyFrom(user);
            self.add(self.NotificationMessage.create({ message: 'Login Successful.' }));
          }
        }).catch(function(a) {
          self.add(self.NotificationMessage.create({ message: a.message + '. Please try again.', type: 'error' }));
        });
      }
    }
  ]
});
<commit_msg>Removed Please try again message from sign in view<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.auth',
  name: 'SignInView',
  extends: 'foam.u2.View',

  documentation: 'Sign In View',

  implements: [
    'foam.mlang.Expressions'
  ],

  imports: [
    'auth',
    'loginSuccess',
    'signUpEnabled',
    'stack',
    'user'
  ],

  exports: [ 'as data' ],

  requires: [
    'foam.comics.DAOCreateControllerView',
    'foam.nanos.auth.resetPassword.EmailView',
    'foam.nanos.auth.User',
    'foam.u2.dialog.NotificationMessage'
  ],

  css: `
    ^{
      width: 490px;
      margin: auto;
    }
    ^ .sign-in-container{
      padding-top: 20px;
      width: 490px;
      height: 230px;
      border-radius: 2px;
      background-color: #ffffff;
    }
    ^ p{
      display: inline-block;
    }
    ^ .full-width-button{
      width: 90%;
      height: 40px;
      border-radius: 2px;
      border: solid 1px #59a5d5;
      margin: 0 auto;
      background-color: #59aadd;
      text-align: center;
      line-height: 40px;
      cursor: pointer;
      color: #ffffff;
      margin-top: 10px;
    }
    ^ .full-width-input{
      width: 90%;
      height: 40px;
      margin-left: 5%;
      margin-bottom: 15px;
      outline: none;
      padding: 10px;
    }
    ^ .full-width-input-password {
      /* Required for password input field */
      width: 90%;
      height: 40px;
      margin-left: 5%;
      margin-bottom: 15px;
      outline: none;
      padding: 10px;
    }
    ^ .label{
      height: 16px;
      font-family: Roboto;
      font-size: 14px;
      font-weight: 300;
      text-align: left;
      color: #093649;
      margin-bottom: 8px;
      margin-left: 25px;
    }
    ^ .foam-u2-ActionView-signIn{
      width: 90%;
      margin-left: 25px;
    }
    ^ .foam-u2-ActionView-signIn > span{
      position: relative;
      top: -5px;
    }
    ^ .link{
      margin-left: 2px;
      color: #59a5d5;
      cursor: pointer;
    }
    ^ .forgot-link{
      margin-left: 2px;
      color: #59a5d5;
      cursor: pointer;
      float: right;
    }
  `,

  properties: [
    {
      class: 'String',
      name: 'email'
    },
    {
      class: 'Password',
      name: 'password',
      view: { class: 'foam.u2.view.PasswordView' }
    }
  ],

  methods: [
    function initE() {
      this.SUPER();
      var self = this;

      this.addClass(this.myClass())
      .start()
        .start('h1').add('Sign In').end()
        .start('form').addClass('sign-in-container')
          .start().addClass('label').add('Email Address').end()
          .start(this.EMAIL).addClass('full-width-input').end()
          .start().addClass('label').add('Password').end()
          .add(this.PASSWORD)
          .start(this.SIGN_IN).addClass('full-width-button').end()
        .end()
        .start('div')
          .callIf(this.signUpEnabled, function() {
            this.start('p').add('Don\'t have an account?').end()
            .start('p').style({ 'margin-left': '2px' }).addClass('link')
              .add('Sign up.')
              .on('click', self.signUp)
            .end();
          })
          .start('p').style({ 'margin-left': '150px' }).addClass('forgot-link')
            .add('Forgot Password?')
            .on('click', function() {
              self.stack.push({ class: 'foam.nanos.auth.resetPassword.EmailView' })
            })
          .end()
        .end()
      .end();
    }
  ],

  listeners: [
    function signUp() {
      var self = this;
      var view = foam.u2.ListCreateController.CreateController.create(
        null,
        this.__context__.createSubContext({
          detailView: foam.nanos.auth.SignUpView,
          back: this.stack.back.bind(this.stack),
          dao: this.userDAO,
          factory: function() {
            return self.User.create();
          },
          showActions: false
        }));
      this.stack.push(view);
    }
  ],

  actions: [
    {
      name: 'signIn',
      label: 'Sign In',
      code: function(X) {
        var self = this;

        if ( ! this.email ) {
          this.add(this.NotificationMessage.create({ message: 'Please enter an email address', type: 'error' }));
          return;
        }

        if ( ! this.password ) {
          this.add(this.NotificationMessage.create({ message: 'Please enter a password', type: 'error' }));
          return;
        }

        this.auth.loginByEmail(null, this.email, this.password).then(function(user) {
          if ( user && user.twoFactorEnabled ) {
            self.loginSuccess = false;
            self.user.copyFrom(user);
            self.stack.push({ class: 'foam.nanos.auth.twofactor.TwoFactorSignInView' });
          } else {
            self.loginSuccess = user ? true : false;
            self.user.copyFrom(user);
            self.add(self.NotificationMessage.create({ message: 'Login Successful.' }));
          }
        }).catch(function(a) {
          self.add(self.NotificationMessage.create({ message: a.message, type: 'error' }));
        });
      }
    }
  ]
});
<|endoftext|>