Files
smartshell/test/test.pty.ts

146 lines
4.6 KiB
TypeScript

import { expect, tap } from '@git.zone/tstest/tapbundle';
import * as smartshell from '../ts/index.js';
// Helper to check if node-pty is available
const isPtyAvailable = async (): Promise<boolean> => {
try {
await import('node-pty');
return true;
} catch {
return false;
}
};
tap.test('PTY: should handle bash read with prompts correctly', async (tools) => {
const ptyAvailable = await isPtyAvailable();
if (!ptyAvailable) {
console.log('Skipping PTY test - node-pty not installed');
return;
}
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// This test should work with PTY (bash read with prompt)
const interactive = await testSmartshell.execInteractiveControlPty("bash -c 'read -p \"Enter name: \" name && echo \"Hello, $name\"'");
// Send input programmatically
await interactive.sendLine('TestUser');
// Wait for completion
const result = await interactive.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('Enter name:'); // Prompt should be visible with PTY
expect(result.stdout).toContain('Hello, TestUser');
});
tap.test('PTY: should handle terminal colors and escape sequences', async (tools) => {
const ptyAvailable = await isPtyAvailable();
if (!ptyAvailable) {
console.log('Skipping PTY test - node-pty not installed');
return;
}
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// ls --color=auto should produce colors in PTY mode
const result = await testSmartshell.execInteractiveControlPty('ls --color=always /tmp');
const finalResult = await result.finalPromise;
expect(finalResult.exitCode).toEqual(0);
// Check for ANSI escape sequences (colors) in output
const hasColors = /\x1b\[[0-9;]*m/.test(finalResult.stdout);
expect(hasColors).toEqual(true);
});
tap.test('PTY: should handle interactive password prompt simulation', async (tools) => {
const ptyAvailable = await isPtyAvailable();
if (!ptyAvailable) {
console.log('Skipping PTY test - node-pty not installed');
return;
}
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Simulate a password prompt scenario
const interactive = await testSmartshell.execStreamingInteractiveControlPty(
"bash -c 'read -s -p \"Password: \" pass && echo && echo \"Got password of length ${#pass}\"'"
);
await tools.delayFor(100);
await interactive.sendLine('secretpass');
const result = await interactive.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('Password:');
expect(result.stdout).toContain('Got password of length 10');
});
tap.test('PTY: should handle terminal size options', async (tools) => {
const ptyAvailable = await isPtyAvailable();
if (!ptyAvailable) {
console.log('Skipping PTY test - node-pty not installed');
return;
}
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Check terminal size using stty
const result = await testSmartshell.execInteractiveControlPty('stty size');
const finalResult = await result.finalPromise;
expect(finalResult.exitCode).toEqual(0);
// Default size should be 30 rows x 120 cols as set in _execCommandPty
expect(finalResult.stdout).toContain('30 120');
});
tap.test('PTY: should handle Ctrl+C (SIGINT) properly', async (tools) => {
const ptyAvailable = await isPtyAvailable();
if (!ptyAvailable) {
console.log('Skipping PTY test - node-pty not installed');
return;
}
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Start a long-running process
const streaming = await testSmartshell.execStreamingInteractiveControlPty('sleep 10');
// Send interrupt after a short delay
await tools.delayFor(100);
await streaming.keyboardInterrupt();
const result = await streaming.finalPromise;
// Process should exit with non-zero code due to interrupt
expect(result.exitCode).not.toEqual(0);
});
tap.test('Regular pipe mode should still work alongside PTY', async () => {
const testSmartshell = new smartshell.Smartshell({
executor: 'bash',
sourceFilePaths: [],
});
// Regular mode should work without PTY
const interactive = await testSmartshell.execInteractiveControl('echo "Pipe mode works"');
const result = await interactive.finalPromise;
expect(result.exitCode).toEqual(0);
expect(result.stdout).toContain('Pipe mode works');
});
export default tap.start();