feat(smartshell): Add passthrough option for exec methods and corresponding tests
This commit is contained in:
Binary file not shown.
42
test/test.passthrough.ts
Normal file
42
test/test.passthrough.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||||
|
import * as smartshell from '../ts/index.js';
|
||||||
|
|
||||||
|
tap.test('should handle passthrough for interactive commands', async () => {
|
||||||
|
const testSmartshell = new smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
sourceFilePaths: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test with a simple echo command that doesn't need input
|
||||||
|
const result = await testSmartshell.execPassthrough('echo "Testing passthrough"');
|
||||||
|
expect(result.exitCode).toEqual(0);
|
||||||
|
expect(result.stdout).toContain('Testing passthrough');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should handle streaming passthrough', async () => {
|
||||||
|
const testSmartshell = new smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
sourceFilePaths: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test streaming passthrough with a simple command
|
||||||
|
const streamingResult = await testSmartshell.execStreamingPassthrough('echo "Testing streaming passthrough"');
|
||||||
|
const finalResult = await streamingResult.finalPromise;
|
||||||
|
|
||||||
|
expect(finalResult.exitCode).toEqual(0);
|
||||||
|
expect(finalResult.stdout).toContain('Testing streaming passthrough');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should allow normal exec without passthrough', async () => {
|
||||||
|
const testSmartshell = new smartshell.Smartshell({
|
||||||
|
executor: 'bash',
|
||||||
|
sourceFilePaths: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Regular exec should still work as before
|
||||||
|
const result = await testSmartshell.exec('echo "Normal exec"');
|
||||||
|
expect(result.exitCode).toEqual(0);
|
||||||
|
expect(result.stdout).toContain('Normal exec');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
@@ -25,6 +25,7 @@ interface IExecOptions {
|
|||||||
strict?: boolean;
|
strict?: boolean;
|
||||||
streaming?: boolean;
|
streaming?: boolean;
|
||||||
interactive?: boolean;
|
interactive?: boolean;
|
||||||
|
passthrough?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Smartshell {
|
export class Smartshell {
|
||||||
@@ -87,6 +88,11 @@ export class Smartshell {
|
|||||||
|
|
||||||
this.smartexit.addProcess(execChildProcess);
|
this.smartexit.addProcess(execChildProcess);
|
||||||
|
|
||||||
|
// Connect stdin if passthrough is enabled
|
||||||
|
if (options.passthrough && execChildProcess.stdin) {
|
||||||
|
process.stdin.pipe(execChildProcess.stdin);
|
||||||
|
}
|
||||||
|
|
||||||
// Capture stdout and stderr output.
|
// Capture stdout and stderr output.
|
||||||
execChildProcess.stdout.on('data', (data) => {
|
execChildProcess.stdout.on('data', (data) => {
|
||||||
if (!options.silent) {
|
if (!options.silent) {
|
||||||
@@ -107,6 +113,11 @@ export class Smartshell {
|
|||||||
execChildProcess.on('exit', (code, signal) => {
|
execChildProcess.on('exit', (code, signal) => {
|
||||||
this.smartexit.removeProcess(execChildProcess);
|
this.smartexit.removeProcess(execChildProcess);
|
||||||
|
|
||||||
|
// Unpipe stdin when process ends if passthrough was enabled
|
||||||
|
if (options.passthrough) {
|
||||||
|
process.stdin.unpipe(execChildProcess.stdin);
|
||||||
|
}
|
||||||
|
|
||||||
const execResult: IExecResult = {
|
const execResult: IExecResult = {
|
||||||
exitCode: typeof code === 'number' ? code : (signal ? 1 : 0),
|
exitCode: typeof code === 'number' ? code : (signal ? 1 : 0),
|
||||||
stdout: shellLogInstance.logStore.toString(),
|
stdout: shellLogInstance.logStore.toString(),
|
||||||
@@ -121,6 +132,10 @@ export class Smartshell {
|
|||||||
|
|
||||||
execChildProcess.on('error', (error) => {
|
execChildProcess.on('error', (error) => {
|
||||||
this.smartexit.removeProcess(execChildProcess);
|
this.smartexit.removeProcess(execChildProcess);
|
||||||
|
// Unpipe stdin when process errors if passthrough was enabled
|
||||||
|
if (options.passthrough && execChildProcess.stdin) {
|
||||||
|
process.stdin.unpipe(execChildProcess.stdin);
|
||||||
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -181,6 +196,14 @@ export class Smartshell {
|
|||||||
await this._exec({ commandString, interactive: true });
|
await this._exec({ commandString, interactive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async execPassthrough(commandString: string): Promise<IExecResult> {
|
||||||
|
return await this._exec({ commandString, passthrough: true }) as IExecResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async execStreamingPassthrough(commandString: string): Promise<IExecResultStreaming> {
|
||||||
|
return await this._exec({ commandString, streaming: true, passthrough: true }) as IExecResultStreaming;
|
||||||
|
}
|
||||||
|
|
||||||
public async execAndWaitForLine(
|
public async execAndWaitForLine(
|
||||||
commandString: string,
|
commandString: string,
|
||||||
regex: RegExp,
|
regex: RegExp,
|
||||||
|
Reference in New Issue
Block a user