Archived
test: close audit cleanup findings
This commit is contained in:
@@ -233,9 +233,11 @@ describe("fallbackDockerStatus", () => {
|
|||||||
const fallback = fallbackDockerStatus({ dockerMode: "dev" }, "generated");
|
const fallback = fallbackDockerStatus({ dockerMode: "dev" }, "generated");
|
||||||
expect(fallback?.generatedAt).toBe("generated");
|
expect(fallback?.generatedAt).toBe("generated");
|
||||||
expect(fallback?.components.web.installation).toEqual({ kind: "docker", dockerMode: "dev" });
|
expect(fallback?.components.web.installation).toEqual({ kind: "docker", dockerMode: "dev" });
|
||||||
expect(fallback?.commands).toMatchObject({
|
expect(fallback?.commands).toEqual({
|
||||||
update: "pi-web-docker --dev update",
|
update: "pi-web-docker --dev update",
|
||||||
restart: "pi-web-docker --dev restart",
|
restart: "pi-web-docker --dev restart",
|
||||||
|
restartWeb: "pi-web-docker --dev restart-web",
|
||||||
|
restartSessiond: "pi-web-docker --dev restart-sessiond",
|
||||||
status: "pi-web-docker --dev status",
|
status: "pi-web-docker --dev status",
|
||||||
});
|
});
|
||||||
expect(fallback?.messages[0]?.id).toBe("docker-status-compatibility");
|
expect(fallback?.messages[0]?.id).toBe("docker-status-compatibility");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it, vi } from "vitest";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
import { activateSelectableRow, activateSelectableRowFromKeyboard, handleSelectableRowKeyboard } from "./selectableRow";
|
import { activateSelectableRow, handleSelectableRowKeyboard } from "./selectableRow";
|
||||||
|
|
||||||
describe("selectable row activation", () => {
|
describe("selectable row activation", () => {
|
||||||
it("activates rows from non-interactive click targets", () => {
|
it("activates rows from non-interactive click targets", () => {
|
||||||
@@ -20,8 +20,8 @@ describe("selectable row activation", () => {
|
|||||||
const enter = keyboardEventWithPath("Enter", matchTarget(() => false));
|
const enter = keyboardEventWithPath("Enter", matchTarget(() => false));
|
||||||
const space = keyboardEventWithPath(" ", matchTarget(() => false));
|
const space = keyboardEventWithPath(" ", matchTarget(() => false));
|
||||||
|
|
||||||
activateSelectableRowFromKeyboard(enter, enterAction);
|
expect(handleSelectableRowKeyboard(enter, { activate: enterAction })).toBe(true);
|
||||||
activateSelectableRowFromKeyboard(space, spaceAction);
|
expect(handleSelectableRowKeyboard(space, { activate: spaceAction })).toBe(true);
|
||||||
|
|
||||||
expect(enterAction).toHaveBeenCalledOnce();
|
expect(enterAction).toHaveBeenCalledOnce();
|
||||||
expect(spaceAction).toHaveBeenCalledOnce();
|
expect(spaceAction).toHaveBeenCalledOnce();
|
||||||
@@ -33,7 +33,7 @@ describe("selectable row activation", () => {
|
|||||||
const action = vi.fn();
|
const action = vi.fn();
|
||||||
const event = keyboardEventWithPath("Enter", matchTarget((selector: string) => selector.includes("button")));
|
const event = keyboardEventWithPath("Enter", matchTarget((selector: string) => selector.includes("button")));
|
||||||
|
|
||||||
activateSelectableRowFromKeyboard(event, action);
|
expect(handleSelectableRowKeyboard(event, { activate: action })).toBe(false);
|
||||||
|
|
||||||
expect(action).not.toHaveBeenCalled();
|
expect(action).not.toHaveBeenCalled();
|
||||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ const interactiveSelector = [
|
|||||||
].join(",");
|
].join(",");
|
||||||
|
|
||||||
type ComposedPathEvent = Pick<Event, "composedPath">;
|
type ComposedPathEvent = Pick<Event, "composedPath">;
|
||||||
type SelectableKeyboardEvent = ComposedPathEvent & Pick<KeyboardEvent, "key" | "preventDefault">;
|
type SelectableNavigationKeyboardEvent = ComposedPathEvent
|
||||||
type SelectableNavigationKeyboardEvent = SelectableKeyboardEvent & Partial<Pick<KeyboardEvent, "currentTarget" | "stopPropagation">>;
|
& Pick<KeyboardEvent, "key" | "preventDefault">
|
||||||
|
& Partial<Pick<KeyboardEvent, "currentTarget" | "stopPropagation">>;
|
||||||
|
|
||||||
export interface SelectableRowKeyboardOptions {
|
export interface SelectableRowKeyboardOptions {
|
||||||
activate: () => void;
|
activate: () => void;
|
||||||
@@ -37,13 +38,6 @@ export function activateSelectableRow(event: ComposedPathEvent, action: () => vo
|
|||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function activateSelectableRowFromKeyboard(event: SelectableKeyboardEvent, action: () => void): void {
|
|
||||||
if (event.key !== "Enter" && event.key !== " ") return;
|
|
||||||
if (isFromInteractiveElement(event)) return;
|
|
||||||
event.preventDefault();
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleSelectableRowKeyboard(event: SelectableNavigationKeyboardEvent, options: SelectableRowKeyboardOptions): boolean {
|
export function handleSelectableRowKeyboard(event: SelectableNavigationKeyboardEvent, options: SelectableRowKeyboardOptions): boolean {
|
||||||
if (isFromInteractiveElement(event)) return false;
|
if (isFromInteractiveElement(event)) return false;
|
||||||
if (event.key === "Enter" || event.key === " ") {
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
|||||||
@@ -37,10 +37,8 @@ describe("pi-web-docker documentation", () => {
|
|||||||
readRepoFile("docker/pi-web-docker"),
|
readRepoFile("docker/pi-web-docker"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for (const command of PI_WEB_DOCKER_USER_COMMANDS) {
|
expect(readDockerCommandMatrix(dockerReadme)).toEqual([...PI_WEB_DOCKER_USER_COMMANDS]);
|
||||||
expect(dockerReadme).toContain(`| \`${command}\` |`);
|
expect(readEntrypointCommandCases(dockerEntrypoint)).toEqual(new Set(PI_WEB_DOCKER_USER_COMMANDS));
|
||||||
expect(dockerEntrypoint).toContain(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(dockerReadme).toContain("`pi-web-docker --dev status`");
|
expect(dockerReadme).toContain("`pi-web-docker --dev status`");
|
||||||
expect(dockerReadme).toContain("`./docker/pi-web-docker --dev start`");
|
expect(dockerReadme).toContain("`./docker/pi-web-docker --dev start`");
|
||||||
@@ -49,6 +47,26 @@ describe("pi-web-docker documentation", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function readDockerCommandMatrix(dockerReadme: string): string[] {
|
||||||
|
const commandMatrixSection = dockerReadme.split("### Command matrix\n")[1]?.split("\n### Installer options")[0] ?? "";
|
||||||
|
return Array.from(commandMatrixSection.matchAll(/^\| `([^`]+)` \|/gm), (match) => {
|
||||||
|
const command = match[1];
|
||||||
|
if (command === undefined) throw new Error("Docker command matrix row did not include a command");
|
||||||
|
return command;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readEntrypointCommandCases(dockerEntrypoint: string): Set<string> {
|
||||||
|
const commandCaseBlock = dockerEntrypoint.slice(dockerEntrypoint.indexOf('case "$command_name" in'));
|
||||||
|
const commandCases = new Set<string>();
|
||||||
|
for (const line of commandCaseBlock.split("\n")) {
|
||||||
|
const match = /^ {2}([a-z][a-z-]*(?:\|[a-z][a-z-]*)*)(?:\|__run-detached)?\)$/.exec(line);
|
||||||
|
if (match?.[1] === undefined) continue;
|
||||||
|
for (const command of match[1].split("|")) commandCases.add(command);
|
||||||
|
}
|
||||||
|
return commandCases;
|
||||||
|
}
|
||||||
|
|
||||||
async function readRepoFile(relativePath: string): Promise<string> {
|
async function readRepoFile(relativePath: string): Promise<string> {
|
||||||
return await readFile(join(repoRoot, relativePath), "utf8");
|
return await readFile(join(repoRoot, relativePath), "utf8");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,10 @@ describe("PiWebPluginService", () => {
|
|||||||
const service = new PiWebPluginService({ roots: [{ path: join(tempDir, "plugins"), source: "test", scope: "local" }], packageProvider: false });
|
const service = new PiWebPluginService({ roots: [{ path: join(tempDir, "plugins"), source: "test", scope: "local" }], packageProvider: false });
|
||||||
|
|
||||||
const manifest = await service.manifest();
|
const manifest = await service.manifest();
|
||||||
expect(manifest.plugins[0]?.module).toMatch(/^\/pi-web-plugins\/updates\/pi-web-plugin\.js\?v=\d+&piWebDockerMode=dev$/u);
|
const moduleUrl = new URL(manifest.plugins[0]?.module ?? "", "http://pi-web.test");
|
||||||
|
expect(moduleUrl.pathname).toBe("/pi-web-plugins/updates/pi-web-plugin.js");
|
||||||
|
expect(moduleUrl.searchParams.get("v")).toMatch(/^\d+$/u);
|
||||||
|
expect(moduleUrl.searchParams.get("piWebDockerMode")).toBe("dev");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("discovers Pi package plugins through an injected package provider", async () => {
|
it("discovers Pi package plugins through an injected package provider", async () => {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { isSessionActive, sessionActivityLabel, isWorkspaceActivityActive } from "./activity";
|
import { isSessionActive, isWorkspaceActivityActive } from "./activity";
|
||||||
import type { SessionStatus, WorkspaceActivity } from "./apiTypes";
|
import type { SessionStatus, WorkspaceActivity } from "./apiTypes";
|
||||||
|
|
||||||
const idleStatus: SessionStatus = {
|
const idleStatus: SessionStatus = {
|
||||||
@@ -14,17 +14,10 @@ const idleStatus: SessionStatus = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
describe("activity helpers", () => {
|
describe("activity helpers", () => {
|
||||||
it("detects and labels active session states consistently", () => {
|
it("detects active session states", () => {
|
||||||
expect(isSessionActive(idleStatus)).toBe(false);
|
expect(isSessionActive(idleStatus)).toBe(false);
|
||||||
expect(sessionActivityLabel(idleStatus)).toBeUndefined();
|
|
||||||
|
|
||||||
expect(isSessionActive({ ...idleStatus, isStreaming: true })).toBe(true);
|
expect(isSessionActive({ ...idleStatus, isStreaming: true })).toBe(true);
|
||||||
expect(sessionActivityLabel({ ...idleStatus, isStreaming: true })).toBe("streaming");
|
|
||||||
|
|
||||||
expect(isSessionActive({ ...idleStatus, pendingMessageCount: 2 })).toBe(true);
|
expect(isSessionActive({ ...idleStatus, pendingMessageCount: 2 })).toBe(true);
|
||||||
expect(sessionActivityLabel({ ...idleStatus, pendingMessageCount: 2 })).toBe("2 pending");
|
|
||||||
|
|
||||||
expect(sessionActivityLabel(idleStatus, { sessionId: "s1", phase: "active", label: "running tool", detail: "read", at: "now" })).toBe("running tool: read");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("detects workspace activity presence without exposing details", () => {
|
it("detects workspace activity presence without exposing details", () => {
|
||||||
|
|||||||
@@ -8,16 +8,6 @@ export function isSessionActive(status?: SessionStatus, activity?: SessionActivi
|
|||||||
|| (status?.pendingMessageCount ?? 0) > 0;
|
|| (status?.pendingMessageCount ?? 0) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sessionActivityLabel(status?: SessionStatus, activity?: SessionActivity): string | undefined {
|
|
||||||
if (activity?.phase === "active") return activity.detail !== undefined && activity.detail !== "" ? `${activity.label}: ${activity.detail}` : activity.label;
|
|
||||||
if (status === undefined) return undefined;
|
|
||||||
if (status.isCompacting) return "compacting";
|
|
||||||
if (status.isBashRunning) return "bash";
|
|
||||||
if (status.isStreaming) return "streaming";
|
|
||||||
if (status.pendingMessageCount > 0) return `${String(status.pendingMessageCount)} pending`;
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isWorkspaceActivityActive(activity: WorkspaceActivity | undefined): boolean {
|
export function isWorkspaceActivityActive(activity: WorkspaceActivity | undefined): boolean {
|
||||||
return activity !== undefined && (activity.hasSessionActivity || activity.hasTerminalActivity);
|
return activity !== undefined && (activity.hasSessionActivity || activity.hasTerminalActivity);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user