Files
kandev/apps/web/e2e/tests/chat/mobile-tool-execute-output.spec.ts
T
2026-08-07 18:14:32 +01:00

149 lines
5.4 KiB
TypeScript

import { test, expect } from "../../fixtures/test-base";
import { SessionPage } from "../../pages/session-page";
async function expectInsideViewport(
testPage: import("@playwright/test").Page,
locator: import("@playwright/test").Locator,
) {
const viewportWidth = testPage.viewportSize()?.width ?? 0;
const box = await locator.boundingBox();
expect(box).not.toBeNull();
expect(box!.x).toBeGreaterThanOrEqual(0);
expect(box!.x + box!.width).toBeLessThanOrEqual(viewportWidth + 1);
}
test.describe("mobile: shell command output", () => {
test("does not show an output disclosure for a command with no output", async ({
testPage,
apiClient,
seedData,
}) => {
const task = await apiClient.createTask(seedData.workspaceId, "Mobile Empty Command Output", {
description: "seeded mobile shell command without output",
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
agent_profile_id: seedData.agentProfileId,
repository_ids: [seedData.repositoryId],
});
const { session_id: sessionId } = await apiClient.seedTaskSession(task.id, {
state: "IDLE",
agentProfileId: seedData.agentProfileId,
});
const command = "true";
await apiClient.seedSessionMessage(sessionId, {
type: "tool_execute",
content: command,
metadata: {
status: "complete",
tool_call_id: "tool-mobile-empty-output",
normalized: {
shell_exec: {
command,
work_dir: "/workspace",
output: { exit_code: 0 },
},
},
},
});
const shellOutputRequests: string[] = [];
testPage.on("request", (request) => {
if (request.url().endsWith("/shell-output")) shellOutputRequests.push(request.url());
});
await testPage.goto(`/t/${task.id}`);
const session = new SessionPage(testPage);
await session.waitForLoad();
const chat = session.activeChat();
await expect(chat.getByTestId("tool-execute-command").filter({ hasText: command })).toBeVisible(
{ timeout: 15_000 },
);
await expect(chat.getByLabel("Command succeeded")).toBeVisible();
await expect(chat.getByRole("button", { name: "Show command output" })).toHaveCount(0);
await expect(chat.getByTestId("tool-execute-output")).toHaveCount(0);
expect(shellOutputRequests).toHaveLength(0);
});
test("keeps a long command and lazy output inside the viewport", async ({
testPage,
apiClient,
seedData,
}) => {
const task = await apiClient.createTask(seedData.workspaceId, "Mobile Command Output", {
description: "seeded mobile shell command output",
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
agent_profile_id: seedData.agentProfileId,
repository_ids: [seedData.repositoryId],
});
const { session_id: sessionId } = await apiClient.seedTaskSession(task.id, {
state: "IDLE",
agentProfileId: seedData.agentProfileId,
});
const command = `printf ${"mobile-command-segment-".repeat(28)}`;
const outputPrefix = "latest output";
await apiClient.seedSessionMessage(sessionId, {
type: "tool_execute",
content: command,
metadata: {
status: "error",
tool_call_id: "tool-mobile-output",
normalized: {
shell_exec: {
command,
work_dir: "/workspace/a/very/long/path/that/must/not/expand/the/page",
output: {
exit_code: 9,
stdout: `${outputPrefix} ${"unbroken-output-".repeat(80)}`,
truncated: true,
},
},
},
},
});
const shellOutputRequests: string[] = [];
testPage.on("request", (request) => {
if (request.url().endsWith("/shell-output")) shellOutputRequests.push(request.url());
});
await testPage.goto(`/t/${task.id}`);
const session = new SessionPage(testPage);
await session.waitForLoad();
const chat = session.activeChat();
const commandRow = chat.getByTestId("tool-execute-command").filter({ hasText: command });
const disclosure = chat.getByRole("button", { name: "Show command output" });
await expect(commandRow).toBeVisible({ timeout: 15_000 });
await expect(commandRow).toHaveText(command);
await expect(chat.getByTestId("tool-execute-output")).toHaveCount(0);
await expect(chat.getByText(outputPrefix)).toHaveCount(0);
expect(shellOutputRequests).toHaveLength(0);
expect(await commandRow.evaluate((element) => element.scrollWidth <= element.clientWidth)).toBe(
true,
);
await expectInsideViewport(testPage, commandRow);
await expectInsideViewport(testPage, disclosure);
const responsePromise = testPage.waitForResponse(
(response) => response.url().endsWith("/shell-output") && response.status() === 200,
);
await disclosure.click();
await responsePromise;
const output = chat.getByTestId("tool-execute-output");
const exitStatus = chat.getByText("Exit code 9");
await expect(output).toContainText(outputPrefix);
await expect(chat.getByText("Output truncated")).toBeVisible();
await expect(exitStatus).toBeVisible();
expect(shellOutputRequests).toHaveLength(1);
await expectInsideViewport(testPage, output);
await expectInsideViewport(testPage, exitStatus);
expect(
await testPage.evaluate(
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
),
).toBe(true);
});
});