UNPKG

jec-cli

Version:

CLI tool for managing JEC projects.

212 lines (195 loc) 7.64 kB
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. // // Copyright 2016-2018 Pascal ECHEMANN. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. import {Command, CliLogger, ConsoleCliLogger} from "jec-tool-cli"; import * as fs from "fs"; import * as fsExtra from "fs-extra"; import * as path from "path"; import {TypescriptParser, File, MethodDeclaration, DeclarationVisibility, ClassDeclaration} from "typescript-parser"; import {TestSuiteTemplateGenerator, TemplateBuilder} from "jec-cli-template"; import {TestSuiteDto} from "../../utils/TestSuiteDto"; /** * The <code>CreateTestSuite</code> command allows create a test suite for the * specified class. */ export class CreateTestSuite implements Command { ////////////////////////////////////////////////////////////////////////////// // Constructor function ////////////////////////////////////////////////////////////////////////////// /** * Creates a new <code>CreateTestSuite</code> instance. */ constructor() {} ////////////////////////////////////////////////////////////////////////////// // Private properties ////////////////////////////////////////////////////////////////////////////// /** * A reference to the dot (<code>.</code>) character. */ private readonly DOT:string = "."; /** * A reference to the slash (<code>/</code>) character. */ private readonly SLASH:string = "/"; /** * A reference to the relative path pattern (<code>../</code>). */ private readonly RELATIVE_PATH:string = "../"; /** * A reference to the TypeScript (<code>.ts</code>) extension. */ private readonly EXTENSION:string = ".ts"; ////////////////////////////////////////////////////////////////////////////// // Private methods ////////////////////////////////////////////////////////////////////////////// /** * Creates and return a config DTO object for the specified user's inputs. * * @param {any} argv the user's inputs for which to create the config DTO. * @return {TestSuiteDto} a config DTO object used to initialize the new test * suite. */ private createDto(argv:any):TestSuiteDto { const result:TestSuiteDto = new TestSuiteDto(); const classRef:string = argv.class; const lastDotId:number = classRef.lastIndexOf(this.DOT); result.className = classRef.substr(lastDotId + 1); result.name = result.className + "Test"; result.classPath = classRef.substr(0, lastDotId) .replace(this.DOT, this.SLASH); result.testPath = path.join("test", result.classPath); this.createRelativeClassPath(result); return result; } /** * A visitor function that creates the relative class path reference for the * specified config DTO. * * @param {TestSuiteDto} dto the config object for which to create the * relative class path reference. */ private createRelativeClassPath(dto:TestSuiteDto):void { const buffer:string[] = dto.testPath.split(this.SLASH); let pathFix: string = this.RELATIVE_PATH; let len:number = buffer.length; while(len--) { pathFix += this.RELATIVE_PATH; } dto.relativeClassPath = `${pathFix}src/${dto.classPath}/${dto.className}`; } /** * Loads the class for which to create the new test suite. * * @param {TestSuiteDto} dto the config object associated with the class to * load. * @param {Function} onLoad the callback function called once the class has * been loaded. */ private loadClass(dto:TestSuiteDto, onLoad:Function):void { const filePath:string = path.join( process.cwd(), "src", dto.classPath, dto.className + this.EXTENSION ); fs.readFile(filePath, (err:NodeJS.ErrnoException, data:any) => { onLoad(err, data) }); } /** * Extracts the methods for which to create test cases from the loaded class * file. * * @param {string} data the class file from which to extract method names. * @param {Function} complete the callback method calld once the method names * have been extracted. */ private extractDeclarations(data:string, complete:Function):void { const result:string[] = new Array<string>(); const parser:TypescriptParser = new TypescriptParser(); const parsed:Promise<File> = parser.parseSource(data); let len:number = -1; let declarations:MethodDeclaration[] = null; let declaration:any = null; parsed.then( (file:File) => { declarations = (file.declarations[0] as ClassDeclaration).methods; len = declarations.length; while(len--) { declaration = declarations[len]; if(declaration.visibility === DeclarationVisibility.Public) { result.push(declaration.name); } } complete(null, result); }, (reason:any) => { complete(reason, null); } ); } /** * Writes a JEC test suite in a new file, depending on the specified options. * * @param {TestSuiteDto} config the config object used to customize the JEC * test suite. * @param {Function} callback the callback method called once the file is * written. */ private write(config:TestSuiteDto, callback:Function):void { const builder:TemplateBuilder = new TemplateBuilder(); const template:string = builder.build(TestSuiteTemplateGenerator, config); const name:string = config.name + this.EXTENSION; fsExtra.ensureDir(config.testPath, (err:Error) => { if(err) callback(err); else { fsExtra.writeFile( path.join(config.testPath, name), template, (err:NodeJS.ErrnoException | null) => { callback(err); } ); } }); } ////////////////////////////////////////////////////////////////////////////// // Public methods ////////////////////////////////////////////////////////////////////////////// /** * @inheritDoc */ public run(argv:any):void { const logger:CliLogger= ConsoleCliLogger.getInstance(); let dto:TestSuiteDto = this.createDto(argv); this.loadClass(dto, (err:NodeJS.ErrnoException, data:any) => { if(err) logger.error(err); else { this.extractDeclarations(data.toString(), (error:any, data:any) => { if(err) logger.error(error); else { dto.methods = data; this.write( dto, (err:NodeJS.ErrnoException | null) => { err ? logger.error(err) : logger.log( `Test suite with name '${dto.name}.ts' created in '${dto.testPath}'.` ); } ); } }); } }); } }