<commit_before>/*
 * Sample response returned from the Interceptor,
 * {
        "reqId": "10891",
        "request": {
            "frameId": 2, "method": "POST",
            "parentFrameId": 0, "requestBodyType": "formData", //formData or rawData
            "requestBody": {},
            "requestId": "10891",
            "tabId": 161, "timestamp": 1393432789060.789,
            "type": "xmlhttprequest",
            "url": "",
            "requestHeaders": [ ],
        },
        "type": "capturedRequest"
    }
 */

describe('Interceptor Library', function() {

	beforeEach(function() {
		this.chromeEventOrder = function(args) {
			onBeforeRequest(args);
			onBeforeSendHeaders(args);
			onSendHeaders(args);
		}
		sinon.stub(chrome.runtime ,'sendMessage');
	});

	it("By Default no request should be returned to Postman.", function() {
        var request = getNewRequest(1);
		this.chromeEventOrder(request);
		expect(chrome.runtime.sendMessage.called).toBe(false);
	});

	it("Should Return a request, back to postman.", function() {
		appOptions.isCaptureStateEnabled = true;
        var request = getNewRequest(1);
		this.chromeEventOrder(request);

		expect(chrome.runtime.sendMessage.args[0][0]).toBe('POSTMAN');
		expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.reqId).toBe(1);
		expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.request).toBe(request);

		appOptions.isCaptureStateEnabled = false;
	});

    it("Filter should allow all domains by default", function() {
        appOptions.isCaptureStateEnabled = true;
        expect(appOptions.filterRequestUrl).toBe(".*");
        var request = getNewRequest(1);
        this.chromeEventOrder(request);

        expect(chrome.runtime.sendMessage.called).toBe(true);
        expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.reqId).toBe(1);
    });

    it("Filter should block incorrect domains when enabled", function() {
        appOptions.isCaptureStateEnabled = true;
        appOptions.filterRequestUrl = "google";
        var request = getNewRequest(1);
        this.chromeEventOrder(request);

        expect(chrome.runtime.sendMessage.called).toBe(false);
    });

    it("Filter should allow correct domains when enabled", function() {
        appOptions.isCaptureStateEnabled = true;
        appOptions.filterRequestUrl = "google";
        var request = getNewRequest(2);
        request.url = "www.google.com/someXHR";
        this.chromeEventOrder(request);

        expect(chrome.runtime.sendMessage.called).toBe(true);
        expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.reqId).toBe(2);
    });

	afterEach(function() {
		this.chromeEventOrder = null;
		chrome.runtime.sendMessage.restore();
	});

});

describe("Postman functionality", function() {

    it("Toggle should send correct message to interceptor", function() {
        var postmanMessage = { postmanMessage: { type: "detectExtension"} };
        var sender = { id: 1 };
        var sendResponse = sinon.stub();
        onExternalMessage(postmanMessage, sender, sendResponse);

        expect(sendResponse.called).toBe(true);
        expect(sendResponse.callCount).toBe(2);
        expect(sendResponse.args[0][0].result).toBe("Ok, got your message");
    });

    it("Interceptor should recieve XHR request from Postman", function() {
        var postmanMessage = { postmanMessage: { type: "xhrRequest"} };
        var sender = { id: 1 };
        var sendResponse = sinon.stub();
        sinon.stub(window, 'sendXhrRequest');
        onExternalMessage(postmanMessage, sender, sendResponse);

        expect(sendResponse.called).toBe(true);
        expect(sendResponse.calledOnce).toBe(true);
        expect(sendResponse.args[0][0].result).toBe("Ok, got your message");
        expect(sendXhrRequest.called).toBe(true);
    });
});
<commit_msg>Renamed tests for readability<commit_after>/*
 * Sample response returned from the Interceptor,
 * {
        "reqId": "10891",
        "request": {
            "frameId": 2, "method": "POST",
            "parentFrameId": 0, "requestBodyType": "formData", //formData or rawData
            "requestBody": {},
            "requestId": "10891",
            "tabId": 161, "timestamp": 1393432789060.789,
            "type": "xmlhttprequest",
            "url": "",
            "requestHeaders": [ ],
        },
        "type": "capturedRequest"
    }
 */

describe('When Interceptor sends a captured request to Postman', function() {

	beforeEach(function() {
		this.chromeEventOrder = function(args) {
			onBeforeRequest(args);
			onBeforeSendHeaders(args);
			onSendHeaders(args);
		}
		sinon.stub(chrome.runtime ,'sendMessage');
	});

	it("By Default no request should be returned to Postman.", function() {
        var request = getNewRequest(1);
		this.chromeEventOrder(request);
		expect(chrome.runtime.sendMessage.called).toBe(false);
	});

	it("Should Return a request, back to postman.", function() {
		appOptions.isCaptureStateEnabled = true;
        var request = getNewRequest(1);
		this.chromeEventOrder(request);

		expect(chrome.runtime.sendMessage.args[0][0]).toBe('POSTMAN');
		expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.reqId).toBe(1);
		expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.request).toBe(request);

		appOptions.isCaptureStateEnabled = false;
	});

    it("Filter should allow all domains by default", function() {
        appOptions.isCaptureStateEnabled = true;
        expect(appOptions.filterRequestUrl).toBe(".*");
        var request = getNewRequest(1);
        this.chromeEventOrder(request);

        expect(chrome.runtime.sendMessage.called).toBe(true);
        expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.reqId).toBe(1);
    });

    it("Filter should block incorrect domains when enabled", function() {
        appOptions.isCaptureStateEnabled = true;
        appOptions.filterRequestUrl = "google";
        var request = getNewRequest(1);
        this.chromeEventOrder(request);

        expect(chrome.runtime.sendMessage.called).toBe(false);
    });

    it("Filter should allow correct domains when enabled", function() {
        appOptions.isCaptureStateEnabled = true;
        appOptions.filterRequestUrl = "google";
        var request = getNewRequest(2);
        request.url = "www.google.com/someXHR";
        this.chromeEventOrder(request);

        expect(chrome.runtime.sendMessage.called).toBe(true);
        expect(chrome.runtime.sendMessage.args[0][1].postmanMessage.reqId).toBe(2);
    });

	afterEach(function() {
		this.chromeEventOrder = null;
		chrome.runtime.sendMessage.restore();
	});

});

describe("When Postman sends a message to Interceptor", function() {

    it("Toggle should send correct message to interceptor", function() {
        var postmanMessage = getPostmanMessage("detectExtension");
        console.log(postmanMessage);
        var sender = { id: 1 };
        var sendResponse = sinon.stub();
        onExternalMessage(postmanMessage, sender, sendResponse);

        expect(sendResponse.called).toBe(true);
        expect(sendResponse.callCount).toBe(2);
        expect(sendResponse.args[0][0].result).toBe("Ok, got your message");
    });

    it("Interceptor should recieve XHR request from Postman", function() {
        var postmanMessage = getPostmanMessage("xhrRequest");
        console.log(postmanMessage);
        var sender = { id: 1 };
        var sendResponse = sinon.stub();
        sinon.stub(window, 'sendXhrRequest');
        onExternalMessage(postmanMessage, sender, sendResponse);

        expect(sendResponse.called).toBe(true);
        expect(sendResponse.calledOnce).toBe(true);
        expect(sendResponse.args[0][0].result).toBe("Ok, got your message");
        expect(sendXhrRequest.called).toBe(true);
        expect(sendXhrRequest.args[0][0].headers[0]["key"]).toBe("Postman-Token");
    });

});
<|endoftext|>


