Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion ui/e2e/live/studio-customize-journey.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, expect, type APIRequestContext, type Page, type Response } from '@playwright/test';
import { test as base, expect, type APIRequestContext, type Page, type Response } from '@playwright/test';

/**
* Live e2e for the ADR-0146 customization journey, walked through the Studio
Expand Down Expand Up @@ -43,6 +43,40 @@ import { test, expect, type APIRequestContext, type Page, type Response } from '
* two-minute budget for the specs step; the readings are on that card's PR.
*/

/**
* The spec's API reads each open their own connection (`Connection: close`).
*
* Playwright's `request` pools its sockets (`http.Agent({ keepAlive: true })`,
* no idle timeout of its own), and the reads here are split by Studio steps
* the browser drives: the read after the customize step reuses a socket that
* has sat idle for about five seconds (4.7–5.2 s measured locally), the read
* after the extension step for about four. A Node 22 server closes an idle
* keep-alive socket at `keepAliveTimeout` + `keepAliveTimeoutBuffer` = 6 s, so
* on a slower runner the read reaches a socket the server is closing and fails
* with `read ECONNRESET` instead of an answer (objectstack#30524, reproduced
* against @hono/node-server at a 5996–5999 ms idle). With a connection per
* read no read depends on that race. The browser's own traffic is unchanged.
*
* The context is otherwise the one the default `request` fixture builds: the
* project's request-relevant `use` options (the signed-in `storageState`,
* `baseURL`, the device's `userAgent`, any `extraHTTPHeaders` and
* `ignoreHTTPSErrors`) are passed on by name, so the reads run as the admin
* session the spec was written for.
*/
const test = base.extend({
request: async ({ playwright, storageState, baseURL, userAgent, extraHTTPHeaders, ignoreHTTPSErrors }, provide) => {
const request = await playwright.request.newContext({
storageState,
baseURL,
userAgent,
ignoreHTTPSErrors,
extraHTTPHeaders: { ...extraHTTPHeaders, Connection: 'close' },
});
await provide(request);
await request.dispose();
},
});

const API = process.env.LIVE_API_URL || 'http://localhost:3000';
const BASE = 'com.example.showcase';
const PAIR = `${BASE}.custom`;
Expand Down
Loading