146 lines
4.6 KiB
TypeScript
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(); |