<commit_before>'use strict';
var util = {
  /**
   * Make your URL absolute.
   * @memberof util
   * @param {String} url The URL to convert to absolute.
   * @returns {String} the absolute URL including host/protocol.
   */
  getAbsoluteURL: function(url) {
    var a = window.document.createElement('a');
    a.href = url;
    return a.href;
  },
  /**
   * Get the hostname from a URL
   * @memberof util
   * @param {String} url The URL.
   * @returns {String} the hostname from the URL.
   */
  getHostname: function(url) {
    var a = window.document.createElement('a');
    a.href = url;
    return a.hostname;
  },
  /**
   * Checks if an element exist in an array.
   *
   * @memberof util
   * @param {String} element the element.
   * @param {Array} array the array.
   * @returns {Boolean} true if the element exist.
   */
  exists: function(element, array) {
    return array.some(function(e) {
      return e === element;
    });
  },
  /**
   * Returns an array filter function for finding tags with an attribute specified value, matching case insensitive.
   * @param attributeName the name of the attribute to look for (html attribute values are always case insensitive).
   * @param attributeValue the value to match against, ignoring case.
   * @returns {Function} function that can be passed to Array#filter
   */
  caseInsensitiveAttributeValueFilter: function(attributeName, attributeValue) {
    return function(item) {
      var attribute = item.getAttribute(attributeName) || '';
      if (attribute.toLowerCase() === attributeValue.toLowerCase()) {
        return item;
      }
      return undefined;
    };
  },
  /**
   * Is the connection used for the main document using HTTP/2? Only
   * works in Chrome and Firefox Nightly + browsers that supports Resource Timing
   * API v2.
   * @memberof util
   * @returns {Boolean} true if the connection is HTTP/2
   */
  isHTTP2: function() {
    var type = util.getConnectionType().toLowerCase();
    return type === 'h2' || type.startsWith('spdy');
  },
  /**
   * Get the connection type used for the main document. Only
   * works in Chrome and Firefox Nightly + browsers that supports Resource Timing
   * API v2.
   * @memberof util
   * @returns {String} http/1 or h2 for http 1 and 2 respectively. 'unknown' if browser lacks api to determine it.
   */
  getConnectionType: function() {
    // it's easy in Chrome
    if (window.chrome) {
      return window.chrome.loadTimes().connectionInfo;
    }
    // if you support resource timing v2
    // it's easy kind of easy too
    else if (window.performance && window.performance.getEntriesByType &&
      window.performance.getEntriesByType('resource')) {
      var resources = window.performance.getEntriesByType('resource');
      // now we "only" need to know if it is v2
      if (resources.length > 1 && resources[0].nextHopProtocol) {
        // if it's the same domain, say it's ok
        var host = document.domain;

        for (var i = 0, len = resources.length; i < len; i += 1) {
          if (host === util.getHostname(resources[i].name)) {
            return resources[i].nextHopProtocol;
          }
        }
      }
    }
    return 'unknown';
  },
  /**
   * Get Javascript requests that are loaded synchronously. All URLs are absolute.
   * @memberof util
   * @param {Object} parentElement the parent element that has all the scripts.
   * @returns {Array} an array with the URL to each Javascript file that is loaded sync.
   */
  getSynchJSFiles: function(parentElement) {
    var scripts = Array.prototype.slice.call(parentElement.getElementsByTagName('script'));

    return scripts.filter(function(s) {
        return ((!s.async) && s.src);
      })
      .map(function(s) {
        return util.getAbsoluteURL(s.src)
      });
  },
  /**
   * Get Javascript requests that are loaded asynchronously. All URLs are absolute.
   * @memberof util
   * @param {Object} parentElement the parent element that has all the scripts.
   * @returns {Array} an array with the URL to each Javascript file that are loaded async.
   */
  getAsynchJSFiles: function(parentElement) {
    var scripts = Array.prototype.slice.call(parentElement.getElementsByTagName('script'));

    return scripts.filter(function(s) {
        return (s.async && s.src);
      })
      .map(function(s) {
        return util.getAbsoluteURL(s.src)
      });
  },
  /**
   * Get Resource Hints hrefs by type
   * @memberof util
   * @param {String} type the name of the Resources hint: dns-prefetch, preconnect, prefetch, prerender
   * @returns {Array} an array of matching hrefs
   */
  getResourceHintsHrefs: function(type) {
    var links = Array.prototype.slice.call(window.document.head.getElementsByTagName('link'));

    return links.filter(function(link) {
        return (link.rel === type);
      })
      .map(function(link) {
        return link.href
      });
  },
  /**
   * Get CSS requests. All URLs are absolute.
   * @memberof util
   * @param {Object} parentElement the parent element that has all the scripts.
   * @returns {Array} an array with the URL to each CSS file that is loaded sync.
   */
  getCSSFiles: function(parentElement) {
    var links = Array.prototype.slice.call(parentElement.getElementsByTagName('link'));

    return links.filter(function(link) {
        // make sure we skip data:
        return (link.rel === 'stylesheet' && link.href.indexOf('http') > -1);
      })
      .map(function(link) {
        return util.getAbsoluteURL(link.href)
      });
  }
};
<commit_msg>get plural<commit_after>'use strict';
var util = {
  /**
   * Make your URL absolute.
   * @memberof util
   * @param {String} url The URL to convert to absolute.
   * @returns {String} the absolute URL including host/protocol.
   */
  getAbsoluteURL: function(url) {
    var a = window.document.createElement('a');
    a.href = url;
    return a.href;
  },
  /**
   * Get the hostname from a URL
   * @memberof util
   * @param {String} url The URL.
   * @returns {String} the hostname from the URL.
   */
  getHostname: function(url) {
    var a = window.document.createElement('a');
    a.href = url;
    return a.hostname;
  },
  /**
   * Checks if an element exist in an array.
   *
   * @memberof util
   * @param {String} element the element.
   * @param {Array} array the array.
   * @returns {Boolean} true if the element exist.
   */
  exists: function(element, array) {
    return array.some(function(e) {
      return e === element;
    });
  },
  /**
   * Returns an array filter function for finding tags with an attribute specified value, matching case insensitive.
   * @param attributeName the name of the attribute to look for (html attribute values are always case insensitive).
   * @param attributeValue the value to match against, ignoring case.
   * @returns {Function} function that can be passed to Array#filter
   */
  caseInsensitiveAttributeValueFilter: function(attributeName, attributeValue) {
    return function(item) {
      var attribute = item.getAttribute(attributeName) || '';
      if (attribute.toLowerCase() === attributeValue.toLowerCase()) {
        return item;
      }
      return undefined;
    };
  },
  /**
   * Is the connection used for the main document using HTTP/2? Only
   * works in Chrome and Firefox Nightly + browsers that supports Resource Timing
   * API v2.
   * @memberof util
   * @returns {Boolean} true if the connection is HTTP/2
   */
  isHTTP2: function() {
    var type = util.getConnectionType().toLowerCase();
    return type === 'h2' || type.startsWith('spdy');
  },
  /**
   * Get the connection type used for the main document. Only
   * works in Chrome and Firefox Nightly + browsers that supports Resource Timing
   * API v2.
   * @memberof util
   * @returns {String} http/1 or h2 for http 1 and 2 respectively. 'unknown' if browser lacks api to determine it.
   */
  getConnectionType: function() {
    // it's easy in Chrome
    if (window.chrome) {
      return window.chrome.loadTimes().connectionInfo;
    }
    // if you support resource timing v2
    // it's easy kind of easy too
    else if (window.performance && window.performance.getEntriesByType &&
      window.performance.getEntriesByType('resource')) {
      var resources = window.performance.getEntriesByType('resource');
      // now we "only" need to know if it is v2
      if (resources.length > 1 && resources[0].nextHopProtocol) {
        // if it's the same domain, say it's ok
        var host = document.domain;

        for (var i = 0, len = resources.length; i < len; i += 1) {
          if (host === util.getHostname(resources[i].name)) {
            return resources[i].nextHopProtocol;
          }
        }
      }
    }
    return 'unknown';
  },
  /**
   * Get Javascript requests that are loaded synchronously. All URLs are absolute.
   * @memberof util
   * @param {Object} parentElement the parent element that has all the scripts.
   * @returns {Array} an array with the URL to each Javascript file that is loaded sync.
   */
  getSynchJSFiles: function(parentElement) {
    var scripts = Array.prototype.slice.call(parentElement.getElementsByTagName('script'));

    return scripts.filter(function(s) {
        return ((!s.async) && s.src);
      })
      .map(function(s) {
        return util.getAbsoluteURL(s.src)
      });
  },
  /**
   * Get Javascript requests that are loaded asynchronously. All URLs are absolute.
   * @memberof util
   * @param {Object} parentElement the parent element that has all the scripts.
   * @returns {Array} an array with the URL to each Javascript file that are loaded async.
   */
  getAsynchJSFiles: function(parentElement) {
    var scripts = Array.prototype.slice.call(parentElement.getElementsByTagName('script'));

    return scripts.filter(function(s) {
        return (s.async && s.src);
      })
      .map(function(s) {
        return util.getAbsoluteURL(s.src)
      });
  },
  /**
   * Get Resource Hints hrefs by type
   * @memberof util
   * @param {String} type the name of the Resources hint: dns-prefetch, preconnect, prefetch, prerender
   * @returns {Array} an array of matching hrefs
   */
  getResourceHintsHrefs: function(type) {
    var links = Array.prototype.slice.call(window.document.head.getElementsByTagName('link'));

    return links.filter(function(link) {
        return (link.rel === type);
      })
      .map(function(link) {
        return link.href
      });
  },
  /**
   * Get CSS requests. All URLs are absolute.
   * @memberof util
   * @param {Object} parentElement the parent element that has all the scripts.
   * @returns {Array} an array with the URL to each CSS file that is loaded sync.
   */
  getCSSFiles: function(parentElement) {
    var links = Array.prototype.slice.call(parentElement.getElementsByTagName('link'));

    return links.filter(function(link) {
        // make sure we skip data:
        return (link.rel === 'stylesheet' && link.href.indexOf('http') > -1);
      })
      .map(function(link) {
        return util.getAbsoluteURL(link.href)
      });
  },
  plural: function(value) {
    return (value > 1) ? 's' : '';
  }
};
<|endoftext|>


