test(pi-tui): add regression tests for loader, cancellable-loader, input

First test coverage for pi-tui components (8 tests):
- Loader: start() idempotency, dispose() cleanup, stop() safety
- CancellableLoader: abort on dispose, callback cleanup, signal state
- Input: paste buffer reset on focus loss, focused getter/setter
This commit is contained in:
Flux Labs 2026-03-15 09:56:50 -05:00
parent b448bf9400
commit ea2b626c98
3 changed files with 125 additions and 0 deletions

View file

@ -0,0 +1,45 @@
// pi-tui CancellableLoader component regression tests
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
import { describe, it, mock, beforeEach, afterEach } from "node:test";
import assert from "node:assert/strict";
import { CancellableLoader } from "../cancellable-loader.js";
function makeMockTUI() {
return { requestRender: mock.fn() } as any;
}
describe("CancellableLoader", () => {
let loader: CancellableLoader;
let tui: ReturnType<typeof makeMockTUI>;
beforeEach(() => {
tui = makeMockTUI();
});
afterEach(() => {
loader?.dispose();
});
it("dispose() aborts the AbortController signal", () => {
loader = new CancellableLoader(tui, (s) => s, (s) => s, "test");
assert.equal(loader.aborted, false);
loader.dispose();
assert.equal(loader.aborted, true);
});
it("dispose() clears the onAbort callback", () => {
loader = new CancellableLoader(tui, (s) => s, (s) => s, "test");
loader.onAbort = () => {};
loader.dispose();
assert.equal(loader.onAbort, undefined);
});
it("signal is aborted after dispose()", () => {
loader = new CancellableLoader(tui, (s) => s, (s) => s, "test");
const signal = loader.signal;
assert.equal(signal.aborted, false);
loader.dispose();
assert.equal(signal.aborted, true);
});
});

View file

@ -0,0 +1,35 @@
// pi-tui Input component regression tests
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { Input } from "../input.js";
describe("Input", () => {
it("paste buffer is cleared when focus is lost", () => {
const input = new Input();
input.focused = true;
// Simulate starting a paste (bracket paste start marker)
input.handleInput("\x1b[200~partial");
// Now lose focus mid-paste
input.focused = false;
// Regain focus — should not have stale paste state
input.focused = true;
// Typing normal text should work without paste buffer corruption
input.handleInput("hello");
assert.equal(input.getValue(), "hello");
});
it("focused getter/setter works correctly", () => {
const input = new Input();
assert.equal(input.focused, false);
input.focused = true;
assert.equal(input.focused, true);
input.focused = false;
assert.equal(input.focused, false);
});
});

View file

@ -0,0 +1,45 @@
// pi-tui Loader component regression tests
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
import { describe, it, mock, beforeEach, afterEach } from "node:test";
import assert from "node:assert/strict";
import { Loader } from "../loader.js";
function makeMockTUI() {
return { requestRender: mock.fn() } as any;
}
describe("Loader", () => {
let loader: Loader;
let tui: ReturnType<typeof makeMockTUI>;
beforeEach(() => {
tui = makeMockTUI();
});
afterEach(() => {
loader?.stop();
});
it("start() is idempotent — calling twice does not leak intervals", () => {
loader = new Loader(tui, (s) => s, (s) => s, "test");
// Constructor calls start() once, call it again
loader.start();
// stop() should clear the interval cleanly without orphaned timers
loader.stop();
});
it("dispose() stops the interval and nulls the TUI reference", () => {
loader = new Loader(tui, (s) => s, (s) => s, "test");
loader.dispose();
// After dispose, calling stop() again should be safe (no-op)
loader.stop();
});
it("stop() is safe to call multiple times", () => {
loader = new Loader(tui, (s) => s, (s) => s, "test");
loader.stop();
loader.stop();
loader.stop();
});
});