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, captureScreenshot,
completeOnboarding, completeOnboarding,
createNamedBot, createNamedBot,
fulfillFetchedRpc,
openNewGroup, openNewGroup,
rpc, rpc,
signup, signup,
@ -35,7 +36,7 @@ async function showFirstMemberWorking(route: Route) {
const body = (await response.json()) as { json?: Record<string, unknown> }; const body = (await response.json()) as { json?: Record<string, unknown> };
const payload = body.json; const payload = body.json;
if (!payload) { if (!payload) {
await route.fulfill({ response }); await fulfillFetchedRpc(route, response, body);
return; return;
} }
if ("groups" in payload) payload.groups = patchGroupMembers(payload.groups); 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 }; const current = payload.current as { groups?: unknown };
current.groups = patchGroupMembers(current.groups); 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) => { 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. */ /** Unterhaltungen rows only — `.sidebar-projekte` is nested inside the same shell block on main. */
export function unterhaltungenOnly(page: Page) { 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; 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`. */ /** Patch a fetched RPC response without passing a consumed `response` into `route.fulfill`. */
export async function routePatchRpc( export async function routePatchRpc(
page: Page, page: Page,
@ -38,11 +53,7 @@ export async function routePatchRpc(
await page.route(`**/rpc/${procedure}`, async (route) => { await page.route(`**/rpc/${procedure}`, async (route) => {
const response = await route.fetch(); const response = await route.fetch();
const body = (await response.json()) as { json: Record<string, unknown> }; const body = (await response.json()) as { json: Record<string, unknown> };
await route.fulfill({ await fulfillFetchedRpc(route, response, { json: patch(body.json) });
status: response.status(),
headers: response.headers(),
json: { json: patch(body.json) },
});
}); });
} }

View File

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

View File

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

View File

@ -1,17 +1,10 @@
import { expect, test } from "@playwright/test"; 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 ({ test("onboarding skips model connect when a default model is already available", async ({
page, page,
}) => { }) => {
await page.route("**/rpc/me", async (route) => { await routePatchRpc(page, "me", (json) => ({ ...json, needsModel: false }));
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 page.route("**/rpc/integrationSetup/get", (route) => await page.route("**/rpc/integrationSetup/get", (route) =>
route.fulfill({ route.fulfill({

View File

@ -1,15 +1,8 @@
import { expect, type Page, test } from "@playwright/test"; import { expect, type Page, test } from "@playwright/test";
import { signup } from "./helpers"; import { routePatchRpc, signup } from "./helpers";
async function forceNeedsModel(page: Page) { async function forceNeedsModel(page: Page) {
await page.route("**/rpc/me", async (route) => { await routePatchRpc(page, "me", (json) => ({ ...json, needsModel: true }));
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 } },
});
});
} }
test("onboarding shows the look picker when a model is still required", async ({ page }) => { test("onboarding shows the look picker when a model is still required", async ({ page }) => {