<commit_before>describe("ilios_base", function() {

  it("should create a global ilios object", function() {
    expect(ilios).toBeDefined();
  });

  describe("ilios", function () {

    it("should have a namespace method", function () {
      expect(typeof ilios.namespace).toBe("function");
    });

    it("should have a lang namespace", function () {
      expect(typeof ilios.lang).toBe("object");
    });

    it("should have an alert namespace", function () {
      expect(typeof ilios.alert).toBe("object");
    });

    it("should have a global namespace", function () {
      expect(typeof ilios.global).toBe("object");
    });

    describe("namespace()", function () {
      afterEach(function () {
        delete ilios.foo;
      });

      it("should create the supplied namespace", function () {
        expect(typeof ilios.foo).toBe("undefined");
        ilios.namespace('foo');
        expect(typeof ilios.foo).toBe("object");
      });

      it("should create namespaces with depth > 1", function () {
        expect(typeof ilios.foo).toBe("undefined");
        ilios.namespace('foo.bar');
        expect(typeof ilios.foo).toBe("object");
        expect(typeof ilios.foo.bar).toBe("object");
      });

      it("should allow you to create a space twice", function () {
        expect(typeof ilios.foo).toBe("undefined");
        ilios.namespace('foo.bar');
        ilios.namespace('foo.baz');
        expect(typeof ilios.foo.bar).toBe("object");
        expect(typeof ilios.foo.baz).toBe("object");
      });
    });

    describe("lang", function () {
      describe("trim()", function () {
        it("should remove leading white space", function () {
          expect(ilios.lang.trim(" foo")).toBe("foo");
        });

        it("should remove trailing white space", function () {
          expect(ilios.lang.trim("foo  ")).toBe("foo");
        });

        it("should remove leading and trailing white space", function () {
          expect(ilios.lang.trim("  foo   ")).toBe("foo");
        });

        it("should not affect strings without leading or trailing spaces", function () {
          expect(ilios.lang.trim("foo")).toBe("foo");
        });

        it("should not affect spaces that are not leading or trailing", function () {
          expect(ilios.lang.trim("foo bar")).toBe("foo bar");
        });
      });

      describe("startsWith()", function () {
        it("should return true if str starts with prefix", function () {
          expect(ilios.lang.startsWith("food", "foo")).toBe(true);
        });

        it("should return false if str does not start with prefix", function () {
          expect(ilios.lang.startsWith("salad", "bar")).toBe(false);
        });

        it("should treat prefix as a string and not a regexp", function () {
          expect(ilios.lang.startsWith("$alad bar", "$a")).toBe(true);
        });
      });

      describe("endsWith()", function () {
        it("should return true if str ends with suffix", function () {
          expect(ilios.lang.endsWith("salad bar", "bar")).toBe(true);
        });

        it("should return false if str does not end with suffix", function () {
          expect(ilios.lang.endsWith("salad", "bar")).toBe(false);
        });

        it("should treat suffix as a string and not a regexp", function () {
          expect(ilios.lang.endsWith("salad bar^", "^")).toBe(true);
        });

        it("should return false if suffix is one character longer than str", function () {
          expect(ilios.lang.endsWith("foo", "food")).toBe(false);
        });
      });

      describe("ellipsisedOfLength()", function () {
        it("should truncate str to specified number of characters and add an ellipsis", function () {
          expect(ilios.lang.ellipsisedOfLength("abcdefghijklmnopqrstuvwxyz", 10)).toBe("abcdefghij...");
        });

        it("should return str if it is shorter than length", function () {
          expect(ilios.lang.ellipsisedOfLength("abc", 10)).toBe("abc");
        });
      });

    });
  });

});
<commit_msg>networkActivityI18NStrings spec<commit_after>describe("ilios_base", function() {

  it("should create a global ilios object", function() {
    expect(ilios).toBeDefined();
  });

  describe("ilios", function () {

    it("should have a namespace method", function () {
      expect(typeof ilios.namespace).toBe("function");
    });

    it("should have a lang namespace", function () {
      expect(typeof ilios.lang).toBe("object");
    });

    it("should have an alert namespace", function () {
      expect(typeof ilios.alert).toBe("object");
    });

    it("should have a global namespace", function () {
      expect(typeof ilios.global).toBe("object");
    });

    describe("namespace()", function () {
      afterEach(function () {
        delete ilios.foo;
      });

      it("should create the supplied namespace", function () {
        expect(typeof ilios.foo).toBe("undefined");
        ilios.namespace('foo');
        expect(typeof ilios.foo).toBe("object");
      });

      it("should create namespaces with depth > 1", function () {
        expect(typeof ilios.foo).toBe("undefined");
        ilios.namespace('foo.bar');
        expect(typeof ilios.foo).toBe("object");
        expect(typeof ilios.foo.bar).toBe("object");
      });

      it("should allow you to create a space twice", function () {
        expect(typeof ilios.foo).toBe("undefined");
        ilios.namespace('foo.bar');
        ilios.namespace('foo.baz');
        expect(typeof ilios.foo.bar).toBe("object");
        expect(typeof ilios.foo.baz).toBe("object");
      });
    });

    describe("lang", function () {
      describe("trim()", function () {
        it("should remove leading white space", function () {
          expect(ilios.lang.trim(" foo")).toBe("foo");
        });

        it("should remove trailing white space", function () {
          expect(ilios.lang.trim("foo  ")).toBe("foo");
        });

        it("should remove leading and trailing white space", function () {
          expect(ilios.lang.trim("  foo   ")).toBe("foo");
        });

        it("should not affect strings without leading or trailing spaces", function () {
          expect(ilios.lang.trim("foo")).toBe("foo");
        });

        it("should not affect spaces that are not leading or trailing", function () {
          expect(ilios.lang.trim("foo bar")).toBe("foo bar");
        });
      });

      describe("startsWith()", function () {
        it("should return true if str starts with prefix", function () {
          expect(ilios.lang.startsWith("food", "foo")).toBe(true);
        });

        it("should return false if str does not start with prefix", function () {
          expect(ilios.lang.startsWith("salad", "bar")).toBe(false);
        });

        it("should treat prefix as a string and not a regexp", function () {
          expect(ilios.lang.startsWith("$alad bar", "$a")).toBe(true);
        });
      });

      describe("endsWith()", function () {
        it("should return true if str ends with suffix", function () {
          expect(ilios.lang.endsWith("salad bar", "bar")).toBe(true);
        });

        it("should return false if str does not end with suffix", function () {
          expect(ilios.lang.endsWith("salad", "bar")).toBe(false);
        });

        it("should treat suffix as a string and not a regexp", function () {
          expect(ilios.lang.endsWith("salad bar^", "^")).toBe(true);
        });

        it("should return false if suffix is one character longer than str", function () {
          expect(ilios.lang.endsWith("foo", "food")).toBe(false);
        });
      });

      describe("ellipsisedOfLength()", function () {
        it("should truncate str to specified number of characters and add an ellipsis", function () {
          expect(ilios.lang.ellipsisedOfLength("abcdefghijklmnopqrstuvwxyz", 10)).toBe("abcdefghij...");
        });

        it("should return str if it is shorter than length", function () {
          expect(ilios.lang.ellipsisedOfLength("abc", 10)).toBe("abc");
        });
      });

    });

    describe("alert", function () {
      describe("networkActivityI18NStrings", function () {
        it("should initialize networkActivityI18NStrings to an empty array", function () {
          expect(ilios.alert.networkActivityI18NStrings).toEqual([]);
        });
      });
    });
  });

});
<|endoftext|>


<commit_before>var exceptionist = require('./lib/exceptionist')
var logger = require('./lib/logger')
var utils = require('./lib/utils')
var config = require('./lib/config')

function Opbeat () {
  this.isInstalled = false

  if (config.isConfigValid(this.options)) {
    this.install()
  }
  config.init()

}

Opbeat.prototype.VERSION = '0.0.1'

Opbeat.prototype.isPlatformSupport = function () {
  return typeof Array.prototype.forEach === 'function' &&
    typeof JSON.stringify === 'function'
}

/*
 * Configure Opbeat with Opbeat.com credentials and other options
 *
 * @param {object} options Optional set of of global options
 * @return {Opbeat}
 */

Opbeat.prototype.config = function (properties) {
  config.setConfig(properties)

  return this
}

/*
 * Installs a global window.onerror error handler
 * to capture and report uncaught exceptions.
 * At this point, install() is required to be called due
 * to the way TraceKit is set up.
 *
 * @return {Opbeat}
 */

Opbeat.prototype.install = function () {
  if (this.isPlatformSupport() && !this.isInstalled) {
    window.onerror = function (msg, file, line, col, error) {
      exceptionist.processWindowError(msg, file, line, col, error, this.options)
    }.bind(this)

    this.isInstalled = true
  }

  return this

}

/*
 * Uninstalls the global error handler.
 *
 * @return {Opbeat}
 */
Opbeat.prototype.uninstall = function () {
  window.TraceKit.report.uninstall()
  this.isInstalled = false

  return this
}

/*
 * Manually capture an exception and send it over to Opbeat.com
 *
 * @param {error} ex An exception to be logged
 * @param {object} options A specific set of options for this error [optional]
 * @return {Opbeat}
 */
Opbeat.prototype.captureException = function (ex, options) {
  if (!this.isInstalled) {
    throw new Error("Can't capture exception. Opbeat isn't intialized")
  }

  if (!(ex instanceof Error)) {
    throw new Error("Can't capture exception. Passed exception needs to be an instanceof Error")
  }

  // TraceKit.report will re-raise any exception passed to it,
  // which means you have to wrap it in try/catch. Instead, we
  // can wrap it here and only re-raise if TraceKit.report
  // raises an exception different from the one we asked to
  // report on.

  // try {
  //   window.TraceKit.report(ex, options)
  // } catch(ex1) {
  //   if (ex !== ex1) {
  //     throw ex1
  //   }
  // }

  return this
}

/*
 * Set/clear a user to be sent along with the payload.
 *
 * @param {object} user An object representing user data [optional]
 * @return {Opbeat}
 */
Opbeat.prototype.setUserContext = function (user) {
  config.set('context.user', user)

  return this
}

/*
 * Set extra attributes to be sent along with the payload.
 *
 * @param {object} extra An object representing extra data [optional]
 * @return {Opbeat}
 */
Opbeat.prototype.setExtraContext = function (extra) {
  config.set('context.extra', extra)

  return this
}

module.exports = new Opbeat()
<commit_msg>More explicit checks before install<commit_after>var exceptionist = require('./lib/exceptionist')
var logger = require('./lib/logger')
var utils = require('./lib/utils')
var config = require('./lib/config')

function Opbeat () {
  this.isInstalled = false

  config.init()

  this.install()
}

Opbeat.prototype.VERSION = '0.0.1'

Opbeat.prototype.isPlatformSupport = function () {
  return typeof Array.prototype.forEach === 'function' &&
    typeof JSON.stringify === 'function'
}

/*
 * Configure Opbeat with Opbeat.com credentials and other options
 *
 * @param {object} options Optional set of of global options
 * @return {Opbeat}
 */

Opbeat.prototype.config = function (properties) {
  config.setConfig(properties)

  return this
}

/*
 * Installs a global window.onerror error handler
 * to capture and report uncaught exceptions.
 * At this point, install() is required to be called due
 * to the way TraceKit is set up.
 *
 * @return {Opbeat}
 */

