ZEST / base.gruntrunner / Source: tasks/test.js

'use strict';
/**
 * @fileOverview this module registers a grunt task to run mocha tests and generate coverage reports.
 * @module tasks/test
 * @requires {@link external:merge}
 * @requires {@link external:grunt-contrib-clean}
 */
var path = require('path');
var merge = require('merge');
/**
 * this function registers a grunt task to run mocha tests and generate coverage reports. The below tasks are created
 *
 *  -  `jshint:lib` for running jshint tests on main source files
 *  -  `jshint:test` for running jshint tests on test files. This task is not created if there are no test files.
 *  -  `jshint:build` for running jshint tests on build files (GruntFile and package.json).
 *  -  `mochacov:test` for running mocha tests. This task is not created if there are no test files
 *  -  `mochacov:coverage` for generating coverage report in terminal. This task is not created if there are no test
 *      files
 *  -  `mochacov:coveralls` for pushing coverage report to coveralls. This task is not created if there are no test
 *      files
 *  -  `coveralls` which is an alias for `mochacov:coveralls`. If there are no tests, this task doesn't do anything
 *  -  `test` which is an alias for `jshint:lib`, `jshint:test`, `jshint:build`, `mochacov:test` and
 *      `mochacov:coverage`. If there are no tests, mochacov:* and jshint:test are not present.
 *
 * @param {*} grunt - the grunt object
 * @param {string} gruntModuleDirectory - the directory where the grunt modules are located
 */
module.exports = function (grunt, gruntModuleDirectory) {
    var isTest = grunt.config('pkg.directories.test');
    // global variables are defined here
    var files = {
        lib: [
            '<%= pkg.directories.lib %>/**/*.js',
            '<%= pkg.directories.lib %>/**/*.json'
        ],
        test: [
            '<%= pkg.directories.test %>/**/*.js',
            '<%= pkg.directories.test %>/**/*.json',
            '!**/node_modules/**'
        ],
        build: [
            'Gruntfile.js',
            'package.json'
        ]
    };
    var mochaGlobals = [
        'describe',
        'it',
        'beforeEach',
        'afterEach',
        'before',
        'after'
    ];
    var jshintOptions = {
        nonew: true,
        plusplus: true,
        curly: true,
        latedef: true,
        maxdepth: 6,
        unused: true,
        noarg: true,
        trailing: true,
        indent: 4,
        forin: true,
        noempty: true,
        quotmark: true,
        maxparams: 6,
        node: true,
        maxstatements: 30,
        eqeqeq: true,
        strict: true,
        undef: true,
        bitwise: true,
        newcap: true,
        immed: true,
        camelcase: true,
        maxcomplexity: 7,
        maxlen: 120,
        nonbsp: true,
        freeze: true
    };
    // load the required npm tasks for code quality
    grunt.loadNpmTasks(path.join(gruntModuleDirectory, 'grunt-contrib-jshint'));
    // load the required npm tasks for running node mocha tests and code coverage reports
    if (isTest) {
        grunt.loadNpmTasks(path.join(gruntModuleDirectory, 'grunt-mocha-cov'));
        // mocha configuration
        grunt.config(
            'mochacov', {
                options: {
                    // set test-case timeout in milliseconds [2000]
                    timeout: 50000,
                    // check for global variable leaks.
                    'check-leaks': true,
                    // specify user-interface (bdd|tdd|exports).
                    ui: 'bdd',
                    // "slow" test threshold in milliseconds [75].
                    slow: 10,
                    files: [
                        '<%= pkg.directories.test %>/**/*.js',
                        '!**/node_modules/**'
                    ]
                },
                // default test option
                test: {
                    options: {
                        reporter: 'spec'
                    }
                },
                coverage: {
                    options: {
                        reporter: 'mocha-term-cov-reporter',
                        coverage: true
                    }
                },
                // for sending coverage report to coveralls
                coveralls: {
                    options: {
                        coveralls: true
                    }
                }
            }
        );
    }
    // js-hint configuration
    grunt.config(
        [
            'jshint',
            'lib'
        ], {
            // validation for all server javascript files
            src: files.lib,
            options: jshintOptions
        }
    );
    // validation for all server javascript specifications
    if (isTest) {
        grunt.config(
            [
                'jshint',
                'test'
            ],
            {
                src: files.test,
                options: merge(
                    {
                        predef: mochaGlobals
                    },
                    jshintOptions
                )
            }
        );
    }
    // validation for all server javascript specifications
    grunt.config(
        [
            'jshint',
            'build'
        ], {
            src: files.build,
            options: jshintOptions
        }
    );
    // for faster builds we make sure that only the changed files are validated
    (function () {
        // save the watch timeouts to keep track of the ongoing watches
        var watchTimeouts = {};
        grunt.event.on(
            'watch', function (action, filepath, target) {
                if (action !== 'deleted' && /\.(js(on)?)$/i.test(filepath)) {
                    var config = [],
                        jshintSrc = 'jshint.' + target + '.src';
                    if (watchTimeouts[target]) {
                        // if there is an ongoing watch event, append the new files
                        // in the file list
                        clearTimeout(watchTimeouts[target]);
                        config = grunt.config(jshintSrc);
                    } else {
                        // in case of a new watch, create a new file list
                        grunt.config(jshintSrc, config);
                    }
                    // pass the file for jshint validation only if it is a javascript or a json file
                    config.push(filepath);
                    grunt.config(jshintSrc, config);
                    watchTimeouts[target] = setTimeout(
                        function () {
                            watchTimeouts[target] = undefined;
                        }, 1000
                    );
                }
            }
        );
    }());
    if (isTest) {
        // coverage script
        grunt.registerTask(
            'coveralls',
            [
                'mochacov:coveralls'
            ]
        );
        // test script
        grunt.registerTask(
            'test',
            [
                'jshint:lib',
                'jshint:test',
                'jshint:build',
                'mochacov:test',
                'mochacov:coverage'
            ]
        );
    } else {
        // coverage script
        grunt.registerTask(
            'coveralls',
            []
        );
        // test script
        grunt.registerTask(
            'test',
            [
                'jshint:lib',
                'jshint:build'
            ]
        );
    }
};