test(updates): extract panel logic into a tested module

Move the Updates plugin's pure decision logic (recommended/additional
commands, panel visibility, installation labels, version formatting)
into a sibling updatesLogic.ts module so it can be unit tested directly,
matching the workspace-tasks multi-file plugin layout. The plugin file
is now thin rendering glue that imports those helpers.

Add unit tests for the extracted logic and render smoke tests that
exercise the panel through its public contribution API, including that
Run actions wire to the terminal with the "pi.plugin": "updates"
metadata. No behavior change; the beta label stays.
This commit is contained in:
Federico Jaramillo Martinez
2026-06-14 17:32:34 +02:00
parent 227187c4ca
commit 59a13f5058
4 changed files with 464 additions and 74 deletions
+2 -74
View File
@@ -1,10 +1,6 @@
import type { TemplateResult } from "lit";
import type { HtmlTemplateTag, PiWebComponentStatus, PiWebInstallationInfo, PiWebPlugin, PiWebStatusMessage, PiWebStatusResponse, PluginRuntimeState, WorkspacePanelTerminal } from "@jmfederico/pi-web/plugin-api";
interface CommandEntry {
label: string;
command: string;
}
import type { HtmlTemplateTag, PiWebComponentStatus, PiWebPlugin, PiWebStatusResponse, PluginRuntimeState, WorkspacePanelTerminal } from "@jmfederico/pi-web/plugin-api";
import { additionalCommands, formatVersion, installationLabel, messageCount, recommendedCommand, shouldShowUpdatesPanel, statusFor } from "./updatesLogic.js";
function runCommandInTerminal(terminal: WorkspacePanelTerminal, label: string, command: string): void {
void terminal.runCommand({
@@ -17,74 +13,6 @@ function runCommandInTerminal(terminal: WorkspacePanelTerminal, label: string, c
});
}
// The single command users should run when they do not want to think: if an
// update is available, `commands.update` already chains the update and a full
// restart; otherwise, when anything is stale, a full restart is enough.
function recommendedCommand(status: PiWebStatusResponse): CommandEntry | undefined {
const { commands, release, components } = status;
if (release.updateAvailable && typeof commands.update === "string" && commands.update !== "") {
return { label: "Update & restart everything", command: commands.update };
}
const restartNeeded = components.web.stale || components.sessiond.stale || !components.sessiond.available;
if (restartNeeded && typeof commands.restart === "string" && commands.restart !== "") {
return { label: "Restart everything", command: commands.restart };
}
return undefined;
}
function additionalCommands(status: PiWebStatusResponse, recommended: CommandEntry | undefined): CommandEntry[] {
return [
["Update", status.commands.update],
["Restart all", status.commands.restart],
["Restart Web/UI", status.commands.restartWeb],
["Restart session daemon", status.commands.restartSessiond],
["Status", status.commands.status],
]
.filter((entry): entry is [string, string] => typeof entry[1] === "string" && entry[1] !== "")
.filter(([, command]) => command !== recommended?.command)
.map(([label, command]) => ({ label, command }));
}
function messagesFor(state: PluginRuntimeState | undefined): PiWebStatusMessage[] {
return state?.piWebStatus?.messages ?? [];
}
function statusFor(state: PluginRuntimeState | undefined): PiWebStatusResponse | undefined {
return state?.piWebStatus;
}
function messageCount(state: PluginRuntimeState | undefined): number {
return messagesFor(state).length;
}
function isLocalOrUnknownInstallation(installation: PiWebInstallationInfo | undefined): boolean {
return installation === undefined || installation.kind === "local" || installation.kind === "unknown";
}
function shouldShowUpdatesPanel(state: PluginRuntimeState | undefined): boolean {
const status = statusFor(state);
if (messageCount(state) > 0) return true;
if (status === undefined) return false;
return isLocalOrUnknownInstallation(status.components.web.installation)
|| isLocalOrUnknownInstallation(status.components.sessiond.installation);
}
function formatVersion(version: string | undefined): string {
return version === undefined || version === "" ? "unknown" : version;
}
function installationLabel(installation: PiWebInstallationInfo | undefined): string {
if (installation === undefined) return "installation unknown";
if (installation.kind === "pi-package") {
const scope = installation.scope === undefined ? "" : ` · ${installation.scope}`;
const source = installation.source ?? "Pi package";
return `${source}${scope}`;
}
if (installation.kind === "npm-global") return "global npm package";
if (installation.kind === "local") return "local checkout";
return "installation unknown";
}
function renderComponent(html: HtmlTemplateTag, component: PiWebComponentStatus): TemplateResult {
const status = !component.available
? "unavailable"