import { assert } from "chai"; // Use require instead of import in order to replace the `spawn` method of child_process const childProcess = require("child_process"); import { SpawnOptions, ChildProcess } from "child_process"; import * as path from "path"; import { EventEmitter } from "events"; import { readFileSync } from "fs"; describe("preuninstall.js", () => { let isSpawnCalled = false; let argsPassedToSpawn: string[] = []; let optionsPassedToSpawn: any[]; let dataPassedToConsoleError: string[] = []; const originalSpawn = childProcess.spawn; const originalConsoleError = console.error; let eventEmitter = new EventEmitter(); beforeEach(() => { isSpawnCalled = false; argsPassedToSpawn = []; dataPassedToConsoleError = []; optionsPassedToSpawn = []; eventEmitter = new EventEmitter(); childProcess.spawn = ( command: string, args?: string[], options?: SpawnOptions ): ChildProcess => { isSpawnCalled = true; argsPassedToSpawn = args; optionsPassedToSpawn.push(options); return eventEmitter; }; console.error = (data: string) => { dataPassedToConsoleError.push(data); }; }); afterEach(() => { childProcess.spawn = originalSpawn; console.error = originalConsoleError; }); it("calls dev-preuninstall command of CLI and prints with console.error the error in case childProcess emits error event", () => { require(path.join(__dirname, "..", "preuninstall")); assert.isTrue( isSpawnCalled, "child_process.spawn must be called from preuninstall.js" ); const expectedPathToCliExecutable = path.join( __dirname, "..", "bin", "tns" ); assert.deepStrictEqual(argsPassedToSpawn, [ expectedPathToCliExecutable, "dev-preuninstall", ]); assert.deepStrictEqual( optionsPassedToSpawn, [{ stdio: "inherit" }], "The stdio must be inherit as this way CLI's command can determine correctly if terminal is in interactive mode." ); assert.deepStrictEqual(dataPassedToConsoleError, []); const errMsg = "this is error message"; eventEmitter.emit("error", new Error(errMsg)); assert.deepStrictEqual(dataPassedToConsoleError, [ `Failed to complete all pre-uninstall steps. Error is ${errMsg}`, ]); }); it("passes --analyticsLogFile option when NS_CLI_PREUNINSTALL_ANALYTICS_LOG_FILE is set", () => { const content = readFileSync( path.join(__dirname, "..", "preuninstall.js") ).toString(); const originalEnvValue = process.env.NS_CLI_PREUNINSTALL_ANALYTICS_LOG_FILE; process.env.NS_CLI_PREUNINSTALL_ANALYTICS_LOG_FILE = "value from env analyticsLog.txt"; /* tslint:disable:no-eval */ eval(content); /* tslint:enable:no-eval */ process.env.NS_CLI_PREUNINSTALL_ANALYTICS_LOG_FILE = originalEnvValue; assert.isTrue( isSpawnCalled, "child_process.spawn must be called from preuninstall.js" ); // NOTE: As the script is eval'd, the `__dirname` in it is resolved to current file's location, // so the expectedPathToCliExecutable should be set as it is located in current dir. const expectedPathToCliExecutable = path.join(__dirname, "bin", "tns"); assert.deepStrictEqual(argsPassedToSpawn, [ expectedPathToCliExecutable, "dev-preuninstall", "--analyticsLogFile", "value from env analyticsLog.txt", ]); assert.deepStrictEqual( optionsPassedToSpawn, [{ stdio: "inherit" }], "The stdio must be inherit as this way CLI's command can determine correctly if terminal is in interactive mode." ); assert.deepStrictEqual(dataPassedToConsoleError, []); }); it("ensure package.json has correct preuninstall script", () => { const packageJsonData = require("../package.json"); assert.equal(packageJsonData.scripts.preuninstall, "node preuninstall.js"); }); });