merge main: resolve db.test.ts import conflict
Combined both branches: TaskStore import from main + rmSync import from our branch (needed for fresh DB test cleanup). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -155,12 +155,19 @@ afterEach(() => {
|
||||
});
|
||||
|
||||
async function waitForFrameContains(lastFrame: () => string | undefined, text: string, timeoutMs = 3000) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < timeoutMs) {
|
||||
if ((lastFrame() ?? "").includes(text)) return;
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
}
|
||||
throw new Error(`Timed out waiting for frame to include: ${text}`);
|
||||
await vi.waitFor(() => {
|
||||
expect(lastFrame() ?? "").toContain(text);
|
||||
}, { timeout: timeoutMs });
|
||||
}
|
||||
|
||||
async function flushFrames() {
|
||||
await Promise.resolve();
|
||||
await Promise.resolve();
|
||||
}
|
||||
|
||||
async function focusSettingsDetailPane(stdin: { write: (chunk: string) => void }, lastFrame: () => string | undefined) {
|
||||
stdin.write("\u001b[C");
|
||||
await waitForFrameContains(lastFrame, "[C/V/X/P/L/U/K/R] remote actions");
|
||||
}
|
||||
|
||||
function findTokenPosition(frame: string, token: string): { row: number; col: number } {
|
||||
@@ -218,7 +225,7 @@ describe("DashboardApp smoke", () => {
|
||||
controller.setMode("interactive");
|
||||
controller.setInteractiveView("board");
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await waitForFrameContains(lastFrame, "alpha");
|
||||
const frame = lastFrame() ?? "";
|
||||
// Board shows the currently selected project; first project "alpha" is selected by default
|
||||
expect(frame).toContain("alpha");
|
||||
@@ -294,7 +301,7 @@ describe("Agents view", () => {
|
||||
controller.setMode("interactive");
|
||||
controller.setInteractiveView("agents");
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await waitForFrameContains(lastFrame, "worker-1");
|
||||
const frame = lastFrame() ?? "";
|
||||
expect(frame).toContain("worker-1");
|
||||
expect(frame).toContain("worker-2");
|
||||
@@ -309,7 +316,7 @@ describe("Agents view", () => {
|
||||
controller.setMode("interactive");
|
||||
controller.setInteractiveView("agents");
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await flushFrames();
|
||||
expect(lastFrame() ?? "").toContain("Agent Detail");
|
||||
unmount();
|
||||
});
|
||||
@@ -375,7 +382,7 @@ describe("Settings view", () => {
|
||||
controller.setMode("interactive");
|
||||
controller.setInteractiveView("settings");
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await waitForFrameContains(lastFrame, "Max Concurrent");
|
||||
const frame = lastFrame() ?? "";
|
||||
expect(frame).toContain("Settings");
|
||||
expect(frame).toContain("Max Concurrent");
|
||||
@@ -393,7 +400,7 @@ describe("Settings view", () => {
|
||||
controller.setMode("interactive");
|
||||
controller.setInteractiveView("settings");
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await waitForFrameContains(lastFrame, "Available Models");
|
||||
const frame = lastFrame() ?? "";
|
||||
expect(frame).toContain("Available Models");
|
||||
expect(frame).toContain("Claude 3.5 Sonnet");
|
||||
@@ -435,14 +442,12 @@ describe("Settings view", () => {
|
||||
controller.setInteractiveView("settings");
|
||||
|
||||
const { lastFrame, stdin, unmount } = render(renderDashboardAppNode(controller));
|
||||
await waitForFrameContains(lastFrame, "Remote");
|
||||
await waitForFrameContains(lastFrame, "Provider: cloudflare");
|
||||
expect(lastFrame() ?? "").toContain("cloudflare");
|
||||
|
||||
stdin.write("\u001B[C");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await focusSettingsDetailPane(stdin, lastFrame);
|
||||
stdin.write("C");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
expect(activateProvider).toHaveBeenCalledWith("cloudflare");
|
||||
await vi.waitFor(() => expect(activateProvider).toHaveBeenCalledWith("cloudflare"));
|
||||
|
||||
stdin.write("V");
|
||||
await waitForFrameContains(lastFrame, "Remote tunnel starting");
|
||||
@@ -471,8 +476,7 @@ describe("Settings view", () => {
|
||||
const { lastFrame, stdin, unmount } = render(renderDashboardAppNode(controller));
|
||||
await waitForFrameContains(lastFrame, "──── Remote ────");
|
||||
|
||||
stdin.write("\u001B[C");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await focusSettingsDetailPane(stdin, lastFrame);
|
||||
stdin.write("L");
|
||||
await waitForFrameContains(lastFrame, "TTL ms:");
|
||||
stdin.write("\r");
|
||||
@@ -507,8 +511,8 @@ describe("Settings view", () => {
|
||||
controller.setInteractiveView("settings");
|
||||
|
||||
const { lastFrame, stdin, unmount } = render(renderDashboardAppNode(controller));
|
||||
stdin.write("\u001B[C");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await waitForFrameContains(lastFrame, "──── Remote ────");
|
||||
await focusSettingsDetailPane(stdin, lastFrame);
|
||||
stdin.write("P");
|
||||
await waitForFrameContains(lastFrame, "Persistent token: tok_****");
|
||||
expect(regeneratePersistentToken).toHaveBeenCalledTimes(1);
|
||||
@@ -530,12 +534,11 @@ describe("Settings view", () => {
|
||||
const { lastFrame, stdin, unmount } = render(renderDashboardAppNode(controller));
|
||||
await waitForFrameContains(lastFrame, "──── Remote ────");
|
||||
|
||||
stdin.write("\u001B[C");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await focusSettingsDetailPane(stdin, lastFrame);
|
||||
stdin.write("L");
|
||||
await waitForFrameContains(lastFrame, "TTL ms:");
|
||||
stdin.write("a");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await flushFrames();
|
||||
expect(controller.getSnapshot().interactiveView).toBe("settings");
|
||||
unmount();
|
||||
});
|
||||
@@ -557,8 +560,8 @@ describe("Settings view", () => {
|
||||
controller.setInteractiveView("settings");
|
||||
|
||||
const { lastFrame, stdin, unmount } = render(renderDashboardAppNode(controller));
|
||||
stdin.write("\u001B[C");
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
await waitForFrameContains(lastFrame, "──── Remote ────");
|
||||
await focusSettingsDetailPane(stdin, lastFrame);
|
||||
stdin.write("K");
|
||||
await waitForFrameContains(lastFrame, "▀▀▀ASCII-QR▀▀▀", 6000);
|
||||
unmount();
|
||||
@@ -580,7 +583,7 @@ describe("Board view", () => {
|
||||
controller.setMode("interactive");
|
||||
controller.setInteractiveView("board");
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
await waitForFrameContains(lastFrame, "TODO");
|
||||
const frame = lastFrame() ?? "";
|
||||
expect(frame).toContain("TODO");
|
||||
expect(frame).toContain("IN PROGRESS");
|
||||
@@ -599,7 +602,7 @@ describe("LogsPanel indicator", () => {
|
||||
// Select index 1 (middle entry)
|
||||
controller.setSelectedLogIndex(1);
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
const frame = lastFrame() ?? "";
|
||||
expect(frame).toContain("▶");
|
||||
unmount();
|
||||
@@ -612,7 +615,7 @@ describe("LogsPanel indicator", () => {
|
||||
controller.log("only message", "test");
|
||||
controller.setSelectedLogIndex(0);
|
||||
const { lastFrame, unmount } = render(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
const frame = lastFrame() ?? "";
|
||||
// The selected entry shows the arrow; it should appear at least once
|
||||
expect(frame).toContain("▶");
|
||||
@@ -634,7 +637,7 @@ describe("StatusModeGrid layout stability", () => {
|
||||
controller.log("small", "worker");
|
||||
controller.log("ok", "db");
|
||||
rendered.rerender(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
|
||||
const shortFrame = rendered.lastFrame() ?? "";
|
||||
const shortSystem = findTokenPosition(shortFrame, "System");
|
||||
@@ -654,7 +657,7 @@ describe("StatusModeGrid layout stability", () => {
|
||||
"super-verbose-component-prefix",
|
||||
);
|
||||
rendered.rerender(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
|
||||
const longFrame = rendered.lastFrame() ?? "";
|
||||
const longSystem = findTokenPosition(longFrame, "System");
|
||||
@@ -693,7 +696,7 @@ describe("StatsPanel memory row", () => {
|
||||
const rendered = render(renderDashboardAppNode(controller));
|
||||
setTerminalSize(rendered, 120, 24);
|
||||
rendered.rerender(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
|
||||
const frame = rendered.lastFrame() ?? "";
|
||||
const pctIndex = frame.indexOf("75.0%");
|
||||
@@ -718,7 +721,7 @@ describe("LogsPanel narrow formatting", () => {
|
||||
const rendered = render(renderDashboardAppNode(controller));
|
||||
setTerminalSize(rendered, 60, 24);
|
||||
rendered.rerender(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
const frame = rendered.lastFrame() ?? "";
|
||||
|
||||
expect(frame).toContain("narrow entry");
|
||||
@@ -739,7 +742,7 @@ describe("LogsPanel narrow formatting", () => {
|
||||
const narrowRender = render(renderDashboardAppNode(narrowController));
|
||||
setTerminalSize(narrowRender, 60, 24);
|
||||
narrowRender.rerender(renderDashboardAppNode(narrowController));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
const narrowFrame = narrowRender.lastFrame() ?? "";
|
||||
expect(narrowFrame).toContain("[very-…]");
|
||||
narrowRender.unmount();
|
||||
@@ -753,7 +756,7 @@ describe("LogsPanel narrow formatting", () => {
|
||||
const wideRender = render(renderDashboardAppNode(wideController));
|
||||
setTerminalSize(wideRender, 120, 24);
|
||||
wideRender.rerender(renderDashboardAppNode(wideController));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
const wideFrame = wideRender.lastFrame() ?? "";
|
||||
expect(wideFrame).toContain("[very-long-sco");
|
||||
expect(wideFrame).not.toContain("[very-…]");
|
||||
@@ -770,7 +773,7 @@ describe("LogsPanel narrow formatting", () => {
|
||||
const rendered = render(renderDashboardAppNode(controller));
|
||||
setTerminalSize(rendered, 120, 24);
|
||||
rendered.rerender(renderDashboardAppNode(controller));
|
||||
await new Promise((r) => setTimeout(r, 10));
|
||||
await flushFrames();
|
||||
const frame = rendered.lastFrame() ?? "";
|
||||
|
||||
expect(frame).toContain("wide timestamp");
|
||||
|
||||
@@ -2375,19 +2375,7 @@ function SettingsInteractiveView({ state, controller }: { state: DashboardState;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!detailFocused) {
|
||||
if (key.upArrow || input === "k") {
|
||||
setSelectedIndex((i) => Math.max(0, i - 1));
|
||||
return;
|
||||
}
|
||||
if (key.downArrow || input === "j") {
|
||||
setSelectedIndex((i) => Math.min(SETTING_DEFS.length - 1, i + 1));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedDef || !localSettings) return;
|
||||
const inputUpper = input.toUpperCase();
|
||||
|
||||
if (ttlInputMode) {
|
||||
if (key.escape) {
|
||||
@@ -2397,14 +2385,14 @@ function SettingsInteractiveView({ state, controller }: { state: DashboardState;
|
||||
return;
|
||||
}
|
||||
|
||||
const inputUpper = input.toUpperCase();
|
||||
|
||||
if (inputUpper === "R") {
|
||||
void refreshRemoteStatus();
|
||||
setStatusMsg("Remote status refreshed");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!localSettings) return;
|
||||
|
||||
if (data?.remote && inputUpper === "C") {
|
||||
const provider = localSettings.remoteActiveProvider;
|
||||
if (!provider) {
|
||||
@@ -2461,6 +2449,20 @@ function SettingsInteractiveView({ state, controller }: { state: DashboardState;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!detailFocused) {
|
||||
if (key.upArrow || input === "k") {
|
||||
setSelectedIndex((i) => Math.max(0, i - 1));
|
||||
return;
|
||||
}
|
||||
if (key.downArrow || input === "j") {
|
||||
setSelectedIndex((i) => Math.min(SETTING_DEFS.length - 1, i + 1));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedDef) return;
|
||||
|
||||
if (selectedDef.type === "boolean" && input === " ") {
|
||||
const current = localSettings[selectedDef.key] as boolean;
|
||||
const updated = { ...localSettings, [selectedDef.key]: !current };
|
||||
|
||||
Reference in New Issue
Block a user