Files
llmx/codex-cli/tests/multiline-dynamic-width.test.tsx
Ilan Bigio 59a180ddec Initial commit
Signed-off-by: Ilan Bigio <ilan@openai.com>
2025-04-16 12:56:08 -04:00

78 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// These tests exercise MultilineTextEditor behaviour when the editor width is
// *not* provided via props so that it has to derive its width from the current
// terminal size. We emulate a terminalresize by mutating
// `process.stdout.columns` and emitting a synthetic `resize` event the
// `useTerminalSize` hook listens for that and causes the component to
// rerender. The test then asserts that
// 1. The rendered line rewraps to the new width, *and*
// 2. The caret (highlighted inverse character) is still kept in view after
// the horizontal shrink so that editing remains possible.
import { renderTui } from "./ui-test-helpers.js";
import MultilineTextEditor from "../src/components/chat/multiline-editor.js";
import * as React from "react";
import { describe, it, expect } from "vitest";
// Helper to synchronously type text then flush Ink's timers so that the next
// `lastFrame()` call sees the updated UI.
async function type(
stdin: NodeJS.WritableStream,
text: string,
flush: () => Promise<void>,
) {
stdin.write(text);
await flush();
}
describe("MultilineTextEditor dynamic width", () => {
// The dynamic horizontal scroll logic is still flaky mark as an expected
// *failing* test so it doesn't break CI until the feature is aligned with
// the Rust implementation.
it("keeps the caret visible when the terminal width shrinks", async () => {
// Fake an initial terminal width large enough that no horizontal
// scrolling is required while we type the long alphabet sequence.
process.stdout.columns = 40; // width seen by useTerminalSize (after padding)
const { stdin, lastFrame, flush, cleanup } = renderTui(
React.createElement(MultilineTextEditor, {
initialText: "",
// width *omitted* component should fall back to terminal columns
height: 3,
}),
);
// Ensure initial render completes.
await flush();
// Type the alphabet longer than the width we'll shrink to.
const alphabet = "abcdefghijklmnopqrstuvwxyz";
await type(stdin, alphabet, flush);
// The cursor (block) now sits on the far right after the 'z'. Verify that
// the character 'z' is visible in the current frame.
expect(lastFrame()?.includes("z")).toBe(true);
/* ----------------------- Simulate resize ----------------------- */
// Shrink the reported terminal width so that the previously visible slice
// would no longer include the cursor *unless* the editor recomputes
// scroll offsets on rerender.
process.stdout.columns = 20; // shrink significantly (remember: padding8)
process.stdout.emit("resize"); // notify listeners
// Allow Ink to schedule the state update and then perform the rerender.
await flush();
await flush();
// After the resize the editor should have scrolled horizontally so that
// the caret (and thus the 'z' character that is blockhighlighted) remains
// visible in the rendered slice.
const frameAfter = lastFrame() || "";
// eslint-disable-next-line no-console
console.log("FRAME AFTER RESIZE:\n" + frameAfter);
expect(frameAfter.includes("z")).toBe(true);
cleanup();
});
});