mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 16:59:43 +02:00
fix: address 7 issues found in xterm-zerolag-input audit
1. Regex global flag safety — strip `g` flag before matching to prevent
lastIndex mutation and missing .index on match results
2. state.visible false before activate — null overlay no longer reports
visible: true
3. removeChar() returns 'pending' | 'flushed' | false instead of boolean
so consumers can distinguish whether to send backspace to PTY
4. removeChar() implements buffer detection cascade (step 3) — detects
existing prompt text when both pending and flushed are empty
5. RenderKey includes text content, not just length — prevents stale
renders when setFlushed() called with same count but different text
6. Remove dead `import type { Terminal }` from test file
7. Remove internal `FontStyle` from public exports
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,18 @@ describe('findPrompt', () => {
|
||||
expect(pos).toBeNull();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('handles global flag safely (strips g to avoid lastIndex)', () => {
|
||||
const { terminal, cleanup } = term(['user@host:~$ cmd']);
|
||||
const finder: PromptFinder = { type: 'regex', pattern: /\$/g };
|
||||
const pos = findPrompt(terminal as unknown as XtermTerminal, finder);
|
||||
expect(pos).not.toBeNull();
|
||||
expect(pos!.col).toBe(11);
|
||||
// Call again — should return same result (no lastIndex drift)
|
||||
const pos2 = findPrompt(terminal as unknown as XtermTerminal, finder);
|
||||
expect(pos2).toEqual(pos);
|
||||
cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom strategy', () => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { createMockTerminal } from './helpers.js';
|
||||
import { ZerolagInputAddon } from '../src/zerolag-input-addon.js';
|
||||
import type { Terminal } from '../src/types.js';
|
||||
|
||||
function setup(lines: string[] = ['$ '], promptChar = '$') {
|
||||
const mock = createMockTerminal({ buffer: { lines } });
|
||||
@@ -83,12 +82,12 @@ describe('ZerolagInputAddon', () => {
|
||||
});
|
||||
|
||||
describe('removeChar', () => {
|
||||
it('removes last character from pendingText', () => {
|
||||
it('returns "pending" when removing from pendingText', () => {
|
||||
const { addon } = tracked();
|
||||
addon.addChar('a');
|
||||
addon.addChar('b');
|
||||
const removed = addon.removeChar();
|
||||
expect(removed).toBe(true);
|
||||
const source = addon.removeChar();
|
||||
expect(source).toBe('pending');
|
||||
expect(addon.pendingText).toBe('a');
|
||||
});
|
||||
|
||||
@@ -97,10 +96,11 @@ describe('ZerolagInputAddon', () => {
|
||||
expect(addon.removeChar()).toBe(false);
|
||||
});
|
||||
|
||||
it('decrements flushed when pending is empty', () => {
|
||||
it('returns "flushed" when removing from flushed text', () => {
|
||||
const { addon } = tracked();
|
||||
addon.setFlushed(3, 'abc');
|
||||
expect(addon.removeChar()).toBe(true);
|
||||
const source = addon.removeChar();
|
||||
expect(source).toBe('flushed');
|
||||
expect(addon.getFlushed().count).toBe(2);
|
||||
expect(addon.getFlushed().text).toBe('ab');
|
||||
});
|
||||
@@ -109,7 +109,8 @@ describe('ZerolagInputAddon', () => {
|
||||
const { addon } = tracked();
|
||||
addon.setFlushed(2, 'ab');
|
||||
addon.addChar('c');
|
||||
expect(addon.removeChar()).toBe(true);
|
||||
const source = addon.removeChar();
|
||||
expect(source).toBe('pending');
|
||||
expect(addon.pendingText).toBe('');
|
||||
expect(addon.getFlushed().count).toBe(2); // flushed unchanged
|
||||
});
|
||||
@@ -120,6 +121,21 @@ describe('ZerolagInputAddon', () => {
|
||||
addon.removeChar();
|
||||
expect(addon.hasPending).toBe(false);
|
||||
});
|
||||
|
||||
it('detects buffer text and removes from it when both empty', () => {
|
||||
const { addon } = tracked(['$ hello']);
|
||||
// Both pending and flushed are empty, but buffer has text
|
||||
const source = addon.removeChar();
|
||||
expect(source).toBe('flushed');
|
||||
// "hello" (5 chars) detected, then one removed = 4
|
||||
expect(addon.getFlushed().count).toBe(4);
|
||||
expect(addon.getFlushed().text).toBe('hell');
|
||||
});
|
||||
|
||||
it('returns false on empty prompt with no buffer text', () => {
|
||||
const { addon } = tracked(['$ ']);
|
||||
expect(addon.removeChar()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear', () => {
|
||||
@@ -276,6 +292,22 @@ describe('ZerolagInputAddon', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('state.visible', () => {
|
||||
it('is false before activate', () => {
|
||||
const addon = new ZerolagInputAddon();
|
||||
expect(addon.state.visible).toBe(false);
|
||||
// No cleanup needed — never activated
|
||||
});
|
||||
|
||||
it('is false after dispose', () => {
|
||||
const { addon, mock } = tracked();
|
||||
addon.addChar('x');
|
||||
addon.dispose();
|
||||
expect(addon.state.visible).toBe(false);
|
||||
mock.cleanup();
|
||||
});
|
||||
});
|
||||
|
||||
describe('rerender / refreshFont', () => {
|
||||
it('rerender does not crash when no text', () => {
|
||||
const { addon } = tracked();
|
||||
|
||||
Reference in New Issue
Block a user