110 lines
3.0 KiB
TypeScript
110 lines
3.0 KiB
TypeScript
|
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||
|
|
import { SmartKvmTerminal } from '../ts/smartkvm.classes.kvmterminal.js';
|
||
|
|
import type {
|
||
|
|
IKvmDriver,
|
||
|
|
IKvmFrame,
|
||
|
|
IOcrEngine,
|
||
|
|
IOcrRecognizeOptions,
|
||
|
|
TKvmKey,
|
||
|
|
TKvmKind,
|
||
|
|
} from '../ts/smartkvm.interfaces.js';
|
||
|
|
|
||
|
|
class MockKvmDriver implements IKvmDriver {
|
||
|
|
public readonly kind: TKvmKind = 'generic';
|
||
|
|
public typedTexts: string[] = [];
|
||
|
|
public pressedKeys: TKvmKey[] = [];
|
||
|
|
public pressedShortcuts: TKvmKey[][] = [];
|
||
|
|
public focusViewerCalls = 0;
|
||
|
|
|
||
|
|
public async connect(): Promise<void> {}
|
||
|
|
|
||
|
|
public async disconnect(): Promise<void> {}
|
||
|
|
|
||
|
|
public async focusViewer(): Promise<void> {
|
||
|
|
this.focusViewerCalls++;
|
||
|
|
}
|
||
|
|
|
||
|
|
public async captureFrame(): Promise<IKvmFrame> {
|
||
|
|
return {
|
||
|
|
timestamp: Date.now(),
|
||
|
|
width: 1,
|
||
|
|
height: 1,
|
||
|
|
mimeType: 'image/png',
|
||
|
|
dataBase64: 'iVBORw0KGgo=',
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
public async typeText(text: string): Promise<void> {
|
||
|
|
this.typedTexts.push(text);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async pressKey(key: TKvmKey): Promise<void> {
|
||
|
|
this.pressedKeys.push(key);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async pressShortcut(keys: TKvmKey[]): Promise<void> {
|
||
|
|
this.pressedShortcuts.push(keys);
|
||
|
|
}
|
||
|
|
|
||
|
|
public async wait(): Promise<void> {}
|
||
|
|
}
|
||
|
|
|
||
|
|
tap.test('SmartKvmTerminal should run wrapped commands through mock KVM and OCR', async () => {
|
||
|
|
const mockKvm = new MockKvmDriver();
|
||
|
|
let recognizeCalls = 0;
|
||
|
|
|
||
|
|
const mockOcrEngine: IOcrEngine = {
|
||
|
|
recognize: async (_frame: IKvmFrame, options?: IOcrRecognizeOptions) => {
|
||
|
|
recognizeCalls++;
|
||
|
|
expect(options?.language).toEqual('eng');
|
||
|
|
const typedText = mockKvm.typedTexts.join('\n');
|
||
|
|
const commandId = typedText.match(/SMARTKVM_START_([a-f0-9]+)/)?.[1] ?? 'missing';
|
||
|
|
if (recognizeCalls === 1) {
|
||
|
|
return {
|
||
|
|
text: `SMARTKVM_START_${commandId}\npartial output`,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return {
|
||
|
|
text: `prompt\nSMARTKVM_START_${commandId}\nhello terminal\nSMARTKVM_END_${commandId}_0\nprompt`,
|
||
|
|
};
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const terminal = new SmartKvmTerminal({
|
||
|
|
kvm: mockKvm,
|
||
|
|
ocrEngine: mockOcrEngine,
|
||
|
|
shellHint: 'bash',
|
||
|
|
commandTimeoutMs: 2000,
|
||
|
|
ocrPollIntervalMs: 1,
|
||
|
|
});
|
||
|
|
|
||
|
|
const result = await terminal.runCommand('echo hello');
|
||
|
|
|
||
|
|
expect(mockKvm.focusViewerCalls).toBeGreaterThan(0);
|
||
|
|
expect(mockKvm.typedTexts[0]).toInclude('echo hello');
|
||
|
|
expect(mockKvm.typedTexts[0]).toInclude('SMARTKVM_START_');
|
||
|
|
expect(mockKvm.pressedKeys).toContain('Enter');
|
||
|
|
expect(recognizeCalls).toEqual(2);
|
||
|
|
expect(result.completed).toBeTrue();
|
||
|
|
expect(result.timedOut).toBeFalse();
|
||
|
|
expect(result.exitCode).toEqual(0);
|
||
|
|
expect(result.combinedText).toEqual('hello terminal');
|
||
|
|
});
|
||
|
|
|
||
|
|
tap.test('SmartKvmTerminal bootstrap should use generic Linux shortcut', async () => {
|
||
|
|
const mockKvm = new MockKvmDriver();
|
||
|
|
const terminal = new SmartKvmTerminal({
|
||
|
|
kvm: mockKvm,
|
||
|
|
ocrEngine: {
|
||
|
|
recognize: async () => ({ text: '' }),
|
||
|
|
},
|
||
|
|
osHint: 'linux',
|
||
|
|
});
|
||
|
|
|
||
|
|
await terminal.bootstrap();
|
||
|
|
expect(mockKvm.pressedShortcuts).toEqual([['Control', 'Alt', 'T']]);
|
||
|
|
});
|
||
|
|
|
||
|
|
export default tap.start();
|