deadunit
Version:
A dead-simple nestable unit testing library for javascript and node.js.
436 lines (372 loc) • 16.7 kB
JavaScript
var Future = require('async-future')
var formatBasic = require("./basicFormatter")
var indent = require("./indent")
var utils = require("./utils")
// unitTest is a deadunit-core UnitTest object
// if consoleColoring is true, the string will contain console color annotations
// if printOnTheFly is true, test results will be printed to the screen in addition to being returned
// returns a future containing a string with the final results
exports.text = function textOutput(unitTest, consoleColors, printOnTheFly, printLateEvents) {
if(printLateEvents === undefined) printLateEvents = true
function color(theColor, theString) {
if(consoleColors !== undefined)
return consoleColors[theColor](theString.toString())
else
return theString.toString()
}
var warningHasBeenPrinted = false
function lateEventsWarning() {
if(ended && !warningHasBeenPrinted && printLateEvents) {
warningHasBeenPrinted = true
return color('red',
'Test results were accessed before asynchronous parts of tests were fully complete'
+" If you have tests with asynchronous parts, make sure to use `this.count` to declare how many assertions you're waiting for."
)+'\n\n'
} else {
return ''
}
}
var ended = false
return formatBasic(unitTest, printOnTheFly, consoleColors, {
group: function(name, totalDuration, testSuccesses, testFailures,
assertSuccesses, assertFailures, exceptions,
testResults, exceptionResults, nestingLevel, timedOut, onTheFly) {
var total = testSuccesses+testFailures
var addResults = function() {
var result = ''
if(testResults.length > 0)
result += '\n'+indent(' ', testResults.join('\n'))
if(exceptionResults.length > 0)
result += '\n'+indent(' ', exceptionResults.join('\n'))
return result
}
var testColor, exceptionColor, failColor, finalColor
testColor = exceptionColor = failColor = finalColor = 'green'
if(testFailures > 0) {
testColor = failColor = finalColor = 'red'
}
if(exceptions > 0) {
finalColor = 'red'
exceptionColor = 'magenta'
}
var durationText = utils.timeText(totalDuration)
if(nestingLevel === 0) {
var resultsLine = ''
if(name) resultsLine += color('cyan', name+' - ')
resultsLine += color(finalColor, testSuccesses+'/'+(testSuccesses+testFailures)+' successful tests. ')+
color('green', assertSuccesses+' pass'+utils.plural(assertSuccesses,"es",""))+
', '+color(failColor, assertFailures+' fail'+utils.plural(assertFailures))+
', and '+color(exceptionColor, exceptions+' exception'+utils.plural(exceptions))+"."
+color('grey', " Took "+durationText+".")
var result = ''
if(name) result += color('cyan', name)+'\n'
result += addResults()
result += '\n\n'+resultsLine
if(timedOut) {
result += color('red', '\n The test timed out')
}
} else {
if(!name) name = "<unnamed test>"
var result = color(finalColor, name)+': '
+color(testColor, testSuccesses+'/'+total)
+" and "+color(exceptionColor, exceptionResults.length+" exception"+utils.plural(exceptionResults.length))
+color('grey', " took "+durationText)
result += addResults()
}
return lateEventsWarning()+result
},
assert: function(result, test, onTheFly) {
if(result.success) {
var word = "Ok! ";
var c = 'green'
} else {
var word = "Fail:";
var c = 'red'
}
var linesDisplay = result.sourceLines
if(result.sourceLines.indexOf("\n") !== -1) {
linesDisplay = "\n"+linesDisplay;
}
var expectations = ""
if(!result.success && (result.actual !== undefined || result.expected !== undefined)) {
var things = []
if(result.expected !== undefined)
things.push("Expected "+utils.valueToMessage(result.expected))
if(result.actual !== undefined)
things.push("Got "+utils.valueToMessage(result.actual))
expectations = " - "+things.join(', ')
}
var column = ''
if(result.column !== undefined) {
column = color('grey', ":"+result.column)
}
return lateEventsWarning()+color(c, word)+" ["+color('grey', result.file)+" "+result.line+column+"] "
+color(c, linesDisplay)
+expectations
},
exception: function(e, onTheFly) {
return lateEventsWarning()+color('red', 'Exception: ')
+color('magenta', utils.errorToString(e))
},
log: function(values, onTheFly) {
return lateEventsWarning()+values.map(function(v) {
return utils.valueToString(v)
}).join(', ')
},
end: function() {
ended = true
}
})
}
var htmlColors = exports.htmlColors = {
red: 'rgb(200,30,30)',
darkRed: 'rgb(90,0,0)',
lightRed: 'rgb(255,210,230)',
black: 'rgb(20,20,20)',
white: 'rgb(240,220,220)',
gray: 'rgb(185, 180, 180)',
green: 'rgb(0,100,20)',
brightGreen: 'rgb(0,200,50)',
purple: 'rgb(190,0,160)',
brightPurple: 'rgb(255,126,255)',
blue: 'rgb(0, 158, 173)',
brightBlue: 'rgb(0, 233, 255)',
yellow: 'rgb(210,182,0)',
darkYellow: 'rgb(106,93,0)'
}
var red = htmlColors.red
var darkRed = htmlColors.darkRed
var lightRed = htmlColors.lightRed
var black = htmlColors.black
var white = htmlColors.white
var green = htmlColors.green
var brightGreen = htmlColors.brightGreen
var purple = htmlColors.purple
var brightPurple = htmlColors.brightPurple
var blue = htmlColors.blue
var brightBlue = htmlColors.brightBlue
var gray = htmlColors.gray
exports.html = function(unitTest, printLateEvents) {
if(printLateEvents === undefined) printLateEvents = true
var getTestDisplayer = function() {
return {
onToggle: function(displayNone, $bgcolor, innerSelector, outerSelector) {
if(displayNone == true) {
$(innerSelector).css({"display":""});
if(outerSelector != undefined) {
$(outerSelector).css({"border":"1px solid "+$bgcolor});
}
} else {
$(innerSelector).css({"display":"none"});
if(outerSelector != undefined) {
$(outerSelector).css({"border":""});
}
}
}
}
}
var formattedTestHtml = formatBasic(unitTest, false, {
group: function(name, totalDuration, testSuccesses, testFailures,
assertSuccesses, assertFailures, exceptions,
testResults, exceptionResults, nestingLevel, timedOut) {
var total = testSuccesses+testFailures
var mainId = getMainId(name)
if(testFailures > 0 || exceptions > 0) {
var bgcolor=red;
var show = "true";
var foregroundColor = lightRed
} else {
var bgcolor=green;
var show = "false";
var foregroundColor = brightGreen
}
var durationText = utils.timeText(totalDuration)
if(nestingLevel === 0) {
var initTestGroup = function(mainId, bgcolor, show) {
$(function()
{ $('#'+mainId).css({"border-color":"'+bgcolor+'"});
TestDisplayer.onToggle(show, bgcolor, '#'+mainId);
$('#'+mainId+'_final').click(function()
{ TestDisplayer.onToggle($('#'+mainId).css("display") == "none", bgcolor, '#'+mainId);
});
});
}
var nameLine = "", titleLine = ''
if(name) {
titleLine = '<h1 class="primaryTitle">'+name+'</h1>'
nameLine = name+' - '
}
var timeoutNote = ""
if(timedOut) {
timeoutNote = 'The test timed out'
}
return titleLine+
'<div class="testResultsArea" id="'+mainId+'">'+
testResults.join('\n')+
exceptionResults.join('\n')+"\n"+
'<div style="color:'+red+'">'+timeoutNote+'</div>'+
'</div>'+
'<div class="testResultsBar link" style="border:2px solid '+bgcolor+';" id="'+mainId+'_final">'+
'<div class="testResultsBarInner" style="background-color:'+bgcolor+';">'+
'<div style="float:right;"><i>click on this bar</i></div>'+
'<div><span class="testResultsName">'+nameLine+'</span>' + testSuccesses+'/'+total+' successful tests. '+
'<span style="color:'+brightGreen+'">'+assertSuccesses+' pass'+utils.plural(assertSuccesses,"es","")+'</span>'+
', <span style="color:'+darkRed+'">'+assertFailures+' fail'+utils.plural(assertFailures)+'</span>'+
', and <span style="color:'+brightPurple+'">'+exceptions+' exception'+utils.plural(exceptions)+'</span>'+
'. <span style="color: '+white+'">Took '+durationText+".</span>"+
'</div>'+
'</div>'+
'<script>;('+initTestGroup+')("'+mainId+'", "'+bgcolor+'", '+show+')</script>'+
'</div>'
} else {
var n = getNewNumber()
var testId = mainId+n
var initTest = function(mainId, bgcolor, show, n) {
$(function()
{ $('#'+mainId).css({borderColor:bgcolor});
TestDisplayer.onToggle(show, bgcolor, '#'+mainId+n+'_inner', '#'+mainId+n);
$('.'+mainId+n+'_status').click(function()
{ TestDisplayer.onToggle
( $('#'+mainId+n+'_inner').css("display") == "none",
bgcolor,
'#'+mainId+n+'_inner',
'#'+mainId+n+''
);
});
});
}
if(!name) name = "<unnamed test>"
return '<div class="resultsArea" id="'+mainId+n+'">'+
'<div class="resultsBar link '+mainId+n+'_status" style="background-color:'+bgcolor+';color:'+foregroundColor+'">'+
name+': '+
testSuccesses+'/'+total+" and "+exceptions+" exception"+utils.plural(exceptions)
+' <span style="color: white">took '+durationText+'</span>'+
'</div>'+
'<div class="resultsAreaInner" id="'+testId+'_inner">'+
'<h2 class="'+testId+'_status link" style="color:'+bgcolor+';">'+name+'</h2>'+
testResults.join('\n')+"\n"+
exceptionResults.join('\n')+"\n"+
'</div>'+
'<script>;('+initTest+')("'+mainId+'", "'+bgcolor+'", '+show+', '+n+')</script>'+
'</div>';
}
},
assert: function(result) {
if(false === result.success) {
var color = red;
var word = "Fail:";
} else {
var color = green;
var word = "Ok!";
}
var linesDisplay = "<i>"+utils.textToHtml(result.sourceLines)+"</i>";
if(result.sourceLines.indexOf("\n") !== -1) {
linesDisplay = "<br>\n"+linesDisplay;
}
var expectations = ""
if(!result.success && (result.actual !== undefined || result.expected !== undefined)) {
var things = []
if(result.expected !== undefined)
things.push("Expected "+utils.textToHtml(utils.valueToMessage(result.expected)))
if(result.actual !== undefined)
things.push("Got "+utils.textToHtml(utils.valueToMessage(result.actual)))
expectations = " - "+things.join(', ')
}
var column = ''
if(result.column !== undefined) {
column = ":"+result.column
}
return '<div style="color:'+color+';"><span >'+word+'</span>'+
" <span class='locationOuter'>[<span class='locationInner'>"
+result.file+" line <span class='lineNumber'>"+result.line+"</span>"+column+"</span>]"
+"</span> "
+linesDisplay
+' <span class="expectations">'+expectations+'</span>'
+"</div>"
},
exception: function(exception) {
var formattedException = utils.textToHtml(utils.errorToString(exception))
return '<div style="color:'+purple+';">Exception: '+formattedException+'</div>'
},
log: function(values) {
return '<div>'
+values.map(function(v) {
return utils.textToHtml(utils.valueToString(v))
}).join(', ')
+'</div>'
}
})
return formattedTestHtml.then(function(formattedHtml) {
return Future('<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>'+
'<style>\
body{\
background-color: '+black+';\
color: '+white+';\
}\
h2{\
margin-bottom: 5px;\
margin-top: 10px;\
}\
.green\
{ color: '+green+';\
}\
.link\
{ cursor:pointer;\
}\
.primaryTitle {\
color: '+blue+';\
}\
.testResultsName {\
color: '+brightBlue+';\
}\
.asyncTime {\
color: '+gray+';\
}\
.resultsArea{\
margin:1px;\
margin-bottom: 5px;\
}\
.resultsAreaInner{\
padding:0 8px;\
}\
.resultsBar{\
color:white;\
margin-bottom:4px;\
padding: 1px 3px;\
}\
.testResultsArea{\
padding:0 8px;\
}\
.testResultsBar{\
background-color:'+black+';color:white;margin:4px 0;\
}\
.testResultsBarInner{\
color:white;margin:1px;padding: 1px 3px;\
}\
\
.locationOuter{\
color:'+white+';\
}\
.locationInner, .expectations {\
color:'+gray+';\
}\
.lineNumber{\
color:'+white+';\
}\
</style>'+
'<script type="text/javascript"> \
var TestDisplayer = ('+getTestDisplayer+')() \
</script>'
+formattedHtml)
})
}
var nextId = 0
var getMainId = function(name) {
nextId++
return 'unitTest_'+nextId//+name.replace(/[^a-zA-Z]/g, "") // get rid of all characters except letters
}
var getNewNumber = function() {
getNewNumber.n++
return getNewNumber.n
}
getNewNumber.n = 0