mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 08:24:46 +00:00
test(creation): adapt handoff gates to combined requests
This commit is contained in:
@@ -9,7 +9,7 @@ import {
|
||||
seedModelProvider,
|
||||
} from "../support/helpers/agent-profiles";
|
||||
import { gotoAppShell } from "../support/helpers/app";
|
||||
import { daemonWsRoutePattern } from "../support/helpers/daemon-port";
|
||||
import { captureWorkspaceAgentRequest } from "../support/helpers/creation";
|
||||
import {
|
||||
openGlobalNewWorkspaceComposer,
|
||||
selectNewWorkspaceProject,
|
||||
@@ -22,53 +22,6 @@ const CREATE_AGENT_PREFERENCES_KEY = "@paseo:create-agent-preferences";
|
||||
const MODELESS_PROVIDER = "modeless-profile-e2e";
|
||||
const MODELESS_MODEL = "pi-profile-model";
|
||||
|
||||
type WebSocketMessage = string | Buffer;
|
||||
|
||||
interface CreateAgentRequestMessage {
|
||||
type: "create_agent_request";
|
||||
config?: {
|
||||
provider?: unknown;
|
||||
modeId?: unknown;
|
||||
featureValues?: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
function getSessionMessage(message: WebSocketMessage): Record<string, unknown> | null {
|
||||
const rawMessage = typeof message === "string" ? message : message.toString("utf8");
|
||||
try {
|
||||
const envelope = JSON.parse(rawMessage) as { type?: unknown; message?: unknown };
|
||||
return envelope.type === "session" && envelope.message && typeof envelope.message === "object"
|
||||
? (envelope.message as Record<string, unknown>)
|
||||
: null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function recordAndBlockCreateAgentRequest(page: Page): Promise<{
|
||||
waitForRequest(): Promise<CreateAgentRequestMessage>;
|
||||
}> {
|
||||
let resolveRequest: ((message: CreateAgentRequestMessage) => void) | null = null;
|
||||
const request = new Promise<CreateAgentRequestMessage>((resolve) => {
|
||||
resolveRequest = resolve;
|
||||
});
|
||||
|
||||
await page.routeWebSocket(daemonWsRoutePattern(), (ws) => {
|
||||
const server = ws.connectToServer();
|
||||
ws.onMessage((message) => {
|
||||
const sessionMessage = getSessionMessage(message);
|
||||
if (sessionMessage?.type === "create_agent_request") {
|
||||
resolveRequest?.(sessionMessage as unknown as CreateAgentRequestMessage);
|
||||
return;
|
||||
}
|
||||
server.send(message);
|
||||
});
|
||||
server.onMessage((message) => ws.send(message));
|
||||
});
|
||||
|
||||
return { waitForRequest: () => request };
|
||||
}
|
||||
|
||||
async function seedPoisonedModelessPreference(page: Page): Promise<void> {
|
||||
await page.addInitScript(
|
||||
({ preferencesKey, provider, model }) => {
|
||||
@@ -143,7 +96,7 @@ test.describe("Agent profiles repair modeless provider preferences", () => {
|
||||
],
|
||||
});
|
||||
await seedPoisonedModelessPreference(page);
|
||||
const createAgentRecorder = await recordAndBlockCreateAgentRequest(page);
|
||||
const createAgentRecorder = await captureWorkspaceAgentRequest(page, { block: true });
|
||||
|
||||
try {
|
||||
await gotoAppShell(page);
|
||||
@@ -206,7 +159,7 @@ test.describe("Agent profiles repair modeless provider preferences", () => {
|
||||
},
|
||||
],
|
||||
});
|
||||
const createAgentRecorder = await recordAndBlockCreateAgentRequest(page);
|
||||
const createAgentRecorder = await captureWorkspaceAgentRequest(page, { block: true });
|
||||
|
||||
try {
|
||||
await gotoAppShell(page);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { expect, test, type Page } from "../support/fixtures";
|
||||
import { expect, type Page } from "../support/fixtures";
|
||||
import { test } from "../support/creation-fixtures";
|
||||
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
||||
import { connectDaemonClient } from "../support/helpers/daemon-client-loader";
|
||||
import type { FormPreferences } from "@/create-agent-preferences/preferences";
|
||||
import { gotoAppShell } from "../support/helpers/app";
|
||||
import { daemonWsRoutePattern } from "../support/helpers/daemon-port";
|
||||
import { captureWorkspaceAgentRequest } from "../support/helpers/creation";
|
||||
import { openAgentRoute } from "../support/helpers/mock-agent";
|
||||
import {
|
||||
openGlobalNewWorkspaceComposer,
|
||||
@@ -17,40 +18,6 @@ import { waitForSidebarHydration } from "../support/helpers/workspace-ui";
|
||||
|
||||
const CREATE_AGENT_PREFERENCES_KEY = "@paseo:create-agent-preferences";
|
||||
|
||||
type WebSocketMessage = string | Buffer;
|
||||
|
||||
interface CreateAgentRequestMessage {
|
||||
type: "create_agent_request";
|
||||
config?: {
|
||||
provider?: unknown;
|
||||
modeId?: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
function parseWebSocketJson(message: WebSocketMessage): unknown {
|
||||
const rawMessage = typeof message === "string" ? message : message.toString("utf8");
|
||||
try {
|
||||
return JSON.parse(rawMessage);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function getSessionMessage(message: WebSocketMessage): Record<string, unknown> | null {
|
||||
const envelope = parseWebSocketJson(message);
|
||||
if (!envelope || typeof envelope !== "object") {
|
||||
return null;
|
||||
}
|
||||
const maybeEnvelope = envelope as { type?: unknown; message?: unknown };
|
||||
if (maybeEnvelope.type !== "session" || !maybeEnvelope.message) {
|
||||
return null;
|
||||
}
|
||||
if (typeof maybeEnvelope.message !== "object") {
|
||||
return null;
|
||||
}
|
||||
return maybeEnvelope.message as Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function seedCodexDefaultPermissionPreferences(page: Page, cwd: string): Promise<string> {
|
||||
const client = await connectDaemonClient<DaemonClient>({
|
||||
clientIdPrefix: "codex-mode-preferences",
|
||||
@@ -142,44 +109,15 @@ async function expectThinkingOptionsFit(page: Page): Promise<void> {
|
||||
await expect(page.getByTestId("combobox-desktop-container")).toHaveCount(0, { timeout: 5_000 });
|
||||
}
|
||||
|
||||
async function recordAndBlockCreateAgentRequests(page: Page): Promise<{
|
||||
waitForCreateAgentRequest(): Promise<CreateAgentRequestMessage>;
|
||||
}> {
|
||||
let resolveRequest: ((message: CreateAgentRequestMessage) => void) | null = null;
|
||||
const createAgentSeen = new Promise<CreateAgentRequestMessage>((resolve) => {
|
||||
resolveRequest = resolve;
|
||||
});
|
||||
|
||||
await page.routeWebSocket(daemonWsRoutePattern(), (ws) => {
|
||||
const server = ws.connectToServer();
|
||||
|
||||
ws.onMessage((message) => {
|
||||
const sessionMessage = getSessionMessage(message);
|
||||
if (sessionMessage?.type === "create_agent_request") {
|
||||
resolveRequest?.(sessionMessage as unknown as CreateAgentRequestMessage);
|
||||
return;
|
||||
}
|
||||
server.send(message);
|
||||
});
|
||||
|
||||
server.onMessage((message) => {
|
||||
ws.send(message);
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
waitForCreateAgentRequest: () => createAgentSeen,
|
||||
};
|
||||
}
|
||||
|
||||
test.describe("New workspace Codex mode preferences", () => {
|
||||
test.describe.configure({ timeout: 240_000 });
|
||||
|
||||
test("keeps Full Access as the global Codex mode after the workspace draft auto-submit handoff", async ({
|
||||
page,
|
||||
startup,
|
||||
}) => {
|
||||
const seeded = await seedWorkspace({ repoPrefix: "codex-mode-preferences-" });
|
||||
const createAgentRecorder = await recordAndBlockCreateAgentRequests(page);
|
||||
const createAgentRecorder = await captureWorkspaceAgentRequest(page, { block: false });
|
||||
try {
|
||||
await seedCodexDefaultPermissionPreferences(page, seeded.repoPath);
|
||||
await gotoAppShell(page);
|
||||
@@ -200,8 +138,12 @@ test.describe("New workspace Codex mode preferences", () => {
|
||||
).toBeVisible();
|
||||
|
||||
await submitNewWorkspacePrompt(page, "Keep Codex full access selected globally.");
|
||||
const createAgentRequest = await createAgentRecorder.waitForCreateAgentRequest();
|
||||
const createAgentRequest = await createAgentRecorder.waitForRequest();
|
||||
|
||||
await expect(page).toHaveURL(/\/workspace\//);
|
||||
await expect(
|
||||
page.getByText("Keep Codex full access selected globally.", { exact: true }).first(),
|
||||
).toBeVisible();
|
||||
expect(createAgentRequest.config).toMatchObject({
|
||||
provider: "codex",
|
||||
modeId: "full-access",
|
||||
@@ -209,6 +151,8 @@ test.describe("New workspace Codex mode preferences", () => {
|
||||
await expect
|
||||
.poll(() => readCodexModePreference(page), { timeout: 10_000 })
|
||||
.toBe("full-access");
|
||||
await startup.fail();
|
||||
await expect(page.getByText(/Creation startup failed for test/).first()).toBeVisible();
|
||||
} finally {
|
||||
await seeded.cleanup();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Page } from "@playwright/test";
|
||||
import { loadSessionMessageReaders } from "./new-workspace";
|
||||
import { daemonWsRoutePattern, wsRoutePatternForPort } from "./daemon-port";
|
||||
|
||||
type WebSocketMessage = string | Buffer;
|
||||
@@ -321,6 +322,7 @@ export async function holdRewindCompletion(
|
||||
export async function delayCreatedAgentInitialTailResponse(
|
||||
page: Page,
|
||||
): Promise<CreatedAgentTimelineGate> {
|
||||
const frames = await loadSessionMessageReaders();
|
||||
let createdAgentId: string | null = null;
|
||||
let releaseRequested = false;
|
||||
let delayedResponseSeen = false;
|
||||
@@ -350,19 +352,24 @@ export async function delayCreatedAgentInitialTailResponse(
|
||||
});
|
||||
|
||||
server.onMessage((message) => {
|
||||
const sessionMessage = getSessionMessage(message);
|
||||
const payload = sessionMessage ? getPayload(sessionMessage) : null;
|
||||
if (sessionMessage?.type === "status" && payload?.status === "agent_created") {
|
||||
const agentId = payload.agentId;
|
||||
if (typeof agentId === "string") {
|
||||
createdAgentId = agentId;
|
||||
resolveCreatedAgent?.(agentId);
|
||||
}
|
||||
const sessionMessage = frames.server(message);
|
||||
let createdId: unknown;
|
||||
if (sessionMessage?.type === "agent.create.response") {
|
||||
createdId = sessionMessage.payload.agent?.id;
|
||||
} else if (
|
||||
sessionMessage?.type === "status" &&
|
||||
sessionMessage.payload.status === "agent_created"
|
||||
) {
|
||||
createdId = sessionMessage.payload.agentId;
|
||||
}
|
||||
if (typeof createdId === "string") {
|
||||
createdAgentId = createdId;
|
||||
resolveCreatedAgent?.(createdId);
|
||||
}
|
||||
|
||||
if (sessionMessage?.type === "fetch_agent_timeline_response") {
|
||||
const agentId = payload?.agentId;
|
||||
const direction = payload?.direction;
|
||||
const agentId = sessionMessage.payload.agentId;
|
||||
const direction = sessionMessage.payload.direction;
|
||||
if (
|
||||
!delayedResponseSeen &&
|
||||
typeof agentId === "string" &&
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { WorkspaceCreateRequest } from "@getpaseo/protocol/messages";
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
import { daemonWsRoutePattern } from "./daemon-port";
|
||||
import { gotoAppShell } from "./app";
|
||||
@@ -19,6 +20,29 @@ import { getServerId } from "./server-id";
|
||||
import { WORKSPACE_DECK_MAX_MOUNTED_WORKSPACES } from "@/screens/workspace/workspace-deck-retention";
|
||||
import type { installDaemonWebSocketGate } from "./daemon-websocket-gate";
|
||||
|
||||
/** Capture the submitted agent options; optionally stop provisioning for wire-only assertions. */
|
||||
export async function captureWorkspaceAgentRequest(page: Page, options: { block: boolean }) {
|
||||
const frames = await loadSessionMessageReaders();
|
||||
type AgentIntent = NonNullable<WorkspaceCreateRequest["agent"]>;
|
||||
let resolveRequest!: (agent: AgentIntent) => void;
|
||||
const request = new Promise<AgentIntent>((resolve) => {
|
||||
resolveRequest = resolve;
|
||||
});
|
||||
await page.routeWebSocket(daemonWsRoutePattern(), (ws) => {
|
||||
const server = ws.connectToServer();
|
||||
ws.onMessage((frame) => {
|
||||
const message = frames.client(frame);
|
||||
if (message?.type === "workspace.create.request" && message.agent) {
|
||||
resolveRequest(message.agent);
|
||||
if (options.block) return;
|
||||
}
|
||||
server.send(frame);
|
||||
});
|
||||
server.onMessage((frame) => ws.send(frame));
|
||||
});
|
||||
return { waitForRequest: () => request };
|
||||
}
|
||||
|
||||
export async function pressSubmitBeforeTheNextRender(page: Page, name: string): Promise<void> {
|
||||
const create = page.getByRole("button", { name, exact: true });
|
||||
await expect(create).toBeEnabled();
|
||||
|
||||
Reference in New Issue
Block a user