Skip to content

Commit faeb475

Browse files
committed
fix(tests): ignore private CSI sequences in terminal frames
1 parent 5cbed17 commit faeb475

9 files changed

Lines changed: 36 additions & 15 deletions

‎packages/cli/src/tests/compact-command.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { syncBuiltinESMExports } from "node:module";
66
import { PassThrough, Writable } from "node:stream";
77
import { test, type TestContext } from "node:test";
88
import { setTimeout } from "node:timers/promises";
9-
import { stripVTControlCharacters } from "node:util";
9+
import { stripTerminalControls } from "./terminal-output";
1010
import React from "react";
1111
import { render } from "ink";
1212
import { SessionManager, type SessionEntry, type SessionMessage } from "@vegamo/deepcode-core";
@@ -75,7 +75,7 @@ async function harness(t: TestContext, compact: (signal: AbortSignal) => Promise
7575
const output = Object.assign(
7676
new Writable({
7777
write(chunk, _encoding, callback) {
78-
const frame = stripVTControlCharacters(chunk.toString());
78+
const frame = stripTerminalControls(chunk.toString());
7979
if (frame.trim()) frames.push(frame);
8080
callback();
8181
},

‎packages/cli/src/tests/permission-prompt.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import React, { useState } from "react";
55
import { render, Text } from "ink";
66
import { PassThrough, Writable } from "node:stream";
77
import { setTimeout } from "node:timers/promises";
8-
import { stripVTControlCharacters } from "node:util";
8+
import { stripTerminalControls } from "./terminal-output";
99
import type { AskPermissionRequest } from "@vegamo/deepcode-core";
1010

1111
test("getScopeRiskColor maps permission scopes by risk", () => {
@@ -34,7 +34,7 @@ async function prompt(t: TestContext, requests: AskPermissionRequest[], planMode
3434
const stdout = Object.assign(
3535
new Writable({
3636
write(chunk, _encoding, done) {
37-
const text = stripVTControlCharacters(chunk.toString());
37+
const text = stripTerminalControls(chunk.toString());
3838
if (text.trim()) frame = text;
3939
done();
4040
},

‎packages/cli/src/tests/process-output.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
22
import { PassThrough, Writable } from "node:stream";
33
import { test, type TestContext } from "node:test";
44
import { setTimeout } from "node:timers/promises";
5-
import { stripVTControlCharacters } from "node:util";
5+
import { stripTerminalControls } from "./terminal-output";
66
import React from "react";
77
import { Box, render } from "ink";
88
import stringWidth from "string-width";
@@ -70,7 +70,7 @@ test("output tabs align to eight columns with wide characters and ANSI colors",
7070
test("wrapped output preserves Unicode, blank lines, indentation and ANSI styles", () => {
7171
const text = "中文👩‍💻".repeat(8);
7272
const lines = wrapProcessOutputLines(`\u001b[31m${text}\u001b[0m\n\n end`, 12);
73-
assert.equal(lines.slice(0, -2).map(stripVTControlCharacters).join(""), text);
73+
assert.equal(lines.slice(0, -2).map(stripTerminalControls).join(""), text);
7474
assert.deepEqual(lines.slice(-2), ["", " end"]);
7575
assert.ok(lines.every((line) => stringWidth(line) <= 12));
7676
assert.ok(lines[1].startsWith("\u001b[31m"));
@@ -82,7 +82,7 @@ async function terminal(t: TestContext, element: React.ReactElement) {
8282
const stdout = Object.assign(
8383
new Writable({
8484
write(chunk, _encoding, done) {
85-
const text = stripVTControlCharacters(chunk.toString());
85+
const text = stripTerminalControls(chunk.toString());
8686
if (text.trim()) frame = text;
8787
done();
8888
},

‎packages/cli/src/tests/raw-mode.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { syncBuiltinESMExports } from "node:module";
66
import { PassThrough, Writable } from "node:stream";
77
import { test, type TestContext } from "node:test";
88
import { setTimeout } from "node:timers/promises";
9-
import { stripVTControlCharacters } from "node:util";
9+
import { stripTerminalControls } from "./terminal-output";
1010
import React from "react";
1111
import { render } from "ink";
1212
import { SessionManager } from "@vegamo/deepcode-core";
@@ -31,7 +31,7 @@ async function harness(t: TestContext, config?: string) {
3131
const output = Object.assign(
3232
new Writable({
3333
write(chunk, _encoding, callback) {
34-
const frame = stripVTControlCharacters(chunk.toString());
34+
const frame = stripTerminalControls(chunk.toString());
3535
if (frame.trim()) frames.push(frame);
3636
callback();
3737
},

‎packages/cli/src/tests/status-line-render.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import assert from "node:assert/strict";
22
import { Writable } from "node:stream";
33
import { test } from "node:test";
44
import { setTimeout } from "node:timers/promises";
5-
import { stripVTControlCharacters } from "node:util";
5+
import { stripTerminalControls } from "./terminal-output";
66
import React from "react";
77
import { render } from "ink";
88
import stringWidth from "string-width";
@@ -13,7 +13,7 @@ test("model status stays on one physical line while the spinner updates and the
1313
const output = Object.assign(
1414
new Writable({
1515
write(chunk, _encoding, callback) {
16-
const frame = stripVTControlCharacters(chunk.toString()).trimEnd();
16+
const frame = stripTerminalControls(chunk.toString()).trimEnd();
1717
if (frame) frames.push(frame);
1818
callback();
1919
},

‎packages/cli/src/tests/terminal-helper.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { TestContext } from "node:test";
22
import { PassThrough, Writable } from "node:stream";
33
import { setTimeout } from "node:timers/promises";
4-
import { stripVTControlCharacters } from "node:util";
4+
import { stripTerminalControls } from "./terminal-output";
55
import type React from "react";
66
import { render } from "ink";
77

@@ -12,7 +12,7 @@ export async function terminal(t: TestContext, element: React.ReactElement, colu
1212
const stdout = Object.assign(
1313
new Writable({
1414
write(chunk, _encoding, done) {
15-
const text = stripVTControlCharacters(chunk.toString());
15+
const text = stripTerminalControls(chunk.toString());
1616
if (text.trim()) {
1717
frame = text;
1818
rawFrame = chunk.toString();
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import assert from "node:assert/strict";
2+
import { test } from "node:test";
3+
import { stripTerminalControls } from "./terminal-output";
4+
5+
test("keyboard mode changes do not become visible terminal frames", () => {
6+
for (const control of ["\u001b[>4;1m", "\u001b[>4;0m", "\u001b[?1004h", "\u001b[?2004l"]) {
7+
assert.equal(stripTerminalControls(control), "");
8+
}
9+
});
10+
11+
test("terminal output retains prompt text while removing styling and mode changes", () => {
12+
const output = "\u001b[>4;1m\u001b[90m> Type your message...\u001b[0m\n4;1m\u001b[>4;0m";
13+
assert.equal(stripTerminalControls(output), "> Type your message...\n4;1m");
14+
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { stripVTControlCharacters } from "node:util";
2+
3+
export function stripTerminalControls(value: string): string {
4+
// Node's ANSI stripper leaves text behind for private CSI parameters,
5+
// including the modifyOtherKeys sequences emitted by the prompt input.
6+
return stripVTControlCharacters(value.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, ""));
7+
}

‎packages/cli/src/tests/undo-draft.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { syncBuiltinESMExports } from "node:module";
66
import { PassThrough, Writable } from "node:stream";
77
import { test, type TestContext } from "node:test";
88
import { setTimeout } from "node:timers/promises";
9-
import { stripVTControlCharacters } from "node:util";
9+
import { stripTerminalControls } from "./terminal-output";
1010
import React from "react";
1111
import { render } from "ink";
1212
import {
@@ -109,7 +109,7 @@ async function harness(t: TestContext, imageUrls: string[] = []) {
109109
const output = Object.assign(
110110
new Writable({
111111
write(chunk, _encoding, callback) {
112-
const frame = stripVTControlCharacters(chunk.toString());
112+
const frame = stripTerminalControls(chunk.toString());
113113
if (frame.trim()) frames.push(frame);
114114
callback();
115115
},

0 commit comments

Comments
 (0)