test(coding-agent): cover inline-picker wrapper mouse offset
Add regression tests for all five inline-picker wrappers asserting a click on the top border row is inert and a click on the first list row below it confirms the first item, pinning the top-border line offset.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { beforeAll, describe, expect, it } from "bun:test";
|
||||
import { ShowImagesSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/show-images-selector";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import type { SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
||||
|
||||
beforeAll(async () => {
|
||||
await initTheme();
|
||||
});
|
||||
|
||||
function leftClick(line: number): SgrMouseEvent {
|
||||
return { button: 0, col: 0, row: line, release: false, wheel: null, motion: false, leftClick: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* The wrapper mounts a single-line top DynamicBorder before its SelectList, so
|
||||
* routed component-local lines are offset by one. Regression guard for the
|
||||
* off-by-one that would let a top-border click select the first row.
|
||||
*/
|
||||
describe("ShowImagesSelectorComponent.routeMouse offset", () => {
|
||||
it("ignores a click on the top border row (line 0)", () => {
|
||||
let selected: boolean | undefined;
|
||||
const component = new ShowImagesSelectorComponent(
|
||||
true,
|
||||
value => {
|
||||
selected = value;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
component.render(80);
|
||||
|
||||
component.routeMouse(leftClick(0), 0, 0);
|
||||
|
||||
expect(selected).toBeUndefined();
|
||||
});
|
||||
|
||||
it("selects the first item when the row below the border is clicked (line 1)", () => {
|
||||
let selected: boolean | undefined;
|
||||
const component = new ShowImagesSelectorComponent(
|
||||
true,
|
||||
value => {
|
||||
selected = value;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
component.render(80);
|
||||
|
||||
component.routeMouse(leftClick(1), 1, 0);
|
||||
|
||||
// First SelectList row is "Yes" → true.
|
||||
expect(selected).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
import { beforeAll, describe, expect, it } from "bun:test";
|
||||
import { Effort } from "@oh-my-pi/pi-ai";
|
||||
import { PluginSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/plugin-selector";
|
||||
import { QueueModeSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/queue-mode-selector";
|
||||
import { ThemeSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/theme-selector";
|
||||
import { ThinkingSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/thinking-selector";
|
||||
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
||||
import type { SgrMouseEvent } from "@oh-my-pi/pi-tui";
|
||||
|
||||
beforeAll(async () => {
|
||||
await initTheme();
|
||||
});
|
||||
|
||||
function leftClick(line: number): SgrMouseEvent {
|
||||
return { button: 0, col: 0, row: line, release: false, wheel: null, motion: false, leftClick: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Every wrapper mounts a single-line top DynamicBorder before its SelectList,
|
||||
* so routed component-local lines are offset by one. These guard the
|
||||
* off-by-one that would let a top-border click select the first row. Each case
|
||||
* asserts line 0 (border) is inert and line 1 (first list row) confirms.
|
||||
*/
|
||||
describe("inline-picker wrapper routeMouse offset", () => {
|
||||
it("ThemeSelectorComponent ignores the border row and selects the first theme below it", () => {
|
||||
let selected: string | undefined;
|
||||
const component = new ThemeSelectorComponent(
|
||||
"alpha",
|
||||
["alpha", "beta"],
|
||||
value => {
|
||||
selected = value;
|
||||
},
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
component.render(80);
|
||||
|
||||
component.routeMouse(leftClick(0), 0, 0);
|
||||
expect(selected).toBeUndefined();
|
||||
|
||||
component.routeMouse(leftClick(1), 1, 0);
|
||||
expect(selected).toBe("alpha");
|
||||
});
|
||||
|
||||
it("ThinkingSelectorComponent ignores the border row and selects the first level below it", () => {
|
||||
let selected: Effort | undefined;
|
||||
const levels = [Effort.Low, Effort.High];
|
||||
const component = new ThinkingSelectorComponent(
|
||||
Effort.Low,
|
||||
levels,
|
||||
value => {
|
||||
selected = value;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
component.render(80);
|
||||
|
||||
component.routeMouse(leftClick(0), 0, 0);
|
||||
expect(selected).toBeUndefined();
|
||||
|
||||
component.routeMouse(leftClick(1), 1, 0);
|
||||
expect(selected).toBe(Effort.Low);
|
||||
});
|
||||
|
||||
it("QueueModeSelectorComponent ignores the border row and selects the first mode below it", () => {
|
||||
let selected: "all" | "one-at-a-time" | undefined;
|
||||
const component = new QueueModeSelectorComponent(
|
||||
"all",
|
||||
value => {
|
||||
selected = value;
|
||||
},
|
||||
() => {},
|
||||
);
|
||||
component.render(80);
|
||||
|
||||
component.routeMouse(leftClick(0), 0, 0);
|
||||
expect(selected).toBeUndefined();
|
||||
|
||||
// First SelectList row is "one-at-a-time" regardless of the preselected mode.
|
||||
component.routeMouse(leftClick(1), 1, 0);
|
||||
expect(selected).toBe("one-at-a-time");
|
||||
});
|
||||
|
||||
it("PluginSelectorComponent ignores the border row and selects the first plugin below it", () => {
|
||||
let selectedName: string | undefined;
|
||||
const component = new PluginSelectorComponent(
|
||||
1,
|
||||
[{ plugin: { name: "alpha", description: "first" }, marketplace: "shop" }],
|
||||
new Set<string>(),
|
||||
{
|
||||
onSelect: name => {
|
||||
selectedName = name;
|
||||
},
|
||||
onCancel: () => {},
|
||||
},
|
||||
);
|
||||
component.render(80);
|
||||
|
||||
component.routeMouse(leftClick(0), 0, 0);
|
||||
expect(selectedName).toBeUndefined();
|
||||
|
||||
component.routeMouse(leftClick(1), 1, 0);
|
||||
expect(selectedName).toBe("alpha");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user