Opbeat.prototype.install = function () {
  if (!this.isPlatformSupport()) {
    return this
  }

  if (!config.isValid()) {
    return this
  }

  if (this.isInstalled) {
    return this
  }

  window.onerror = function (msg, file, line, col, error) {
    exceptionist.processWindowError(msg, file, line, col, error, this.config)
  }.bind(this)

  this.isInstalled = true

  return this

}

/*
 * Uninstalls the global error handler.
 *
 * @return {Opbeat}
 */
Opbeat.prototype.uninstall = function () {
  window.TraceKit.report.uninstall()
  this.isInstalled = false

  return this
}

/*
 * Manually capture an exception and send it over to Opbeat.com
 *
 * @param {error} ex An exception to be logged
 * @param {object} options A specific set of options for this error [optional]
 * @return {Opbeat}
 */
Opbeat.prototype.captureException = function (ex, options) {
  if (!this.isInstalled) {
    throw new Error("Can't capture exception. Opbeat isn't intialized")
  }

  if (!(ex instanceof Error)) {
    throw new Error("Can't capture exception. Passed exception needs to be an instanceof Error")
  }

  // TraceKit.report will re-raise any exception passed to it,
  // which means you have to wrap it in try/catch. Instead, we
  // can wrap it here and only re-raise if TraceKit.report
  // raises an exception different from the one we asked to
  // report on.

  // try {
  //   window.TraceKit.report(ex, options)
  // } catch(ex1) {
  //   if (ex !== ex1) {
  //     throw ex1
  //   }
  // }

  return this
}

/*
 * Set/clear a user to be sent along with the payload.
 *
 * @param {object} user An object representing user data [optional]
 * @return {Opbeat}
 */
Opbeat.prototype.setUserContext = function (user) {
  config.set('context.user', user)

  return this
}

/*
 * Set extra attributes to be sent along with the payload.
 *
 * @param {object} extra An object representing extra data [optional]
 * @return {Opbeat}
 */
Opbeat.prototype.setExtraContext = function (extra) {
  config.set('context.extra', extra)

  return this
}

module.exports = new Opbeat()
<|endoftext|>


<commit_before>/*jshint globalstrict:false, strict:false */
/*global arango, assertEqual, assertTrue, assertEqual, assertMatch, fail */

////////////////////////////////////////////////////////////////////////////////
/// @brief test the traditional key generators
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

const jsunity = require("jsunity");
const arangodb = require("@arangodb");
const db = arangodb.db;
const ERRORS = arangodb.errors;
const cluster = require("internal").isCluster();

const cn = "UnitTestsKeyGen";

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: traditional key gen
////////////////////////////////////////////////////////////////////////////////

function TraditionalSuite () {
  'use strict';

  return {

    setUp : function () {
      db._drop(cn);
    },

    tearDown : function () {
      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////

    testCreateInvalidKeyNonDefaultSharding1 : function () {
      var c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: "traditional", allowUserKeys: false } });

      try {
        c.save({ _key: "1234" }); // no user keys allowed
        fail();
      } catch (err) {
        assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                   err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
      }
    },

    testCreateInvalidKeyNonDefaultSharding2 : function () {
      if (!cluster) {
        return;
      }

      var c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: "traditional", allowUserKeys: true } });

      try {
        c.save({ _key: "1234" }); // no user keys allowed
        fail();
      } catch (err) {
        assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                   err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
      }
    },

    testCreateKeyNonDefaultSharding : function () {
      if (!cluster) {
        return;
      }

      var c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: "traditional", allowUserKeys: true } });

      let key = c.save({ value: "1" }); // no user keys allowed
      let doc = c.document(key);
      assertEqual("1", doc.value);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////

    testCreateInvalidKey1 : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: false } });

      try {
        c.save({ _key: "1234" }); // no user keys allowed
        fail();
      } catch (err) {
        assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                   err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////

    testCreateInvalidKey2 : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: true } });

      try {
        c.save({ _key: "öä .mds 3 -6" }); // invalid key
        fail();
      } catch (err) {
        assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_KEY_BAD.code, err.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////

    testCreateOk1 : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional" } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(true, options.allowUserKeys);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////

    testCreateOk2 : function () {
      var c = db._create(cn, { keyOptions: { } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(true, options.allowUserKeys);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////

    testCreateOk3 : function () {
      var c = db._create(cn, { keyOptions: { allowUserKeys: false } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(false, options.allowUserKeys);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with user key
////////////////////////////////////////////////////////////////////////////////

    testCheckUserKey : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: true } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(true, options.allowUserKeys);

      var d1 = c.save({ _key: "1234" });
      assertEqual("1234", d1._key);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////

    testCheckAutoValues : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional" } });

      var d1 = parseFloat(c.save({ })._key);
      var d2 = parseFloat(c.save({ })._key);
      var d3 = parseFloat(c.save({ })._key);
      var d4 = parseFloat(c.save({ })._key);
      var d5 = parseFloat(c.save({ })._key);

      assertTrue(d1 < d2);
      assertTrue(d2 < d3);
      assertTrue(d3 < d4);
      assertTrue(d4 < d5);
    },

    testInvalidKeyGenerator : function () {
      try {
        db._create(cn, { keyOptions: { type: "der-fuchs" } });
        fail();
      } catch (err) {
        assertEqual(ERRORS.ERROR_ARANGO_INVALID_KEY_GENERATOR.code, err.errorNum);
      }
    },

    testAutoincrementGeneratorInCluster : function () {
      if (!cluster) {
        return;
      }

      try {
        db._create(cn, { keyOptions: { type: "autoincrement" } });
        fail();
      } catch (err) {
        assertEqual(ERRORS.ERROR_CLUSTER_UNSUPPORTED.code, err.errorNum);
      }
    },

    testUuid : function () {
      let c = db._create(cn, { keyOptions: { type: "uuid" } });

      let options = c.properties().keyOptions;
      assertEqual("uuid", options.type);
      assertTrue(options.allowUserKeys);

      for (let i = 0; i < 100; ++i) {
        let doc = c.insert({});
        assertMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, doc._key);
      }
    },

    testPadded : function () {
      let c = db._create(cn, { keyOptions: { type: "padded" } });

      let options = c.properties().keyOptions;
      assertEqual("padded", options.type);
      assertTrue(options.allowUserKeys);

      for (let i = 0; i < 100; ++i) {
        let doc = c.insert({});
        assertEqual(16, doc._key.length);
        assertMatch(/^[0-9a-f]{16}$/, doc._key);
      }
    },

    testTraditionalTracking : function () {
      let c = db._create(cn, { keyOptions: { type: "traditional" } });

      let options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertTrue(options.allowUserKeys);

      let lastKey = null;
      for (let i = 0; i < 100; ++i) {
        let key = c.insert({})._key;
        if (lastKey !== null) {
          assertTrue(Number(key) > Number(lastKey));
        }
        lastKey = key;
      }

      if (!cluster) {
        assertEqual(lastKey, c.properties().keyOptions.lastValue);
      }
    },

    testPaddedTracking : function () {
      let c = db._create(cn, { keyOptions: { type: "padded" } });

      let options = c.properties().keyOptions;
      assertEqual("padded", options.type);
      assertTrue(options.allowUserKeys);

      let lastKey = null;
      for (let i = 0; i < 100; ++i) {
        let key = c.insert({})._key;
        assertEqual(16, key.length);
        if (lastKey !== null) {
          assertTrue(key > lastKey);
        }
        lastKey = key;
      }

      if (!cluster) {
        let hex = c.properties().keyOptions.lastValue.toString(16);
        assertEqual(lastKey, Array(16 + 1 - hex.length).join("0") + hex);
      }
    }

  };
}

function AllowUserKeysSuite () {
  'use strict';

  let generators = function() {
    let generators = [
      "traditional",
      "padded",
      "uuid"
    ];
    if (!cluster) {
      generators.push("autoincrement");
    }
    return generators;
  };

  return {
    setUp : function () {
      db._drop(cn);
    },

    tearDown : function () {
      db._drop(cn);
    },

    testAllowUserKeysTrueDefaultSharding : function () {
      generators().forEach((generator) => {
        let c = db._create(cn, { keyOptions: { type: generator, allowUserKeys: true } });
        try {
          let doc = c.insert({ _key: "1234" });
          assertEqual("1234", doc._key);
          assertEqual(1, c.count());
          doc = c.document("1234");
          assertEqual("1234", doc._key);
        } finally {
          db._drop(cn);
        }
      });
    },

    testAllowUserKeysFalseDefaultSharding : function () {
      generators().forEach((generator) => {
        let c = db._create(cn, { keyOptions: { type: generator, allowUserKeys: false } });
        try {
          c.insert({ _key: "1234" });
          fail();
        } catch (err) {
          assertEqual(0, c.count());
          assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                     err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
        } finally {
          db._drop(cn);
        }
      });
    },

    testAllowUserKeysTrueCustomSharding : function () {
      if (!cluster) {
        return;
      }

      generators().forEach((generator) => {
        let c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: generator, allowUserKeys: true } });
        try {
          c.insert({ _key: "1234" });
          fail();
        } catch (err) {
          assertEqual(0, c.count());
          assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                     err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
        } finally {
          db._drop(cn);
        }
      });
    },

    testAllowUserKeysFalseCustomSharding : function () {
      if (!cluster) {
        return;
      }

      generators().forEach((generator) => {
        let c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: generator, allowUserKeys: false } });
        try {
          c.insert({ _key: "1234" });
          fail();
        } catch (err) {
          assertEqual(0, c.count());
          assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                     err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
        } finally {
          db._drop(cn);
        }
      });
    },

  };
}

//jsunity.run(TraditionalSuite);
jsunity.run(AllowUserKeysSuite);

