import * as GenerateBot from '../src/main' ; import crypto from 'crypto' ; import fs from 'fs' ; import recursive from 'recursive-readdir' ; import rimraf from 'rimraf' ; import os from 'os' ; import path from 'path' ; import Handlebars from 'handlebars' ; import { describe , it } from 'mocha' ; import * as assert from 'assert' ; import { execSync , ExecSyncOptions } from 'child_process' ; const handlebarsFileRegex = /.hbs$/ ; const readAllFiles = function ( dirNameRead , contentString ) : string { const files = fs . readdirSync ( dirNameRead ) . sort ( ) ; contentString = contentString || [ ] ; files . forEach ( file => { const readName = path . join ( dirNameRead , file ) ; if ( fs . statSync ( readName ) . isDirectory ( ) ) { contentString = readAllFiles ( readName , contentString ) ; } else { if ( ! readName . includes ( 'package.json' ) ) { contentString += fs . readFileSync ( readName , 'utf8' ) ; } } } ) ; return contentString as string ; } ; let tempTestDirectory ; let fileLocation ; let defaultProgramOptions ; before ( ( ) => { tempTestDirectory = fs . mkdtempSync ( `${ os . tmpdir ( ) }${ path . sep }` ) ; } ) ; beforeEach ( ( ) => { const suffix = crypto . randomBytes ( 16 ) . toString ( 'hex' ) ; fileLocation = path . join ( tempTestDirectory , suffix ) ; defaultProgramOptions = { programName : '{{programName}}' , exportName : '{{exportName}}' , description : '{{description}}' , fileLocation , platform : GenerateBot . Platform . CLOUD_FUNCTIONS , } ; } ) ; after ( ( ) => { rimraf . sync ( tempTestDirectory ) ; } ) ; describe ( 'file structure' , ( ) => { it ( 'should compile handlebars files while preserving non-hbs files' , async ( ) => { const originalStack = await recursive ( './templates' ) ; const options = { programName : 'testProgramName' , exportName : 'testExportName' , description : 'testDescription' , } ; GenerateBot . creatingBotFiles ( { ... defaultProgramOptions , ... options , } ) ; for ( const fileName of originalStack ) { const generatedFileNameBeforeCompile = fileName . replace ( handlebarsFileRegex , '' ) . replace ( /^templates/ , fileLocation ) ; const generatedfileName = Handlebars . compile ( generatedFileNameBeforeCompile ) ( options ) ; const templateContent = fs . readFileSync ( fileName , 'utf8' ) ; const generatedContent = fs . readFileSync ( generatedfileName , 'utf8' ) ; if ( handlebarsFileRegex . test ( fileName ) ) { const message = `Expected '${ fileName }', a handlebars file, to be generated to '${ generatedfileName }' with different content` ; assert . notStrictEqual ( templateContent , generatedContent , message ) ; } else { const message = `Expected '${ fileName }' to be copied as-is` ; assert . strictEqual ( templateContent , generatedContent , message ) ; } } } ) ; it ( 'should create identical files for each platform' , async ( ) => { let lastStack = [ ] ; for ( const platform of Object . values ( GenerateBot . Platform ) ) { const platformFileLocation = path . join ( fileLocation , platform ) ; GenerateBot . creatingBotFiles ( { ... defaultProgramOptions , platform , fileLocation : platformFileLocation , } ) ; const createdStack = ( await recursive ( platformFileLocation ) ) . map ( contents => contents . replace ( platformFileLocation , '' ) ) . sort ( ) ; if ( lastStack . length ) { assert . deepStrictEqual ( lastStack , createdStack ) ; } lastStack = createdStack ; } } ) ; it ( 'should create nested folders if they do not yet exist' , async ( ) => { GenerateBot . creatingBotFiles ( { ... defaultProgramOptions , programName : 'helloWorld' , description : 'says hi' , fileLocation : path . join ( defaultProgramOptions . fileLocation , 'nested' , 'directory' ) , } ) ; const content = readAllFiles ( fileLocation , '' ) . replace ( /\r/g , '' ) ; assert . ok ( content ) ; } ) ; } ) ; describe ( 'user input' , ( ) => { it ( 'checks that integers and other characters are not passed' , ( ) => { const integerTest = GenerateBot . checkValidity ( { ... defaultProgramOptions , programName : 'does,notpass' , description : '5oesN0tP4SS' , } ) ; assert . strictEqual ( integerTest , false ) ; } ) ; it ( 'checks that only valid characters are passed' , ( ) => { const validCharTest = GenerateBot . checkValidity ( { ... defaultProgramOptions , programName : 'pas-s' , description : 'pas_s' , } ) ; assert . strictEqual ( validCharTest , true ) ; } ) ; it ( 'checks that nulls are not passed' , ( ) => { const nullTest = GenerateBot . checkValidity ( { ... defaultProgramOptions , programName : '' , description : 'pass' , } ) ; assert . strictEqual ( nullTest , false ) ; } ) ; it ( 'checks that files are not overwritten' , ( ) => { const overWritingTest = GenerateBot . checkValidity ( { ... defaultProgramOptions , programName : 'templates' , description : 'pass' , fileLocation : './templates' , } ) ; assert . strictEqual ( overWritingTest , false ) ; } ) ; it ( 'checks that the program name is not upper-cased' , ( ) => { const programNameToLower = { ... defaultProgramOptions , programName : 'PassButMakeLowerCase' , description : 'pass' , } ; assert . ok ( GenerateBot . checkValidity ( programNameToLower ) ) ; assert . strictEqual ( programNameToLower . programName , 'passbutmakelowercase' ) ; } ) ; it ( 'checks that the program has a default location' , ( ) => { const fileLocationDefault = { ... defaultProgramOptions , programName : 'pass' , description : 'pass' , fileLocation : '' , } ; GenerateBot . checkValidity ( fileLocationDefault ) ; const fileLocation = fileLocationDefault . fileLocation . toString ( ) . replace ( /\\/g , '/' ) ; const regexLinux = new RegExp ( 'packages/pass' ) ; assert . strictEqual ( regexLinux . test ( fileLocation ) , true ) ; } ) ; it ( 'checks that the export name uses underscores' , ( ) => { const programNameWithDash = { ... defaultProgramOptions , programName : 'with-dash' , description : 'pass' , } ; assert . ok ( GenerateBot . checkValidity ( programNameWithDash ) ) ; assert . strictEqual ( programNameWithDash . exportName , 'with_dash' ) ; } ) ; } ) ; describe ( 'end to end' , ( ) => { const programName = 'testy' ; for ( const platform of Object . values ( GenerateBot . Platform ) ) { describe ( platform , ( ) => { it ( 'should generate a working package' , async ( ) => { await GenerateBot . creatingBotFiles ( { ... defaultProgramOptions , programName , description : programName , platform , } ) ; const execOptions = { cwd : fileLocation , stdio : 'inherit' , encoding : 'utf8' , } ; execSync ( 'npm install' , execOptions ) ; execSync ( 'npm test' , execOptions ) ; } ) ; } ) ; } } ) ;