mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 07:33:18 +00:00
* fix(app): show a lone tool call as itself and group from the second call A run of one tool call or thought renders as the call. When a second call arrives the row becomes a group that keeps the first call's id, so the row key stays put. Tool call and group open state move into a per-agent store: opening a lone call also opens its future group without notifying the timeline, so a click re-renders only that tool call. * test(app): cover lone tool calls turning into groups Desktop journeys check that an opened lone call keeps its details open inside the new group, that clicking it renders no timeline component, and that the group header lands where the call was. Compact covers the call sheet and a closed group.
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import type { Locator } from "@playwright/test";
|
|
import { test, expect } from "../support/fixtures";
|
|
import { gateSecondToolCall } from "../support/helpers/tool-call-gate";
|
|
import { expectComposerVisible } from "../support/helpers/composer";
|
|
import { openAgentRoute, seedMockAgentWorkspace } from "../support/helpers/mock-agent";
|
|
|
|
interface ShimmerEvidence {
|
|
animationDuration: string;
|
|
endPx: number;
|
|
label: string;
|
|
renderedWidth: number;
|
|
startPx: number;
|
|
}
|
|
|
|
async function readShimmerEvidence(locator: Locator): Promise<ShimmerEvidence> {
|
|
return locator.evaluate((root) => {
|
|
const shimmer = Array.from(root.querySelectorAll<HTMLElement>("*")).find((element) =>
|
|
getComputedStyle(element).animationName.includes("paseo-toolcall-shimmer"),
|
|
);
|
|
if (!shimmer) {
|
|
throw new Error("Expected a running shimmer inside the badge");
|
|
}
|
|
const style = getComputedStyle(shimmer);
|
|
return {
|
|
animationDuration: style.animationDuration,
|
|
endPx: Number.parseFloat(style.getPropertyValue("--paseo-shimmer-end")),
|
|
label: shimmer.textContent ?? "",
|
|
renderedWidth: shimmer.getBoundingClientRect().width,
|
|
startPx: Number.parseFloat(style.getPropertyValue("--paseo-shimmer-start")),
|
|
};
|
|
});
|
|
}
|
|
|
|
test("measures an overview heading that becomes loading after its idle mount", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.setTimeout(120_000);
|
|
await page.addInitScript(() => {
|
|
localStorage.setItem(
|
|
"@paseo:app-settings",
|
|
JSON.stringify({ chatDisplay: "collapseToolCalls" }),
|
|
);
|
|
});
|
|
const agent = await seedMockAgentWorkspace({
|
|
repoPrefix: "tool-call-shimmer-",
|
|
title: "Tool-call shimmer",
|
|
model: "ten-second-stream",
|
|
});
|
|
|
|
try {
|
|
const gate = await gateSecondToolCall(page, agent.agentId);
|
|
await openAgentRoute(page, {
|
|
workspaceId: agent.workspaceId,
|
|
agentId: agent.agentId,
|
|
});
|
|
await expectComposerVisible(page);
|
|
await agent.client.sendAgentMessage(agent.agentId, "Prove the overview shimmer lifecycle.");
|
|
|
|
await gate.waitForFirstCompleted();
|
|
const group = page.getByTestId("tool-call-group").first();
|
|
await expect(group).toBeVisible();
|
|
const idleGroupHandle = await group.elementHandle();
|
|
if (!idleGroupHandle) {
|
|
throw new Error("Expected the idle tool-call group to be mounted");
|
|
}
|
|
await expect(group.locator('[style*="paseo-toolcall-shimmer"]')).toHaveCount(0);
|
|
|
|
await gate.waitForSecondRunning();
|
|
gate.releaseSecondRunning();
|
|
await expect(group.locator('[style*="paseo-toolcall-shimmer"]')).not.toHaveCount(0);
|
|
const sameGroupNode = await group.evaluate(
|
|
(node, previous) => node === previous,
|
|
idleGroupHandle,
|
|
);
|
|
|
|
await group.click();
|
|
const runningChild = page.getByTestId("tool-call-badge").last();
|
|
await expect(runningChild).toBeVisible();
|
|
const header = await readShimmerEvidence(group);
|
|
const child = await readShimmerEvidence(runningChild);
|
|
const evidence = { sameGroupNode, header, child };
|
|
await testInfo.attach("tool-call-shimmer-evidence", {
|
|
body: JSON.stringify(evidence, null, 2),
|
|
contentType: "application/json",
|
|
});
|
|
|
|
expect(sameGroupNode).toBe(true);
|
|
expect(child.endPx).toBeGreaterThan(0);
|
|
expect(
|
|
header.endPx,
|
|
`The retained header rendered ${header.renderedWidth}px wide but its shimmer still ends at ${header.endPx}px`,
|
|
).toBeGreaterThan(0);
|
|
} finally {
|
|
await agent.cleanup();
|
|
}
|
|
});
|