fix(test): stabilize tool call shimmer lifecycle gate in browser e2e
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import type { Locator, Page } from "@playwright/test";
|
import type { Locator, Page } from "@playwright/test";
|
||||||
import { test, expect } from "../support/fixtures";
|
import { test, expect } from "../support/fixtures";
|
||||||
import { daemonWsRoutePattern } from "../support/helpers/daemon-port";
|
import { daemonWsRoutePattern } from "../support/helpers/daemon-port";
|
||||||
|
import { expectComposerVisible } from "../support/helpers/composer";
|
||||||
import { openAgentRoute, seedMockAgentWorkspace } from "../support/helpers/mock-agent";
|
import { openAgentRoute, seedMockAgentWorkspace } from "../support/helpers/mock-agent";
|
||||||
|
|
||||||
type WebSocketMessage = string | Buffer;
|
type WebSocketMessage = string | Buffer;
|
||||||
@@ -64,9 +65,8 @@ async function gateSecondToolCall(page: Page, agentId: string) {
|
|||||||
let firstCallId: string | null = null;
|
let firstCallId: string | null = null;
|
||||||
let secondCallId: string | null = null;
|
let secondCallId: string | null = null;
|
||||||
let secondRunningMessage: WebSocketMessage | null = null;
|
let secondRunningMessage: WebSocketMessage | null = null;
|
||||||
|
let pauseAfterSecondRunning = false;
|
||||||
let releaseSecondRequested = false;
|
let releaseSecondRequested = false;
|
||||||
let pauseServerMessages = false;
|
|
||||||
let secondRunningForwarded = false;
|
|
||||||
let forwardSecondRunning: (() => void) | null = null;
|
let forwardSecondRunning: (() => void) | null = null;
|
||||||
let resolveFirstCompleted!: () => void;
|
let resolveFirstCompleted!: () => void;
|
||||||
let resolveSecondRunning!: () => void;
|
let resolveSecondRunning!: () => void;
|
||||||
@@ -80,15 +80,14 @@ async function gateSecondToolCall(page: Page, agentId: string) {
|
|||||||
await page.routeWebSocket(daemonWsRoutePattern(), (ws) => {
|
await page.routeWebSocket(daemonWsRoutePattern(), (ws) => {
|
||||||
const server = ws.connectToServer();
|
const server = ws.connectToServer();
|
||||||
forwardSecondRunning = () => {
|
forwardSecondRunning = () => {
|
||||||
if (!secondRunningMessage || secondRunningForwarded) {
|
if (secondRunningMessage) {
|
||||||
return;
|
ws.send(secondRunningMessage);
|
||||||
|
secondRunningMessage = null;
|
||||||
}
|
}
|
||||||
ws.send(secondRunningMessage);
|
|
||||||
secondRunningForwarded = true;
|
|
||||||
};
|
};
|
||||||
ws.onMessage((message) => server.send(message));
|
ws.onMessage((message) => server.send(message));
|
||||||
server.onMessage((message) => {
|
server.onMessage((message) => {
|
||||||
if (pauseServerMessages) {
|
if (pauseAfterSecondRunning) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +96,7 @@ async function gateSecondToolCall(page: Page, agentId: string) {
|
|||||||
ws.send(message);
|
ws.send(message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!firstCallId) {
|
if (!firstCallId) {
|
||||||
firstCallId = toolCall.callId;
|
firstCallId = toolCall.callId;
|
||||||
}
|
}
|
||||||
@@ -109,24 +109,11 @@ async function gateSecondToolCall(page: Page, agentId: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
secondCallId ??= toolCall.callId;
|
secondCallId ??= toolCall.callId;
|
||||||
if (
|
if (toolCall.callId === secondCallId) {
|
||||||
toolCall.callId === secondCallId &&
|
const transformedMessage =
|
||||||
(toolCall.status === "running" || toolCall.status === "executing")
|
toolCall.status === "completed" ? replaceToolCallStatus(message, "running") : message;
|
||||||
) {
|
secondRunningMessage = transformedMessage;
|
||||||
secondRunningMessage = message;
|
pauseAfterSecondRunning = true;
|
||||||
pauseServerMessages = true;
|
|
||||||
resolveSecondRunning();
|
|
||||||
if (releaseSecondRequested) {
|
|
||||||
forwardSecondRunning?.();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (toolCall.callId === secondCallId && toolCall.status === "completed") {
|
|
||||||
// Keep the second real tool call inspectably active. Production providers
|
|
||||||
// send this same status shape while their work remains in flight.
|
|
||||||
secondRunningMessage = replaceToolCallStatus(message, "running");
|
|
||||||
pauseServerMessages = true;
|
|
||||||
resolveSecondRunning();
|
resolveSecondRunning();
|
||||||
if (releaseSecondRequested) {
|
if (releaseSecondRequested) {
|
||||||
forwardSecondRunning?.();
|
forwardSecondRunning?.();
|
||||||
@@ -143,10 +130,7 @@ async function gateSecondToolCall(page: Page, agentId: string) {
|
|||||||
waitForSecondRunning: () => secondRunning,
|
waitForSecondRunning: () => secondRunning,
|
||||||
releaseSecondRunning() {
|
releaseSecondRunning() {
|
||||||
releaseSecondRequested = true;
|
releaseSecondRequested = true;
|
||||||
pauseServerMessages = false;
|
forwardSecondRunning?.();
|
||||||
if (secondRunningMessage) {
|
|
||||||
forwardSecondRunning?.();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -192,10 +176,11 @@ test("measures an overview heading that becomes loading after its idle mount", a
|
|||||||
workspaceId: agent.workspaceId,
|
workspaceId: agent.workspaceId,
|
||||||
agentId: agent.agentId,
|
agentId: agent.agentId,
|
||||||
});
|
});
|
||||||
|
await expectComposerVisible(page);
|
||||||
await agent.client.sendAgentMessage(agent.agentId, "Prove the overview shimmer lifecycle.");
|
await agent.client.sendAgentMessage(agent.agentId, "Prove the overview shimmer lifecycle.");
|
||||||
|
|
||||||
await gate.waitForFirstCompleted();
|
await gate.waitForFirstCompleted();
|
||||||
const group = page.getByTestId("tool-call-group");
|
const group = page.getByTestId("tool-call-group").first();
|
||||||
await expect(group).toBeVisible();
|
await expect(group).toBeVisible();
|
||||||
const idleGroupHandle = await group.elementHandle();
|
const idleGroupHandle = await group.elementHandle();
|
||||||
if (!idleGroupHandle) {
|
if (!idleGroupHandle) {
|
||||||
|
|||||||
Reference in New Issue
Block a user