Source: apc-abstract/task/worker/generate_test_files.js

/**
 * Generate test files unless exist.
 * @function task.worker.generateTestFiles
 * @param {object} config - Task configuration.
 * @param {string} config.srcDir - Source files directory.
 * @param {string} [config.ignore] - Ignore  file pattern. Glob.
 * @param {string} [config.pattern='*'] - Source file pattern. Glob.
 * @param {string} config.destDir - Destination test directory.
 * @param {string} [config.testFilenamePrefix=""]  - Test filename prefix.
 * @param {string} [config.testFilenameSuffix="_test"]  - Test filename suffix.
 * @param {object} [config.data] - Test template data.
 * @param {boolean} [config.capitalize=false] - Capitalize the module name in the test file.
 * @param {string} config.tmpl - Test file template file name.
 * @param {function} callback - Callback when done.
 * @author Taka Okunishi
 *
 */
var path = require('path'),
    fs = require('fs'),
    debug = require('./_debug'),
    file = require('../../lib/file'),
    object = require('../../lib/object'),
    string = require('../../lib/string'),
    deepCopy = object.deepCopy,
    camelize = string.camelize,
    _expandGlobUnlessIgnored = require('./_expand_glob_unless_ignored'),
    loadHbsTmpl = file.loadHbsTmpl;

exports = module.exports = function (config, callback) {
    var async = require('async'),
        srcDir = path.resolve(config.srcDir),
        destDir = path.resolve(config.destDir),
        ignore = config.ignore && path.resolve(srcDir, config.ignore),
        src = path.resolve(srcDir, config.pattern || '*'),
        tmplFilename = path.resolve(config.tmpl),
        data = config.data || {};
    var capitalizeModuleName = config.capitalize || false;
    loadHbsTmpl(tmplFilename, function (err, tmpl) {
        if (err) {
            callback(err);
            return;
        }
        _expandGlobUnlessIgnored(src, ignore, function (err, filenames) {
            if (err) {
                callback(err);
                return;
            }
            async.parallel(
                filenames
                    .map(function (filename) {
                        return function (callback) {
                            var dest = exports._nameTestFile(
                                    config.testFilenamePrefix || '',
                                    path.resolve(destDir, path.relative(srcDir, path.dirname(filename))),
                                    filename,
                                    config.testFilenameSuffix || '_test'
                                )
                                ;
                            fs.stat(filename, function (err, stats) {
                                if (err) {
                                    callback(err);
                                    return;
                                }
                                var isDir = stats.isDirectory();
                                if (isDir) {
                                    callback(null);
                                    return;
                                }
                                fs.exists(dest, function (exists) {
                                    if (exists) {
                                        callback(null);
                                    } else {
                                        var extname = path.extname(filename),
                                            basename = path.basename(filename, extname);
                                        var content = tmpl(deepCopy(data, {
                                            testFileName: path.basename(dest, path.extname(dest)),
                                            moduleName: camelize(basename, capitalizeModuleName),
                                            modulePath: data.modulePath || exports._requireName(dest, filename),
                                            testName: string.capitalize(basename.replace(/^_/, '').split(/\-|_/g).join(' ')) + '.'
                                        }));
                                        fs.writeFile(dest, content, function (err) {
                                            if (!err) {
                                                debug.didCreateFile(dest);
                                            }
                                            callback(err);
                                        });
                                    }
                                });
                            });
                        }
                    }),
                function (err) {
                    callback(err)
                }
            )
        });
    });
};
exports._requireName = function (from, to) {
    var requireName = path.relative(path.dirname(from), to);
    if (requireName.match(/^\./)) {
        return requireName;
    } else {
        return path.join('.', requireName);
    }
};
exports._nameTestFile = function (prefix, dirname, src, suffix) {
    var extname = path.extname(src),
        basename = path.basename(src, extname);
    return path.resolve(dirname, prefix + basename + suffix + '.js');
};