import { describe, expect, test } from "bun:test"; const sliceAnsi = (await import("../sliceAnsi")).default; const ESC = "\x1b"; describe("sliceAnsi", () => { test("plain text slice identical to String.slice", () => { expect(sliceAnsi("hello world", 0, 5)).toBe("hello"); expect(sliceAnsi("hello world", 6)).toBe("world"); }); test("slice entire string", () => { expect(sliceAnsi("abc", 0)).toBe("abc"); }); test("empty slice (start === end)", () => { expect(sliceAnsi("abc", 2, 2)).toBe(""); }); test("preserves ANSI color codes within slice", () => { const input = "\x1b[31mred\x1b[0m normal"; const result = sliceAnsi(input, 0, 3); expect(result).toContain("\x1b[31m"); expect(result).toContain("red"); }); test("closes opened ANSI styles at slice end", () => { const input = "\x1b[31mhello world\x1b[0m"; const result = sliceAnsi(input, 0, 5); expect(result).toContain("\x1b[31m"); expect(result).toContain("hello"); // undoAnsiCodes uses specific close codes (e.g. \x1b[39m for foreground) expect(result).toMatch(new RegExp(`${ESC}\\[\\d+m`)); // The result should start with open code and end with a close code const withoutText = result.replace("hello", ""); // Should have at least one open and one close code expect(withoutText.length).toBeGreaterThan(0); }); test("slice starting mid-ANSI skips codes before start", () => { const input = "\x1b[31mhello\x1b[0m \x1b[32mworld\x1b[0m"; const result = sliceAnsi(input, 6, 11); expect(result).toContain("world"); expect(result).toContain("\x1b[32m"); expect(result).not.toContain("\x1b[31m"); }); test("slice of plain text from middle", () => { expect(sliceAnsi("abcdefgh", 2, 5)).toBe("cde"); }); test("slice past end of string returns everything", () => { expect(sliceAnsi("abc", 0, 100)).toBe("abc"); }); test("slice starting at end returns empty", () => { expect(sliceAnsi("abc", 3)).toBe(""); }); test("handles empty string", () => { expect(sliceAnsi("", 0, 5)).toBe(""); }); test("multiple ANSI codes nested", () => { const input = "\x1b[1m\x1b[31mbold red\x1b[0m\x1b[0m"; const result = sliceAnsi(input, 0, 4); expect(result).toContain("bold"); // Both styles should be opened and then closed expect(result).toContain("\x1b[1m"); expect(result).toContain("\x1b[31m"); }); test("slice with no end parameter returns to end of string", () => { expect(sliceAnsi("hello world", 6)).toBe("world"); }); test("ANSI codes at boundaries are handled correctly", () => { const input = "a\x1b[31mb\x1b[0mc"; // "abc" visually, position: a=0, b=1, c=2 const result = sliceAnsi(input, 1, 2); // undoAnsiCodes uses \x1b[39m for foreground reset, not \x1b[0m expect(result).toContain("b"); expect(result).toContain("\x1b[31m"); expect(result).toMatch(new RegExp(`${ESC}\\[\\d+m.*${ESC}\\[\\d+m`)); // open + close codes }); });