test: add sanitize-ansi parity tests from Ink (+24)

Port 24 missing tests from Ink's sanitize-ansi test suite covering:
- C1 OSC hyperlinks with various terminators (C1 ST, ESC ST, BEL)
- C1 CSI sequences (both SGR and non-SGR)
- DCS passthrough (tmux wrappers, incomplete, BEL in payload)
- SOS control strings (ESC and C1 variants, various terminators)
- Standalone C1 control characters and ST bytes
- ESC ST sequences, malformed ESC sequences, incomplete CSI

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yunfei He
2026-05-27 14:42:19 +08:00
parent 26d91d9f2d
commit 6655e32f25
@@ -1,25 +1,245 @@
import { describe, test, expect } from "vite-plus/test";
import { sanitizeAnsi } from "./sanitize-ansi.ts";
// Minimal ANSI-stripping helper for test assertions (avoids strip-ansi dep).
function stripAnsi(s: string): string {
// eslint-disable-next-line no-control-regex
return s.replace(
/[›][[()#;?]*(?:[0-9]{1,4}(?:[:;][0-9]{0,4})*)?[0-9A-ORZcf-nqry=><~]|[]\].*?(?:|\\|œ)|[˜][\s\S]*?(?:|\\|œ)|[€-Ÿ]/g,
"",
);
}
describe("sanitize-ansi", () => {
test("preserves SGR (color) sequences", () => {
expect(sanitizeAnsi("\x1b[31mred\x1b[0m")).toBe("\x1b[31mred\x1b[0m");
expect(sanitizeAnsi("red")).toBe("red");
});
test("preserve SGR sequences (colon-form 24-bit color)", () => {
const output = sanitizeAnsi("A[38:2::255:100:0mcolorB");
expect(output).toContain("[38:2::255:100:0m");
expect(stripAnsi(output)).toBe("AcolorB");
});
test("strips cursor movement", () => {
expect(sanitizeAnsi("\x1b[2Ahello")).toBe("hello");
expect(sanitizeAnsi("hello")).toBe("hello");
});
test("strips screen clearing", () => {
expect(sanitizeAnsi("\x1b[2Jhello")).toBe("hello");
expect(sanitizeAnsi("hello")).toBe("hello");
});
test("preserves OSC (hyperlinks)", () => {
const link = "\x1b]8;;https://example.com\x07click\x1b]8;;\x07";
const link = "]8;;https://example.comclick]8;;";
expect(sanitizeAnsi(link)).toBe(link);
});
test("preserve OSC hyperlinks (ESC-ST terminated)", () => {
const output = sanitizeAnsi("]8;;https://example.com\\link]8;;\\");
expect(output).toContain("]8;;https://example.com");
expect(stripAnsi(output)).toBe("link");
});
test("passes through plain text unchanged", () => {
expect(sanitizeAnsi("hello world")).toBe("hello world");
});
// --- Ink parity tests below ---
test("preserve OSC hyperlinks terminated by C1 ST", () => {
const output = sanitizeAnsi("]8;;https://example.comœlink]8;;œ");
expect(output).toContain("]8;;https://example.comœ");
expect(stripAnsi(output)).toBe("link");
});
test("preserve C1 OSC hyperlinks terminated by C1 ST", () => {
const input = "8;;https://example.comœlink8;;œ";
const output = sanitizeAnsi(input);
expect(output).toContain("8;;https://example.comœ");
expect(output).toBe(input);
});
test("preserve C1 OSC hyperlinks terminated by ESC ST", () => {
const input = "8;;https://example.com\\link8;;\\";
const output = sanitizeAnsi(input);
expect(output).toContain("8;;https://example.com\\");
expect(output).toBe(input);
});
test("preserve C1 OSC hyperlinks terminated by BEL", () => {
const input = "8;;https://example.comlink8;;";
const output = sanitizeAnsi(input);
expect(output).toContain("8;;https://example.com");
expect(output).toBe(input);
});
test("strip non-SGR CSI sequences as complete units", () => {
const output = sanitizeAnsi("A[>4;2mB[2 qC");
expect(output).not.toContain("4;2m");
expect(output).not.toContain(" q");
expect(stripAnsi(output)).toBe("ABC");
});
test("strip C1 non-SGR CSI sequences as complete units", () => {
const output = sanitizeAnsi("A›>4;2mB›2 qC");
expect(output).not.toContain("4;2m");
expect(output).not.toContain(" q");
expect(stripAnsi(output)).toBe("ABC");
});
test("preserve C1 SGR CSI sequences", () => {
const output = sanitizeAnsi("A›31mgreen›0mB");
expect(output).toContain("›31m");
expect(stripAnsi(output)).toBe("AgreenB");
});
test("strip private-parameter m-sequences that are not SGR", () => {
const output = sanitizeAnsi("A[>4;2mB");
expect(output).not.toContain("[>4;2m");
expect(stripAnsi(output)).toBe("AB");
});
test("strip tmux DCS passthrough wrappers with escaped ST payload terminators", () => {
const wrappedHyperlinkStart = "Ptmux;]8;;https://example.com\\\\";
const wrappedHyperlinkEnd = "Ptmux;]8;;\\\\";
const output = sanitizeAnsi(`${wrappedHyperlinkStart}link${wrappedHyperlinkEnd}`);
expect(output).not.toContain("tmux;");
expect(output).not.toContain("P");
expect(stripAnsi(output)).toBe("link");
});
test("strip incomplete DCS passthrough sequences to avoid payload leaks", () => {
const output = sanitizeAnsi("APtmux;link");
expect(output).not.toContain("tmux;");
expect(stripAnsi(output)).toBe("A");
});
test("strip DCS control strings with BEL in payload until ST terminator", () => {
const output = sanitizeAnsi("APpayloadstill-payload\\B");
expect(output).not.toContain("payload");
expect(output).not.toContain("still-payload");
expect(stripAnsi(output)).toBe("AB");
});
test("strip ESC SOS control strings as complete units", () => {
const output = sanitizeAnsi("AXpayload\\B");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("AB");
});
test("strip ESC SOS control strings with C1 ST terminator", () => {
const output = sanitizeAnsi("AXpayloadœB");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("AB");
});
test("strip C1 SOS control strings as complete units with C1 ST terminator", () => {
const output = sanitizeAnsi("A˜payloadœB");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("AB");
});
test("strip C1 SOS control strings as complete units with ESC ST terminator", () => {
const output = sanitizeAnsi("A˜payload\\B");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("AB");
});
test("strip ESC SOS with BEL terminator as malformed control string", () => {
const output = sanitizeAnsi("AXpayloadB");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("A");
});
test("strip C1 SOS with BEL terminator as malformed control string", () => {
const output = sanitizeAnsi("A˜payloadB");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("A");
});
test("strip incomplete ESC SOS control strings to avoid payload leaks", () => {
const output = sanitizeAnsi("AXpayload");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("A");
});
test("strip incomplete C1 SOS control strings to avoid payload leaks", () => {
const output = sanitizeAnsi("A˜payload");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("A");
});
test("strip SOS with escaped ESC in payload until final ST terminator", () => {
const output = sanitizeAnsi("AXfoo\\bar\\B");
expect(output).not.toContain("foo");
expect(output).not.toContain("bar");
expect(stripAnsi(output)).toBe("AB");
});
test("preserve SGR around stripped SOS control strings", () => {
const output = sanitizeAnsi("ARXpayload\\B");
expect(output).toContain("");
expect(output).toContain("");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("ARB");
});
test("strip ESC ST sequences", () => {
const output = sanitizeAnsi("A\\B");
expect(output).not.toContain("\\");
expect(stripAnsi(output)).toBe("AB");
});
test("strip malformed ESC control sequences with intermediates and non-final bytes", () => {
const output = sanitizeAnsi("A#payload");
expect(output).not.toContain("payload");
expect(stripAnsi(output)).toBe("A");
});
test("strip incomplete CSI after preserving prior SGR content", () => {
const output = sanitizeAnsi("AB[");
expect(output).toContain("");
expect(stripAnsi(output)).toBe("AB");
});
test("strip standalone ST bytes", () => {
const output = sanitizeAnsi("AœB");
expect(output).not.toContain("œ");
expect(stripAnsi(output)).toBe("AB");
});
test("strip standalone C1 control characters", () => {
const output = sanitizeAnsi("A…BŽC");
expect(output).not.toContain("…");
expect(output).not.toContain("Ž");
expect(stripAnsi(output)).toBe("ABC");
});
});