grunt-blanket-mocha-server
Version:
Easily serve mocha tests with (or without) blanket coverage. "Goes Great" with grunt-blanket-mocha.
246 lines (204 loc) • 8.32 kB
text/coffeescript
##
# grunt-blanket-mocha-server
#
# Copyright (c) 2013 Stu Salsbury
# Licensed under the MIT license.
#
###* @module ###
module.exports = (grunt) ->
path = require('path')
fs = require('fs')
_ = grunt.util._
###*
* Fail with message if array is not of minimum length or any file
* does not exist. obj[prop] may be a single file path or an array of them.
* @param {string} grunt The grunt process object
* @param {string} prop Name of the property being tested
* @param {number} min Minimum acceptable extant files
* @return {undefined}
###
minViolationTest = (obj, prop, min) ->
if _.isUndefined(min) then min = 0
if obj[prop]
files = if _.isArray(obj[prop]) then obj[prop] else [obj[prop]]
if files.length < min
grunt.fatal "Configuration property \"#{prop}\" did not match the " +
"minimum of #{min} files (it matched #{actual})."
_.each files, (file) ->
if !fs.existsSync(file)
grunt.fatal "File not found: \"#{file}\""
else
if min > 0
grunt.fatal "Configuration property \"#{prop}\" needs to specify at " +
"least one file."
undefined
###*
* Return a normalized path with the process directory stripped.
* Result is suitable for a src reference that is absolute to the root
* of the webserver.
* @param {object} obj The config object
* @param {[type]} prop The property to modify
* @return {undefined}
###
resolveFile = (obj, prop) ->
obj[prop] = path.resolve obj[prop]
minViolationTest obj, prop
preNodeModulesMatch = obj[prop].match /^(.*)\/node_modules\/.*$/
if preNodeModulesMatch
obj[prop] = obj[prop].replace preNodeModulesMatch[1], ''
else
inAppPath = process.cwd() + path.sep + 'node_modules' + path.sep + 'grunt-blanket-mocha-server'
if !(fs.existsSync(inAppPath))
obj[prop] = obj[prop].replace(
path.resolve(__dirname + '/..'),
''
)
else
# console.log obj[prop].red
# console.log path.resolve(__dirname + '/../..').green
# console.log path.resolve(__dirname + '\\..\\..').green
obj[prop] = obj[prop].replace(
path.resolve(__dirname + '/../..'),
# path.resolve(__dirname + '/..'),
'/node_modules'
# '/node_modules/grunt-blanket-mocha-server'
)
# console.log obj[prop].yellow
obj[prop] = obj[prop].replace /[\\]/g, '/'
undefined
###*
* Applies grunt.file.expand to the property of the object and optionally can
* ensures that a minimum number of files matches.
* @param {Object} grunt The Grunt object
* @param {Object} obj The object with the property on which to operate
* @param {string} prop The name of the property to expand
* @param {number} [min] If specified, the minimum number of matching files
* that is considered valid for configuration.
* @return {undefined}
###
expandFiles = (obj, prop, min) ->
cwd = process.cwd() # seek files relative to the grunt process
if obj[prop]
obj[prop] = grunt.file.expand { cwd: cwd },
if _.isArray(obj[prop]) then obj[prop] else [obj[prop]]
minViolationTest obj, prop, min
if obj[prop]
# passed the tests -- no clean up the dir base
_.each obj[prop], (val, index) ->
obj[prop][index] = path.resolve(val)
.replace(path.resolve(process.cwd()), '')
.replace(/[\\]/g, '/')
undefined
# ###*
# * Wraps each specified path in an html script tag specifying type
# * text/javascript and charset utf-8.
# * @param {Array.<string>} patterns of paths to wrap
# * @param {number} minimum number of scripts that should match pattern
# * @return {undefined} html script tags for each script file in patterns
# ###
# wrapScriptTags = (obj, prop, min) ->
# paths = expandFiles obj, prop, process.cwd(), min
# if paths && paths.length
# joinedPaths = paths.join(
# '" type="text/javascript" charset="utf-8"></script>\n' +
# '<script src="/'
# )
# obj[prop] = "<script src=\"#{joinedPaths}\" type=\"text/javascript\" " +
# "charset=\"utf-8\"></script>\n"
# else
# obj[prop] = ''
# undefined
###*
* Produce html attributes for key value pairs
* @param {Object} attributes Key value pairs
* @return {string} space-separated html attributes
###
produceAttributes = (attributes) ->
_.reduce(
attributes,
(result, attributeVal, attributeKey) ->
result += " #{attributeKey}=\"#{attributeVal}\" "
, ''
)
###*
* Produce html attributes for key value pairs
* @param {Object} blanketOptions Key value pairs representing runtime options
* to set on blanket
* @return {string} script statements to assign options, e.g.
* 'blanket.options('myKey', 'myVal');'
###
producePhantomOptions = (blanketOptions) ->
_.reduce(
blanketOptions,
(result, optionVal, optionKey) ->
result += "blanket.options('#{optionKey}', '#{optionVal}');\n"
, '\n'
)
grunt.registerMultiTask 'blanket_mocha_server',
'Serve runtime-configured browser-based mocha tests with ' +
'blanket.js coverage.',
() ->
myDirName = __dirname.replace /[\\]/g, '/'
gbmsLib = myDirName + '/../lib/'
gbmsSupport = myDirName + '/support/'
gbmPath = '/node_modules/grunt-blanket-mocha/support'
if fs.existsSync(myDirName + '/..' + gbmPath)
gbmSupport = myDirName + '/..' + gbmPath + '/'
else
gbmSupport = process.cwd() + gbmPath + '/'
defaultOptions =
server: true
htmlFile: null
sutFiles: null
testFiles: null
blanketOptions: null
blanketPhantomOptions: null
noCoverage: false
mochaPath: gbmsLib + 'mocha.js'
mochaCssPath: gbmsLib + 'mocha.css'
mochaSetupScript: 'mocha.setup(\'bdd\');'
blanketPath: gbmsLib + 'blanket.js'
gruntReporterPath: gbmSupport + 'grunt-reporter.js'
mochaBlanketPath: gbmSupport + 'mocha-blanket.js'
assertionLibs: gbmsLib + 'chai.js'
assertionsSetupScript:
'window.expect = chai.expect; var should = chai.should();'
runnerTemplate: gbmsSupport + 'test-runner-template.html'
options = _.merge defaultOptions, @options()
config = options
resolveFile config, 'mochaPath', 1
resolveFile config, 'mochaCssPath', 0
resolveFile config, 'blanketPath', 0
resolveFile config, 'gruntReporterPath', 0
resolveFile config, 'mochaBlanketPath', 0
expandFiles config, 'assertionLibs', 1
expandFiles config, 'sutFiles'
expandFiles config, 'testFiles'
config.runnerTemplate = path.resolve config.runnerTemplate
if !fs.existsSync(config.runnerTemplate)
grunt.fatal "Specified runnerTemplate (#{config.runnerTempalate} " +
") not found."
config.assertionsSetupScript
config.blanketOptions = produceAttributes config.blanketOptions
config.blanketPhantomOptions =
producePhantomOptions config.blanketPhantomOptions
runnerTarget = path.resolve config.htmlFile
runnerTargetDir = path.dirname(runnerTarget) # runnerTarget.replace /\/[^\/]*$/, '/'
if !fs.existsSync(runnerTargetDir)
grunt.log.writeln 'making runner directory: ' + runnerTargetDir
fs.mkdirSync runnerTargetDir
grunt.log.writeln 'writing runner: ' + runnerTarget
fs.writeFileSync(
runnerTarget,
grunt.template.process(
fs.readFileSync(config.runnerTemplate, 'utf8'),
{ data: config }
)
)
if options.server
cwd = process.cwd()
process.chdir __dirname + '/..'
grunt.loadNpmTasks 'grunt-contrib-connect'
process.chdir cwd
grunt.config 'connect', {blanket_mocha_server_connect: {options: options}}
grunt.task.run 'connect:blanket_mocha_server_connect'