UNPKG

jsboost

Version:

A tiny library that extends the capability of javascript

310 lines (236 loc) 7.8 kB
/** * Author: JCloudYu * Create: 2019/02/06 **/ (async()=>{ let fs, path, dir, acquire; if ( typeof require === "undefined" ) { const {default:os} = await import( 'os' ); ({default:fs} = await import( 'fs' )); ({default:path} = await import( 'path' )); const is_win = (os.platform().substring(0, 3) === "win"); const [, script_path] = process.argv; dir = path.dirname(path.resolve(script_path)); const __proto = `file://${is_win? "/" : ""}`; acquire = async function import_es_module(path, check_name=true) { if ( check_name && path.substr(-8) !== ".test.js" && path.substr(-9) !== ".test.mjs" ) return; return import( `${__proto}${path}` ); }; } else { fs = require( 'fs' ); path = require( 'path' ); dir = __dirname; acquire = async function import_module(path, check_name=true) { if ( check_name && path.substr(-8) !== ".test.js" ) return; return require( path ); }; } const MAX_TOTAL_LEVELS = 10; const MAX_NESTED_LEVEL = MAX_TOTAL_LEVELS - 1; const root_procedure = __CREATE_PROCEDURE(); root_procedure.type = 'none'; root_procedure.script = null; const procedure_id_map = Object.create(null); const requested_proc = []; let current_script = null; let current_scope = root_procedure; global.init_context = __INIT_TEST_CONTEXT; global.test_group = __SCHEDULE_GROUP_PROCEDURE; global.unit_test = __SCHEDULE_TEST_PROCEDURE; global.run_procedure = __SCHEDULE_SILENT_PROCEDURE; // INFO: Load all modules ended with .test.js { try { const TEST_PATH = `${dir}/tests`; const dir_list = fs.readdirSync(TEST_PATH, {withFileTypes:true}); for(const fInfo of dir_list) { const fPath = `${TEST_PATH}/${fInfo.name}`; if ( !fInfo.isFile() ) continue; await acquire( current_script=fPath ); } } catch(e) { throw e; } for ( const procedure_id of process.argv.slice(2) ) { requested_proc.push(procedure_id); } } // region [ Run tests ] const selected_procedures = []; for( const id of requested_proc ) { const procedure = procedure_id_map[id]; if ( !procedure ) continue; selected_procedures.push(procedure); } if ( requested_proc.length > 0 && selected_procedures.length === 0 ) { console.log("\u001b[91mNo matched procedures! Exiting...\u001b[39m"); process.exit(0); } if ( selected_procedures.length === 0 ) { await __RUN_PROCEDURE(root_procedure); await __PRINT_RESULT(root_procedure); } else { for( const procedure of selected_procedures ) { await __RUN_PROCEDURE(procedure); } for( const procedure of selected_procedures ) { await __PRINT_RESULT(procedure); } } // endregion // region [ Internal Helper Functions ] async function __RUN_PROCEDURE(procedure) { current_scope = procedure; let group_passed = true; if ( procedure.parent !== null ) { let _start_time = Date.now(); try { await procedure.operation(); procedure.exec_time = Date.now() - _start_time; } catch(e) { procedure.error = e; procedure.passed = false; procedure.exec_time = Date.now() - _start_time; return; } } for( const _procedure of procedure.children ) { await __RUN_PROCEDURE(_procedure); group_passed = group_passed && _procedure.passed; } return (procedure.passed = group_passed); } async function __PRINT_RESULT(procedure) { if ( procedure.type === 'silent' ) return; if ( procedure.parent !== null ) { const color = procedure.passed ? "\u001b[92m" : "\u001b[91m"; const prefix = procedure.passed ? "\u2714 " : "\u2718 "; const indent = __INDENT(procedure.level); const exec_time = (procedure.type === "test") ? ` ( \u001b[93m${procedure.exec_time/1000} s ${color})` : ''; let error_msg = ''; if (procedure.error) { error_msg = `\u001b[93m( ${procedure.error} )\u001b[39m`; } console.log(`${indent}${color}${prefix}${procedure.message}${exec_time}\u001b[39m ${error_msg}`); if (procedure.error) { console.error(procedure.error); } } for(const _procedure of procedure.children) { await __PRINT_RESULT(_procedure); } } function __INIT_TEST_CONTEXT(id, group_op) { if ( arguments.length < 2 ) { group_op = id; id = null; } if ( typeof group_op !== "function" ) { throw new SyntaxError( "The argument of `init_context` must be a function!" ); } if ( current_scope.type !== 'none' ) { throw new SyntaxError( "`init_context can only be invoked once in the top most scope!`" ); } if ( current_scope.level >= MAX_NESTED_LEVEL ) { throw new RangeError( "Maximum nested level has been reached!" ); } const group = __CREATE_PROCEDURE(); group.type = 'group'; group.is_context = true; group.message = current_script; group.operation = group_op; group.level = current_scope.level+1; group.parent = current_scope; current_scope.children.push(group); if ( id !== null ) { procedure_id_map[id] = group; } } function __SCHEDULE_GROUP_PROCEDURE(message, group_op) { if ( typeof group_op !== "function" ) { throw new SyntaxError( "The second argument of `test_group` must be a function!" ); } if ( current_scope.type === 'none' ) { throw new SyntaxError( "`init_context` must be invoked before `test_group` call!" ); } else if ( current_scope.type !== 'group' ) { throw new SyntaxError( "`test_group` can only be invoked within another `test_group` call!" ); } if ( current_scope.level >= MAX_NESTED_LEVEL ) { throw new RangeError( "Maximum nested level has been reached!" ); } const group = __CREATE_PROCEDURE(); group.type = 'group'; group.message = message; group.operation = group_op; group.level = current_scope.level+1; group.parent = current_scope; current_scope.children.push(group); } function __SCHEDULE_TEST_PROCEDURE(message, test_op) { if ( typeof test_op !== "function" ) { throw new SyntaxError( "The second argument of `unit_test` must be a function!" ); } if ( current_scope.type === 'none' ) { throw new SyntaxError( "`init_context` must be invoked before `unit_test` call!" ); } else if ( current_scope.type !== 'group' ) { throw new SyntaxError( "`unit_test` can only be invoked within a `test_group` call!" ); } const test = __CREATE_PROCEDURE(); test.type = 'test'; test.message = message; test.operation = test_op; test.level = current_scope.level+1; test.parent = current_scope; current_scope.children.push(test); } function __SCHEDULE_SILENT_PROCEDURE(operation_op) { if ( typeof operation_op !== "function" ) { throw new SyntaxError( "The argument of `init_context` must be a function!" ); } if ( current_scope.type === 'none' ) { throw new SyntaxError( "`init_context` must be invoked before `run_procedure` call!" ); } else if ( current_scope.type !== 'group' ) { throw new SyntaxError( "`run_procedure` can only be invoked within another `run_procedure` call!" ); } if ( current_scope.level >= MAX_NESTED_LEVEL ) { throw new RangeError( "Maximum nested level has been reached!" ); } const group = __CREATE_PROCEDURE(); group.type = 'silent'; group.operation = operation_op; group.level = current_scope.level+1; group.parent = current_scope; current_scope.children.push(group); } function __INDENT(step=0) { let indent = ''; for(let i=0; i<step; i++) { indent += ' '; } return indent; } function __CREATE_PROCEDURE() { return Object.assign(Object.create(null), { type: 'scheduled', level: -1, is_context: false, message: null, operation: null, children: [], parent: null, passed: true, error: null }); } // endregion })().catch((e)=>{throw e;});