fix(e2e): harden onboarding-model-labels rpc/me intercept (#89)
* fix(e2e): harden onboarding-model-labels rpc/me intercept
Avoid passing a consumed fetch Response into route.fulfill alongside
json, which intermittently throws Response has been disposed in CI.
Add routePatchRpc helper that reads the body once and fulfills with
status, headers, and patched json only.
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
* 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>
* style(e2e): biome-format host-computer-prompt rpc/me cast
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
8bf343f0f8
commit
fc173918f7
@ -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) => {
|
||||
|
||||
@ -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,32 @@ 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,
|
||||
procedure: string,
|
||||
patch: (json: Record<string, unknown>) => Record<string, unknown>,
|
||||
) {
|
||||
await page.route(`**/rpc/${procedure}`, async (route) => {
|
||||
const response = await route.fetch();
|
||||
const body = (await response.json()) as { json: Record<string, unknown> };
|
||||
await fulfillFetchedRpc(route, response, { json: patch(body.json) });
|
||||
});
|
||||
}
|
||||
|
||||
async function visible(locator: Locator, timeout: number) {
|
||||
return locator.isVisible({ timeout }).catch(() => false);
|
||||
}
|
||||
|
||||
@ -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,15 @@ 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?" });
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -1,22 +1,13 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { signup } from "./helpers";
|
||||
import { routePatchRpc, signup } from "./helpers";
|
||||
|
||||
test("onboarding no longer gates first run on a model picker", 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: true,
|
||||
defaultProvider: "openrouter",
|
||||
defaultModel: "openai/gpt-5.6-luna",
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
await routePatchRpc(page, "me", (json) => ({
|
||||
...json,
|
||||
needsModel: true,
|
||||
defaultProvider: "openrouter",
|
||||
defaultModel: "openai/gpt-5.6-luna",
|
||||
}));
|
||||
|
||||
const stamp = Date.now();
|
||||
await signup(page, `model-labels-${stamp}@rakazo.test`, "password12", `Model labels ${stamp}`);
|
||||
|
||||
@ -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 }) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user