Nodejs Shell Program Examples

nodejs

const ArgumentParser = require('argparse').ArgumentParser;
const path = require('path');
const fs = require('fs');
const exec = require('child_process').exec;
const spawn = require('child_process').spawn;
const fork = require('child_process').fork;
const execSync = require('child_process').execSync;
const clear = require('clear-screen');
const mkdirp = require('mkdirp');
const recursive = require('recursive-readdir');
const Promise = require('promise');

var targetPath = path.resolve(process.cwd(), 'node_modules');
const ArgumentParser = require('argparse').ArgumentParser;
const path = require('path');
const fs = require('fs');
const exec = require('child_process').exec;
const spawn = require('child_process').spawn;
const fork = require('child_process').fork;
const execSync = require('child_process').execSync;
const clear = require('clear-screen');
const mkdirp = require('mkdirp');
const recursive = require('recursive-readdir');
const Promise = require('promise');

const opener = require("opener");
const ncp = require('ncp').ncp;
const remapIstanbul = require('remap-istanbul');
const istanbul = require('istanbul');
const rimraf = require('rimraf');

// Create an instance of ArgumentParser and invoke addArguments to 
// add various arguments that we need to support.

let parser = new ArgumentParser({
    addHelp: true
});

parser.addArgument(
    ['-appname'],
    {
        help: 'app name'
    }
);

parser.addArgument(
    ['-appfolder'],
    {
        help: 'override the app folder location'
    }
);

parser.addArgument(
    ['-addlsrc'],
    {
        help: 'other source folders that need to be copied'
    }
);

parser.addArgument(
    ['-runfunc'],
    {
        help: 'run functional tests (true/false) -- default true'
    }
);

parser.addArgument(
    ['-buildsite'],
    {
        help: 'build site (true/false) -- useful if you have functional tests but don\'t need to create site  -- default true'
    }
);

parser.addArgument(
    ['-rununit'],
    {
        help: 'run unit tests (true/false) -- default true'
    }
);

parser.addArgument(
    ['-testsuite'],
    {
        help: 'specify test suite to run'
    }
);

// Parse the provided arguments and validate:

let args = parser.parseArgs();
if (args.appname == null || args.appname == undefined) {
  throw new Error('app name is required');
}
args.runfunc = (args.runfunc == null || args.runfunc == undefined) ? 
  true : JSON.parse(args.runfunc.toLowerCase());
args.rununit = (args.rununit == null || args.rununit == undefined) ? 
  true : JSON.parse(args.rununit.toLowerCase());
args.buildsite = (args.buildsite == null || args.buildsite == undefined) ? 
  true : JSON.parse(args.buildsite.toLowerCase());

//clear screen
clear();

const appFolder = (args.appfolder == null || args.appfolder == undefined) ? 
  path.resolve('./apps', args.appname) : path.resolve(args.appfolder); 

let testingFolder = path.resolve('./testing', args.appname);
let srcTestingFolder = path.resolve(testingFolder, 'client');
let compileFolder = path.resolve(testingFolder, '.testing');
let srcFolder = path.resolve(appFolder, 'client', 'app');
let funcTestSrcFolder = path.resolve(appFolder, 'client', 'functional-tests');

rimraf(testingFolder, function(e) {

    if(!fs.existsSync(srcTestingFolder)) mkdirp.sync(srcTestingFolder);

    let buildSteps = [];
    //running build steps async
    buildSteps.push(copySrc()); //copy the source
    buildSteps.push(createConfig()); //create the test config
    buildSteps.push(createSymLinks());  //create symbolic links

    Promise.all(buildSteps)
    .then(function(resolved) {
        return compile(resolved[1]) //resolved[1] should be the result from createConfig
    })
    .catch(function(err) {
        console.log(err);
        process.exit();
    })
    .then(function() {
        return updateJsMap();
    })
    .catch(function(err) {
        console.log(err);
        process.exit();
    })
    .then(function() {

        let nextSteps = [];

        nextSteps.push(getTests('functional-tests', 'functionaltests.js', args.runfunc)); //consolidate functional tests
        nextSteps.push(getTests('app', 'unittests.js', args.rununit)); //consolidate unit tests

        if(args.runfunc && args.buildsite) nextSteps.push(buildSite());

        return Promise.all(nextSteps);
    })
    .catch(function(err) {
        console.log(err);
        process.exit();
    })
    .then(function(resolved) {
        return runTests();
    });
});

function buildSite() {
    let p = new Promise(
        function(resolve, reject) {

            let cmd = 'npm run build-test -- -cleandist=false -appname=' + args.appname + ' -appfolder=' + appFolder;
            console.log('building ' + args.appname + ': ' + cmd);
            execSync(cmd, { stdio: ['inherit', 'inherit', 'inherit'] });

            resolve();
        }
    );

    return p;
}