return jsunity.done();
<commit_msg>add more test cases<commit_after>/*jshint globalstrict:false, strict:false */
/*global arango, assertEqual, assertTrue, assertFalse,, assertMatch, fail */

////////////////////////////////////////////////////////////////////////////////
/// @brief test the traditional key generators
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

const jsunity = require("jsunity");
const arangodb = require("@arangodb");
const db = arangodb.db;
const ERRORS = arangodb.errors;
const cluster = require("internal").isCluster();

const cn = "UnitTestsKeyGen";

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite: traditional key gen
////////////////////////////////////////////////////////////////////////////////

function TraditionalSuite () {
  'use strict';

  return {

    setUp : function () {
      db._drop(cn);
    },

    tearDown : function () {
      db._drop(cn);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////

    testCreateInvalidKeyNonDefaultSharding1 : function () {
      var c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: "traditional", allowUserKeys: false } });

      try {
        c.save({ _key: "1234" }); // no user keys allowed
        fail();
      } catch (err) {
        assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                   err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
      }
    },

    testCreateInvalidKeyNonDefaultSharding2 : function () {
      if (!cluster) {
        return;
      }

      var c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: "traditional", allowUserKeys: true } });

      try {
        c.save({ _key: "1234" }); // no user keys allowed
        fail();
      } catch (err) {
        assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                   err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
      }
    },

    testCreateKeyNonDefaultSharding : function () {
      if (!cluster) {
        return;
      }

      var c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: "traditional", allowUserKeys: true } });

      let key = c.save({ value: "1" }); // no user keys allowed
      let doc = c.document(key);
      assertEqual("1", doc.value);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////

    testCreateInvalidKey1 : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: false } });

      try {
        c.save({ _key: "1234" }); // no user keys allowed
        fail();
      } catch (err) {
        assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                   err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with key
////////////////////////////////////////////////////////////////////////////////

    testCreateInvalidKey2 : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: true } });

      try {
        c.save({ _key: "öä .mds 3 -6" }); // invalid key
        fail();
      } catch (err) {
        assertEqual(ERRORS.ERROR_ARANGO_DOCUMENT_KEY_BAD.code, err.errorNum);
      }
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////

    testCreateOk1 : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional" } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(true, options.allowUserKeys);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////

    testCreateOk2 : function () {
      var c = db._create(cn, { keyOptions: { } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(true, options.allowUserKeys);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with valid properties
////////////////////////////////////////////////////////////////////////////////

    testCreateOk3 : function () {
      var c = db._create(cn, { keyOptions: { allowUserKeys: false } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(false, options.allowUserKeys);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief create with user key
////////////////////////////////////////////////////////////////////////////////

    testCheckUserKey : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional", allowUserKeys: true } });

      var options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertEqual(true, options.allowUserKeys);

      var d1 = c.save({ _key: "1234" });
      assertEqual("1234", d1._key);
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief check auto values
////////////////////////////////////////////////////////////////////////////////

    testCheckAutoValues : function () {
      var c = db._create(cn, { keyOptions: { type: "traditional" } });

      var d1 = parseFloat(c.save({ })._key);
      var d2 = parseFloat(c.save({ })._key);
      var d3 = parseFloat(c.save({ })._key);
      var d4 = parseFloat(c.save({ })._key);
      var d5 = parseFloat(c.save({ })._key);

      assertTrue(d1 < d2);
      assertTrue(d2 < d3);
      assertTrue(d3 < d4);
      assertTrue(d4 < d5);
    },

    testInvalidKeyGenerator : function () {
      try {
        db._create(cn, { keyOptions: { type: "der-fuchs" } });
        fail();
      } catch (err) {
        assertEqual(ERRORS.ERROR_ARANGO_INVALID_KEY_GENERATOR.code, err.errorNum);
      }
    },

    testAutoincrementGeneratorInCluster : function () {
      if (!cluster) {
        return;
      }

      try {
        db._create(cn, { keyOptions: { type: "autoincrement" } });
        fail();
      } catch (err) {
        assertEqual(ERRORS.ERROR_CLUSTER_UNSUPPORTED.code, err.errorNum);
      }
    },

    testUuid : function () {
      let c = db._create(cn, { keyOptions: { type: "uuid" } });

      let options = c.properties().keyOptions;
      assertEqual("uuid", options.type);
      assertTrue(options.allowUserKeys);

      for (let i = 0; i < 100; ++i) {
        let doc = c.insert({});
        assertMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/, doc._key);
      }
    },

    testPadded : function () {
      let c = db._create(cn, { keyOptions: { type: "padded" } });

      let options = c.properties().keyOptions;
      assertEqual("padded", options.type);
      assertTrue(options.allowUserKeys);

      for (let i = 0; i < 100; ++i) {
        let doc = c.insert({});
        assertEqual(16, doc._key.length);
        assertMatch(/^[0-9a-f]{16}$/, doc._key);
      }
    },

    testTraditionalTracking : function () {
      let c = db._create(cn, { keyOptions: { type: "traditional" } });

      let options = c.properties().keyOptions;
      assertEqual("traditional", options.type);
      assertTrue(options.allowUserKeys);

      let lastKey = null;
      for (let i = 0; i < 100; ++i) {
        let key = c.insert({})._key;
        if (lastKey !== null) {
          assertTrue(Number(key) > Number(lastKey));
        }
        lastKey = key;
      }

      if (!cluster) {
        assertEqual(lastKey, c.properties().keyOptions.lastValue);
      }
    },

    testPaddedTracking : function () {
      let c = db._create(cn, { keyOptions: { type: "padded" } });

      let options = c.properties().keyOptions;
      assertEqual("padded", options.type);
      assertTrue(options.allowUserKeys);

      let lastKey = null;
      for (let i = 0; i < 100; ++i) {
        let key = c.insert({})._key;
        assertEqual(16, key.length);
        if (lastKey !== null) {
          assertTrue(key > lastKey);
        }
        lastKey = key;
      }

      if (!cluster) {
        let hex = c.properties().keyOptions.lastValue.toString(16);
        assertEqual(lastKey, Array(16 + 1 - hex.length).join("0") + hex);
      }
    }

  };
}

function AllowUserKeysSuite () {
  'use strict';

  let generators = function() {
    let generators = [
      "traditional",
      "padded",
      "uuid"
    ];
    if (!cluster) {
      generators.push("autoincrement");
    }
    return generators;
  };

  return {
    setUp : function () {
      db._drop(cn);
    },

    tearDown : function () {
      db._drop(cn);
    },

    testAllowUserKeysTrueDefaultSharding : function () {
      generators().forEach((generator) => {
        let c = db._create(cn, { keyOptions: { type: generator, allowUserKeys: true }, numberOfShards: 2 });
        try {
          let p = c.properties();
          assertTrue(p.keyOptions.allowUserKeys);
          assertEqual(generator, p.keyOptions.type);

          let doc = c.insert({ _key: "1234" });
          assertEqual("1234", doc._key);
          assertEqual(1, c.count());
          doc = c.document("1234");
          assertEqual("1234", doc._key);

          doc = c.insert({});
          assertMatch(/^[a-zA-z0-9]+/, doc._key);
          assertEqual(2, c.count());
          assertTrue(c.exists(doc._key));
        } finally {
          db._drop(cn);
        }
      });
    },

    testAllowUserKeysFalseDefaultSharding : function () {
      generators().forEach((generator) => {
        let c = db._create(cn, { keyOptions: { type: generator, allowUserKeys: false }, numberOfShards: 2 });
        try {
          let p = c.properties();
          assertFalse(p.keyOptions.allowUserKeys);
          assertEqual(generator, p.keyOptions.type);

          c.insert({ _key: "1234" });
          fail();
        } catch (err) {
          assertEqual(0, c.count());
          assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                     err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);

          let doc = c.insert({});
          assertMatch(/^[a-zA-z0-9]+/, doc._key);
          assertEqual(1, c.count());
          assertTrue(c.exists(doc._key));
        } finally {
          db._drop(cn);
        }
      });
    },

    testAllowUserKeysTrueCustomSharding : function () {
      if (!cluster) {
        return;
      }

      generators().forEach((generator) => {
        let c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: generator, allowUserKeys: true }, numberOfShards: 2 });
        try {
          let p = c.properties();
          assertTrue(p.keyOptions.allowUserKeys);
          assertEqual(generator, p.keyOptions.type);

          c.insert({ _key: "1234" });
          fail();
        } catch (err) {
          assertEqual(0, c.count());
          assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                     err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);

          let doc = c.insert({});
          assertMatch(/^[a-zA-z0-9]+/, doc._key);
          assertEqual(1, c.count());
          assertTrue(c.exists(doc._key));
        } finally {
          db._drop(cn);
        }
      });
    },

    testAllowUserKeysFalseCustomSharding : function () {
      if (!cluster) {
        return;
      }

      generators().forEach((generator) => {
        let c = db._create(cn, { shardKeys: ["value"], keyOptions: { type: generator, allowUserKeys: false }, numberOfShards: 2 });
        try {
          let p = c.properties();
          assertFalse(p.keyOptions.allowUserKeys);
          assertEqual(generator, p.keyOptions.type);

          c.insert({ _key: "1234" });
          fail();
        } catch (err) {
          assertEqual(0, c.count());
          assertTrue(err.errorNum === ERRORS.ERROR_ARANGO_DOCUMENT_KEY_UNEXPECTED.code ||
                     err.errorNum === ERRORS.ERROR_CLUSTER_MUST_NOT_SPECIFY_KEY.code);

          let doc = c.insert({});
          assertMatch(/^[a-zA-z0-9]+/, doc._key);
          assertEqual(1, c.count());
          assertTrue(c.exists(doc._key));
        } finally {
          db._drop(cn);
        }
      });
    },

  };
}

