co-mono/packages/tui/test/editor.test.ts
2025-11-21 03:12:42 +01:00

132 lines
3.9 KiB
TypeScript

import assert from "node:assert";
import { describe, it } from "node:test";
import { Editor } from "../src/components/editor.js";
import { defaultEditorTheme } from "./test-themes.js";
describe("Editor component", () => {
describe("Unicode text editing behavior", () => {
it("inserts mixed ASCII, umlauts, and emojis as literal text", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("H");
editor.handleInput("e");
editor.handleInput("l");
editor.handleInput("l");
editor.handleInput("o");
editor.handleInput(" ");
editor.handleInput("ä");
editor.handleInput("ö");
editor.handleInput("ü");
editor.handleInput(" ");
editor.handleInput("😀");
const text = editor.getText();
assert.strictEqual(text, "Hello äöü 😀");
});
it("deletes single-code-unit unicode characters (umlauts) with Backspace", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("ä");
editor.handleInput("ö");
editor.handleInput("ü");
// Delete the last character (ü)
editor.handleInput("\x7f"); // Backspace
const text = editor.getText();
assert.strictEqual(text, "äö");
});
it("deletes multi-code-unit emojis with repeated Backspace", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("😀");
editor.handleInput("👍");
// Delete the last emoji (👍) - requires 2 backspaces since emojis are 2 code units
editor.handleInput("\x7f"); // Backspace
editor.handleInput("\x7f"); // Backspace
const text = editor.getText();
assert.strictEqual(text, "😀");
});
it("inserts characters at the correct position after cursor movement over umlauts", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("ä");
editor.handleInput("ö");
editor.handleInput("ü");
// Move cursor left twice
editor.handleInput("\x1b[D"); // Left arrow
editor.handleInput("\x1b[D"); // Left arrow
// Insert 'x' in the middle
editor.handleInput("x");
const text = editor.getText();
assert.strictEqual(text, "äxöü");
});
it("moves cursor in code units across multi-code-unit emojis before insertion", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("😀");
editor.handleInput("👍");
editor.handleInput("🎉");
// Move cursor left over last emoji (🎉)
editor.handleInput("\x1b[D"); // Left arrow
editor.handleInput("\x1b[D"); // Left arrow
// Move cursor left over second emoji (👍)
editor.handleInput("\x1b[D");
editor.handleInput("\x1b[D");
// Insert 'x' between first and second emoji
editor.handleInput("x");
const text = editor.getText();
assert.strictEqual(text, "😀x👍🎉");
});
it("preserves umlauts across line breaks", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("ä");
editor.handleInput("ö");
editor.handleInput("ü");
editor.handleInput("\n"); // new line
editor.handleInput("Ä");
editor.handleInput("Ö");
editor.handleInput("Ü");
const text = editor.getText();
assert.strictEqual(text, "äöü\nÄÖÜ");
});
it("replaces the entire document with unicode text via setText (paste simulation)", () => {
const editor = new Editor(defaultEditorTheme);
// Simulate bracketed paste / programmatic replacement
editor.setText("Hällö Wörld! 😀 äöüÄÖÜß");
const text = editor.getText();
assert.strictEqual(text, "Hällö Wörld! 😀 äöüÄÖÜß");
});
it("moves cursor to document start on Ctrl+A and inserts at the beginning", () => {
const editor = new Editor(defaultEditorTheme);
editor.handleInput("a");
editor.handleInput("b");
editor.handleInput("\x01"); // Ctrl+A (move to start)
editor.handleInput("x"); // Insert at start
const text = editor.getText();
assert.strictEqual(text, "xab");
});
});
});