function updateJsMap() {

    let p = new Promise(
        function(resolve, reject) {

            let regex = /.*\.js\.map$/gi;

            recursive(srcTestingFolder, function(err, files) {

                if(err) {
                    console.log(err);
                    reject(err);
                    return;
                }

                files.forEach((f) => {
                    let dirName = path.dirname(f);
                    let p = dirName.replace(/\\/gi, '/');
                    let srcTestingAppFolder = path.resolve(srcTestingFolder, 'app');
                    if((p.indexOf(path.resolve(srcTestingFolder, 'tx-core-modules').replace(/\\/gi, '/')) == -1) && 
                        (p.indexOf(path.resolve(srcTestingFolder, 'node_modules').replace(/\\/gi, '/')) == -1)) {
                        if(regex.test(f)) {
                            let sourceMap = JSON.parse(fs.readFileSync(f));
                            for(let i = 0; i < sourceMap.sources.length; i++) {
                                let srcFile = sourceMap.sources[i];
                                let relsrcPath = path.join(path.relative(srcTestingAppFolder, dirName), path.basename(srcFile));
                                sourceMap.sources[i] = path.resolve(srcFolder, relsrcPath).replace(/\\/gi, '/');
                                if(!fs.existsSync(sourceMap.sources[i])) throw sourceMap.sources[i] + ' does not exist -- ' + f;
                            }
                            fs.writeFileSync(f, JSON.stringify(sourceMap), 'utf8');
                        }
                    }
                });

                resolve();
            });
        }
    );

    return p;
}

//copy all source files to testing folder
function copySrc() {

    let p = new Promise(
        function(resolve, reject) {

            ncp(srcFolder, path.resolve(srcTestingFolder, 'app'), function(e) {
                if(e) {
                    console.log(e);
                    reject(e);
                    return;
                }

                console.log('copied src files');
                resolve();
            });
        }
    );

    p = p.then(
        function() {
            return new Promise(
                function(resolve, reject) {
                    ncp(funcTestSrcFolder, path.resolve(srcTestingFolder, 'functional-tests'), function(e) {
                        if(e) {
                            console.log(e);
                            reject(e);
                            return;
                        }
                        console.log('copied functional tests');
                        resolve();
                    });
            });
        }
    );

    if(args.addlsrc != null && args.addlsrc != undefined) {
        let addlSrcPaths = args.addlsrc.split(",");
        for(let i=0; i<addlSrcPaths.length; i++) {
            p = p.then(
                function(){ 
                    return new Promise(
                        function(resolve, reject){
                            ncp(path.resolve(appFolder, addlSrcPaths[i]), path.resolve(testingFolder, addlSrcPaths[i]), function(e) {
                                if(e) {
                                    console.log(e);
                                    reject(e);
                                    return;
                                }
                                console.log("copied " + addlSrcPaths[i]);
                                resolve();
                            });
                        }
                    );
                }
            );
        }
    }

    return p;
}

function createSymLinks() {

    let p = new Promise(
        function(resolve, reject) {

            let links = [];
            links.push({
                lnk: path.resolve(srcTestingFolder, 'node_modules'),
                targetPath: path.resolve(process.cwd(), 'node_modules'),
                type: 'dir'
            });

            links.push({
                lnk: path.resolve(srcTestingFolder, 'tx-core-modules'),
                targetPath: path.resolve(process.cwd(), 'dist/tx-core-modules'),
                type: 'dir'
            });

            fs.mkdirSync(path.resolve(srcTestingFolder, 'results'));
            links.push({
                lnk: path.resolve(srcTestingFolder, 'results/intern-html-reporter-templates'),
                targetPath: path.resolve(process.cwd(), 'config/intern-html-reporter-templates'),
                type: 'dir'
            });

            links.push({
                lnk: path.resolve(srcTestingFolder, 'intern.config.js'),
                targetPath: path.resolve(process.cwd(), 'config/intern.config.js'),
                type: 'file'
            });

            links.push({
                lnk: path.resolve(srcTestingFolder, 'intern-reporter.js'),
                targetPath: path.resolve(process.cwd(), 'config/intern-reporter.js'),
                type: 'file'
            });

            links.push({
                lnk: path.resolve(srcTestingFolder, 'intern-html-reporter.js'),
                targetPath: path.resolve(process.cwd(), 'config/intern-html-reporter.js'),
                type: 'file'
            });

            links.push({
                lnk: path.resolve(srcTestingFolder, 'package.json'),
                targetPath: path.resolve(process.cwd(), 'package.json'),
                type: 'file'
            });

            for(let i = 0; i < links.length; i++) {
                let lnk = links[i].lnk;
                let targetPath = links[i].targetPath;
                let type = links[i].type;

                console.log('creating symbolic link ' + lnk + ' -> ' + targetPath);
                fs.symlinkSync(targetPath, lnk, type);
            }

            resolve();
        }
    );

    return p;    
}

