Files
kandev/apps/web/e2e/tests/settings/agent-runtime-update.spec.ts
T
2026-08-01 11:18:37 +01:00

183 lines
7.5 KiB
TypeScript

import { test, expect } from "../../fixtures/test-base";
import { installRuntimeUpdateFixture, updateJob } from "./agent-runtime-update-helpers";
test.describe("managed agent runtime updates", () => {
test("previews, approves, and streams an update without putting details on the card", async ({
testPage,
prCapture,
}) => {
const runtime = await installRuntimeUpdateFixture(testPage);
let documentNavigations = 0;
testPage.on("framenavigated", (frame) => {
if (frame === testPage.mainFrame()) documentNavigations += 1;
});
await testPage.goto("/settings/agents");
const navigationsAfterLoad = documentNavigations;
const trigger = testPage.getByTestId(`agent-update-trigger-${runtime.agentName}`);
await expect(trigger).toBeVisible();
await expect(testPage.getByTestId(`agent-update-control-${runtime.agentName}`)).toHaveCount(0);
const profileAction = testPage.getByRole("link", { name: "Setup Profile" });
const [profileActionBox, triggerBox] = await Promise.all([
profileAction.boundingBox(),
trigger.boundingBox(),
]);
expect(profileActionBox).not.toBeNull();
expect(triggerBox).not.toBeNull();
expect(Math.abs(profileActionBox!.y - triggerBox!.y)).toBeLessThanOrEqual(8);
expect(triggerBox!.x).toBeGreaterThan(profileActionBox!.x);
expect(profileActionBox!.height).toBe(28);
expect(triggerBox!.height).toBe(profileActionBox!.height);
await trigger.click();
const dialog = testPage.getByTestId(`agent-update-dialog-${runtime.agentName}`);
await expect(dialog).toBeVisible();
await expect(dialog).toContainText("0.62.0 → 0.63.0");
await expect(dialog).toContainText(
'npm exec --yes --prefer-online --package=@agentclientprotocol/claude-agent-acp -- node -e ""',
);
await expect(dialog).toContainText("Active sessions keep running");
const viewport = testPage.viewportSize();
expect(viewport).not.toBeNull();
await expect
.poll(() => dialog.evaluate((element) => element.getBoundingClientRect().height))
.toBeLessThan(viewport!.height * 0.6);
expect(runtime.previewCount()).toBe(1);
expect(runtime.postCount()).toBe(0);
await prCapture.screenshot("desktop-update-preview", {
caption: "Desktop update preview before approval",
});
await testPage.getByTestId(`agent-update-confirm-${runtime.agentName}`).click();
expect(runtime.postCount()).toBe(1);
await runtime.emitUpdate(updateJob());
await runtime.emitOutput("Installed @agentclientprotocol/claude-agent-acp@0.63.0\n");
await expect(dialog.getByTestId(`agent-update-phase-${runtime.agentName}`)).toContainText(
"Updating runtime",
);
await expect(dialog.getByTestId(`agent-update-log-${runtime.agentName}`)).toContainText(
"Installed @agentclientprotocol/claude-agent-acp@0.63.0",
);
await runtime.emitUpdate(
updateJob({
status: "succeeded",
output: "Installed @agentclientprotocol/claude-agent-acp@0.63.0\n",
finished_at: "2026-07-26T12:01:00.000Z",
}),
);
await runtime.emitCatalogue([
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6" },
{ id: "claude-opus-5", name: "Claude Opus 5" },
]);
await expect(testPage.getByText("Claude refreshed", { exact: true })).toBeVisible();
await expect(dialog.getByTestId(`agent-update-result-${runtime.agentName}`)).toContainText(
"Runtime updated successfully",
);
expect(documentNavigations).toBe(navigationsAfterLoad);
await testPage.reload();
await expect(trigger).toBeVisible();
await expect(testPage.getByTestId(`agent-update-dialog-${runtime.agentName}`)).toHaveCount(0);
await expect(testPage.getByTestId(`agent-update-result-${runtime.agentName}`)).toHaveCount(0);
});
test("requires a current runtime version before approval", async ({ testPage }) => {
const runtime = await installRuntimeUpdateFixture(testPage, {
previewResponse: {
agent_name: "claude-acp",
package: "@agentclientprotocol/claude-agent-acp",
current_version: "",
target_version: "0.63.0",
command: ["npm", "exec"],
command_string: "npm exec",
},
});
await testPage.goto("/settings/agents");
await testPage.getByTestId(`agent-update-trigger-${runtime.agentName}`).click();
const dialog = testPage.getByTestId(`agent-update-dialog-${runtime.agentName}`);
await expect(dialog).toContainText("Unknown → 0.63.0");
await expect(testPage.getByTestId(`agent-update-confirm-${runtime.agentName}`)).toBeDisabled();
expect(runtime.postCount()).toBe(0);
});
test("shows an up-to-date preview and disables approval when versions match", async ({
testPage,
prCapture,
}) => {
const runtime = await installRuntimeUpdateFixture(testPage, {
previewResponse: {
agent_name: "claude-acp",
package: "@agentclientprotocol/claude-agent-acp",
current_version: "0.64.0",
target_version: "0.64.0",
command: ["npm", "exec"],
command_string: "npm exec",
},
});
await testPage.goto("/settings/agents");
await testPage.getByTestId(`agent-update-trigger-${runtime.agentName}`).click();
const dialog = testPage.getByTestId(`agent-update-dialog-${runtime.agentName}`);
await expect(dialog.getByText("0.64.0", { exact: true })).toBeVisible();
await expect(dialog.getByText("Up to date", { exact: true })).toBeVisible();
await expect(dialog).not.toContainText("0.64.0 → 0.64.0");
await expect(testPage.getByTestId(`agent-update-confirm-${runtime.agentName}`)).toBeDisabled();
expect(runtime.postCount()).toBe(0);
await prCapture.screenshot("desktop-update-up-to-date", {
caption: "Desktop update preview when the managed runtime is up to date",
});
});
test("uses the job versions for a stale retry decision", async ({ testPage }) => {
const runtime = await installRuntimeUpdateFixture(testPage, {
postResponse: updateJob({
status: "failed",
current_version: "0.63.0",
target_version: "0.63.0",
error: "The package registry is unavailable",
finished_at: "2026-07-26T12:01:00.000Z",
}),
});
await testPage.goto("/settings/agents");
await testPage.getByTestId(`agent-update-trigger-${runtime.agentName}`).click();
const dialog = testPage.getByTestId(`agent-update-dialog-${runtime.agentName}`);
const retryUpdate = testPage.getByTestId(`agent-update-confirm-${runtime.agentName}`);
await retryUpdate.click();
await expect(dialog.getByText("0.63.0", { exact: true })).toBeVisible();
await expect(dialog.getByText("Up to date", { exact: true })).toBeVisible();
await expect(retryUpdate).toHaveText("Retry update");
await expect(retryUpdate).toBeDisabled();
expect(runtime.postCount()).toBe(1);
});
test("retries an update after its job fails", async ({ testPage }) => {
const runtime = await installRuntimeUpdateFixture(testPage);
await testPage.goto("/settings/agents");
await testPage.getByTestId(`agent-update-trigger-${runtime.agentName}`).click();
const retryUpdate = testPage.getByTestId(`agent-update-confirm-${runtime.agentName}`);
await retryUpdate.click();
expect(runtime.postCount()).toBe(1);
await runtime.emitUpdate(
updateJob({
status: "failed",
error: "The package registry is unavailable",
finished_at: "2026-07-26T12:01:00.000Z",
}),
);
await expect(retryUpdate).toHaveText("Retry update");
await expect(retryUpdate).toBeEnabled();
await retryUpdate.click();
expect(runtime.postCount()).toBe(2);
});
});