<commit_before>// Generate same page layout for all routes
Router.configure({layoutTemplate: 'layout'});

// Route / to home.html
Router.route('/', {template: 'home'});

// Route /about to about.html
Router.route('/about');

// Test Node.js HTTP request/response processing
Router.route('/test/print/:val', httpTest, {where: 'server'});

// Test route for executing server-side javascript code
//Router.route('/test/hello', serverHello, {where: 'server'});

// Route for CSV export of issues
Router.route('issues.csv', exportIssuesCSV, {where: 'server'});

// Route for CSV export of issues.
Router.route('/report/csv/:prjName', genPrjIssueReportCSV, {where: 'server'});

// Route for HTML export of issues.
Router.route('/report/html/:prjName', genPrjIssueReportHtml, {where: 'server'});

/***  Functions used in above routes  ***/

// Generate issue report for a specific project in CSV format
function genPrjIssueReportCSV() {
    var prjName  = this.params.prjName;
    var filename = prjName + '-issues.csv';
    var fileData = "";
    var records = issueColl.find({PrjName: prjName}).fetch();

    // Build a CSV string. .
    console.log("Got " + records.length + " issue records for project " + prjName);
    fileData += toCsv(records);

    var headers = {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=" + filename,
      'Content-Length': fileData.length
    };
    this.response.writeHead(200, headers);
    this.response.end(fileData);
}

// Generate issue report for a specific project in CSV format
function genPrjIssueReportHtml() {
    var prjName  = this.params.prjName;
    var filename = prjName + '-issues.html';
    var fileData = "";
    var records = issueColl.find({PrjName: prjName},{sort: {IPriority: -1, TIssueName: 1}}).fetch();

    // Build a CSV string. .
    console.log("Got " + records.length + " issue records for project " + prjName);
    fileData += toHtml(records, prjName);

    var headers = {
      'Content-type': 'text/html',
      'Content-Disposition': "attachment; filename=" + filename,
      'Content-Length': fileData.length
    };
    this.response.writeHead(200, headers);
    this.response.end(fileData);
}

// Export all Issue data to CSV format
function exportIssuesCSV() {
    var filename = 'all-issues.csv';
    var fileData = "";
    var records = issueColl.find().fetch();

    // Build a CSV string. Oversimplified. You'd have to escape quotes and commas.
    //records.forEach(function(rec) {
    console.log("Got " + records.length + " issue records")
      fileData += toCsv(records);
    //});

    var headers = {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=" + filename,
      'Content-Length': fileData.length
    };
    this.response.writeHead(200, headers);
    this.response.end(fileData);
}

