Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions apps/ade-cli/src/tuiClient/__tests__/ApprovalPrompt.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,39 @@ describe("ApprovalPrompt", () => {
expect(frame).toContain("next/send");
});

it("renders every legacy request-level option", () => {
const approval: PendingApproval = {
itemId: "legacy-question",
description: "Choose one option.",
highStakes: false,
mode: "question",
request: {
requestId: "legacy-question",
source: "codex",
kind: "question",
title: "Choose one option",
description: "Choose one option.",
allowsFreeform: true,
blocking: true,
canProceedWithoutAnswer: false,
questions: [],
options: Array.from({ length: 8 }, (_, index) => ({
label: `Option ${index + 1}`,
value: `option-${index + 1}`,
})),
},
};

const frame = stripAnsi(render(
<ApprovalPrompt approval={approval} width={100} />,
).lastFrame() ?? "");

expect(frame).toContain("1 Option 1");
expect(frame).toContain("6 Option 6");
expect(frame).toContain("7 Option 7");
expect(frame).toContain("8 Option 8");
});

it("renders orchestration model-selection briefing metadata", () => {
const approval: PendingApproval = {
itemId: "model-1",
Expand Down
35 changes: 35 additions & 0 deletions apps/ade-cli/src/tuiClient/__tests__/Palettes.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,24 @@ describe("MentionPalette", () => {
expect(frame).toContain("Tab insert");
expect(frame).not.toContain("Type");
});

it("keeps mention chrome visible when there are no matches", () => {
const frame = stripAnsi(
render(
<MentionPalette
suggestions={[]}
selectedIndex={0}
query="missing"
width={100}
/>,
).lastFrame() ?? "",
);

expect(frame).toContain("References");
expect(frame).toContain("@missing");
expect(frame).toContain("0 matches");
expect(frame).toContain("No references found");
});
});

describe("SlashPalette", () => {
Expand Down Expand Up @@ -127,4 +145,21 @@ describe("SlashPalette", () => {
expect(frame).toContain("Submit ADE feedback to GitHub issues");
expect(frame).not.toContain("Runtime feedback command");
});

it("keeps slash chrome visible when there are no matches", () => {
const frame = stripAnsi(render(
<SlashPalette
query="/missing"
userCommands={[]}
selectedIndex={0}
provider="claude"
width={80}
/>,
).lastFrame() ?? "");

expect(frame).toContain("Commands");
expect(frame).toContain("/missing");
expect(frame).toContain("0 matches");
expect(frame).toContain("No matching commands");
});
});
9 changes: 9 additions & 0 deletions apps/ade-cli/src/tuiClient/__tests__/appInput.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ import {
splitTerminalControlInput,
stableInkViewportRows,
subagentSnapshotsFromEvents,
terminalAlternateScreenDisableSequence,
terminalAlternateScrollDisableSequence,
terminalInteractiveRestoreSequence,
terminalMouseTrackingDisableSequence,
terminalMouseTrackingEnableSequence,
isClaudePlaceholderTitle,
Expand Down Expand Up @@ -1510,6 +1513,12 @@ describe("terminal mouse tracking", () => {
expect(terminalMouseTrackingDisableSequence()).toContain("\x1b[?1003l");
expect(terminalMouseTrackingDisableSequence()).toContain("\x1b[?1015l");
});

it("restores mouse tracking, alternate scroll, and the alt screen together", () => {
expect(terminalInteractiveRestoreSequence()).toBe(
`${terminalMouseTrackingDisableSequence()}${terminalAlternateScrollDisableSequence()}${terminalAlternateScreenDisableSequence()}`,
);
});
});

describe("encodeTerminalPromptSubmit", () => {
Expand Down
63 changes: 62 additions & 1 deletion apps/ade-cli/src/tuiClient/__tests__/appPolling.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ vi.mock("../adeApi", async () => {
};
});

import { AdeCodeApp, isLaneWorktreeAvailable, shouldHydrateRefreshHistory } from "../app";
import { AdeCodeApp, isLaneWorktreeAvailable, MENTION_REMOTE_DEBOUNCE_MS, shouldHydrateRefreshHistory } from "../app";

const reactActGlobal = globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean };
let previousReactActEnvironment: boolean | undefined;
Expand Down Expand Up @@ -258,6 +258,67 @@ describe("AdeCodeApp polling", () => {

await unmountApp(instance);
});

it("shows startup retry guidance and automatically reconnects", async () => {
mocks.connectToAde
.mockRejectedValueOnce(new Error("socket down"))
.mockResolvedValueOnce(connection);

const instance = render(<AdeCodeApp project={project} />);
await flushAsyncEffects();

expect(instance.lastFrame()).toContain("r retry now");
expect(mocks.connectToAde).toHaveBeenCalledTimes(1);

await act(async () => {
await vi.advanceTimersByTimeAsync(3_000);
});
await flushAsyncEffects();

expect(mocks.connectToAde).toHaveBeenCalledTimes(2);

instance.unmount();
});

it("debounces mention RPCs and caches lane git/pr suggestions", async () => {
mocks.listChatSessions.mockResolvedValue([chat({ status: "idle" })]);
const actionMock = vi.fn(async (domain: string, action: string, args?: Record<string, unknown>) => {
if (domain === "file" && action === "quickOpen") return [{ path: `src/${String(args?.query ?? "query")}.ts` }];
if (domain === "git" && action === "listRecentCommits") return [{ shortSha: "abc1234", subject: "Mention cache" }];
if (domain === "pr" && action === "listAll") return [{ id: "42", number: 42, title: "Mention PR" }];
return [];
});
connection.action = actionMock as unknown as AdeCodeConnection["action"];

const instance = render(<AdeCodeApp project={project} />);
await flushAsyncEffects();

await act(async () => {
instance.stdin.write("@a");
await vi.advanceTimersByTimeAsync(MENTION_REMOTE_DEBOUNCE_MS - 1);
});
await flushAsyncEffects();

expect(connection.action).not.toHaveBeenCalledWith("git", "listRecentCommits", expect.anything());

await act(async () => {
await vi.advanceTimersByTimeAsync(1);
});
await flushAsyncEffects();

await act(async () => {
instance.stdin.write("b");
await vi.advanceTimersByTimeAsync(MENTION_REMOTE_DEBOUNCE_MS);
});
await flushAsyncEffects();

const calls = actionMock.mock.calls;
expect(calls.filter(([domain, action]) => domain === "file" && action === "quickOpen")).toHaveLength(2);
expect(calls.filter(([domain, action]) => domain === "git" && action === "listRecentCommits")).toHaveLength(1);
expect(calls.filter(([domain, action]) => domain === "pr" && action === "listAll")).toHaveLength(1);

instance.unmount();
});
});

describe("isLaneWorktreeAvailable", () => {
Expand Down
Loading