UNPKG

5.32 kBJavaScriptView Raw
1/*
2 * grunt
3 * https://github.com/cowboy/grunt
4 *
5 * Copyright (c) 2012 "Cowboy" Ben Alman
6 * Licensed under the MIT license.
7 * http://benalman.com/about/license/
8 */
9
10var path = require('path');
11var nodeunit = require('nodeunit');
12var nodeunitUtils = require('nodeunit/lib/utils');
13
14// ============================================================================
15// CUSTOM NODEUNIT REPORTER
16// ============================================================================
17
18// Keep track of the last-started module.
19var currentModule;
20// Keep track of the last-started test(s).
21var unfinished = {};
22
23// If Nodeunit explodes because a test was missing test.done(), handle it.
24process.on('exit', function() {
25 var len = Object.keys(unfinished).length;
26 // If there are unfinished tests, tell the user why Nodeunit killed grunt.
27 if (len > 0) {
28 log.muted = false;
29 verbose.error().or.writeln('F'.red);
30 log.error('Incomplete tests/setups/teardowns:');
31 Object.keys(unfinished).forEach(log.error, log);
32 fail.fatal('A test was missing test.done(), so nodeunit exploded. Sorry!',
33 Math.min(99, 90 + len));
34 }
35});
36
37// Keep track of failed assertions for pretty-printing.
38var failedAssertions = [];
39function logFailedAssertions() {
40 var assertion, stack;
41 // Print each assertion error + stack.
42 while (assertion = failedAssertions.shift()) {
43 nodeunitUtils.betterErrors(assertion);
44 verbose.or.error(assertion.testName);
45 if (assertion.error.name === 'AssertionError' && assertion.message) {
46 log.error('AssertionMessage: ' + assertion.message.magenta);
47 }
48 stack = assertion.error.stack.replace(/ {4}(at)/g, ' $1');
49 stack = stack.replace(/:(.*?\n)/, '$1'.magenta);
50 log.error(stack + '\n').writeln();
51 }
52}
53
54// Define our own Nodeunit reporter.
55nodeunit.reporters.grunt = {
56 info: 'Grunt reporter',
57 run: function(files, options, callback) {
58 var opts = {
59 // No idea.
60 testspec: undefined,
61 // Executed when the first test in a file is run. If no tests exist in
62 // the file, this doesn't execute.
63 moduleStart: function(name) {
64 // Keep track of this so that moduleDone output can be suppressed in
65 // cases where a test file contains no tests.
66 currentModule = name;
67 verbose.subhead('Testing ' + name).or.write('Testing ' + name);
68 },
69 // Executed after a file is done being processed. This executes whether
70 // tests exist in the file or not.
71 moduleDone: function(name) {
72 // Abort if no tests actually ran.
73 if (name !== currentModule) { return; }
74 // Print assertion errors here, if verbose mode is disabled.
75 if (!option('verbose')) {
76 if (failedAssertions.length > 0) {
77 log.writeln();
78 logFailedAssertions();
79 } else {
80 log.ok();
81 }
82 }
83 },
84 // Executed before each test is run.
85 testStart: function(name) {
86 // Keep track of the current test, in case test.done() was omitted
87 // and Nodeunit explodes.
88 unfinished[name] = name;
89 verbose.write(name + '...');
90 // Mute output, in cases where a function being tested logs through
91 // grunt (for testing grunt internals).
92 log.muted = true;
93 },
94 // Executed after each test and all its assertions are run.
95 testDone: function(name, assertions) {
96 delete unfinished[name];
97 // Un-mute output.
98 log.muted = false;
99 // Log errors if necessary, otherwise success.
100 if (assertions.failures()) {
101 assertions.forEach(function(ass) {
102 if (ass.failed()) {
103 ass.testName = name;
104 failedAssertions.push(ass);
105 }
106 });
107 if (option('verbose')) {
108 log.error();
109 logFailedAssertions();
110 } else {
111 log.write('F'.red);
112 }
113 } else {
114 verbose.ok().or.write('.');
115 }
116 },
117 // Executed when everything is all done.
118 done: function (assertions) {
119 if (assertions.failures()) {
120 fail.warn(assertions.failures() + '/' + assertions.length +
121 ' assertions failed (' + assertions.duration + 'ms)',
122 Math.min(99, 90 + assertions.failures()));
123 } else {
124 verbose.writeln();
125 log.ok(assertions.length + ' assertions passed (' +
126 assertions.duration + 'ms)');
127 }
128 // Tell the task manager we're all done.
129 callback(); // callback(assertions.failures() === 0);
130 }
131 };
132
133 // Nodeunit needs absolute paths.
134 var paths = files.map(function (filepath) {
135 return path.join(process.cwd(), filepath);
136 });
137 nodeunit.runFiles(paths, opts);
138 }
139};
140
141// ============================================================================
142// TASKS
143// ============================================================================
144
145task.registerBasicTask('test', 'Run unit tests.', function(data, name) {
146 // File paths.
147 var filepaths = file.expand(data);
148 // Clear all tests' cached require data, in case this task is run inside a
149 // "watch" task loop.
150 file.clearRequireCache(filepaths);
151 // Run test(s)... asynchronously!
152 nodeunit.reporters.grunt.run(filepaths, {}, this.async());
153});