jsunity.run(TraditionalSuite);
jsunity.run(AllowUserKeysSuite);

return jsunity.done();
<|endoftext|>


<commit_before>var chalk = require('chalk');
var fs = require('fs-extra');
var request = require('request');
var tildify = require('tildify');
var ProgressBar = require('progress');
var figures = require('figures');
var path = require('./path');
var debug = require('debug')('mongodb-version-manager:download');

function progressbar(pkg, res, totalSize) {
  var bar = new ProgressBar('  Downloading MongoDB v'
    + pkg.version + ' [:bar] :percent :etasec', {
      complete: '=',
      incomplete: ' ',
      width: 40,
      total: totalSize
    });

  res.on('data', function(chunk) {
    bar.tick(chunk.length);
  });
}
/* eslint no-sync:0 */
module.exports = function(pkg, fn) {
  var dest = path.artifact(pkg);
  var url = pkg.url;
  var version = pkg.version;

  fs.mkdirs(path.artifacts(), function(err) {
    if (err) {
      return fn(err);
    }

    fs.exists(dest, function(exists) {
      if (exists) {
        debug('already have artifact at `%s`', dest);
        return fn();
      }

      debug('downloading artifact from `%s` to `%s`...', url, tildify(dest));

      var out = fs.createWriteStream(dest);
      var onError = function(err) {
        out.removeListener('finish', onFinish);
        debug('removing incomplete artifact from `%s`', dest);
        fs.unlink(dest, function() {
          fn(err);
        });
      };

      onFinish = function() {
        out.removeListener('error', onError);
        console.log(chalk.bold.green(figures.tick),
          ' Downloaded MongoDB', version);
        fn(null, dest);
      };
      out.once('error', onError).once('finish', onFinish);

      var req = request(url);
      req.on('response', function(res) {
        var totalSize = parseInt(res.headers['content-length'], 10);
        debug('total size %dMB', (totalSize / 1024 / 1024).toFixed(2));

        if (!totalSize || totalSize < 10 * 1024 * 1024) {
          return onError(new Error('Download size looks too small'));
        }
        if (!process.env.CI) {
          progressbar(pkg, res, totalSize);
        } else {
          console.log('Downloading `%s`...', url);
        }
      });
      req.pipe(out);
      req.on('error', function(err) {
        onError(err);
      });
    });
  });
};
<commit_msg>:zap: better error message for download too small<commit_after>var chalk = require('chalk');
var fs = require('fs-extra');
var request = require('request');
var tildify = require('tildify');
var ProgressBar = require('progress');
var figures = require('figures');
var format = require('util').format;
var path = require('./path');
var debug = require('debug')('mongodb-version-manager:download');

function progressbar(pkg, res, totalSize) {
  var bar = new ProgressBar('  Downloading MongoDB v'
    + pkg.version + ' [:bar] :percent :etasec', {
      complete: '=',
      incomplete: ' ',
      width: 40,
      total: totalSize
    });

  res.on('data', function(chunk) {
    bar.tick(chunk.length);
  });
}
/* eslint no-sync:0 */
module.exports = function(pkg, fn) {
  var dest = path.artifact(pkg);
  var url = pkg.url;
  var version = pkg.version;

  fs.mkdirs(path.artifacts(), function(err) {
    if (err) {
      return fn(err);
    }

    fs.exists(dest, function(exists) {
      if (exists) {
        debug('already have artifact at `%s`', dest);
        return fn();
      }

      debug('downloading artifact from `%s` to `%s`...', url, tildify(dest));

      var out = fs.createWriteStream(dest);
      var onError = function(err) {
        out.removeListener('finish', onFinish);
        debug('removing incomplete artifact from `%s`', dest);
        fs.unlink(dest, function() {
          fn(err);
        });
      };

      onFinish = function() {
        out.removeListener('error', onError);
        console.log(chalk.bold.green(figures.tick),
          ' Downloaded MongoDB', version);
        fn(null, dest);
      };
      out.once('error', onError).once('finish', onFinish);

      var req = request(url);
      req.on('response', function(res) {
        var totalSize = parseInt(res.headers['content-length'], 10);
        debug('total size %dMB', (totalSize / 1024 / 1024).toFixed(2));

        if (!totalSize || totalSize < 10 * 1024 * 1024) {
          var msg = format('Download looks too small! %s is only %s bytes!',
            url, totalSize);
          return onError(new Error(msg));
        }
        if (!process.env.CI) {
          progressbar(pkg, res, totalSize);
        } else {
          console.log('Downloading `%s`...', url);
        }
      });
      req.pipe(out);
      req.on('error', function(err) {
        onError(err);
      });
    });
  });
};
<|endoftext|>


<commit_before>import { Component } from 'react'
import h from 'react-hyperscript'
// can we remove this? import {Link} from 'react-router-dom'
import styled from 'styled-components'
import { StageDisplayNames, Stage, CompletedStages } from '../../../constants.js'
import { Headline, Label, Body } from '../../Global/Text.ts'
import {LinkPrimary} from '../../Global/Links.ts'
import { ButtonSecondary } from '../../Global/Buttons.ts'
import progressDots from '../../Global/progressDots.ts'
import { statusMessage, mapAssessmentStageToStep } from '../../../utils.js'

export class AssessmentCard extends Component {
  render () {
    const assessment = this.props.assessment
    let stage = assessment.stage

    // set assessee/assessor view
    let isAssessee = this.props.userAddress === assessment.assessee
    let status = statusMessage(isAssessee, assessment, this.props.transactions)
    return (
      h(cardContainer, [
        h(cardContainerInfo, [
          h(cardTextObject, [
            h(Label, 'Assessment'),
            h(Headline, assessment.conceptData.name)
          ]),
          h(cardTextObject, [
            h(Label, 'Assessee'),
            h(Body, isAssessee ? 'You' : assessment.assessee.substring(0, 8) + '...')
          ])
        ]),
        h(cardContainerStatus, [
          h(cardTextStatus, [
            h(cardRowStatus, [
              h(Label, 'Status'),
              h(cardContainerProgressBar, {},
                h(progressDots, {
                  length: 4,
                  step: mapAssessmentStageToStep(stage) - 1,
                  failed: assessment.violation || false
                }))
            ]),
            h(Body, status)
          ]),
          h('div', {className: 'flex flex-row justify-between w-100'}, linkButtons(assessment, isAssessee, this.props.setCardVisibility))
        ])
      ])
    )
  }
}

function linkButtons (assessment, isAssessee, setCardVisibility) {
  let userFault = (assessment.violation && assessment.userStage === assessment.stage) || assessment.userStage === Stage.Burned
  if (assessment.violation) {
    if (userFault) return [h(ButtonSecondary, 'Why?'), h(LinkPrimary, {to: '/'}, 'Closed')] // TODO why should be a link
    // not  userFault
    if (assessment.refunded) {
      // no assessment contract exits -> no link to detail-view
      return [h(ButtonSecondary, 'Why?'), h(LinkPrimary, {to: '/'}, 'Refunded')]
    } else {
      // not refunded yet -> provide link
      return [h(ButtonSecondary, 'Why?'), h(LinkPrimary, {to: '/assessment/' + assessment.address}, 'Refund')]
    }
  } else {
    // NOTE this section could be refactored to be smaller, as the only thing that varies is the text of the button. But i am keeping this
    // longer structure becasue once we want to provide different links on the why-button, it will come in handy to have the cases be more explicit.
    // no violation!
    // is the user done (for the respective stage?)
    let buttonList = [
      h(ButtonSecondary, {
        onClick: () => setCardVisibility(assessment.address, !assessment.hidden)
      }, assessment.hidden ? 'Unhide' : 'Hide')
    ]
    if (assessment.stage < Stage.Done && assessment.userStage === assessment.stage) {
      buttonList.push(
        h(LinkPrimary,
          {to: '/assessment/' + assessment.address},
          assessment.userStage === Stage.None ? 'View' : StageDisplayNames[assessment.stage]))
    } else {
      buttonList.push(
        h(LinkPrimary,
          {to: '/assessment/' + assessment.address},
          assessment.userStage === Stage.None ? 'View' : CompletedStages[assessment.stage]))
    }
    return buttonList
  }
}

export default AssessmentCard

// styles
const cardContainer = styled('div').attrs({
  className: 'flex flex-column ma3 br2 shadow-4'
})`height: 420px; width: 300px;background: linear-gradient(180.1deg, #FFFFFF 0.05%, #E9F7FD 52.48%, #CFF9EF 85.98%);
`

const cardContainerInfo = styled('div').attrs({
  className: 'flex justify-between flex-column w-100 pa3'
})`
height: 60%;
`

const cardTextObject = styled('div').attrs({
  className: 'flex flex-column tl'
})`
`

const cardContainerStatus = styled('div').attrs({
  className: 'relative flex content-between flex-column w-100 pa3 justify-between'
})`
height: 40%;
background-color: #D3ECF7;
`

const cardTextStatus = styled('div').attrs({
  className: 'flex flex-column tl'
})`
max-height: 80px;
overflow: hidden;
`

const cardRowStatus = styled('div').attrs({
  className: 'flex flex-row items-center'
})`
`

const cardContainerProgressBar = styled('div').attrs({
  className: 'flex items-center'
})`
`
<commit_msg>implement 'why' toggle on the assessmentCard<commit_after>import { Component } from 'react'
import h from 'react-hyperscript'
// can we remove this? import {Link} from 'react-router-dom'
import styled from 'styled-components'
import { StageDisplayNames, Stage, CompletedStages } from '../../../constants.js'
import { Headline, Label, Body } from '../../Global/Text.ts'
import {LinkPrimary} from '../../Global/Links.ts'
import { ButtonSecondary } from '../../Global/Buttons.ts'
import progressDots from '../../Global/progressDots.ts'
import { statusMessage, mapAssessmentStageToStep } from '../../../utils.js'

