From 196d98d64f396e99449113eade3271aecdaa270c Mon Sep 17 00:00:00 2001
From: Nick Schonning <nschonni@gmail.com>
Date: Wed, 8 Oct 2014 00:37:48 -0400
Subject: [PATCH] Testing: Update the YUI Test harness for Grunt

- Fails the build on test failure
- Splits out our custom Grunt tasks
- Update the relative require paths after moving the tasks from the
Gruntfile
---
 Gruntfile.js            | 241 +---------------------------------------
 tasks/changelog.js      |  72 ++++++++++++
 tasks/test_rhino.js     |  23 ++++
 tasks/yuitest.js        | 105 +++++++++++++++++
 tests/cli/cli-common.js |   6 +-
 5 files changed, 208 insertions(+), 239 deletions(-)
 create mode 100644 tasks/changelog.js
 create mode 100644 tasks/test_rhino.js
 create mode 100644 tasks/yuitest.js

diff --git a/Gruntfile.js b/Gruntfile.js
index 75078270..d7058ec5 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,4 +1,4 @@
-/* jshint camelcase:false, evil:true, node:true */
+/* jshint camelcase:false, node:true */
 
 "use strict";
 
@@ -166,7 +166,7 @@ module.exports = function(grunt) {
                 jshintrc: ".jshintrc"
             },
             gruntfile: {
-                src: "Gruntfile.js"
+                src: ["Gruntfile.js","tasks/*.js"]
             },
             demo: {
                 src: "demos/*.js"
@@ -217,6 +217,9 @@ module.exports = function(grunt) {
     grunt.loadNpmTasks("grunt-contrib-watch");
     grunt.loadNpmTasks("grunt-include-replace");
 
+    // Load custom tasks
+    grunt.loadTasks("tasks");
+
     // Default task.
     grunt.registerTask("default", ["test"]);
 
@@ -228,238 +231,4 @@ module.exports = function(grunt) {
     grunt.registerTask("rhino", ["clean:build", "jshint", "concat", "test_rhino"]);
 
     grunt.registerTask("release", ["test", "clean:release", "copy:release", "includereplace:release", "changelog"]);
-
-    //Run the YUITest suite
-    grunt.registerMultiTask("yuitest", "Run the YUITests for the project", function() {
-
-        var YUITest = require("yuitest");
-        var CSSLint = require("./build/csslint-node").CSSLint;  // jshint ignore:line
-        var files = this.filesSrc;
-        var TestRunner = YUITest.TestRunner;
-        var done = this.async();
-        var errors = [],
-            failures = [],
-            stack = [];
-
-        //Eval each file so the tests are brought into this scope where CSSLint and YUITest are loaded already
-        files.forEach(function(filepath) {
-            eval(grunt.file.read(filepath));
-        });
-
-        // From YUITest Node CLI
-        function filterStackTrace(stackTrace){
-            if (stackTrace){
-                var lines = stackTrace.split("\n"),
-                    result = [],
-                    i, len;
-
-                //skip first line, it's the error
-                for (i=1, len=lines.length; i < len; i++){
-                    if (lines[i].indexOf("yuitest-node") > -1){
-                        break;
-                    } else {
-                        result.push(lines[i]);
-                    }
-                }
-
-                return result.join("\n");
-            } else {
-                return "Unavailable.";
-            }
-        }
-
-        // From YUITest Node CLI with minor colourization changes
-        function handleEvent(event){
-
-            var message = "",
-                results = event.results,
-                i, len;
-
-            switch(event.type){
-                case TestRunner.BEGIN_EVENT:
-                    message = "YUITest for Node.js\n";
-
-                    if (TestRunner._groups){
-                        message += "Filtering on groups '" + TestRunner._groups.slice(1,-1) + "'\n";
-                    }
-                    break;
-
-                case TestRunner.COMPLETE_EVENT:
-                    message = "\nTotal tests: " + results.total + ", " +
-                        ("Failures: " + results.failed).red + ", " +
-                        ("Skipped: " + results.ignored).yellow +
-                        ", Time: " + (results.duration/1000) + " seconds\n";
-
-                    if (failures.length){
-                        message += "\nTests failed:\n".red;
-
-                        for (i=0,len=failures.length; i < len; i++){
-                            message += "\n" + (i+1) + ") " + failures[i].name + " : " + failures[i].error.getMessage() + "\n";
-                            message += "Stack trace:\n" + filterStackTrace(failures[i].error.stack) + "\n";
-                        }
-
-                        message += "\n";
-                    }
-
-                    if (errors.length){
-                        message += "\nErrors:\n".red;
-
-                        for (i=0,len=errors.length; i < len; i++){
-                            message += "\n" + (i+1) + ") " + errors[i].name + " : " + errors[i].error.message + "\n";
-                            message += "Stack trace:\n" + filterStackTrace(errors[i].error.stack) + "\n";
-                        }
-
-                        message += "\n";
-                    }
-
-                    message += "\n\n";
-                    //Tell grunt we're done the async operation
-                    done();
-                    break;
-
-                case TestRunner.TEST_FAIL_EVENT:
-                    message = "F".red;
-                    failures.push({
-                        name: stack.concat([event.testName]).join(" > "),
-                        error: event.error
-                    });
-
-                    break;
-
-                case TestRunner.ERROR_EVENT:
-                    errors.push({
-                        name: stack.concat([event.methodName]).join(" > "),
-                        error: event.error
-                    });
-
-                    break;
-
-                case TestRunner.TEST_IGNORE_EVENT:
-                    message = "S".yellow;
-                    break;
-
-                case TestRunner.TEST_PASS_EVENT:
-                    message = ".".green;
-                    break;
-
-                case TestRunner.TEST_SUITE_BEGIN_EVENT:
-                    stack.push(event.testSuite.name);
-                    break;
-
-                case TestRunner.TEST_CASE_COMPLETE_EVENT:
-                case TestRunner.TEST_SUITE_COMPLETE_EVENT:
-                    stack.pop();
-                    break;
-
-                case TestRunner.TEST_CASE_BEGIN_EVENT:
-                    stack.push(event.testCase.name);
-                    break;
-
-                //no default
-            }
-
-            grunt.log.write(message);
-        }
-        //Add event listeners
-        TestRunner.subscribe(TestRunner.BEGIN_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_PASS_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.ERROR_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_IGNORE_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_CASE_BEGIN_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_CASE_COMPLETE_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_SUITE_BEGIN_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.TEST_SUITE_COMPLETE_EVENT, handleEvent);
-        TestRunner.subscribe(TestRunner.COMPLETE_EVENT, handleEvent);
-        TestRunner.run();
-    });
-
-    grunt.registerMultiTask("changelog", "Write the changelog file", function() {
-        var done = this.async();
-        var lastTag;
-        var files = this.filesSrc;
-
-
-        grunt.util.spawn({
-            cmd: "git",
-            args: ["tag"]
-        }, function(error, result) {
-            //Find the latest git tag
-            var tags = result.stdout.split("\n"),
-                semver = tags[0].replace("v","").split("."),
-                major = parseInt(semver[0], 10),
-                minor = parseInt(semver[1], 10),
-                patch = parseInt(semver[2], 10);
-
-            //A simple array sort can't be used because of the comparison of
-            //the strings "0.9.9" > "0.9.10"
-            for (var i = 1, len = tags.length; i < len; i++) {
-                semver = tags[i].replace("v", "").split(".");
-
-                var currentMajor = parseInt(semver[0], 10);
-                if (currentMajor < major) {
-                    continue;
-                } else if (currentMajor > major) {
-                    major = currentMajor;
-                }
-
-                var currentMinor = parseInt(semver[1], 10);
-                if (currentMinor < minor) {
-                    continue;
-                } else if (currentMinor > minor) {
-                    minor = currentMinor;
-                }
-
-                var currentPatch = parseInt(semver[2], 10);
-                if (currentPatch < patch) {
-                    continue;
-                } else if (currentPatch > patch) {
-                    patch = currentPatch;
-                }
-            }
-
-            lastTag = "v" + major + "." + minor + "." + patch;
-
-            grunt.verbose.write("Last tag: " + lastTag).writeln();
-
-            //
-            grunt.util.spawn({
-                cmd: "git",
-                args: ["log", "--pretty=format:'* %s (%an)'", lastTag + "..HEAD"]
-            }, function(error, result) {
-                var prettyPrint =  result.stdout.split("'\n'").join("\n").replace(/\"$/, "").replace(/^\"/, "");
-
-                grunt.verbose.writeln().write(prettyPrint).writeln();
-
-                var template = "<%= grunt.template.today('mmmm d, yyyy') %> - v<%= pkg.version %>\n\n" +
-                    prettyPrint + "\n\n" +
-                    grunt.file.read(files[0]);
-
-                grunt.file.write(files[0], grunt.template.process(template));
-
-                done();
-            });
-        });
-
-    });
-
-    //Run test suite through rhino
-    grunt.registerMultiTask("test_rhino", "Run the test suite through rhino", function() {
-        var done = this.async();
-        var files = this.filesSrc;
-        var progress = files.length;
-
-        files.forEach(function(filepath) {
-            grunt.util.spawn({
-                cmd: "java",
-                args: ["-jar", "lib/js.jar", "lib/yuitest-rhino-cli.js", "build/csslint.js", filepath],
-                opts: {stdio: "inherit"}
-            }, function() {
-                progress--;
-                if (progress === 0) {
-                    done();
-                }
-            });
-        });
-    });
 };
diff --git a/tasks/changelog.js b/tasks/changelog.js
new file mode 100644
index 00000000..a7a0c875
--- /dev/null
+++ b/tasks/changelog.js
@@ -0,0 +1,72 @@
+/* jshint node:true */
+module.exports = function( grunt ) {
+    "use strict";
+    grunt.registerMultiTask("changelog", "Write the changelog file", function() {
+        var done = this.async();
+        var lastTag;
+        var files = this.filesSrc;
+
+
+        grunt.util.spawn({
+            cmd: "git",
+            args: ["tag"]
+        }, function(error, result) {
+            //Find the latest git tag
+            var tags = result.stdout.split("\n"),
+                semver = tags[0].replace("v","").split("."),
+                major = parseInt(semver[0], 10),
+                minor = parseInt(semver[1], 10),
+                patch = parseInt(semver[2], 10);
+
+            //A simple array sort can't be used because of the comparison of
+            //the strings "0.9.9" > "0.9.10"
+            for (var i = 1, len = tags.length; i < len; i++) {
+                semver = tags[i].replace("v", "").split(".");
+
+                var currentMajor = parseInt(semver[0], 10);
+                if (currentMajor < major) {
+                    continue;
+                } else if (currentMajor > major) {
+                    major = currentMajor;
+                }
+
+                var currentMinor = parseInt(semver[1], 10);
+                if (currentMinor < minor) {
+                    continue;
+                } else if (currentMinor > minor) {
+                    minor = currentMinor;
+                }
+
+                var currentPatch = parseInt(semver[2], 10);
+                if (currentPatch < patch) {
+                    continue;
+                } else if (currentPatch > patch) {
+                    patch = currentPatch;
+                }
+            }
+
+            lastTag = "v" + major + "." + minor + "." + patch;
+
+            grunt.verbose.write("Last tag: " + lastTag).writeln();
+
+            //
+            grunt.util.spawn({
+                cmd: "git",
+                args: ["log", "--pretty=format:'* %s (%an)'", lastTag + "..HEAD"]
+            }, function(error, result) {
+                var prettyPrint =  result.stdout.split("'\n'").join("\n").replace(/\"$/, "").replace(/^\"/, "");
+
+                grunt.verbose.writeln().write(prettyPrint).writeln();
+
+                var template = "<%= grunt.template.today('mmmm d, yyyy') %> - v<%= pkg.version %>\n\n" +
+                    prettyPrint + "\n\n" +
+                    grunt.file.read(files[0]);
+
+                grunt.file.write(files[0], grunt.template.process(template));
+
+                done();
+            });
+        });
+
+    });
+};
diff --git a/tasks/test_rhino.js b/tasks/test_rhino.js
new file mode 100644
index 00000000..223e8910
--- /dev/null
+++ b/tasks/test_rhino.js
@@ -0,0 +1,23 @@
+/* jshint node:true */
+module.exports = function( grunt ) {
+    "use strict";
+    // Run test suite through rhino
+    grunt.registerMultiTask("test_rhino", "Run the test suite through rhino", function() {
+        var done = this.async();
+        var files = this.filesSrc;
+        var progress = files.length;
+
+        files.forEach(function(filepath) {
+            grunt.util.spawn({
+                cmd: "java",
+                args: ["-jar", "lib/js.jar", "lib/yuitest-rhino-cli.js", "build/csslint.js", filepath],
+                opts: {stdio: "inherit"}
+            }, function() {
+                progress--;
+                if (progress === 0) {
+                    done();
+                }
+            });
+        });
+    });
+};
diff --git a/tasks/yuitest.js b/tasks/yuitest.js
new file mode 100644
index 00000000..c3564496
--- /dev/null
+++ b/tasks/yuitest.js
@@ -0,0 +1,105 @@
+/* jshint evil:true, node:true */
+module.exports = function( grunt ) {
+    "use strict";
+    grunt.registerMultiTask("yuitest", "Run the YUITests for the project", function() {
+
+        var YUITest = require("yuitest");
+        var CSSLint = require("../build/csslint-node").CSSLint;  // jshint ignore:line
+        var files = this.filesSrc;
+        var TestRunner = YUITest.TestRunner;
+        var done = this.async();
+        var failures = [],
+            stack = [];
+
+        // Eval each file so the tests are brought into this scope where CSSLint and YUITest are loaded already
+        files.forEach(function(filepath) {
+            eval(grunt.file.read(filepath));
+        });
+
+        // From YUITest Node CLI with minor colourization changes
+        function handleEvent(event){
+
+            var message = "",
+                results = event.results,
+                i, len;
+
+            switch(event.type){
+                case TestRunner.BEGIN_EVENT:
+                    grunt.verbose.subhead("YUITest for Node.js");
+
+                    if (TestRunner._groups){
+                        grunt.verbose.writeln("Filtering on groups '" + TestRunner._groups.slice(1,-1) + "'");
+                    }
+                    break;
+
+                case TestRunner.COMPLETE_EVENT:
+                    grunt.log.writeln("Total tests: " + results.total + ", " +
+                        ("Failures: " + results.failed).red + ", " +
+                        ("Skipped: " + results.ignored).yellow +
+                        ", Time: " + (results.duration/1000) + " seconds\n");
+
+                    if (failures.length){
+                        grunt.log.writeln("Tests failed:");
+
+                        for (i=0,len=failures.length; i < len; i++){
+                            grunt.fail.warn(failures[i].name + "\n" + failures[i].error);
+                        }
+                    }
+
+                    // Tell grunt we're done the async operation
+                    done();
+                    break;
+
+                case TestRunner.TEST_FAIL_EVENT:
+                    message = "F".red;
+                    failures.push({
+                        name: stack.concat([event.testName]).join(" > "),
+                        error: event.error
+                    });
+
+                    break;
+
+                case TestRunner.ERROR_EVENT:
+                    grunt.fail.fatal(event.error, stack);
+                    break;
+
+                case TestRunner.TEST_IGNORE_EVENT:
+                    message = "S".yellow;
+                    break;
+
+                case TestRunner.TEST_PASS_EVENT:
+                    message = ".".green;
+                    break;
+
+                case TestRunner.TEST_SUITE_BEGIN_EVENT:
+                    stack.push(event.testSuite.name);
+                    break;
+
+                case TestRunner.TEST_CASE_COMPLETE_EVENT:
+                case TestRunner.TEST_SUITE_COMPLETE_EVENT:
+                    stack.pop();
+                    break;
+
+                case TestRunner.TEST_CASE_BEGIN_EVENT:
+                    stack.push(event.testCase.name);
+                    break;
+
+                //no default
+            }
+
+            grunt.log.write(message);
+        }
+        //Add event listeners
+        TestRunner.subscribe(TestRunner.BEGIN_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_FAIL_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_PASS_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.ERROR_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_IGNORE_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_CASE_BEGIN_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_CASE_COMPLETE_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_SUITE_BEGIN_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.TEST_SUITE_COMPLETE_EVENT, handleEvent);
+        TestRunner.subscribe(TestRunner.COMPLETE_EVENT, handleEvent);
+        TestRunner.run();
+    });
+};
diff --git a/tests/cli/cli-common.js b/tests/cli/cli-common.js
index 012196bd..5871fa5d 100644
--- a/tests/cli/cli-common.js
+++ b/tests/cli/cli-common.js
@@ -16,15 +16,15 @@ function include(path, sandbox) {
 
     var Assert = YUITest.Assert,
         suite   = new YUITest.TestSuite("General Tests for CLI"),
-        apiStub = require("./tests/cli/assets/apiStub.js"),
-        data = require("./tests/cli/assets/data.js"),
+        apiStub = require("../tests/cli/assets/apiStub.js"),
+        data = require("../tests/cli/assets/data.js"),
         suites = data.suites,
         suiteix,
         sandbox = {
             CSSLint: CSSLint
         };
 
-    include(__dirname + "/src/cli/common.js", sandbox); /* expose sandbox.cli */
+    include("./src/cli/common.js", sandbox); /* expose sandbox.cli */
 
     for (suiteix in suites) {
         if (suites.hasOwnProperty(suiteix)) {