function createConfig() {

    let p = new Promise(
        function(resolve, reject) {
            let config = fs.readFileSync('./config/tsconfig.testing.json', 'utf8');

            config = config.replace(/\{srcFolder\}/gi, srcTestingFolder.replace(/\\/gi, '/'));
            config = config.replace(/\{baseFolder\}/gi, srcTestingFolder.replace(/\\/gi, '/'));
            config = config.replace(/\{relTxCoreFolder\}/gi, path.relative(srcTestingFolder, path.resolve(srcTestingFolder, 'tx-core-modules')).replace(/\\/gi, '/'));

            let configFileName = path.resolve(srcTestingFolder, 'tsconfig.testing.' + args.appname + '.json');
            fs.writeFileSync(configFileName, config, 'utf8');

            resolve(configFileName);
        }
    );

    return p;
}

function getTests(folder, testfile, addTests) {

    let p = new Promise(
        function(resolve, reject) {

            let regex = /.*\.intern\.js$/gi;

            recursive(path.resolve(srcTestingFolder, folder), function(err, files) {

                if(err) {
                    console.log(err);
                    reject(err);
                } else {
                    let tests = [];
                    if(addTests || args.testsuite) {
                        files.forEach((f) => {

                            let filepath = f;
                            let fileName = path.basename(filepath);

                            if(fileName.endsWith('.intern.js') || (folder == 'functional-tests' && fileName.endsWith('.js'))) {

                                let moduleName = path.relative(srcTestingFolder, filepath).replace('.js', '');

                                if(args.testsuite) {
                                    if(moduleName.toLowerCase().indexOf(args.testsuite.toLowerCase()) >= 0) {
                                        tests.push(moduleName.replace(/\\/gi, '/'));
                                    }
                                } else {
                                    tests.push(moduleName.replace(/\\/gi, '/'));
                                }
                            }
                        });
                    }

                    fs.writeFileSync(path.resolve(srcTestingFolder, testfile),
                        'define(["require", "exports"], function (require, exports) {\n    "use string";\n    exports.tests=' + JSON.stringify(tests) + ';\n});', 'utf8');

                    resolve();
                }
            });
        }
    );

    return p;
}

function compile(configFileName) {

    let p = new Promise(
        function(resolve, reject) {

            let cmd = 'npm run tsc -- -p ' + configFileName;
            console.log('compiling: ' + cmd);
            execSync(cmd, { stdio: ['inherit', 'inherit', 'inherit'] });

            resolve();
        }
    );

    return p;
}

function runTests() {

    let p = new Promise(
        function(resolve, reject) {

            let cwd = process.cwd();
            let chromeProc = spawn('./drivers/chromedriver', ['--port=4444', '--url-base=wd/hub'], { stdio: ['inherit', 'inherit', 'inherit'] });

            //changing working folder to make running tests easier
            process.chdir(srcTestingFolder);

            try {
                let cmd = 'npm run intern-runner';
                console.log('running tests: ' + cmd);

                execSync(cmd, { stdio: ['inherit', 'inherit', 'inherit'] });

            } catch(ex) {
                console.log(ex);
            } finally {
                chromeProc.kill('SIGINT');
            }

            //remap code coverage

            fs.access(path.resolve(srcTestingFolder, 'results/coverage.json'), fs.constants.F_OK, function(err) {

                console.log('remapping code coverage');
                remapIstanbul(path.resolve(srcTestingFolder, 'results/coverage.json'), {
                    'json': path.resolve(srcTestingFolder, 'results/coverage-remapped.json'),
                    'html': path.resolve(srcTestingFolder, 'results/coverage/')
                })
                .then(function() {
                    console.log('opening coverage report');
                    return new Promise(function(res, rej) {
                        fs.access(path.resolve(srcTestingFolder, 'results/coverage/index.html'), fs.constants.F_OK, function(err) {
                            if(err) {
                                console.log(err);
                                rej(err);
                            } else {
                                opener(path.resolve(srcTestingFolder, 'results/coverage/index.html'));
                                res();
                            }
                        })
                    });

                })
                .then(function() {
                    console.log('opening test results');
                    return new Promise(function(res, rej) {
                        fs.access(path.resolve(srcTestingFolder, 'results/test-results.html'), fs.constants.F_OK, function(err) {
                            if(err) {
                                console.log(err);
                                rej(err);
                            } else {
                                opener(path.resolve(srcTestingFolder, 'results/test-results.html'));
                                res();
                            }
                        })
                    });
                })
                .then(function() {
                    process.chdir(cwd);
                    resolve();
                    return;
                })
                .catch(
                    function(result) {
                        console.log(result);
                    }
                );
            });
        }
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License