export class AssessmentCard extends Component {
  constructor (props) {
    super(props)
    this.state={
      toggleWhy:false
    }
  }

  toggleWhy(){
    this.setState({toggleWhy:!this.state.toggleWhy})
  }

  linkButtons (assessment, isAssessee, setCardVisibility) {
    let userFault = (assessment.violation && assessment.userStage === assessment.stage) || assessment.userStage === Stage.Burned
    if (assessment.violation) {
      if (userFault) return [h(ButtonSecondary,{onClick:this.toggleWhy.bind(this)}, 'Why?'), h(LinkPrimary, {to: '/'}, 'Closed')]
      // not  userFault
      if (assessment.refunded) {
        // no assessment contract exits -> no link to detail-view
        return [h(ButtonSecondary,{onClick:this.toggleWhy.bind(this)}, 'Why?'), h(LinkPrimary, {to: '/'}, 'Refunded')]
      } else {
        // not refunded yet -> provide link
        return [h(ButtonSecondary,{onClick:this.toggleWhy.bind(this)}, 'Why?'), h(LinkPrimary, {to: '/assessment/' + assessment.address}, 'Refund')]
      }
    } else {
      // NOTE this section could be refactored to be smaller, as the only thing that varies is the text of the button. But i am keeping this
      // longer structure becasue once we want to provide different links on the why-button, it will come in handy to have the cases be more explicit.
      // no violation!
      // is the user done (for the respective stage?)
      let buttonList = [
        h(ButtonSecondary, {
          onClick: () => setCardVisibility(assessment.address, !assessment.hidden)
        }, assessment.hidden ? 'Unhide' : 'Hide')
      ]
      if (assessment.stage < Stage.Done && assessment.userStage === assessment.stage) {
        buttonList.push(
          h(LinkPrimary,
            {to: '/assessment/' + assessment.address},
            assessment.userStage === Stage.None ? 'View' : StageDisplayNames[assessment.stage]))
      } else {
        buttonList.push(
          h(LinkPrimary,
            {to: '/assessment/' + assessment.address},
            assessment.userStage === Stage.None ? 'View' : CompletedStages[assessment.stage]))
      }
      return buttonList
    }
  }

  render () {
    const assessment = this.props.assessment
    let stage = assessment.stage

    // set assessee/assessor view
    let isAssessee = this.props.userAddress === assessment.assessee
    let status = statusMessage(isAssessee, assessment, this.props.transactions)

    let regularCardContent=h(cardContainer, [
        h(cardContainerInfo, [
          h(cardTextObject, [
            h(Label, 'Assessment'),
            h(Headline, assessment.conceptData.name)
          ]),
          h(cardTextObject, [
            h(Label, 'Assessee'),
            h(Body, isAssessee ? 'You' : assessment.assessee.substring(0, 8) + '...')
          ])
        ]),
        h(cardContainerStatus, [
          h(cardTextStatus, [
            h(cardRowStatus, [
              h(Label, 'Status'),
              h(cardContainerProgressBar, {},
                h(progressDots, {
                  length: 4,
                  step: mapAssessmentStageToStep(stage) - 1,
                  failed: assessment.violation || false
                }))
            ]),
            h(Body, status)
          ]),
          h('div', {className: 'flex flex-row justify-between w-100'}, this.linkButtons(assessment, isAssessee, this.props.setCardVisibility))
        ])
      ])

    let explainerCard=h(cardContainer, ["ok"
      ])

    if (this.state.toggleWhy){
      return explainerCard
    } else {
      return regularCardContent
    }
  }
}

export default AssessmentCard

// styles
const cardContainer = styled('div').attrs({
  className: 'flex flex-column ma3 br2 shadow-4'
})`height: 420px; width: 300px;background: linear-gradient(180.1deg, #FFFFFF 0.05%, #E9F7FD 52.48%, #CFF9EF 85.98%);
`

const cardContainerInfo = styled('div').attrs({
  className: 'flex justify-between flex-column w-100 pa3'
})`
height: 60%;
`

const cardTextObject = styled('div').attrs({
  className: 'flex flex-column tl'
})`
`

const cardContainerStatus = styled('div').attrs({
  className: 'relative flex content-between flex-column w-100 pa3 justify-between'
})`
height: 40%;
background-color: #D3ECF7;
`

const cardTextStatus = styled('div').attrs({
  className: 'flex flex-column tl'
})`
max-height: 80px;
overflow: hidden;
`

const cardRowStatus = styled('div').attrs({
  className: 'flex flex-row items-center'
})`
`

const cardContainerProgressBar = styled('div').attrs({
  className: 'flex items-center'
})`
`
<|endoftext|>


<commit_before>var templateName = 'dropdown';
var TemplateClass = Template[templateName];

var defaultText = 'Select';

TemplateClass.created = function() {
  var data = this.data;
  var name = data.name;
  if (!name) {
    throw new Error('Name required for dropdown.');
  }
  // The next value to set once the item becomes available.
  this.nextValue = null;
};

TemplateClass.rendered = function() {
  this.isRendered = true;
  var $dropdown = getDropdown(this).dropdown();
  setUpDropdown();
  var $input = getValueInput(this);
  var data = this.data;
  // "atts" is only provided when used with AutoForm.
  var atts = data.atts || data;

  var schemaKey = atts.schemaKey;
  schemaKey = schemaKey !== undefined ? schemaKey : true;

  // Handle changes to the collection.
  var items = atts.items;
  var cursor = items;
  // TODO(aramk) Add dependency to Collections.
  if (Collections.isCollection(items)) {
    cursor = items.find();
  }
  if (Collections.isCursor(cursor)) {
    var shouldObserve = false;
    // If passed a cursor, listen for changes and update the items reactively.
    var changeHandler = _.debounce(function() {
      shouldObserve && setUpDropdown(this);
    }.bind(this), 500);
    this._changeHandle = cursor.observe({
      added: changeHandler,
      changed: changeHandler,
      removed: changeHandler
    });
    // Delay observing to avoid the added() callback firing for existing items.
    shouldObserve = true;
  }

  if (!schemaKey) {
    $input.removeAttr('data-schema-key');
  }
};

TemplateClass.destroyed = function() {
  this._changeHandle && this._changeHandle.stop();
};

TemplateClass.hasValue = function(em, value) {
  return $('.menu [data-value="' + value + '"]', resolveElement(em)).length > 0;
};

TemplateClass.setValue = function(em, value) {
  var $em = resolveElement(em);
  var template = Templates.getInstanceFromElement(em);
  var shouldClear = !value;
  // Sanitize the values to null if they are falsey so we don't attempt to set the value to null
  // when it's currently an empty string.
  var existingValue = TemplateClass.getValue($em) || null;
  value = value || null;
  var hasValue = TemplateClass.hasValue($em, value);
  if (value !== existingValue) {
    if (hasValue) {
      $em.dropdown('set value', value).dropdown('set selected', value);
    } else if (shouldClear) {
      $em.dropdown('set text', defaultText);
    }
    template.nextValue = hasValue ? null : value;
    return true;
  } else {
    return false;
  }
};

TemplateClass.getValue = function(em) {
  var $em = resolveElement(em);
  return $em.length > 0 ? $em.dropdown('get value') : null;
};

TemplateClass.getItem = function(em, value) {
  var $menu = $('.menu', resolveElement(em));
  return $('.item[data-value=' + value + ']', $menu);
};

TemplateClass.bindVarToElement = function(em, reactiveVariable, args) {
  Templates.bindVarToElement(resolveElement(em), reactiveVariable, _.extend({
    getValue: function() {
      return TemplateClass.getValue($(this));
    },
    setValue: function(value) {
      TemplateClass.setValue(value);
    }
  }, args));
};

TemplateClass.isDropdown = function(em) {
  return resolveElement(em).length === 1;
};

