Skip to content

test: browser tests for the demo's MCP + OAuth flows - #4

Merged
siarheidudko merged 2 commits into
mainfrom
claude/mcp-remote-http-oauth-ges1mj
Aug 16, 2026
Merged

test: browser tests for the demo's MCP + OAuth flows#4
siarheidudko merged 2 commits into
mainfrom
claude/mcp-remote-http-oauth-ges1mj

Conversation

@siarheidudko

@siarheidudko siarheidudko commented Aug 16, 2026

Copy link
Copy Markdown
Member

Why

The bug that shipped in the MCP panel (#3) was not reachable by any unit test: an unrelated useEffect rewrote location while the hook was awaiting a dynamic import, so the authorization code was gone by the time it looked. Effect ordering across a real render, a real navigation away and back, and IndexedDB persistence need a browser.

What

Playwright drives the production build of the demo — including the /agent-web-react/ subpath it is actually deployed under, so the tested layout is the shipped one — against a real MCP server (McpServer + StreamableHTTPServerTransport) and a real authorization server on loopback. Both are CORS-enabled with WWW-Authenticate exposed, which makes the suite a check of the documented deployment requirement rather than of a convenient fiction.

Test Pins
connect, no auth tools discovered from a live server and listed
OAuth round-trip challenge → dynamic registration → authorize → back with a code → connected; the spent code is gone from the address bar; the visitor lands back on the panel they were using
reload stored tokens and registration reused — no second round-trip to the AS
unreachable server an error is surfaced instead of the UI hanging

Are they load-bearing?

I checked rather than assumed: reinstating the demo's old query-stripping router makes the OAuth test fail, so it holds the line on the class of bug that shipped.

The narrower "reads the callback before the first await" ordering is not pinned here — it is guaranteed by construction (the read happens synchronously at the top of the effect) plus the readCallbackParams unit tests. Reproducing that exact interleaving from outside the page needs timing assumptions too fragile to be worth a flaky test; I tried an init-script rival and dropped it.

CI

A third job (Browser (demo)) runs alongside the existing library and demo jobs, installs Chromium via playwright install --with-deps chromium, and uploads traces on failure. Locally the config uses a preinstalled browser when the sandbox provides one.

That job failed on its first run and the second commit fixes it — worth recording, because the failure mode is silent. Playwright timed out after 180s waiting for a web server that had started perfectly well: Vite's default host is localhost, resolved through DNS at listen time, while Playwright polls the literal 127.0.0.1. Passing the same literal address to both removes the resolver from the equation. The log showed a build and then nothing because Playwright ignores web-server stdout by default (only stderr is piped, which is why the build's chunk-size warning appeared alone) — so stdout: 'pipe' is now set, and the next failure will show the server's own banner or its error.

typecheck, format:check, build, test (24 passing) and test:e2e (4 passing, ~20s including the demo build) are green, and all three CI jobs are green on the head commit.


Generated by Claude Code

claude added 2 commits August 16, 2026 18:21
The bug that shipped in the MCP panel was not reachable by any unit test:
an unrelated useEffect rewrote location while the hook was awaiting a
dynamic import, so the authorization code was gone by the time it looked.
Effect ordering across a real render, a real navigation away and back,
and IndexedDB persistence need a browser.

Playwright drives the production build of the demo — including the
/agent-web-react/ subpath it is actually deployed under — against a real
MCP server and a real authorization server on loopback, both CORS-enabled
with WWW-Authenticate exposed. That makes the suite a check of the
documented deployment requirement rather than of a convenient fiction.

Covered: connecting to an unauthenticated server; the whole OAuth
round-trip (challenge → dynamic registration → authorize → back with a
code → connected), with the spent code gone from the address bar and the
visitor back on the panel they were using; a reload reusing the stored
tokens without a second registration; and an unreachable server surfacing
an error rather than hanging.

I checked these are load-bearing rather than decorative: reinstating the
demo's old query-stripping router makes the OAuth test fail. The narrower
"reads the callback before the first await" ordering is pinned by
construction and by the readCallbackParams unit tests — reproducing that
interleaving from outside the page turned out to need timing assumptions
too fragile to be worth a flaky test.

Chromium comes from `playwright install` in CI; a preinstalled browser is
used when the sandbox provides one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FKop4At26QqqkwVGEjJur
The Browser job timed out waiting 180s for a web server that had started
fine. Vite's default host is `localhost`, resolved through DNS at listen
time; Playwright polls the literal 127.0.0.1. When those disagree the run
waits out the whole timeout against a healthy server. Passing the same
literal address to both takes the resolver out of it.

Also pipe the web server's stdout: Playwright ignores it by default, which
is why the failure showed a build log followed by silence — the preview
banner and any startup error were being dropped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011FKop4At26QqqkwVGEjJur
@siarheidudko
siarheidudko merged commit 8139318 into main Aug 16, 2026
3 checks passed
@siarheidudko siarheidudko mentioned this pull request Aug 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants