Skip to content

Commit 12a2214

Browse files
committed
fix(remote): preserve cached empty results and detail retries
1 parent 54da045 commit 12a2214

2 files changed

Lines changed: 85 additions & 3 deletions

File tree

src/ui/remote.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -370,8 +370,7 @@ export async function browseRemotePackages(
370370
return;
371371
}
372372

373-
// Check cache first
374-
let allPackages: NpmPackage[] = [];
373+
let allPackages: NpmPackage[] | undefined;
375374

376375
if (isCacheValid(query)) {
377376
const cache = getSearchCache();
@@ -380,7 +379,7 @@ export async function browseRemotePackages(
380379
}
381380
}
382381

383-
if (allPackages.length === 0) {
382+
if (!allPackages) {
384383
const results = await runTaskWithLoader(
385384
ctx,
386385
{
@@ -501,6 +500,7 @@ async function showPackageDetails(
501500

502501
if (!text) {
503502
notify(ctx, `Loading ${packageName} details was cancelled.`, "info");
503+
await showPackageDetails(packageName, ctx, pi, previousQuery, previousOffset);
504504
return;
505505
}
506506

test/remote-ui.test.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import test from "node:test";
2+
import assert from "node:assert/strict";
3+
import { browseRemotePackages } from "../src/ui/remote.js";
4+
import { clearSearchCache, setSearchCache } from "../src/packages/discovery.js";
5+
import { createMockHarness } from "./helpers/mocks.js";
6+
7+
void test("browseRemotePackages honors an empty in-memory cache", async () => {
8+
setSearchCache({
9+
query: "no-results",
10+
results: [],
11+
timestamp: Date.now(),
12+
});
13+
14+
const { pi, ctx, notifications } = createMockHarness({ hasUI: true });
15+
let customCalls = 0;
16+
17+
(
18+
ctx.ui as unknown as {
19+
custom: (factory: unknown, options?: unknown) => Promise<unknown>;
20+
}
21+
).custom = () => {
22+
customCalls += 1;
23+
return Promise.resolve(undefined);
24+
};
25+
26+
try {
27+
await browseRemotePackages(ctx, "no-results", pi);
28+
29+
assert.equal(customCalls, 0);
30+
assert.ok(
31+
notifications.some((entry) => entry.message.includes("No packages found for: no-results"))
32+
);
33+
} finally {
34+
clearSearchCache();
35+
}
36+
});
37+
38+
void test("browseRemotePackages returns to package details after a cancelled load", async () => {
39+
setSearchCache({
40+
query: "demo",
41+
results: [
42+
{
43+
name: "demo-pkg",
44+
version: "1.0.0",
45+
description: "Demo package",
46+
},
47+
],
48+
timestamp: Date.now(),
49+
});
50+
51+
const { pi, ctx, notifications, selectPrompts } = createMockHarness({ hasUI: true });
52+
const customResults: unknown[] = [
53+
{ type: "package", name: "demo-pkg" },
54+
undefined,
55+
{ type: "cancel" },
56+
];
57+
const selectResults = ["View npm info", "Back to results"];
58+
59+
(
60+
ctx.ui as unknown as {
61+
custom: (factory: unknown, options?: unknown) => Promise<unknown>;
62+
}
63+
).custom = () => Promise.resolve(customResults.shift());
64+
(ctx.ui as { select: (title: string, items?: string[]) => Promise<string | undefined> }).select =
65+
(title) => {
66+
selectPrompts.push(title);
67+
return Promise.resolve(selectResults.shift());
68+
};
69+
70+
try {
71+
await browseRemotePackages(ctx, "demo", pi);
72+
73+
assert.equal(selectPrompts.filter((title) => title === "demo-pkg").length, 2);
74+
assert.ok(
75+
notifications.some((entry) =>
76+
entry.message.includes("Loading demo-pkg details was cancelled.")
77+
)
78+
);
79+
} finally {
80+
clearSearchCache();
81+
}
82+
});

0 commit comments

Comments
 (0)