fix(e2e): migrate rpc/me intercept anti-pattern across specs

Extend helpers with fulfillFetchedRpc for multi-procedure or in-place
patches. Move onboarding, mobile, host-computer, and group-chats specs
off route.fulfill({ response, json }) after response.json().

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-09-24 05:22:39 +00:00
parent f0593bad07
commit 95c5544786
6 changed files with 33 additions and 37 deletions

View File

@ -3,6 +3,7 @@ import {
captureScreenshot,
completeOnboarding,
createNamedBot,
fulfillFetchedRpc,
openNewGroup,
rpc,
signup,
@ -35,7 +36,7 @@ async function showFirstMemberWorking(route: Route) {
const body = (await response.json()) as { json?: Record<string, unknown> };
const payload = body.json;
if (!payload) {
await route.fulfill({ response });
await fulfillFetchedRpc(route, response, body);
return;
}
if ("groups" in payload) payload.groups = patchGroupMembers(payload.groups);
@ -49,7 +50,7 @@ async function showFirstMemberWorking(route: Route) {
const current = payload.current as { groups?: unknown };
current.groups = patchGroupMembers(current.groups);
}
await route.fulfill({ response, json: body });
await fulfillFetchedRpc(route, response, body);
}
test("create group from + and see two bots in one transcript", async ({ page }, testInfo) => {

View File

@ -1,4 +1,6 @@
import { expect, type Locator, type Page, type TestInfo } from "@playwright/test";
import { expect, type Locator, type Page, type Route, type TestInfo } from "@playwright/test";
type RpcEnvelope = { json?: Record<string, unknown> };
/** Unterhaltungen rows only — `.sidebar-projekte` is nested inside the same shell block on main. */
export function unterhaltungenOnly(page: Page) {
@ -29,6 +31,19 @@ export async function rpc<T>(page: Page, procedure: string, body: unknown): Prom
return parsed.json as T;
}
/** Fulfill a fetched RPC response without passing a consumed `response` into `route.fulfill`. */
export async function fulfillFetchedRpc(
route: Route,
response: Awaited<ReturnType<Route["fetch"]>>,
body: RpcEnvelope,
) {
await route.fulfill({
status: response.status(),
headers: response.headers(),
json: body,
});
}
/** Patch a fetched RPC response without passing a consumed `response` into `route.fulfill`. */
export async function routePatchRpc(
page: Page,
@ -38,11 +53,7 @@ export async function routePatchRpc(
await page.route(`**/rpc/${procedure}`, async (route) => {
const response = await route.fetch();
const body = (await response.json()) as { json: Record<string, unknown> };
await route.fulfill({
status: response.status(),
headers: response.headers(),
json: { json: patch(body.json) },
});
await fulfillFetchedRpc(route, response, { json: patch(body.json) });
});
}

View File

@ -1,5 +1,5 @@
import { expect, test } from "@playwright/test";
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
import { captureScreenshot, completeOnboarding, fulfillFetchedRpc, signup } from "./helpers";
for (const platform of ["darwin", "win32"]) {
test(`host computer choice explains file access on ${platform}`, async ({ page }, testInfo) => {
@ -29,11 +29,13 @@ for (const platform of ["darwin", "win32"]) {
}, platform);
await page.route(/\/rpc\/(me|bootstrap)$/, async (route) => {
const response = await route.fetch();
const body = await response.json();
const body = (await response.json()) as { json: Record<string, unknown> & { me?: Record<string, unknown> } };
const me = route.request().url().endsWith("/me") ? body.json : body.json.me;
me.canChooseHostComputer = true;
me.computerHost = null;
await route.fulfill({ response, json: body });
if (me) {
me.canChooseHostComputer = true;
me.computerHost = null;
}
await fulfillFetchedRpc(route, response, body);
});
await page.reload();
const dialog = page.getByRole("dialog", { name: "Where should bots run?" });

View File

@ -1,6 +1,6 @@
import type { Page } from "@playwright/test";
import { expect, test } from "@playwright/test";
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
import { captureScreenshot, completeOnboarding, routePatchRpc, signup } from "./helpers";
test.use({
hasTouch: true,
@ -9,11 +9,7 @@ test.use({
});
async function prepareOnboarding(page: Page) {
await page.route("**/rpc/me", async (route) => {
const response = await route.fetch();
const body = (await response.json()) as { json: Record<string, unknown> };
await route.fulfill({ response, json: { json: { ...body.json, needsModel: false } } });
});
await routePatchRpc(page, "me", (json) => ({ ...json, needsModel: false }));
await page.route("**/rpc/integrationSetup/get", (route) =>
route.fulfill({
json: {

View File

@ -1,17 +1,10 @@
import { expect, test } from "@playwright/test";
import { signup } from "./helpers";
import { routePatchRpc, signup } from "./helpers";
test("onboarding skips model connect when a default model is already available", async ({
page,
}) => {
await page.route("**/rpc/me", async (route) => {
const response = await route.fetch();
const body = (await response.json()) as { json: Record<string, unknown> };
await route.fulfill({
response,
json: { json: { ...body.json, needsModel: false } },
});
});
await routePatchRpc(page, "me", (json) => ({ ...json, needsModel: false }));
await page.route("**/rpc/integrationSetup/get", (route) =>
route.fulfill({

View File

@ -1,15 +1,8 @@
import { expect, type Page, test } from "@playwright/test";
import { signup } from "./helpers";
import { routePatchRpc, signup } from "./helpers";
async function forceNeedsModel(page: Page) {
await page.route("**/rpc/me", async (route) => {
const response = await route.fetch();
const body = (await response.json()) as { json: Record<string, unknown> };
await route.fulfill({
response,
json: { json: { ...body.json, needsModel: true } },
});
});
await routePatchRpc(page, "me", (json) => ({ ...json, needsModel: true }));
}
test("onboarding shows the look picker when a model is still required", async ({ page }) => {