mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 17:30:31 +00:00
Fix no-non-null-assertion lint
This commit is contained in:
parent
0614756acb
commit
c12cf90c76
5 changed files with 55 additions and 37 deletions
|
|
@ -11,6 +11,11 @@ import {
|
|||
import type { ServerRequest } from "../../src/generated/app-server/ServerRequest";
|
||||
import type { CommandExecutionApprovalDecision } from "../../src/generated/app-server/v2/CommandExecutionApprovalDecision";
|
||||
|
||||
function expectPresent<T>(value: T | null | undefined): T {
|
||||
if (value === null || value === undefined) throw new Error("Expected value to be present");
|
||||
return value;
|
||||
}
|
||||
|
||||
describe("approval model", () => {
|
||||
it("classifies command approvals and builds v2 decisions", () => {
|
||||
const request: ServerRequest = {
|
||||
|
|
@ -29,12 +34,11 @@ describe("approval model", () => {
|
|||
proposedNetworkPolicyAmendments: [],
|
||||
},
|
||||
};
|
||||
const approval = toPendingApproval(request);
|
||||
const approval = expectPresent(toPendingApproval(request));
|
||||
|
||||
expect(approval).not.toBeNull();
|
||||
expect(approvalTitle(approval!)).toBe("Command approval");
|
||||
expect(approvalSummary(approval!)).toBe("npm run build");
|
||||
expect(approvalResponse(approval!, "accept-session")).toEqual({ decision: "acceptForSession" });
|
||||
expect(approvalTitle(approval)).toBe("Command approval");
|
||||
expect(approvalSummary(approval)).toBe("npm run build");
|
||||
expect(approvalResponse(approval, "accept-session")).toEqual({ decision: "acceptForSession" });
|
||||
});
|
||||
|
||||
it("builds permission grants only for accept actions", () => {
|
||||
|
|
@ -51,7 +55,7 @@ describe("approval model", () => {
|
|||
permissions: { network: { enabled: true }, fileSystem: null },
|
||||
},
|
||||
};
|
||||
const approval = toPendingApproval(request)!;
|
||||
const approval = expectPresent(toPendingApproval(request));
|
||||
|
||||
expect(approvalResponse(approval, "decline")).toEqual({ permissions: {}, scope: "turn" });
|
||||
expect(approvalResponse(approval, "accept")).toEqual({
|
||||
|
|
@ -82,16 +86,16 @@ describe("approval model", () => {
|
|||
availableDecisions: [networkDecision, "decline"],
|
||||
},
|
||||
};
|
||||
const approval = toPendingApproval(request)!;
|
||||
const approval = expectPresent(toPendingApproval(request));
|
||||
const options = approvalActionOptions(approval);
|
||||
|
||||
expect(options.map((option) => option.label)).toEqual(["Allow network rule", "Deny"]);
|
||||
expect(approvalResponse(approval, options[0]!.action)).toEqual({ decision: networkDecision });
|
||||
expect(approvalResponse(approval, options[1]!.action)).toEqual({ decision: "decline" });
|
||||
expect(approvalResponse(approval, expectPresent(options[0]).action)).toEqual({ decision: networkDecision });
|
||||
expect(approvalResponse(approval, expectPresent(options[1]).action)).toEqual({ decision: "decline" });
|
||||
});
|
||||
|
||||
it("shows approval reasons first in pending request summaries", () => {
|
||||
const command = toPendingApproval({
|
||||
const command = expectPresent(toPendingApproval({
|
||||
id: 20,
|
||||
method: "item/commandExecution/requestApproval",
|
||||
params: {
|
||||
|
|
@ -106,8 +110,8 @@ describe("approval model", () => {
|
|||
proposedExecpolicyAmendment: null,
|
||||
proposedNetworkPolicyAmendments: [],
|
||||
},
|
||||
})!;
|
||||
const fileChange = toPendingApproval({
|
||||
}));
|
||||
const fileChange = expectPresent(toPendingApproval({
|
||||
id: 21,
|
||||
method: "item/fileChange/requestApproval",
|
||||
params: {
|
||||
|
|
@ -118,8 +122,8 @@ describe("approval model", () => {
|
|||
reason: "Write outside workspace",
|
||||
grantRoot: "/tmp/project",
|
||||
},
|
||||
})!;
|
||||
const permissions = toPendingApproval({
|
||||
}));
|
||||
const permissions = expectPresent(toPendingApproval({
|
||||
id: 22,
|
||||
method: "item/permissions/requestApproval",
|
||||
params: {
|
||||
|
|
@ -131,7 +135,7 @@ describe("approval model", () => {
|
|||
reason: "Need network",
|
||||
permissions: { network: { enabled: true }, fileSystem: null },
|
||||
},
|
||||
})!;
|
||||
}));
|
||||
|
||||
expect(approvalSummary(command)).toBe("Needs unsandboxed access\nnpm run build");
|
||||
expect(approvalSummary(fileChange)).toBe("Write outside workspace\ngrant root: /tmp/project");
|
||||
|
|
@ -139,7 +143,7 @@ describe("approval model", () => {
|
|||
});
|
||||
|
||||
it("keeps approval details semantic and omits raw payloads", () => {
|
||||
const approval = toPendingApproval({
|
||||
const approval = expectPresent(toPendingApproval({
|
||||
id: 23,
|
||||
method: "item/permissions/requestApproval",
|
||||
params: {
|
||||
|
|
@ -151,7 +155,7 @@ describe("approval model", () => {
|
|||
reason: "Need network",
|
||||
permissions: { network: { enabled: true }, fileSystem: null },
|
||||
},
|
||||
})!;
|
||||
}));
|
||||
|
||||
expect(approvalDetails(approval)).toEqual([
|
||||
{ key: "reason", value: "Need network" },
|
||||
|
|
@ -161,7 +165,7 @@ describe("approval model", () => {
|
|||
});
|
||||
|
||||
it("omits empty approval detail rows", () => {
|
||||
const approval = toPendingApproval({
|
||||
const approval = expectPresent(toPendingApproval({
|
||||
id: 24,
|
||||
method: "item/commandExecution/requestApproval",
|
||||
params: {
|
||||
|
|
@ -176,7 +180,7 @@ describe("approval model", () => {
|
|||
proposedExecpolicyAmendment: null,
|
||||
proposedNetworkPolicyAmendments: [],
|
||||
},
|
||||
})!;
|
||||
}));
|
||||
|
||||
expect(approvalDetails(approval)).toEqual([
|
||||
{ key: "command", value: "npm test" },
|
||||
|
|
@ -225,7 +229,7 @@ describe("approval model", () => {
|
|||
];
|
||||
|
||||
for (const { request, acceptSession, cancel } of requests) {
|
||||
const approval = toPendingApproval(request)!;
|
||||
const approval = expectPresent(toPendingApproval(request));
|
||||
expect(approvalResponse(approval, "accept-session")).toEqual(acceptSession);
|
||||
expect(approvalResponse(approval, "cancel")).toEqual(cancel);
|
||||
expect(approvalResponse(approval, "decline")).toEqual({ decision: "decline" });
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ function controllerForState(
|
|||
});
|
||||
}
|
||||
|
||||
function expectPresent<T>(value: T | null | undefined): T {
|
||||
if (value === null || value === undefined) throw new Error("Expected value to be present");
|
||||
return value;
|
||||
}
|
||||
|
||||
describe("PanelController", () => {
|
||||
describe("active turn routing", () => {
|
||||
it("ignores item notifications for a different active thread", () => {
|
||||
|
|
@ -624,8 +629,8 @@ describe("PanelController", () => {
|
|||
const respondToServerRequest = vi.fn(() => true);
|
||||
const controller = controllerForState(state, { respondToServerRequest });
|
||||
|
||||
controller.handleServerRequest(supportedApprovalRequests()[2]!);
|
||||
controller.resolveApproval(state.approvals[0]!, "accept-session");
|
||||
controller.handleServerRequest(expectPresent(supportedApprovalRequests()[2]));
|
||||
controller.resolveApproval(expectPresent(state.approvals[0]), "accept-session");
|
||||
|
||||
expect(respondToServerRequest).toHaveBeenCalledWith(12, {
|
||||
scope: "session",
|
||||
|
|
@ -763,7 +768,7 @@ describe("PanelController", () => {
|
|||
requestId: 50,
|
||||
method: "item/commandExecution/requestApproval",
|
||||
params: {
|
||||
...supportedApprovalRequests()[0]!.params,
|
||||
...expectPresent(supportedApprovalRequests()[0]).params,
|
||||
threadId: "thread-active",
|
||||
} as Extract<ServerRequest, { method: "item/commandExecution/requestApproval" }>["params"],
|
||||
},
|
||||
|
|
@ -834,7 +839,7 @@ describe("PanelController", () => {
|
|||
{
|
||||
requestId: 10,
|
||||
method: "item/commandExecution/requestApproval",
|
||||
params: supportedApprovalRequests()[0]!.params as Extract<
|
||||
params: expectPresent(supportedApprovalRequests()[0]).params as Extract<
|
||||
ServerRequest,
|
||||
{ method: "item/commandExecution/requestApproval" }
|
||||
>["params"],
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ describe("settings tab", () => {
|
|||
vi.stubGlobal("HTMLDivElement", dom.window.HTMLDivElement);
|
||||
vi.stubGlobal("HTMLInputElement", dom.window.HTMLInputElement);
|
||||
vi.stubGlobal("HTMLSelectElement", dom.window.HTMLSelectElement);
|
||||
vi.stubGlobal("Event", dom.window.Event);
|
||||
withAppServerSessionMock.mockReset();
|
||||
notices.length = 0;
|
||||
});
|
||||
|
|
@ -110,7 +111,7 @@ describe("settings tab", () => {
|
|||
if (!shortcut) throw new Error("Missing send shortcut dropdown");
|
||||
|
||||
shortcut.value = "mod-enter";
|
||||
shortcut.dispatchEvent(new shortcut.ownerDocument.defaultView!.Event("change"));
|
||||
shortcut.dispatchEvent(new Event("change"));
|
||||
await flushPromises();
|
||||
|
||||
expect(saveSettings).toHaveBeenCalledOnce();
|
||||
|
|
@ -130,13 +131,13 @@ describe("settings tab", () => {
|
|||
if (!toggle || !folder || !filename || !tags) throw new Error("Missing archive export controls");
|
||||
|
||||
toggle.checked = true;
|
||||
toggle.dispatchEvent(new toggle.ownerDocument.defaultView!.Event("change"));
|
||||
toggle.dispatchEvent(new Event("change"));
|
||||
folder.value = "Saved Threads";
|
||||
folder.dispatchEvent(new folder.ownerDocument.defaultView!.Event("change"));
|
||||
folder.dispatchEvent(new Event("change"));
|
||||
filename.value = "{{date}} {{title}}.md";
|
||||
filename.dispatchEvent(new filename.ownerDocument.defaultView!.Event("change"));
|
||||
filename.dispatchEvent(new Event("change"));
|
||||
tags.value = "codex, archive";
|
||||
tags.dispatchEvent(new tags.ownerDocument.defaultView!.Event("change"));
|
||||
tags.dispatchEvent(new Event("change"));
|
||||
await flushPromises();
|
||||
|
||||
expect(saveSettings).toHaveBeenCalledTimes(4);
|
||||
|
|
|
|||
|
|
@ -1725,9 +1725,10 @@ describe("toolbar renderer decisions", () => {
|
|||
expect(startRenameThread).toHaveBeenCalledWith("thread");
|
||||
|
||||
const input = parent.querySelector<HTMLInputElement>(".codex-panel__thread-rename-input");
|
||||
expect(input?.value).toBe("Draft title");
|
||||
input!.value = "New title";
|
||||
input!.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
if (!input) throw new Error("Missing thread rename input");
|
||||
expect(input.value).toBe("Draft title");
|
||||
input.value = "New title";
|
||||
input.dispatchEvent(new Event("input", { bubbles: true }));
|
||||
expect(updateRenameDraft).toHaveBeenCalledWith("editing", "New title");
|
||||
|
||||
parent.querySelector<HTMLButtonElement>('[aria-label="Save thread name"]')?.click();
|
||||
|
|
@ -1877,7 +1878,9 @@ describe("pending request renderer decisions", () => {
|
|||
|
||||
const buttons = [...parent.querySelectorAll<HTMLButtonElement>(".codex-panel__pending-request-button")];
|
||||
expect(buttons.map((button) => button.textContent)).toEqual(["Allow network rule", "Deny"]);
|
||||
buttons[0]!.click();
|
||||
const allowButton = buttons[0];
|
||||
if (!allowButton) throw new Error("Missing allow button");
|
||||
allowButton.click();
|
||||
expect(resolveApproval).toHaveBeenCalledWith(approval, {
|
||||
kind: "command-decision",
|
||||
decision: { applyNetworkPolicyAmendment: { network_policy_amendment: { host: "registry.npmjs.org", action: "allow" } } },
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import { questionDefaultAnswer, toPendingUserInput, userInputResponse } from "..
|
|||
import { pendingRequestsSignature } from "../../src/panel/request-state";
|
||||
import type { ServerRequest } from "../../src/generated/app-server/ServerRequest";
|
||||
|
||||
function expectPresent<T>(value: T | null | undefined): T {
|
||||
if (value === null || value === undefined) throw new Error("Expected value to be present");
|
||||
return value;
|
||||
}
|
||||
|
||||
describe("user input model", () => {
|
||||
it("classifies requestUserInput and builds answers", () => {
|
||||
const request: ServerRequest = {
|
||||
|
|
@ -26,16 +31,16 @@ describe("user input model", () => {
|
|||
},
|
||||
};
|
||||
|
||||
const input = toPendingUserInput(request);
|
||||
const input = expectPresent(toPendingUserInput(request));
|
||||
expect(input).toMatchObject({ requestId: 7, method: "item/tool/requestUserInput" });
|
||||
expect(questionDefaultAnswer(request.params.questions[0])).toBe("Recommended");
|
||||
expect(userInputResponse(input!, { direction: "Recommended" })).toEqual({
|
||||
expect(userInputResponse(input, { direction: "Recommended" })).toEqual({
|
||||
answers: { direction: { answers: ["Recommended"] } },
|
||||
});
|
||||
});
|
||||
|
||||
it("signs pending request content and drafts deterministically", () => {
|
||||
const input = toPendingUserInput({
|
||||
const input = expectPresent(toPendingUserInput({
|
||||
id: 7,
|
||||
method: "item/tool/requestUserInput",
|
||||
params: {
|
||||
|
|
@ -53,7 +58,7 @@ describe("user input model", () => {
|
|||
},
|
||||
],
|
||||
},
|
||||
})!;
|
||||
}));
|
||||
const drafts = new Map([
|
||||
["z", "last"],
|
||||
["a", "first"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue