import * as os from 'os' ; import * as path from 'path' ; import * as child_process from 'child_process' ; import { Executable , IExecutableSpawnSyncOptions } from '../Executable' ; import { FileSystem } from '../FileSystem' ; import { PosixModeBits } from '../PosixModeBits' ; import { Text } from '../Text' ; const executableFolder = path . join ( __dirname , '..' , '..' , 'src' , 'test' , 'test-data' , 'executable' ) ; let environment ; if ( os . platform ( ) === 'win32' ) { environment = { PATH : [ path . join ( executableFolder , 'skipped' ) , path . join ( executableFolder , 'success' ) , path . join ( executableFolder , 'fail' ) , path . dirname ( process . execPath ) ] . join ( path . delimiter ) , PATHEXT : '.COM;.EXE;.BAT;.CMD;.VBS' , TEST_VAR : '123' } ; } else { environment = { PATH : [ path . join ( executableFolder , 'skipped' ) , path . join ( executableFolder , 'success' ) , path . join ( executableFolder , 'fail' ) , path . dirname ( process . execPath ) , '/usr/local/bin' , '/usr/bin' , '/bin' ] . join ( path . delimiter ) , TEST_VAR : '123' } ; } const options = { environment : environment , currentWorkingDirectory : executableFolder , stdio : 'pipe' } ; beforeAll ( ( ) => { expect ( FileSystem . exists ( executableFolder ) ) . toEqual ( true ) ; if ( os . platform ( ) !== 'win32' ) { FileSystem . changePosixModeBits ( path . join ( executableFolder , 'success' , 'npm-binary-wrapper' ) , PosixModeBits . AllRead | PosixModeBits . AllWrite | PosixModeBits . AllExecute ) ; FileSystem . changePosixModeBits ( path . join ( executableFolder , 'success' , 'bash-script.sh' ) , PosixModeBits . AllRead | PosixModeBits . AllWrite | PosixModeBits . AllExecute ) ; } } ) ; afterAll ( ( ) => { if ( os . platform ( ) !== 'win32' ) { FileSystem . changePosixModeBits ( path . join ( executableFolder , 'success' , 'npm-binary-wrapper' ) , PosixModeBits . AllRead | PosixModeBits . AllWrite ) ; FileSystem . changePosixModeBits ( path . join ( executableFolder , 'success' , 'bash-script.sh' ) , PosixModeBits . AllRead | PosixModeBits . AllWrite ) ; } } ) ; test ( 'Executable.tryResolve() pathless' , ( ) => { const resolved = Executable . tryResolve ( 'npm-binary-wrapper' , options ) ; expect ( resolved ) . toBeDefined ( ) ; const resolvedRelative = Text . replaceAll ( path . relative ( executableFolder , resolved ! ) , '\\' , '/' ) ; if ( os . platform ( ) === 'win32' ) { expect ( resolvedRelative ) . toEqual ( 'success/npm-binary-wrapper.cmd' ) ; } else { expect ( resolvedRelative ) . toEqual ( 'success/npm-binary-wrapper' ) ; } expect ( Executable . tryResolve ( 'missing-extension' , options ) ) . toBeUndefined ( ) ; } ) ; test ( 'Executable.tryResolve() with path' , ( ) => { const resolved = Executable . tryResolve ( './npm-binary-wrapper' , options ) ; expect ( resolved ) . toBeUndefined ( ) ; } ) ; function executeNpmBinaryWrapper ( args ) { const result = Executable . spawnSync ( 'npm-binary-wrapper' , args , options ) ; expect ( result . error ) . toBeUndefined ( ) ; expect ( result . stderr ) . toBeDefined ( ) ; expect ( result . stderr . toString ( ) ) . toEqual ( '' ) ; expect ( result . stdout ) . toBeDefined ( ) ; const outputLines = result . stdout . toString ( ) . split ( /[\r\n]+/g ) . map ( ( x ) => x . trim ( ) ) ; let lineIndex = 0 ; if ( os . platform ( ) === 'win32' ) { expect ( outputLines [ lineIndex ++ ] ) . toEqual ( 'Executing npm-binary-wrapper.cmd with args:' ) ; } else { expect ( outputLines [ lineIndex ++ ] ) . toEqual ( 'Executing npm-binary-wrapper with args:' ) ; } ++ lineIndex ; expect ( outputLines [ lineIndex ++ ] ) . toEqual ( 'Executing javascript-file.js with args:' ) ; const stringifiedArgv = outputLines [ lineIndex ++ ] ; expect ( stringifiedArgv . substr ( 0 , 2 ) ) . toEqual ( '["' ) ; const argv = JSON . parse ( stringifiedArgv ) ; argv . shift ( ) ; argv . shift ( ) ; return argv ; } test ( 'Executable.spawnSync("npm-binary-wrapper") simple' , ( ) => { const args = [ 'arg1' , 'arg2' , 'arg3' ] ; expect ( executeNpmBinaryWrapper ( args ) ) . toEqual ( args ) ; } ) ; test ( 'Executable.spawnSync("npm-binary-wrapper") edge cases 1' , ( ) => { const args = [ '' , '/' , ' \t ' , '"a' , 'b"' , '"c"' , '\\"\\d' , '!' , '!TEST_VAR!' ] ; expect ( executeNpmBinaryWrapper ( args ) ) . toEqual ( args ) ; } ) ; test ( 'Executable.spawnSync("npm-binary-wrapper") edge cases 2' , ( ) => { const args = [ '~!@#$*()_+`={}[]:";\'?,./' , '~!@#$*()_+`={}[]:";\'?,./' ] ; expect ( executeNpmBinaryWrapper ( args ) ) . toEqual ( args ) ; } ) ; test ( 'Executable.spawnSync("npm-binary-wrapper") edge cases 2' , ( ) => { const args = [ '~!@#$*()_+`={}[]:";\'?,./' , '~!@#$*()_+`={}[]:";\'?,./' ] ; expect ( executeNpmBinaryWrapper ( args ) ) . toEqual ( args ) ; } ) ; test ( 'Executable.spawnSync("npm-binary-wrapper") bad characters' , ( ) => { if ( os . platform ( ) === 'win32' ) { expect ( ( ) => { executeNpmBinaryWrapper ( [ 'abc%123' ] ) ; } ) . toThrowError ( 'The command line argument "abc%123" contains a special character "%"' + ' that cannot be escaped for the Windows shell' ) ; expect ( ( ) => { executeNpmBinaryWrapper ( [ 'abc<>123' ] ) ; } ) . toThrowError ( 'The command line argument "abc<>123" contains a special character "<"' + ' that cannot be escaped for the Windows shell' ) ; } } ) ; test ( 'Executable.spawn("npm-binary-wrapper")' , async ( ) => { const executablePath = path . join ( executableFolder , 'success' , 'npm-binary-wrapper' ) ; await expect ( ( ( ) => { const childProcess = Executable . spawn ( executablePath , [ '1' , '2' , '3' ] , { environment , currentWorkingDirectory : executableFolder } ) ; return new Promise < string > ( ( resolve , reject ) => { childProcess . on ( 'exit' , ( code ) => { resolve ( `Exit with code=${ code }` ) ; } ) ; childProcess . on ( 'error' , ( error ) => { reject ( `Failed with error: ${ error . message }` ) ; } ) ; } ) ; } ) ( ) ) . resolves . toBe ( 'Exit with code=0' ) ; } ) ;