Fix pipe desync: async drain on _send, robust Playwright tests

Root cause: OcamlBridge._send() used write() without drain().
asyncio.StreamWriter buffers writes — without drain(), multiple
commands accumulate and flush as a batch. The kernel processes
them sequentially, sending responses, but Python only reads one
response per command → pipe desync → "unexpected response" errors.

Fix: _send() is now async, calls drain() after every write.
All 14 callers updated to await.

Playwright tests rewritten:
- test_home_has_header: verifies #logo-opacity visible (was only
  checking for "sx" text — never caught missing header)
- test_home_has_nav_children: Geography link must be visible
- test_home_has_main_panel: #main-panel must have child elements
- TestDirectPageLoad: fresh browser.new_context() per test to
  avoid stale component hash in localStorage
- _setup_error_capture + _check_no_fatal_errors helpers

_render_to_sx uses aser_slot (not aser) — layout wrappers contain
re-parsed content that needs full expansion capability.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 16:11:55 +00:00
parent f819fda587
commit d3b3b4b720
4 changed files with 102 additions and 74 deletions

View File

@@ -216,18 +216,8 @@ async def eval_sx_url(raw_path: str) -> Any:
Keyword("content"), wrapped_ast,
]
full_text = serialize(full_ast)
has_nl = chr(10) in full_text
if has_nl:
logger.error("NEWLINE in aser_slot input at char %d!",
full_text.index(chr(10)))
import time as _time
_t0 = _time.monotonic()
body_sx = SxExpr(await bridge.aser_slot(
full_text, ctx=ocaml_ctx))
_elapsed = _time.monotonic() - _t0
logger.info("aser_slot: %.1fs, input=%d chars, output=%d chars, starts=%s",
_elapsed, len(full_text), len(body_sx),
str(body_sx)[:100])
tctx = await get_template_context()
return await make_response(
await sx_page(tctx, body_sx), 200)