function setUpDropdown(template) {
  template = getTemplate(template);
  if (!template.isRendered) {
    return;
  }

  var data = template.data;
  // "atts" is only provided when used with AutoForm.
  var atts = data.atts || data;
  var labelAttr = atts.labelAttr || 'name';
  var valueAttr = atts.valueAttr || '_id';
  var allowEmpty = atts.allowEmpty;
  var origValue = data.value;
  // The last non-empty value.
  var lastValue;
  var nextValue = template.nextValue;
  var $dropdown = getDropdown(template);

  // Save the last valid selection and restore it when possible after setup.
  var currentValue = TemplateClass.getValue($dropdown);
  if (currentValue !== '') {
    lastValue = currentValue;
  } else if (nextValue) {
    lastValue = nextValue;
  }
  // TODO(aramk) This isn't efficient since we're manually recreating all items instead of
  // using the reactive template. We need a way to detect the template is re-rendered. Until then
  // this is a compromise.
  var items;
  if (data.selectOptions) {
    // AutoForm schema allowedValues.
    items = _.map(data.selectOptions, function(option) {
      var item = {};
      item[valueAttr] = option.value;
      item[labelAttr] = option.label;
      return item;
    });
  } else if (atts.items) {
    // Collection, cursor or array.
    items = Collections.getItems(atts.items);
  }
  if (!items) {
    throw new Error('Dropdown items not defined.');
  }

  // Sort items based on label.
  items.sort(function(itemA, itemB) {
    return itemA[labelAttr] < itemB[labelAttr] ? -1 : 1;
  });

  if (allowEmpty) {
    var emptyItem = {};
    emptyItem[valueAttr] = emptyItem[labelAttr] = '';
    items.unshift(emptyItem);
  }
  var $menu = template.$('.menu');
  $menu.empty();
  _.each(items, function(item) {
    if (!Types.isObject(item)) {
      var value = item;
      item = {};
      item[valueAttr] = value;
      item[labelAttr] = value;
    }
    var $item = $('
' + item[labelAttr] +
        '
');
    $menu.append($item);
  });

  $dropdown.on('change', function() {
    var text = atts.text;
    if (text !== undefined) {
      $dropdown.dropdown('set text', text);
    }
  });

  var value = lastValue !== undefined ? lastValue : origValue;
  if (value) {
    if (!TemplateClass.hasValue($dropdown, value)) {
      value = '';
    }
    // Without a defer, setting the value doesn't work after the initial load.
    _.defer(function() {
      TemplateClass.setValue($dropdown, value);
    });
  }
  $dropdown.trigger('load');
}

function getDropdown(template) {
  return getTemplate(template).$('.ui.dropdown');
}

function getValueInput(template) {
  return getTemplate(template).$('input');
}

function getTemplate(template) {
  return Templates.getNamedInstance(templateName, template);
}

function resolveElement(em) {
  return $(em).closest('.ui.dropdown');
}

function registerAutoForm() {
  var autoform = Package['aldeed:autoform'];
  if (typeof autoform === 'undefined') {
    console.error('Cannot register input type - AutoForm not found.');
    return;
  }
  autoform.AutoForm.addInputType('dropdown', {
    template: 'dropdown',
    valueOut: function() {
      return TemplateClass.getValue(this.closest('.dropdown'));
    }
  });
}

registerAutoForm();

TemplateClass.helpers({

  cls: function() {
    return this.name.trim().replace(/[\.\s]+/g, '-');
  },

  setUpDropdown: function() {
    setUpDropdown();
  }

//  processedItems: function () {
//    var items = Collections.getItems(this.items);
//    var labelAttr = this.labelAttr || 'name';
//    var valueAttr = this.valueAttr || '_id';
//    return _.map(items, function(item) {
//      return {
//        label: item[labelAttr],
//        value: item[valueAttr]
//      }
//    });
//  },
//  afterUpdate: function () {
//    // Only requested to ensure this is called on updates to the
//    var items = Collections.getItems(this.items);
//  }
//  labelAttr: function () {
//    return this.labelAttr || 'name';
//  },
//  valueAttr: function () {
//    return this.valueAttr || '_id';
//  }

});
<commit_msg>Added support for empty item label. Set empty value to null.<commit_after>var templateName = 'dropdown';
var TemplateClass = Template[templateName];

var defaultText = 'Select';

TemplateClass.created = function() {
  var data = this.data;
  var name = data.name;
  if (!name) {
    throw new Error('Name required for dropdown.');
  }
  // The next value to set once the item becomes available.
  this.nextValue = null;
};

TemplateClass.rendered = function() {
  this.isRendered = true;
  var $dropdown = getDropdown(this).dropdown();
  setUpDropdown();
  var $input = getValueInput(this);
  var data = this.data;
  // "atts" is only provided when used with AutoForm.
  var atts = data.atts || data;

  var schemaKey = atts.schemaKey;
  schemaKey = schemaKey !== undefined ? schemaKey : true;

  // Handle changes to the collection.
  var items = atts.items;
  var cursor = items;
  // TODO(aramk) Add dependency to Collections.
  if (Collections.isCollection(items)) {
    cursor = items.find();
  }
  if (Collections.isCursor(cursor)) {
    var shouldObserve = false;
    // If passed a cursor, listen for changes and update the items reactively.
    var changeHandler = _.debounce(function() {
      shouldObserve && setUpDropdown(this);
    }.bind(this), 500);
    this._changeHandle = cursor.observe({
      added: changeHandler,
      changed: changeHandler,
      removed: changeHandler
    });
    // Delay observing to avoid the added() callback firing for existing items.
    shouldObserve = true;
  }

  if (!schemaKey) {
    $input.removeAttr('data-schema-key');
  }
};

TemplateClass.destroyed = function() {
  this._changeHandle && this._changeHandle.stop();
};

TemplateClass.hasValue = function(em, value) {
  return $('.menu [data-value="' + value + '"]', resolveElement(em)).length > 0;
};

TemplateClass.setValue = function(em, value) {
  var $em = resolveElement(em);
  var template = Templates.getInstanceFromElement(em);
  var shouldClear = !value;
  // Sanitize the values to null if they are falsey so we don't attempt to set the value to null
  // when it's currently an empty string.
  var existingValue = TemplateClass.getValue($em) || null;
  value = value || null;
  var hasValue = TemplateClass.hasValue($em, value);
  if (value !== existingValue) {
    if (hasValue) {
      $em.dropdown('set value', value).dropdown('set selected', value);
    } else if (shouldClear) {
      $em.dropdown('set text', defaultText);
    }
    template.nextValue = hasValue ? null : value;
    return true;
  } else {
    return false;
  }
};

TemplateClass.getValue = function(em) {
  var $em = resolveElement(em);
  return $em.length > 0 ? $em.dropdown('get value') : null;
};

TemplateClass.getItem = function(em, value) {
  var $menu = $('.menu', resolveElement(em));
  return $('.item[data-value=' + value + ']', $menu);
};

TemplateClass.bindVarToElement = function(em, reactiveVariable, args) {
  Templates.bindVarToElement(resolveElement(em), reactiveVariable, _.extend({
    getValue: function() {
      return TemplateClass.getValue($(this));
    },
    setValue: function(value) {
      TemplateClass.setValue(value);
    }
  }, args));
};

TemplateClass.isDropdown = function(em) {
  return resolveElement(em).length === 1;
};

function setUpDropdown(template) {
  template = getTemplate(template);
  if (!template.isRendered) {
    return;
  }

  var data = template.data;
  // "atts" is only provided when used with AutoForm.
  var atts = data.atts || data;
  var labelAttr = atts.labelAttr || 'name';
  var valueAttr = atts.valueAttr || '_id';
  var allowEmpty = atts.allowEmpty;
  var emptyLabel = atts.emptyLabel || 'None';
  var origValue = data.value;
  // The last non-empty value.
  var lastValue;
  var nextValue = template.nextValue;
  var $dropdown = getDropdown(template);

  // Save the last valid selection and restore it when possible after setup.
  var currentValue = TemplateClass.getValue($dropdown);
  if (currentValue !== '') {
    lastValue = currentValue;
  } else if (nextValue) {
    lastValue = nextValue;
  }
  // TODO(aramk) This isn't efficient since we're manually recreating all items instead of
  // using the reactive template. We need a way to detect the template is re-rendered. Until then
  // this is a compromise.
  var items;
  if (data.selectOptions) {
    // AutoForm schema allowedValues.
    items = _.map(data.selectOptions, function(option) {
      var item = {};
      item[valueAttr] = option.value;
      item[labelAttr] = option.label;
      return item;
    });
  } else if (atts.items) {
    // Collection, cursor or array.
    items = Collections.getItems(atts.items);
  }
  if (!items) {
    throw new Error('Dropdown items not defined.');
  }

  // Sort items based on label.
  items.sort(function(itemA, itemB) {
    return itemA[labelAttr] < itemB[labelAttr] ? -1 : 1;
  });

  if (allowEmpty) {
    var emptyItem = {};
    emptyItem[labelAttr] = emptyLabel;
    emptyItem[valueAttr] = null;
    items.unshift(emptyItem);
  }
  var $menu = template.$('.menu');
  $menu.empty();
  _.each(items, function(item) {
    if (!Types.isObject(item)) {
      var value = item;
      item = {};
      item[valueAttr] = value;
      item[labelAttr] = value;
    }
    var $item = $('
' + item[labelAttr] +
        '
');
    $menu.append($item);
  });

  $dropdown.on('change', function() {
    var text = atts.text;
    if (text !== undefined) {
      $dropdown.dropdown('set text', text);
    }
  });

  var value = lastValue !== undefined ? lastValue : origValue;
  if (value) {
    if (!TemplateClass.hasValue($dropdown, value)) {
      value = '';
    }
    // Without a defer, setting the value doesn't work after the initial load.
    _.defer(function() {
      TemplateClass.setValue($dropdown, value);
    });
  }
  $dropdown.trigger('load');
}

function getDropdown(template) {
  return getTemplate(template).$('.ui.dropdown');
}

function getValueInput(template) {
  return getTemplate(template).$('input');
}

function getTemplate(template) {
  return Templates.getNamedInstance(templateName, template);
}

function resolveElement(em) {
  return $(em).closest('.ui.dropdown');
}

function registerAutoForm() {
  var autoform = Package['aldeed:autoform'];
  if (typeof autoform === 'undefined') {
    console.error('Cannot register input type - AutoForm not found.');
    return;
  }
  autoform.AutoForm.addInputType('dropdown', {
    template: 'dropdown',
    valueOut: function() {
      return TemplateClass.getValue(this.closest('.dropdown'));
    }
  });
}

registerAutoForm();

TemplateClass.helpers({

  cls: function() {
    return this.name.trim().replace(/[\.\s]+/g, '-');
  },

  setUpDropdown: function() {
    setUpDropdown();
  }

//  processedItems: function () {
//    var items = Collections.getItems(this.items);
//    var labelAttr = this.labelAttr || 'name';
//    var valueAttr = this.valueAttr || '_id';
//    return _.map(items, function(item) {
//      return {
//        label: item[labelAttr],
//        value: item[valueAttr]
//      }
//    });
//  },
//  afterUpdate: function () {
//    // Only requested to ensure this is called on updates to the
//    var items = Collections.getItems(this.items);
//  }
//  labelAttr: function () {
//    return this.labelAttr || 'name';
//  },
//  valueAttr: function () {
//    return this.valueAttr || '_id';
//  }

});
<|endoftext|>