/**
* Converts a value to a string appropriate for entry into a CSV table (surrounded by quotes).
* @param {string|number|object} theValue
* @param {string} sDelimiter The string delimiter.  Defaults to a double quote (") if omitted.
*/
function toCsvValue(theValue, sDelimiter) {
    var t = typeof (theValue), output;
    if (theValue === null)
        theValue = "";

    theValue.replace(/"/g, '""');

    if (typeof (sDelimiter) === "undefined" || sDelimiter === null) {
            sDelimiter = '"';
    }

    if (t === "undefined" || t === null) {
            output = "";
    } else if (t === "string") {
            output = sDelimiter + theValue + sDelimiter;
    } else {
            output = String(theValue);
    }

    return output;
}

/**
* Converts an array of objects (with identical schemas) into a CSV table.
* @param {Array} objArray An array of objects.  Each object in the array must have the same property list.
* @param {string} sDelimiter The string delimiter.  Defaults to a double quote (") if omitted.
* @param {string} cDelimiter The column delimiter.  Defaults to a comma (,) if omitted.
* @return {string} The CSV equivalent of objArray.
*/
function toCsv(objArray, sDelimiter, cDelimiter) {
	var i, l, names = [], name, value, obj, row, output = "", n, nl;

	// Initialize default parameters.
	if (typeof (sDelimiter) === "undefined" || sDelimiter === null) {
		sDelimiter = '"';
	}
	if (typeof (cDelimiter) === "undefined" || cDelimiter === null) {
		cDelimiter = ",";
	}

	for (i = 0, l = objArray.length; i < l; i += 1) {
            // Get the names of the properties.
            obj = objArray[i];
            row = "";
            if (i === 0) {
                // Loop through the names
                for (name in obj) {
                    if (obj.hasOwnProperty(name)) {
                        if ((name !== 'IEvidence') && (name !== 'INotes')){
                            names.push(name);
                            row += [sDelimiter, name, sDelimiter, cDelimiter].join("");
                        }
                    }
                }
                row = row.substring(0, row.length - 1);
                output += row;
            }

            output += "\n";
            row = "";
            for (n = 0, nl = names.length; n < nl; n += 1) {
                name = names[n];
                if ((name !== 'IEvidence') && (name !== 'INotes')){
                    value = obj[name];
                    if (n > 0) {
                        row += cDelimiter;
                    }
                    row += toCsvValue(value, '"');
                }
            }
            output += row;
	}

	return output;
}

// Run a simple HTTP test with parameter
function httpTest() {
  // NodeJS request object
  var request = this.request;

  // NodeJS  response object
  var response = this.response;

  this.response.end('Parameter passed to httpTest(): ' + this.params.val);
}

/* Remove so that we don't have a dependency on npm-container/meteorhacks:npm (causing startup issues on waptr1)
// Run a simple test for node.js API (using os module)
function serverHello(){
  os = Npm.require('os');
  var req = this.request;
  var res = this.response;
  res.end('Hello from '+os.hostname());
}
*/

/**
* Converts an array of objects (with identical schemas) into HTML
* @param {Array} objArray An array of objects.  Each object in the array must have the same property list.
* @return {string} The CSV equivalent of objArray.
*/
function toHtml(objArray, prjName) {
	var obj = {};
        var output = "\n";
        var priority = "N/A", prevPrio = "";
        var cweUriBase   = "https://cwe.mitre.org/data/definitions/";
        output += "\n";

        // Traverse the array of issue objects
        output += "\n\n";

        // Generate issue summary
        output += "
Issue Summary
"
        output += "
Project ID: " + prjName + "
\n";
        output += "
The below table contains a summary of issues. You may click the links in the Issue column to jump to the specific issue details.
"
        output += "
\n";
        output += "
Priority	Issue	Count
";
	for (var i = 0; i < objArray.length; i++) {
            obj = objArray[i];
            prevPrio = priority;
            priority = obj.IPriorityText;

            // Don't print the informational findings (for the tester)
            if (priority === 'Info')
                continue;

            // Count the number of URIs
            var count=0;
            if (obj.IURIs !== undefined)
                count = obj.IURIs.split("\n").length;

            // Print each issue with the issue as the header and the details as part of a table.
            if ((priority !== undefined)&&(priority !== "")&&(priority !== prevPrio))
                output += "
" + priority + "	";
            else
                output += "
	";
            output += "" + obj.TIssueName + "	";
            output += "" + count + "	";
            output += "
\n";
	}
        output += "
\n";

        // Generate detailed issue report
        output += "
Issue Details
"
        output += "
\n"
	for (var i = 0; i < objArray.length; i++) {
            obj = objArray[i];
            prevPrio = priority;
            priority = obj.IPriorityText;

            // Don't print the informational findings (for the tester)
            if (priority === 'Info')
                continue;

            // Print each issue with the issue as the header and the details as part of a table.
            output += "
	" + obj.TIssueName + "
\n";
            if ((obj.CweId !== undefined)&&(obj.CweId !== ""))
                output += "
CWE ID: 	" + obj.CweId + "
\n";
            if ((obj.IURIs !== undefined)&&(obj.IURIs !== "")){
                output += "
URI(s):
";
                var uri = obj.IURIs.split("\n");
                for (var j=0; j<uri.length; j++)
                    output += "
" + htmlEncode(uri[j], true, 4) + "
\n";
                output += "

\n";
            }
            if ((obj.TSeverityText !== undefined)&&(obj.TSeverityText !== ""))
                output += "
Severity: 	" + obj.TSeverityText + "
\n";
            if ((obj.IPriorityText !== undefined)&&(obj.IPriorityText !== ""))
                output += "
Priority: 	" + obj.IPriorityText + "
\n";
            if ((obj.INotes !== undefined)&&(obj.INotes !== ""))
                output += "
Notes: 	" + htmlEncode(obj.INotes, true, 4) + "
\n";
            if ((obj.IEvidence !== undefined)&&(obj.IEvidence !== ""))
                output += "
Evidence: 	" + htmlEncode(obj.IEvidence, true, 4) + "
\n";
            output += "

\n";
	}
        output += "
\n";
        output += "\n\n";
	return output;
}

/*
HTMLEncode - Encode HTML special characters.
Copyright (c) 2006-2010 Thomas Peri, http://www.tumuski.com/
MIT License
*/

/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true,
	plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */

/**
 * HTML-Encode the supplied input
 *
 * Parameters:
 *
 * (String)  source    The text to be encoded.
 *
 * (boolean) display   The output is intended for display.
 *
 *                     If true:
 *                     * Tabs will be expanded to the number of spaces
 *                       indicated by the 'tabs' argument.
 *                     * Line breaks will be converted to
.
 *
 *                     If false:
 *                     * Tabs and linebreaks get turned into &#____;
 *                       entities just like all other control characters.
 *
 * (integer) tabs      The number of spaces to expand tabs to.  (Ignored
 *                     when the 'display' parameter evaluates to false.)
 *
 * version 2010-11-08
 */
var htmlEncode = function (source, display, tabs) {
	var i, s, ch, peek, line, result,
		next, endline, push,
		spaces;

        if (source === undefined)
            return "";

	// Stash the next character and advance the pointer
	next = function () {
		peek = source.charAt(i);
		i += 1;
	};

	// Start a new "line" of output, to be joined later by

	endline = function () {
		line = line.join('');
		if (display) {
			// If a line starts or ends with a space, it evaporates in html
			// unless it's an nbsp.
			line = line.replace(/(^ )|( $)/g, ' ');
		}
		result.push(line);
		line = [];
	};

	// Push a character or its entity onto the current line
	push = function () {
		if (ch < ' ' || ch > '~') {
			line.push('&#' + ch.charCodeAt(0) + ';');
		} else {
			line.push(ch);
		}
	};

	// Use only integer part of tabs, and default to 4
	tabs = (tabs >= 0) ? Math.floor(tabs) : 4;

	result = [];
	line = [];

	i = 0;
	next();
	while (i <= source.length) { // less than or equal, because i is always one ahead
		ch = peek;
		next();

		// HTML special chars.
		switch (ch) {
		case '<':
			line.push('<');
			break;
		case '>':
			line.push('>');
			break;
		case '&':
			line.push('&');
			break;
		case '"':
			line.push('"');
			break;
		case "'":
			line.push(''');
			break;
		default:
			// If the output is intended for display,
			// then end lines on newlines, and replace tabs with spaces.
			if (display) {
				switch (ch) {
				case '\r':
					// If this \r is the beginning of a \r\n, skip over the \n part.
					if (peek === '\n') {
						next();
					}
					endline();
					break;
				case '\n':
					endline();
					break;
				case '\t':
					// expand tabs
					spaces = tabs - (line.length % tabs);
					for (s = 0; s < spaces; s += 1) {
						line.push(' ');
					}
					break;
				default:
					// All other characters can be dealt with generically.
					push();
				}
			} else {
				// If the output is not for display,
				// then none of the characters need special treatment.
				push();
			}
		}
	}
	endline();

	// If you can't beat 'em, join 'em.
	result = result.join('
');

	if (display) {
		// Break up contiguous blocks of spaces with non-breaking spaces
		result = result.replace(/ {2}/g, '  ');
	}

	// tada!
	return result;
};
<commit_msg>Minor update.<commit_after>// Generate same page layout for all routes
Router.configure({layoutTemplate: 'layout'});

// Route / to home.html
Router.route('/', {template: 'home'});

// Route /about to about.html
Router.route('/about');

// Test Node.js HTTP request/response processing
Router.route('/test/print/:val', httpTest, {where: 'server'});

// Test route for executing server-side javascript code
//Router.route('/test/hello', serverHello, {where: 'server'});

// Route for CSV export of issues
Router.route('issues.csv', exportIssuesCSV, {where: 'server'});

// Route for CSV export of issues.
Router.route('/report/csv/:prjName', genPrjIssueReportCSV, {where: 'server'});

// Route for HTML export of issues.
Router.route('/report/html/:prjName', genPrjIssueReportHtml, {where: 'server'});

/***  Functions used in above routes  ***/

// Generate issue report for a specific project in CSV format
function genPrjIssueReportCSV() {
    var prjName  = this.params.prjName;
    var filename = prjName + '-issues.csv';
    var fileData = "";
    var records = issueColl.find({PrjName: prjName}).fetch();

    // Build a CSV string. .
    console.log("Got " + records.length + " issue records for project " + prjName);
    fileData += toCsv(records);

    var headers = {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=" + filename,
      'Content-Length': fileData.length
    };
    this.response.writeHead(200, headers);
    this.response.end(fileData);
}

// Generate issue report for a specific project in CSV format
function genPrjIssueReportHtml() {
    var prjName  = this.params.prjName;
    var filename = prjName + '-issues.html';
    var fileData = "";
    var records = issueColl.find({PrjName: prjName},{sort: {IPriority: -1, TIssueName: 1}}).fetch();

    // Build a CSV string. .
    console.log("Got " + records.length + " issue records for project " + prjName);
    fileData += toHtml(records, prjName);

    var headers = {
      'Content-type': 'text/html',
      'Content-Disposition': "attachment; filename=" + filename,
      'Content-Length': fileData.length
    };
    this.response.writeHead(200, headers);
    this.response.end(fileData);
}

// Export all Issue data to CSV format
function exportIssuesCSV() {
    var filename = 'all-issues.csv';
    var fileData = "";
    var records = issueColl.find().fetch();

    // Build a CSV string. Oversimplified. You'd have to escape quotes and commas.
    //records.forEach(function(rec) {
    console.log("Got " + records.length + " issue records")
      fileData += toCsv(records);
    //});

    var headers = {
      'Content-type': 'text/csv',
      'Content-Disposition': "attachment; filename=" + filename,
      'Content-Length': fileData.length
    };
    this.response.writeHead(200, headers);
    this.response.end(fileData);
}

/**
* Converts a value to a string appropriate for entry into a CSV table (surrounded by quotes).
* @param {string|number|object} theValue
* @param {string} sDelimiter The string delimiter.  Defaults to a double quote (") if omitted.
*/
function toCsvValue(theValue, sDelimiter) {
    var t = typeof (theValue), output;
    if (theValue === null)
        theValue = "";

    theValue.replace(/"/g, '""');

    if (typeof (sDelimiter) === "undefined" || sDelimiter === null) {
            sDelimiter = '"';
    }

    if (t === "undefined" || t === null) {
            output = "";
    } else if (t === "string") {
            output = sDelimiter + theValue + sDelimiter;
    } else {
            output = String(theValue);
    }

    return output;
}

/**
* Converts an array of objects (with identical schemas) into a CSV table.
* @param {Array} objArray An array of objects.  Each object in the array must have the same property list.
* @param {string} sDelimiter The string delimiter.  Defaults to a double quote (") if omitted.
* @param {string} cDelimiter The column delimiter.  Defaults to a comma (,) if omitted.
* @return {string} The CSV equivalent of objArray.
*/
function toCsv(objArray, sDelimiter, cDelimiter) {
	var i, l, names = [], name, value, obj, row, output = "", n, nl;

	// Initialize default parameters.
	if (typeof (sDelimiter) === "undefined" || sDelimiter === null) {
		sDelimiter = '"';
	}
	if (typeof (cDelimiter) === "undefined" || cDelimiter === null) {
		cDelimiter = ",";
	}

	for (i = 0, l = objArray.length; i < l; i += 1) {
            // Get the names of the properties.
            obj = objArray[i];
            row = "";
            if (i === 0) {
                // Loop through the names
                for (name in obj) {
                    if (obj.hasOwnProperty(name)) {
                        if ((name !== 'IEvidence') && (name !== 'INotes')){
                            names.push(name);
                            row += [sDelimiter, name, sDelimiter, cDelimiter].join("");
                        }
                    }
                }
                row = row.substring(0, row.length - 1);
                output += row;
            }

            output += "\n";
            row = "";
            for (n = 0, nl = names.length; n < nl; n += 1) {
                name = names[n];
                if ((name !== 'IEvidence') && (name !== 'INotes')){
                    value = obj[name];
                    if (n > 0) {
                        row += cDelimiter;
                    }
                    row += toCsvValue(value, '"');
                }
            }
            output += row;
	}

	return output;
}

// Run a simple HTTP test with parameter
function httpTest() {
  // NodeJS request object
  var request = this.request;

  // NodeJS  response object
  var response = this.response;

  this.response.end('Parameter passed to httpTest(): ' + this.params.val);
}

/* Remove so that we don't have a dependency on npm-container/meteorhacks:npm (causing startup issues on waptr1)
// Run a simple test for node.js API (using os module)
function serverHello(){
  os = Npm.require('os');
  var req = this.request;
  var res = this.response;
  res.end('Hello from '+os.hostname());
}
*/

/**
* Converts an array of objects (with identical schemas) into HTML
* @param {Array} objArray An array of objects.  Each object in the array must have the same property list.
* @return {string} The CSV equivalent of objArray.
*/
function toHtml(objArray, prjName) {
	var obj = {};
        var output = "\n";
        var priority = "N/A", prevPrio = "";
        var cweUriBase   = "https://cwe.mitre.org/data/definitions/";
        output += "\n";

        // Traverse the array of issue objects
        output += "\n\n";

        // Generate issue summary
        output += "
Issue Summary
"
        output += "
Project ID: " + prjName + "
\n";
        output += "
The below table contains a summary of issues. You may click the links in the Issue column to jump to the specific issue details.
"
        output += "
\n";
        output += "
Priority	Issue	Count
";
	for (var i = 0; i < objArray.length; i++) {
            obj = objArray[i];
            prevPrio = priority;
            priority = obj.IPriorityText;

            // Don't print the informational findings (for the tester)
            if (priority === 'Info')
                continue;

            // Count the number of URIs
            var count=0;
            if (obj.IURIs !== undefined)
                count = obj.IURIs.split("\n").length;

            // Print each issue with the issue as the header and the details as part of a table.
            if ((priority !== undefined)&&(priority !== "")&&(priority !== prevPrio))
                output += "
" + priority + "	";
            else
                output += "
	";
            output += "" + obj.TIssueName + "	";
            output += "" + count + "	";
            output += "
\n";
	}
        output += "
\n";

        // Generate detailed issue report
        output += "
Issue Details
"
        output += "
\n"
	for (var i = 0; i < objArray.length; i++) {
            obj = objArray[i];
            prevPrio = priority;
            priority = obj.IPriorityText;

            // Don't print the informational findings (for the tester)
            if (priority === 'Info')
                continue;

            // Print each issue with the issue as the header and the details as part of a table.
            output += "
	" + obj.TIssueName + "
\n";
            if ((obj.CweId !== undefined)&&(obj.CweId !== ""))
                output += "
CWE ID: 	" + obj.CweId + "
\n";
            if ((obj.IURIs !== undefined)&&(obj.IURIs !== "")){
                output += "
URI(s):
";
                var uri = obj.IURIs.split("\n");
                for (var j=0; j<uri.length; j++)
                    output += "
" + htmlEncode(uri[j], true, 4) + "
\n";
                output += "

\n";
            }
            if ((obj.TSeverityText !== undefined)&&(obj.TSeverityText !== ""))
                output += "
Severity: 	" + obj.TSeverityText + "
\n";
            if ((obj.IPriorityText !== undefined)&&(obj.IPriorityText !== ""))
                output += "
Priority: 	" + obj.IPriorityText + "
\n";
            if ((obj.INotes !== undefined)&&(obj.INotes !== ""))
                output += "
Notes: 	" + htmlEncode(obj.INotes, true, 4) + "
\n";
            if ((obj.IEvidence !== undefined)&&(obj.IEvidence !== ""))
                output += "
Evidence: 	" + htmlEncode(obj.IEvidence, true, 4) + "
\n";
            output += "

\n";
	}
        output += "
\n";
        output += "\n\n";
	return output;
}

/*
HTMLEncode - Encode HTML special characters.
Copyright (c) 2006-2010 Thomas Peri, http://www.tumuski.com/
MIT License
*/

/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true,
	plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */

/**
 * HTML-Encode the supplied input
 *
 * Parameters:
 *
 * (String)  source    The text to be encoded.
 *
 * (boolean) display   The output is intended for display.
 *
 *                     If true:
 *                     * Tabs will be expanded to the number of spaces
 *                       indicated by the 'tabs' argument.
 *                     * Line breaks will be converted to
.
 *
 *                     If false:
 *                     * Tabs and linebreaks get turned into &#____;
 *                       entities just like all other control characters.
 *
 * (integer) tabs      The number of spaces to expand tabs to.  (Ignored
 *                     when the 'display' parameter evaluates to false.)
 *
 * version 2010-11-08
 */
var htmlEncode = function (source, display, tabs) {
	var i, s, ch, peek, line, result,
		next, endline, push,
		spaces;

        if (source === undefined)
            return "";

	// Stash the next character and advance the pointer
	next = function () {
		peek = source.charAt(i);
		i += 1;
	};

	// Start a new "line" of output, to be joined later by

	endline = function () {
		line = line.join('');
		if (display) {
			// If a line starts or ends with a space, it evaporates in html
			// unless it's an nbsp.
			line = line.replace(/(^ )|( $)/g, ' ');
		}
		result.push(line);
		line = [];
	};

	// Push a character or its entity onto the current line
	push = function () {
		if (ch < ' ' || ch > '~') {
			line.push('&#' + ch.charCodeAt(0) + ';');
		} else {
			line.push(ch);
		}
	};

	// Use only integer part of tabs, and default to 4
	tabs = (tabs >= 0) ? Math.floor(tabs) : 4;

	result = [];
	line = [];

	i = 0;
	next();
	while (i <= source.length) { // less than or equal, because i is always one ahead
		ch = peek;
		next();

		// HTML special chars.
		switch (ch) {
		case '<':
			line.push('<');
			break;
		case '>':
			line.push('>');
			break;
		case '&':
			line.push('&');
			break;
		case '"':
			line.push('"');
			break;
		case "'":
			line.push(''');
			break;
		default:
			// If the output is intended for display,
			// then end lines on newlines, and replace tabs with spaces.
			if (display) {
				switch (ch) {
				case '\r':
					// If this \r is the beginning of a \r\n, skip over the \n part.
					if (peek === '\n') {
						next();
					}
					endline();
					break;
				case '\n':
					endline();
					break;
				case '\t':
					// expand tabs
					spaces = tabs - (line.length % tabs);
					for (s = 0; s < spaces; s += 1) {
						line.push(' ');
					}
					break;
				default:
					// All other characters can be dealt with generically.
					push();
				}
			} else {
				// If the output is not for display,
				// then none of the characters need special treatment.
				push();
			}
		}
	}
	endline();

	// If you can't beat 'em, join 'em.
	result = result.join('
');

	if (display) {
		// Break up contiguous blocks of spaces with non-breaking spaces
		result = result.replace(/ {2}/g, '  ');
	}

	// tada!
	return result;
};
<|endoftext|>


<commit_before>/**
 * Copyright 2015 Kitware Inc.
 *
 * 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.
 */

/**
 * Define tasks related to loading, configuring, and building plugins.
 */
module.exports = function (grunt) {
    var _ = require('underscore');
    var fs = require('fs');
    var path = require('path');
    var customWebpackPlugins = require('./webpack.plugins.js');
    var paths = require('./webpack.paths.js');

    var buildAll = grunt.option('all-plugins');
    var plugins = grunt.option('plugins');

    if (_.isString(plugins) && plugins) {
        plugins = plugins.split(',');
    } else if (!buildAll) {
        return;
    }

    grunt.config.merge({
        default: {
            plugin: {}
        }
    });

    /**
     * Adds configuration for plugin related multitasks:
     *
     *   * copy:plugin-<plugin name>
     *      Copies any other files that are served statically
     */
    var addMultitasks = function (plugin) {
        var pluginTarget = 'plugin-' + plugin,
            pluginPath = path.resolve(grunt.config.get('pluginDir'), plugin),
            staticPath = path.resolve(grunt.config.get('staticDir'), 'built', 'plugins', plugin),
            cfg = {
                copy: {},
                default: {},
                plugin: {}
            };

        // create the plugin's build directory under the web-root
        grunt.file.mkdir(staticPath);

        // TODO: this could be moved to Webpack using kevlened/copy-webpack-plugin
        cfg.copy[pluginTarget] = {
            files: [{
                expand: true,
                cwd: pluginPath + '/web_client',
                src: ['extra/**'],
                dest: staticPath
            }]
        };

        // the task 'plugin:<plugin name>' is a task alias to run all
        // of the main tasks defined above and possibly more if
        // the plugin itself appends tasks to this array
        cfg.plugin[plugin] = {
            tasks: [
                'copy:' + pluginTarget
            ]
        };

        grunt.config.merge(cfg);
    };

    var configurePluginForBuilding = function (dir) {
        var plugin = path.basename(dir);
        var json = path.resolve(dir, 'plugin.json');
        var yml = path.resolve(dir, 'plugin.yml');
        var config = {};
        var cfgFile = 'no config file';

        if (!fs.statSync(dir).isDirectory()) {
            grunt.fail.warn('Plugin directory not found: ' + dir);
            return;
        }

        if (fs.existsSync(json)) {
            config = grunt.file.readYAML(json);
            cfgFile = 'plugin.json';
        } else if (fs.existsSync(yml)) {
            cfgFile = 'plugin.yml';
            config = grunt.file.readYAML(yml);
        }

        console.log(`Configuring plugin ${plugin.magenta} (${cfgFile})`);

        var doAutoBuild = (
            !_.isObject(config.grunt) ||
            _.isUndefined(config.grunt.autobuild) ||
            _.isNull(config.grunt.autobuild) ||
            !!config.grunt.autobuild
        );

        if (doAutoBuild) {
            // merge in configuration for the main plugin build tasks
            addMultitasks(plugin);
        }

        // Find the plugin's webpack helper file; default to the identity
        // function.
        var webpackHelperFile = path.resolve(dir, config.webpack && config.webpack.configHelper || 'webpack.helper.js');
        var webpackHelper;
        if (fs.existsSync(webpackHelperFile)) {
            console.log(`Loading webpack helper from ${webpackHelperFile}`);
            webpackHelper = require(webpackHelperFile);
        } else {
            console.log('No webpack helper file found.');
            webpackHelper = function (x) {
                return x;
            };
        }

        // Configure the output file; default to 'plugin.min.js' - Girder loads
        // files named "plugin.min.js" into the Girder web client at runtime, so
        // the user can control whether this is a "Girder client extension" or
        // just a standalone web client.
        var output = config.webpack && config.webpack.output || 'plugin';
        var pluginEntry =  `plugins/${plugin}/${output}`;

        var pluginNodeDir = path.resolve(process.cwd(), 'node_modules_' + plugin, 'node_modules');
        var helperConfig = {
            plugin: plugin,
            pluginEntry: pluginEntry,
            pluginDir: dir,
            nodeDir: pluginNodeDir
        };

        // Add webpack target and name resolution for this plugin if
        // web_client/main.js (or user-specified name) exists.
        var webClient = path.resolve(dir + '/web_client');
        var main = config.webpack && config.webpack.main && path.resolve(dir, config.webpack.main) || webClient + '/main.js';

        if (fs.existsSync(main)) {
            grunt.config.merge({
                webpack: {
                    [`plugin_${plugin}`]: {
                        entry: {
                            [helperConfig.pluginEntry]: [main]
                        },
                        plugins: [
                            new customWebpackPlugins.DllReferenceByPathPlugin({
                                context: '.',
                                manifest: path.join(paths.web_built, 'girder_lib-manifest.json')
                            })
                        ]
                    },
                    options: {
                        // Add an import alias to the global config for this plugin
                        resolve: {
                            alias: {
                                [`girder_plugins/${plugin}/node`]: pluginNodeDir,
                                [`girder_plugins/${plugin}`]: webClient
                            },
                            modules: [
                                path.resolve(process.cwd(), 'node_modules'),
                                pluginNodeDir
                            ]
                        },
                        resolveLoader: {
                            modules: [
                                path.resolve(process.cwd(), 'node_modules'),
                                pluginNodeDir
                            ]
                        }
                    }
                }
            });

            grunt.config.merge({
                default: {
                    [`webpack:plugin_${plugin}`]: {
                        dependencies: ['build'] // plugin builds must run after core build
                    }
                }
            });

            // If the plugin config has no webpack section, no defaultLoaders
            // property in the webpack section, or the defaultLoaders is
            // explicitly set to anything besides false, then augment the
            // webpack loader configurations with the plugin source directory.
            if (!config.webpack || config.webpack.defaultLoaders === undefined || config.webpack.defaultLoaders !== false) {
                var numLoaders = grunt.config.get('webpack.options.module.loaders').length;
                for (var i = 0; i < numLoaders; i++) {
                    var selector = 'webpack.options.module.loaders.' + i + '.include';
                    var loaders = grunt.config.get(selector);
                    grunt.config.set(selector, loaders.concat([path.resolve(dir)]));
                }
            }

            var newConfig = webpackHelper(grunt.config.get('webpack.options'), helperConfig);
            grunt.config.set('webpack.options', newConfig);
        }

        grunt.registerTask('npm-install', 'Install plugin NPM dependencies', function (plugin) {
            var done = this.async();

            var target = path.resolve('node_modules_' + plugin);

            if (fs.existsSync(target + '/node_modules')) {
                grunt.log.writeln('NPM dependencies for plugin ' + plugin + ' already installed.');
                done();
            }

            var npm = require('npm');
            var npmConf = {
                prefix: target,
                g: true
            };
            var errorHandler = require('npm/lib/utils/error-handler');

            var modules = Array.prototype.slice.call(arguments, 1);
            npm.load(npmConf, function (err) {
                if (err) {
                    return errorHandler(err);
                }
                npm.commands.install(modules, function (err, results) {
                    if (err) {
                        return errorHandler(err);
                    }
                    done();
                });
            });
        });

        function addDependencies(deps) {
            // install any additional npm packages during init
            var npm = (
                _(deps || [])
                    .map(function (version, dep) {
                        return [
                            dep.replace(':', '\\:'),
                            version.replace(':', '\\:')
                        ].join('@');
                    })
            );

            if (npm.length) {
                grunt.config.set('default.npm-install:' + plugin + ':' + grunt.config.escape(npm.join(':')), {});
            }
        }

        if (config.npm) {
            // If the config contains a "file" section, load NPM dependencies
            // from it.
            if (config.npm.file) {
                var npmFile = require(path.resolve(dir, config.npm.file));
                var fields = config.npm.fields || ['devDependencies', 'dependencies', 'optionalDependencies'];
                fields.forEach(function (field) {
                    addDependencies(npmFile[field]);
                });
            }

            // Additionally add any extra dependencies found in the
            // "dependencies" property.
            addDependencies(config.npm.dependencies);
        }

        if (config.grunt) {
            grunt.log.writeln((
                'Configuring plugin: ' + plugin + ' (custom Gruntfile)'
            ).bold);

            addDependencies(config.grunt.dependencies);

            // load the plugin's gruntfile
            try {
                require(
                    path.resolve(dir, config.grunt.file || 'Gruntfile.js')
                )(grunt);
            } catch (e) {
                // the error can be safely ignored when doing `grunt init`
                // otherwise a default task will most likely fail later on
                // write out a warning to help the developers debug errors
                grunt.log.writeln((
                    'Failed to load ' + plugin + '/' + (config.grunt.file || 'Gruntfile.js') + ':'
                ).yellow);
                grunt.log.writeln('>>> ' + e.toString().split('\n').join('\n>>> ').yellow);
            }

            // add default targets
            _(config.grunt.defaultTargets || []).each(function (target) {
                grunt.config.set('default.' + target, {});
            });
        }
    };

    if (buildAll) {
        // Glob for plugins and configure each one to be built
        grunt.file.expand(grunt.config.get('pluginDir') + '/*').forEach(function (dir) {
            configurePluginForBuilding(dir);
        });
    } else {
        // Build only the plugins that were requested via --plugins
        plugins.forEach(function (name) {
            configurePluginForBuilding(path.resolve(grunt.config.get('pluginDir'), name));
        });
    }

    /**
     * Register a "meta" task that will configure and run other tasks
     * to build a plugin. Keys in the config for this task should be the
     * directory of the plugin within the base plugins path.
     */
    grunt.registerMultiTask('plugin', 'Build and configure plugins', function () {
        var plugin = this.target,
            tasks = 'plugin.' + plugin + '.tasks';

        this.requiresConfig('pluginDir', tasks);

        // queue the build tasks
        grunt.config.get(tasks).forEach(function (task) {
            grunt.task.run(task);
        });
    });
};
<commit_msg>Invoke npm directly via child_process.spawn()<commit_after>/**
 * Copyright 2015 Kitware Inc.
 *
 * 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.
 */

/**
 * Define tasks related to loading, configuring, and building plugins.
 */
module.exports = function (grunt) {
    var _ = require('underscore');
    var fs = require('fs');
    var path = require('path');
    var child_process = require('child_process');

    var customWebpackPlugins = require('./webpack.plugins.js');
    var paths = require('./webpack.paths.js');

    var buildAll = grunt.option('all-plugins');
    var plugins = grunt.option('plugins');

    if (_.isString(plugins) && plugins) {
        plugins = plugins.split(',');
    } else if (!buildAll) {
        return;
    }

    grunt.config.merge({
        default: {
            plugin: {}
        }
    });

    /**
     * Adds configuration for plugin related multitasks:
     *
     *   * copy:plugin-<plugin name>
     *      Copies any other files that are served statically
     */
    var addMultitasks = function (plugin) {
        var pluginTarget = 'plugin-' + plugin,
            pluginPath = path.resolve(grunt.config.get('pluginDir'), plugin),
            staticPath = path.resolve(grunt.config.get('staticDir'), 'built', 'plugins', plugin),
            cfg = {
                copy: {},
                default: {},
                plugin: {}
            };

        // create the plugin's build directory under the web-root
        grunt.file.mkdir(staticPath);

        // TODO: this could be moved to Webpack using kevlened/copy-webpack-plugin
        cfg.copy[pluginTarget] = {
            files: [{
                expand: true,
                cwd: pluginPath + '/web_client',
                src: ['extra/**'],
                dest: staticPath
            }]
        };

        // the task 'plugin:<plugin name>' is a task alias to run all
        // of the main tasks defined above and possibly more if
        // the plugin itself appends tasks to this array
        cfg.plugin[plugin] = {
            tasks: [
                'copy:' + pluginTarget
            ]
        };

        grunt.config.merge(cfg);
    };

    var configurePluginForBuilding = function (dir) {
        var plugin = path.basename(dir);
        var json = path.resolve(dir, 'plugin.json');
        var yml = path.resolve(dir, 'plugin.yml');
        var config = {};
        var cfgFile = 'no config file';

        if (!fs.statSync(dir).isDirectory()) {
            grunt.fail.warn('Plugin directory not found: ' + dir);
            return;
        }

        if (fs.existsSync(json)) {
            config = grunt.file.readYAML(json);
            cfgFile = 'plugin.json';
        } else if (fs.existsSync(yml)) {
            cfgFile = 'plugin.yml';
            config = grunt.file.readYAML(yml);
        }

        console.log(`Configuring plugin ${plugin.magenta} (${cfgFile})`);

        var doAutoBuild = (
            !_.isObject(config.grunt) ||
            _.isUndefined(config.grunt.autobuild) ||
            _.isNull(config.grunt.autobuild) ||
            !!config.grunt.autobuild
        );

        if (doAutoBuild) {
            // merge in configuration for the main plugin build tasks
            addMultitasks(plugin);
        }

        // Find the plugin's webpack helper file; default to the identity
        // function.
        var webpackHelperFile = path.resolve(dir, config.webpack && config.webpack.configHelper || 'webpack.helper.js');
        var webpackHelper;
        if (fs.existsSync(webpackHelperFile)) {
            console.log(`Loading webpack helper from ${webpackHelperFile}`);
            webpackHelper = require(webpackHelperFile);
        } else {
            console.log('No webpack helper file found.');
            webpackHelper = function (x) {
                return x;
            };
        }

        // Configure the output file; default to 'plugin.min.js' - Girder loads
        // files named "plugin.min.js" into the Girder web client at runtime, so
        // the user can control whether this is a "Girder client extension" or
        // just a standalone web client.
        var output = config.webpack && config.webpack.output || 'plugin';
        var pluginEntry =  `plugins/${plugin}/${output}`;

        var pluginNodeDir = path.resolve(process.cwd(), 'node_modules_' + plugin, 'node_modules');
        var helperConfig = {
            plugin: plugin,
            pluginEntry: pluginEntry,
            pluginDir: dir,
            nodeDir: pluginNodeDir
        };

        // Add webpack target and name resolution for this plugin if
        // web_client/main.js (or user-specified name) exists.
        var webClient = path.resolve(dir + '/web_client');
        var main = config.webpack && config.webpack.main && path.resolve(dir, config.webpack.main) || webClient + '/main.js';

        if (fs.existsSync(main)) {
            grunt.config.merge({
                webpack: {
                    [`plugin_${plugin}`]: {
                        entry: {
                            [helperConfig.pluginEntry]: [main]
                        },
                        plugins: [
                            new customWebpackPlugins.DllReferenceByPathPlugin({
                                context: '.',
                                manifest: path.join(paths.web_built, 'girder_lib-manifest.json')
                            })
                        ]
                    },
                    options: {
                        // Add an import alias to the global config for this plugin
                        resolve: {
                            alias: {
                                [`girder_plugins/${plugin}/node`]: pluginNodeDir,
                                [`girder_plugins/${plugin}`]: webClient
                            },
                            modules: [
                                path.resolve(process.cwd(), 'node_modules'),
                                pluginNodeDir
                            ]
                        },
                        resolveLoader: {
                            modules: [
                                path.resolve(process.cwd(), 'node_modules'),
                                pluginNodeDir
                            ]
                        }
                    }
                }
            });

            grunt.config.merge({
                default: {
                    [`webpack:plugin_${plugin}`]: {
                        dependencies: ['build'] // plugin builds must run after core build
                    }
                }
            });

            // If the plugin config has no webpack section, no defaultLoaders
            // property in the webpack section, or the defaultLoaders is
            // explicitly set to anything besides false, then augment the
            // webpack loader configurations with the plugin source directory.
            if (!config.webpack || config.webpack.defaultLoaders === undefined || config.webpack.defaultLoaders !== false) {
                var numLoaders = grunt.config.get('webpack.options.module.loaders').length;
                for (var i = 0; i < numLoaders; i++) {
                    var selector = 'webpack.options.module.loaders.' + i + '.include';
                    var loaders = grunt.config.get(selector);
                    grunt.config.set(selector, loaders.concat([path.resolve(dir)]));
                }
            }

            var newConfig = webpackHelper(grunt.config.get('webpack.options'), helperConfig);
            grunt.config.set('webpack.options', newConfig);
        }

        grunt.registerTask('npm-install', 'Install plugin NPM dependencies', function (plugin) {
            var done = this.async();

            var target = path.resolve('node_modules_' + plugin);

            // Formulate path to npm executable.
            var npm = path.resolve(path.dirname(process.argv[0]), 'npm');

            // Launch npm install process.
            var modules = Array.prototype.slice.call(arguments, 1);
            var args = ['--color=always', '--prefix', target, 'install'].concat(modules);

            var child = child_process.spawn(npm, args);
            child.stdout.on('data', function (data) {
                process.stdout.write(data);
            });

            child.stderr.on('data', function (data) {
                process.stderr.write(data);
            });

            child.on('close', function (code) {
                done(code === 0);
            });
        });

        function addDependencies(deps) {
            // install any additional npm packages during init
            var npm = (
                _(deps || [])
                    .map(function (version, dep) {
                        return [
                            dep.replace(':', '\\:'),
                            version.replace(':', '\\:')
                        ].join('@');
                    })
            );

            if (npm.length) {
                grunt.config.set('default.npm-install:' + plugin + ':' + grunt.config.escape(npm.join(':')), {});
            }
        }

        if (config.npm) {
            // If the config contains a "file" section, load NPM dependencies
            // from it.
            if (config.npm.file) {
                var npmFile = require(path.resolve(dir, config.npm.file));
                var fields = config.npm.fields || ['devDependencies', 'dependencies', 'optionalDependencies'];
                fields.forEach(function (field) {
                    addDependencies(npmFile[field]);
                });
            }

            // Additionally add any extra dependencies found in the
            // "dependencies" property.
            addDependencies(config.npm.dependencies);
        }

        if (config.grunt) {
            grunt.log.writeln((
                'Configuring plugin: ' + plugin + ' (custom Gruntfile)'
            ).bold);

            addDependencies(config.grunt.dependencies);

            // load the plugin's gruntfile
            try {
                require(
                    path.resolve(dir, config.grunt.file || 'Gruntfile.js')
                )(grunt);
            } catch (e) {
                // the error can be safely ignored when doing `grunt init`
                // otherwise a default task will most likely fail later on
                // write out a warning to help the developers debug errors
                grunt.log.writeln((
                    'Failed to load ' + plugin + '/' + (config.grunt.file || 'Gruntfile.js') + ':'
                ).yellow);
                grunt.log.writeln('>>> ' + e.toString().split('\n').join('\n>>> ').yellow);
            }

            // add default targets
            _(config.grunt.defaultTargets || []).each(function (target) {
                grunt.config.set('default.' + target, {});
            });
        }
    };

    if (buildAll) {
        // Glob for plugins and configure each one to be built
        grunt.file.expand(grunt.config.get('pluginDir') + '/*').forEach(function (dir) {
            configurePluginForBuilding(dir);
        });
    } else {
        // Build only the plugins that were requested via --plugins
        plugins.forEach(function (name) {
            configurePluginForBuilding(path.resolve(grunt.config.get('pluginDir'), name));
        });
    }

    /**
     * Register a "meta" task that will configure and run other tasks
     * to build a plugin. Keys in the config for this task should be the
     * directory of the plugin within the base plugins path.
     */
    grunt.registerMultiTask('plugin', 'Build and configure plugins', function () {
        var plugin = this.target,
            tasks = 'plugin.' + plugin + '.tasks';

        this.requiresConfig('pluginDir', tasks);

        // queue the build tasks
        grunt.config.get(tasks).forEach(function (task) {
            grunt.task.run(task);
        });
    });
};
<|endoftext|>


<commit_before>import Editor from 'editor/model/Editor';

describe('StyleManager properties logic', () => {
  let obj;
  let em;
  let domc;
  let dv;
  let cssc;
  let sm;
  let cmp;
  let rule1;
  let rule2;

  beforeEach(() => {
    em = new Editor({
      mediaCondition: 'max-width',
      avoidInlineStyle: true,
      styleManager: { custom: true },
    });
    domc = em.get('DomComponents');
    cssc = em.get('CssComposer');
    dv = em.get('DeviceManager');
    sm = em.get('SelectorManager');
    obj = em.get('StyleManager');
    em.get('PageManager').onLoad();
    cmp = domc.addComponent(`<div class="cls"></div>`);
    [rule1, rule2] = cssc.addRules(`
        .cls { color: red; }
        @media (max-width: 992px) {
            .cls { color: blue; }
        }
    `);
  });

  afterEach(() => {
    obj = null;
    em.destroy();
  });

  describe('Composite type', () => {
    const sectorTest = 'sector-test';
    const propTest = 'padding';
    const propInTest = 'padding-top';
    let compTypeProp;
    let compTypePropInn;

    beforeEach(() => {
      obj.addSector(sectorTest, {
        properties: [
          {
            extend: propTest,
            detached: true,
          },
        ],
      });
      compTypeProp = obj.getProperty(sectorTest, propTest);
      compTypePropInn = compTypeProp.getProperty(propInTest);
      em.setSelected(cmp);
      obj.__upSel();
    });

    test('Property exists', () => {
      expect(compTypeProp).toBeTruthy();
      expect(compTypeProp.getProperties().length).toBe(4);
    });

    test('Has inner property', () => {
      expect(compTypePropInn).toBeTruthy();
    });

    test('Inner property has no value', () => {
      expect(compTypeProp.hasValue()).toBe(false);
      expect(compTypePropInn.hasValue()).toBe(false);
    });

    test('Rule selected', () => {
      expect(obj.getLastSelected()).toBe(rule1);
    });

    test('Updating inner property, it reflects on the rule', () => {
      compTypePropInn.upValue('55%');
      const style = rule1.getStyle();
      const otherProps = Object.keys(style).filter(p => p.indexOf('padding') >= 0 && p !== propInTest);
      expect(style[propInTest]).toBe('55%');
      expect(compTypeProp.hasValue()).toBe(true);
      expect(compTypePropInn.hasValue()).toBe(true);
      otherProps.forEach(prop => expect(style[prop]).toBe(''));
    });
  });
});
<commit_msg>Update tests<commit_after>import Editor from 'editor/model/Editor';

describe('StyleManager properties logic', () => {
  let obj;
  let em;
  let domc;
  let dv;
  let cssc;
  let sm;
  let cmp;
  let rule1;
  let rule2;

  beforeEach(() => {
    em = new Editor({
      mediaCondition: 'max-width',
      avoidInlineStyle: true,
      styleManager: { custom: true },
    });
    domc = em.get('DomComponents');
    cssc = em.get('CssComposer');
    dv = em.get('DeviceManager');
    sm = em.get('SelectorManager');
    obj = em.get('StyleManager');
    em.get('PageManager').onLoad();
    cmp = domc.addComponent(`<div class="cls"></div>`);
    [rule1, rule2] = cssc.addRules(`
        .cls { color: red; }
        @media (max-width: 992px) {
            .cls { color: blue; }
        }
    `);
  });

  afterEach(() => {
    obj = null;
    em.destroy();
  });

  describe('Composite type', () => {
    const sectorTest = 'sector-test';
    const propTest = 'padding';
    const propInTest = 'padding-top';
    let compTypeProp;
    let compTypePropInn;

    beforeEach(() => {
      obj.addSector(sectorTest, {
        properties: [
          {
            extend: propTest,
            detached: true,
          },
        ],
      });
      compTypeProp = obj.getProperty(sectorTest, propTest);
      compTypePropInn = compTypeProp.getProperty(propInTest);
      em.setSelected(cmp);
      obj.__upSel();
    });

    test('Property exists', () => {
      expect(compTypeProp).toBeTruthy();
      expect(compTypeProp.getProperties().length).toBe(4);
    });

    test('Has inner property', () => {
      expect(compTypePropInn).toBeTruthy();
    });

    test('Inner property has no value', () => {
      expect(compTypeProp.hasValue()).toBe(false);
      expect(compTypePropInn.hasValue()).toBe(false);
    });

    test('Rule selected', () => {
      expect(obj.getLastSelected()).toBe(rule1);
    });

    test('Updating inner property, it reflects on the rule', () => {
      compTypePropInn.upValue('55%');
      const style = rule1.getStyle();
      const otherProps = Object.keys(style).filter(p => p.indexOf('padding') >= 0 && p !== propInTest);
      expect(style[propInTest]).toBe('55%');
      expect(compTypeProp.hasValue()).toBe(true);
      expect(compTypePropInn.hasValue()).toBe(true);
      otherProps.forEach(prop => {
        expect(style[prop]).toBe('');
        if (prop !== propTest) {
          expect(compTypeProp.getProperty(prop).hasValue()).toBe(false);
        }
      });
    });
  });
});
<|endoftext|>


<commit_before>// The changes_couchdb command-line interface.
//

var COUCH = 'http://localhost:5984';
var DB    = 'cqs_test';

if(process.env.charles)
  COUCH = 'http://jhs-mac.local:15984';
  //COUCH = 'http://192.168.3.10:15984';

if(require.isBrowser) {
  COUCH = window.location.protocol + '//' + window.location.host;
  DB    = 'cqs_browser_test';
}

var cqs = require('../api').defaults({'couch':COUCH, 'db':DB})
  , util = require('util'), I = util.inspect
  , assert = require('assert')
  , request = require('request')
  ;

var state = {};
module.exports = [ // TESTS

function setup(done) {
  var url = COUCH + '/' + DB;
  request({method:'DELETE', uri:url}, function(er, resp, body) {
    if(er) throw er;
    var json = JSON.parse(body);

    var already_gone = (resp.statusCode === 404 && json.error === 'not_found');
    var deleted      = (resp.statusCode === 200 && json.ok    === true);

    if(! (already_gone || deleted))
      throw new Error('Unknown DELETE response: ' + resp.statusCode + ' ' + body);

    request({method:'PUT', uri:url}, function(er, resp, body) {
      if(er) throw er;
      var json = JSON.parse(body);

      if(resp.statusCode !== 201 || json.ok !== true)
        throw new Error('Unknown PUT response: ' + resp.statusCode + ' ' + body);

      done();
    })
  })
},

// =-=-=-=-=-=-=-=-=

function create_queue(done) {
  cqs.CreateQueue('foo', function(er, queue) {
    if(er) return done(er);
    assert.equal(queue.name, 'foo', "CreateQueue returns the queue name");
    state.foo = queue;
    done();
  })
},

function create_queue_with_obj(done) {
  cqs.CreateQueue({name:'bar', DefaultVisibilityTimeout:111}, function(er, queue) {
    if(er) return done(er);
    assert.equal(queue.name, 'bar', "CreateQueue returns the queue name");
    assert.equal(queue.VisibilityTimeout, 111, "Created with visibility 111");
    state.bar = queue;
    done();
  })
},

function instantiate_queue_loads_from_couch(done) {
  var should_be_bar = new cqs.Queue('bar');
  should_be_bar.confirm(function(er) {
    if(er) throw er;
    assert.equal(should_be_bar.VisibilityTimeout, 111, "Should get bar's visibility timeout");
    assert.equal(should_be_bar.VisibilityTimeout, state.bar.VisibilityTimeout, "Should get bar's visibility timeout");
    done();
  })
},

function list_queues(done) {
  cqs.ListQueues(function(er, queues) {
    if(er) throw er;
    assert.equal(2, queues.length);

    assert.any(queues, "Queue list should include foo", function(q) { return q.name == 'foo' });
    assert.any(queues, "Queue list should include bar", function(q) { return q.name == 'bar' });
    done();
  })
},

function list_queues_with_prefix(done) {
  cqs.ListQueues('f', function(er, queues) {
    if(er) throw er;
    assert.equal(1, queues.length);

    function is_foo(q) { return q.name == 'foo' }
    function is_bar(q) { return q.name == 'bar' }

    assert.none(queues, "Queues should not have bar", is_bar);
    assert.any(queues , "Queues should have foo"    , is_foo);

    cqs.ListQueues('b', function(er, queues) {
      if(er) throw er;
      assert.equal(1, queues.length);
      assert.none(queues, "Queues should not have foo", is_foo);
      assert.any(queues , "Queues should have bar"    , is_bar);
      done();
    })
  })
},

function send_message(done) {
  state.bar.send('Message one', function(er, msg) {
    if(er) throw er;

    // TODO: confirm MD5.

    ; ["Body", "MD5OfMessageBody", "MessageId"].forEach(function(key) {
      assert.ok(key in msg, "SendMessage result needs key: " + key);
    })

    assert.equal(msg.Body, 'Message one', "Message body should be what was sent");

    state.message_one = msg;
    done();
  })
},

function receive_no_message(done) {
  cqs.ReceiveMessage(state.foo, function(er, messages) {
    if(er) throw er;

    assert.equal(messages.length, 0, 'Foo queue should not have any messages yet');
    done();
  })
},

function receive_message(done) {
  state.bar.receive(function(er, messages) {
    if(er) throw er;

    assert.equal(messages.length, 1, 'Bar queue should have message from earlier');
    var msg = messages[0];
    assert.equal(msg.Body, state.message_one.Body, "Message should be message one's body");
    assert.equal(msg.Body, 'Message one'         , "Message should be message one's body");

    done();
  })
},

function set_queue_attribs(done) {
  cqs.SetQueueAttributes(state.foo, {'VisibilityTimeout':1.5}, function(er) {
    if(er) throw er;

    assert.equal(state.foo.VisibilityTimeout, 1.5, "Foo should have 1.5 second visibility now");
    new cqs.Queue('foo').confirmed(function(er, foo2) {
      if(er) throw er;

      assert.equal(foo2.VisibilityTimeout, state.foo.VisibilityTimeout, "Both foos should be 1.5");
      assert.equal(foo2.VisibilityTimeout,                         1.5, "Both foos should be 1.5");

      done();
    })
  })
},

function make_sure_new_message_has_the_attributes(done) {
  cqs.SendMessage(state.foo, "Should be 1.5 visibility timeout", function(er) {
    if(er) throw er;
    var now = new Date;
    cqs.ReceiveMessage(state.foo, function(er, msg) {
      if(er) throw er;
      msg = msg[0];

      var invisible_ms = msg.visible_at - now;
      assert.almost(invisible_ms, 1500, "Not-visible time (should be 1500): " + invisible_ms);

      state.half_sec = msg;
      done();
    })
  })
},

function send_message_api(done) {
  cqs.SendMessage('foo', 'API with string arg', function(er) {
    if(er) throw er;
    cqs.SendMessage(state.foo, 'API with queue arg', function(er) {
      if(er) throw er;
      state.foo.send('queue method call', function(er) {
        if(er) throw er;
        done();
      })
    })
  })
},

function receive_message_api(done) {
  cqs.ReceiveMessage('foo', function(er, msg) {
    if(er) throw er;
    assert.equal(msg[0].Body, 'API with string arg', 'Messages should arrive in order');
    cqs.ReceiveMessage(state.foo, function(er, msg) {
      if(er) throw er;
      assert.equal(msg[0].Body, 'API with queue arg', 'Messages should arrive in order');
      state.foo.receive(function(er, msg) {
        if(er) throw er;
        assert.equal(msg[0].Body, 'queue method call', 'Messages should arrive in order');
        done();
      })
    })
  })
},

{'timeout': 2000},
function delete_message(done) {
  var now = new Date;
  var vis_at = state.half_sec.visible_at;
  assert.ok(vis_at);
  assert.ok(vis_at - now > 0, "Too late to run this test: " + (vis_at - now));

  cqs.DeleteMessage(state.half_sec, function(er) {
    if(er) throw er;

    function check() {
      cqs.ReceiveMessage('foo', function(er, msg) {
        if(er) throw er;
        assert.equal(msg.length, 0, "Should be no more messages left");
        done();
      })
    }

    var remaining = vis_at - (new Date);
    if(remaining < 0)
      check();
    else
      setTimeout(check, remaining + 50);
  })
},

function get_queue_attributes(done) {
  cqs.GetQueueAttributes('bar', function(er, attrs) {
    if(er) throw er;

    assert.equal(attrs.VisibilityTimeout, state.bar.VisibilityTimeout, "Should be bar's visibility timeout");
    assert.equal(attrs.VisibilityTimeout,                         111, "Should be bar's visibility timeout");

    cqs.GetQueueAttributes(state.foo, '--force', ['all'], function(er, attrs) {
      if(er) throw er;

      assert.equal(attrs.VisibilityTimeout, state.foo.VisibilityTimeout, "Should be bar's visibility timeout");
      assert.equal(attrs.VisibilityTimeout,                         1.5, "Should be bar's visibility timeout");

      done();
    })
  })
},

] // TESTS
<commit_msg>Delete the messages so they don't crop up later<commit_after>// The changes_couchdb command-line interface.
//

var COUCH = 'http://localhost:5984';
var DB    = 'cqs_test';

if(process.env.charles)
  COUCH = 'http://jhs-mac.local:15984';
  //COUCH = 'http://192.168.3.10:15984';

if(require.isBrowser) {
  COUCH = window.location.protocol + '//' + window.location.host;
  DB    = 'cqs_browser_test';
}

var cqs = require('../api').defaults({'couch':COUCH, 'db':DB})
  , util = require('util'), I = util.inspect
  , assert = require('assert')
  , request = require('request')
  ;

var state = {};
module.exports = [ // TESTS

function setup(done) {
  var url = COUCH + '/' + DB;
  request({method:'DELETE', uri:url}, function(er, resp, body) {
    if(er) throw er;
    var json = JSON.parse(body);

    var already_gone = (resp.statusCode === 404 && json.error === 'not_found');
    var deleted      = (resp.statusCode === 200 && json.ok    === true);

    if(! (already_gone || deleted))
      throw new Error('Unknown DELETE response: ' + resp.statusCode + ' ' + body);

    request({method:'PUT', uri:url}, function(er, resp, body) {
      if(er) throw er;
      var json = JSON.parse(body);

      if(resp.statusCode !== 201 || json.ok !== true)
        throw new Error('Unknown PUT response: ' + resp.statusCode + ' ' + body);

      done();
    })
  })
},

// =-=-=-=-=-=-=-=-=

function create_queue(done) {
  //cqs.CreateQueue('foo', function(er, queue) {
  cqs.CreateQueue('foo', function(er, queue) {
    if(er) return done(er);
    assert.equal(queue.name, 'foo', "CreateQueue returns the queue name");
    state.foo = queue;
    done();
  })
},

function create_queue_with_obj(done) {
  cqs.CreateQueue({name:'bar', DefaultVisibilityTimeout:111}, function(er, queue) {
    if(er) return done(er);
    assert.equal(queue.name, 'bar', "CreateQueue returns the queue name");
    assert.equal(queue.VisibilityTimeout, 111, "Created with visibility 111");
    state.bar = queue;
    done();
  })
},

function instantiate_queue_loads_from_couch(done) {
  var should_be_bar = new cqs.Queue('bar');
  should_be_bar.confirm(function(er) {
    if(er) throw er;
    assert.equal(should_be_bar.VisibilityTimeout, 111, "Should get bar's visibility timeout");
    assert.equal(should_be_bar.VisibilityTimeout, state.bar.VisibilityTimeout, "Should get bar's visibility timeout");
    done();
  })
},

function list_queues(done) {
  cqs.ListQueues(function(er, queues) {
    if(er) throw er;
    assert.equal(2, queues.length);

    assert.any(queues, "Queue list should include foo", function(q) { return q.name == 'foo' });
    assert.any(queues, "Queue list should include bar", function(q) { return q.name == 'bar' });
    done();
  })
},

function list_queues_with_prefix(done) {
  cqs.ListQueues('f', function(er, queues) {
    if(er) throw er;
    assert.equal(1, queues.length);

    function is_foo(q) { return q.name == 'foo' }
    function is_bar(q) { return q.name == 'bar' }

    assert.none(queues, "Queues should not have bar", is_bar);
    assert.any(queues , "Queues should have foo"    , is_foo);

    cqs.ListQueues('b', function(er, queues) {
      if(er) throw er;
      assert.equal(1, queues.length);
      assert.none(queues, "Queues should not have foo", is_foo);
      assert.any(queues , "Queues should have bar"    , is_bar);
      done();
    })
  })
},

function send_message(done) {
  state.bar.send('Message one', function(er, msg) {
    if(er) throw er;

    // TODO: confirm MD5.

    ; ["Body", "MD5OfMessageBody", "MessageId"].forEach(function(key) {
      assert.ok(key in msg, "SendMessage result needs key: " + key);
    })

    assert.equal(msg.Body, 'Message one', "Message body should be what was sent");

    state.message_one = msg;
    done();
  })
},

function receive_no_message(done) {
  cqs.ReceiveMessage(state.foo, function(er, messages) {
    if(er) throw er;

    assert.equal(messages.length, 0, 'Foo queue should not have any messages yet');
    done();
  })
},

function receive_message(done) {
  state.bar.receive(function(er, messages) {
    if(er) throw er;

    assert.equal(messages.length, 1, 'Bar queue should have message from earlier');
    var msg = messages[0];
    assert.equal(msg.Body, state.message_one.Body, "Message should be message one's body");
    assert.equal(msg.Body, 'Message one'         , "Message should be message one's body");

    done();
  })
},

function set_queue_attribs(done) {
  cqs.SetQueueAttributes(state.foo, {'VisibilityTimeout':1.5}, function(er) {
    if(er) throw er;

    assert.equal(state.foo.VisibilityTimeout, 1.5, "Foo should have 1.5 second visibility now");
    new cqs.Queue('foo').confirmed(function(er, foo2) {
      if(er) throw er;

      assert.equal(foo2.VisibilityTimeout, state.foo.VisibilityTimeout, "Both foos should be 1.5");
      assert.equal(foo2.VisibilityTimeout,                         1.5, "Both foos should be 1.5");

      done();
    })
  })
},

function make_sure_new_message_has_the_attributes(done) {
  cqs.SendMessage(state.foo, "Should be 1.5 visibility timeout", function(er) {
    if(er) throw er;
    var now = new Date;
    cqs.ReceiveMessage(state.foo, function(er, msg) {
      if(er) throw er;
      msg = msg[0];

      var invisible_ms = msg.visible_at - now;
      assert.almost(invisible_ms, 1500, "Not-visible time (should be 1500): " + invisible_ms);

      state.half_sec = msg;
      done();
    })
  })
},

function send_message_api(done) {
  cqs.SendMessage('foo', 'API with string arg', function(er) {
    if(er) throw er;
    cqs.SendMessage(state.foo, 'API with queue arg', function(er) {
      if(er) throw er;
      state.foo.send('queue method call', function(er) {
        if(er) throw er;
        done();
      })
    })
  })
},

function receive_message_api(done) {
  var messages = [];

  cqs.ReceiveMessage('foo', function(er, msg) {
    if(er) throw er;
    assert.equal(msg[0].Body, 'API with string arg', 'Messages should arrive in order');

    messages.push(msg[0]);
    cqs.ReceiveMessage(state.foo, function(er, msg) {
      if(er) throw er;
      assert.equal(msg[0].Body, 'API with queue arg', 'Messages should arrive in order');

      messages.push(msg[0]);
      state.foo.receive(function(er, msg) {
        if(er) throw er;
        assert.equal(msg[0].Body, 'queue method call', 'Messages should arrive in order');

        messages.push(msg[0]);

        var deleted = 0;
        messages.forEach(function(msg) {
          msg.delete(function() {
            deleted += 1;
            if(deleted == 3)
              done();
          })
        })
      })
    })
  })
},

{'timeout': 2000},
function delete_message(done) {
  var now = new Date;
  var vis_at = state.half_sec.visible_at;
  assert.ok(vis_at);
  assert.ok(vis_at - now > 0, "Too late to run this test: " + (vis_at - now));

  cqs.DeleteMessage(state.half_sec, function(er) {
    if(er) throw er;

    function check() {
      cqs.ReceiveMessage('foo', function(er, msg) {
        if(er) throw er;
        assert.equal(msg.length, 0, "Should be no more messages left: " + I(msg));
        done();
      })
    }

    var remaining = vis_at - (new Date);
    if(remaining < 0)
      check();
    else
      setTimeout(check, remaining * 1.10);
  })
},

function get_queue_attributes(done) {
  cqs.GetQueueAttributes('bar', function(er, attrs) {
    if(er) throw er;

    assert.equal(attrs.VisibilityTimeout, state.bar.VisibilityTimeout, "Should be bar's visibility timeout");
    assert.equal(attrs.VisibilityTimeout,                         111, "Should be bar's visibility timeout");

    cqs.GetQueueAttributes(state.foo, '--force', ['all'], function(er, attrs) {
      if(er) throw er;

      assert.equal(attrs.VisibilityTimeout, state.foo.VisibilityTimeout, "Should be bar's visibility timeout");
      assert.equal(attrs.VisibilityTimeout,                         1.5, "Should be bar's visibility timeout");

      done();
    })
  })
},

] // TESTS
<|endoftext|>


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

describe("API", function() {
  var apiPath = "./api";

  describe("v1", function() {
    var versionPath = apiPath + "/1";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v2", function() {
    var versionPath = apiPath + "/2";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v3", function() {
    var versionPath = apiPath + "/3";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v4", function() {
    var versionPath = apiPath + "/4";

    describe("sets.json", function() {
      var setsPath = versionPath + "/sets.json";
      var sets = JSON.parse(fs.readFileSync(setsPath, "utf8"));

      it("should be an array", function() {
        expect(sets).to.be.an('array');
      });

      it("should have between 6 and 10 sets", function() {
        expect(sets).to.have.length.within(6, 12);
      });

      sets.forEach(function(set) {
        describe(set.name, function() {
          describe("name", function() {
            var name = set.name;

            it("should be a string", function() {
              expect(name).to.be.a.string;
            });

            it("should have a nonzero length", function() {
              expect(name).to.not.have.length(0);
            });
          });

          describe("block", function() {
            var block = set.block;

            it("should be a string", function() {
              expect(block).to.be.a.string;
            });

            it("should have a nonzero length", function() {
              expect(block).to.not.have.length(0);
            });

            it("should be the same as the previous set's block or equal to this set's name", function() {
              var validNames = new Set([set.name]);
              if (sets.indexOf(set) > 0) {
                validNames.add(sets[sets.indexOf(set) - 1].block);
              }

              expect(block).to.be.oneOf([...validNames]);
            });
          });

          describe("code", function() {
            var code = set.code;

            it("should be a string", function() {
              expect(code).to.be.a.string;
            });

            it("should be three characters", function() {
              expect(code).to.have.length(3);
            });

            it("should be all caps", function() {
              expect(code).to.equal(code.toUpperCase());
            });
          });

          describe("symbol", function() {
            var symbol = set.symbol;

            it("should be absent", function() {
              expect(symbol).to.be.undefined;
            });
          });

          describe("enter date", function() {
            var enterDate = set.enter_date;

            it("should be a string", function() {
              expect(enterDate).to.be.a.string;
            });

            it("should be an ISO 8601 datetime", function() {
              expect(Date.parse(enterDate)).to.be.a('number');
            });

            it("should represent midnight", function() {
              expect(enterDate).to.contain("00:00:00.00");
            });
          });

          describe("exit date", function() {
            var exitDate = set.exit_date;

            it("should be a string or null", function() {
              expect(exitDate || "").to.be.a.string;
            });

            it("should be an ISO 8601 datetime if present", function() {
              expect(Date.parse(exitDate || "1970-01-01T00:00:00.000Z")).to.be.a('number');
            });

            it("should represent midnight if present", function() {
              expect(exitDate || "1970-01-01T00:00:00.000Z").to.contain("00:00:00.00");
            });
          });

          describe("rough exit date", function() {
            var roughExitDate = set.rough_exit_date;

            it("should be a string", function() {
              expect(roughExitDate).to.be.a.string;
            });

            it("should be QX 20XX or late 20XX", function() {
              expect(roughExitDate).to.match(/Q\d 20\d\d/);
            });
          });
        });
      });
    });
  });

  describe("v5", function() {
    var versionPath = apiPath + "/v5";

    describe("sets.json", function() {
      var setsPath = versionPath + "/sets.json";
      var body = JSON.parse(fs.readFileSync(setsPath, "utf8"));

      it("should be an object", function() {
        expect(body).to.be.an('object');
      });

      it("should be marked not deprecated", function() {
        expect(body.deprecated).to.be.false;
      });

      it("should have a sets array", function() {
        expect(body.sets).to.be.an('array');
      });

      it("should have between 8 and 14 sets", function() {
        expect(body.sets).to.have.length.within(8, 14);
      });

      body.sets.forEach(function(set) {
        describe(set.name, function() {
          describe("name", function() {
            var name = set.name;

            it("should be a string", function() {
              expect(name).to.be.a.string;
            });

            it("should have a nonzero length", function() {
              expect(name).to.not.have.length(0);
            });
          });

          describe("block", function() {
            var block = set.block;

            it("should be a string", function() {
              expect(block).to.be.a.string;
            });

            it("should have a nonzero length or be null", function() {
              expect(block || "abc").to.not.have.length(0);
            });

            it("should be the same as the previous set's block or equal to this set's name or be null", function() {
              var validNames = new Set([set.name, null]);
              if (body.sets.indexOf(set) > 0) {
                validNames.add(body.sets[body.sets.indexOf(set) - 1].block);
              }

              expect(block).to.be.oneOf([...validNames]);
            });
          });

          describe("code", function() {
            var code = set.code;

            it("should be a string", function() {
              expect(code).to.be.a.string;
            });

            it("should be three characters", function() {
              expect(code).to.have.length(3);
            });

            it("should be all caps", function() {
              expect(code).to.equal(code.toUpperCase());
            });
          });

          describe("symbol", function() {
            var symbol = set.symbol;

            it("should be absent", function() {
              expect(symbol).to.be.undefined;
            });
          });

          describe("enter date", function() {
            var enterDate = set.enter_date;

            it("should be a string", function() {
              expect(enterDate).to.be.a.string;
            });

            it("should be an ISO 8601 datetime", function() {
              expect(Date.parse(enterDate)).to.be.a('number');
            });

            it("should represent midnight", function() {
              expect(enterDate).to.contain("00:00:00.00");
            });
          });

          describe("exit date", function() {
            var exitDate = set.exit_date;

            it("should be a string or null", function() {
              expect(exitDate || "").to.be.a.string;
            });

            it("should be an ISO 8601 datetime if present", function() {
              expect(Date.parse(exitDate || "1970-01-01T00:00:00.000Z")).to.be.a('number');
            });

            it("should represent midnight if present", function() {
              expect(exitDate || "1970-01-01T00:00:00.000Z").to.contain("00:00:00.00");
            });
          });

          describe("rough exit date", function() {
            var roughExitDate = set.rough_exit_date;

            it("should be a string", function() {
              expect(roughExitDate).to.be.a.string;
            });

            it("should be QX 20XX or late 20XX", function() {
              expect(roughExitDate).to.match(/Q\d 20\d\d/);
            });
          });
        });
      });
    });
  });
});
<commit_msg>Update tests<commit_after>var fs = require("fs");
var chai = require("chai");
var expect = chai.expect;

describe("API", function() {
  var apiPath = "./api";

  describe("v1", function() {
    var versionPath = apiPath + "/1";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v2", function() {
    var versionPath = apiPath + "/2";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v3", function() {
    var versionPath = apiPath + "/3";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v4", function() {
    var versionPath = apiPath + "/4";

    it("shouldn't exist", function() {
      var exists = fs.existsSync(versionPath);
      expect(exists).to.be.false;
    });
  });

  describe("v5", function() {
    var versionPath = apiPath + "/v5";

    describe("sets.json", function() {
      var setsPath = versionPath + "/sets.json";
      var body = JSON.parse(fs.readFileSync(setsPath, "utf8"));

      it("should be an object", function() {
        expect(body).to.be.an('object');
      });

      it("should be marked not deprecated", function() {
        expect(body.deprecated).to.be.false;
      });

      it("should have a sets array", function() {
        expect(body.sets).to.be.an('array');
      });

      it("should have between 8 and 14 sets", function() {
        expect(body.sets).to.have.length.within(8, 14);
      });

      body.sets.forEach(function(set) {
        describe(set.name, function() {
          describe("name", function() {
            var name = set.name;

            it("should be a string", function() {
              expect(name).to.be.a.string;
            });

            it("should have a nonzero length", function() {
              expect(name).to.not.have.length(0);
            });
          });

          describe("block", function() {
            var block = set.block;

            it("should be a string", function() {
              expect(block).to.be.a.string;
            });

            it("should have a nonzero length or be null", function() {
              expect(block || "abc").to.not.have.length(0);
            });

            it("should be the same as the previous set's block or equal to this set's name or be null", function() {
              var validNames = new Set([set.name, null]);
              if (body.sets.indexOf(set) > 0) {
                validNames.add(body.sets[body.sets.indexOf(set) - 1].block);
              }

              expect(block).to.be.oneOf([...validNames]);
            });
          });

          describe("code", function() {
            var code = set.code;

            it("should be a string", function() {
              expect(code).to.be.a.string;
            });

            it("should be three characters", function() {
              expect(code).to.have.length(3);
            });

            it("should be all caps", function() {
              expect(code).to.equal(code.toUpperCase());
            });
          });

          describe("symbol", function() {
            var symbol = set.symbol;

            it("should be absent", function() {
              expect(symbol).to.be.undefined;
            });
          });

          describe("enter date", function() {
            var enterDate = set.enter_date;

            it("should be a string", function() {
              expect(enterDate).to.be.a.string;
            });

            it("should be an ISO 8601 datetime", function() {
              expect(Date.parse(enterDate)).to.be.a('number');
            });

            it("should represent midnight", function() {
              expect(enterDate).to.contain("00:00:00.00");
            });
          });

          describe("exit date", function() {
            var exitDate = set.exit_date;

            it("should be a string or null", function() {
              expect(exitDate || "").to.be.a.string;
            });

            it("should be an ISO 8601 datetime if present", function() {
              expect(Date.parse(exitDate || "1970-01-01T00:00:00.000Z")).to.be.a('number');
            });

            it("should represent midnight if present", function() {
              expect(exitDate || "1970-01-01T00:00:00.000Z").to.contain("00:00:00.00");
            });
          });

          describe("rough exit date", function() {
            var roughExitDate = set.rough_exit_date;

            it("should be a string", function() {
              expect(roughExitDate).to.be.a.string;
            });

            it("should be QX 20XX or late 20XX", function() {
              expect(roughExitDate).to.match(/Q\d 20\d\d/);
            });
          });
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>module.exports = function fuckOff () {
  document.getElementById('donTweets').remove()
  document.getElementById('hilTweets').remove()

}
<commit_msg>commit<commit_after>module.exports = function fuckOff () {
  var don = document.getElementById('donTweets')
  console.log('This is the don ',don);

  while(don.firstChild) {
    don.removeChild(don.firstChild)
  }

  var hil = document.getElementById('hilTweets')
  while(hil.firstChild) {
    hil.removeChild(hil.firstChild)
  }
}
<|endoftext|>