phase: 180_markdown_inline_links_images
All verification complete. Final state summary: **Report — phase 180 final verification pass** - Verified (all already implemented in working tree): `inline()` chain = code→strong→em→linked-image→image→link→headings→lists in `frontend/assets/markdown.js`; `allowLinkUrl`/`allowImageUrl` allowlists (A5), shared `mdAnchorOpen` (target/rel only for absolute http(s)); `####`→`<h5>`, `#####`/`######`→`<h6>`, `#`/`##`→`<h3>`/`###`→`<h4>` unchanged; `.md-link`/`.md-img`/h5-h6 scale in `styles.css` (palette vars only). All surfaces (chat, thinking, restore, viewer, modal) route through the one renderer (pinned in `app.js`/`document.js`). - Completion criteria: - `[text](url)`→`a.md-link`, `javascript:`/`data:` inert — ✅ (node probe + unit + E2E) - ``→`img.md-img loading="lazy"` with `alt`; bad src inert — ✅ - `####`/`#####`/`######` ladder; shallow rungs unchanged — ✅ - Fenced link/image/`####` stay code; cell-level link renders in `.md-table` — ✅ (node probe) - Byte-identity fixture + phase-44/138 suites pass unedited (git-verified untouched; 6 + 3 E2E pass in isolation) — ✅ - `uv run pytest --cov=app --cov-report=term-missing`: **4198 passed, TOTAL 99%**; `uv run pytest tests/e2e/test_markdown_inline_links_images.py -v --no-cov`: **4 passed**; `uv run ruff check .`: clean; `uv run pyright`: **0 errors** — ✅ - Docs: full README/AGENTS.md review vs the whole project — env-example↔README table complete, page routes match `app/main.py`, AGENTS quick-ref artifacts exist. Fixed 1 inconsistency: README understated the image allowlist (impl per task 02 accepts first segments ≤2 chars, `^.{0,2}/`, not only `./`/`../`); phase-180 paragraphs otherwise accurate. - Notable: image allowlist `.{0,2}/` is broader than the overview's `^\.{1,2}/` but is what task 02 explicitly specifies and the docstring documents — not a deviation from the tasks; SVG sizing deliberately deferred to phase 181. - Next pending phase: `181_svg_document_preview`.
This commit is contained in:
@@ -98,6 +98,7 @@ def login(page: Page, app_url: str, password: str | None = None, next: str | Non
|
||||
expect(page.locator("#login-password")).to_be_visible(timeout=2_000)
|
||||
except AssertionError:
|
||||
expect(page).to_have_url(app_url + (next or DEFAULT_NEXT), timeout=30_000)
|
||||
_wait_for_app_boot(page)
|
||||
return
|
||||
else:
|
||||
expect(page.locator("#login-password")).to_be_visible()
|
||||
@@ -108,3 +109,22 @@ def login(page: Page, app_url: str, password: str | None = None, next: str | Non
|
||||
expect(page).to_have_url(url) # no redirect on failure
|
||||
return
|
||||
expect(page).to_have_url(app_url + (next or DEFAULT_NEXT), timeout=30_000)
|
||||
_wait_for_app_boot(page)
|
||||
|
||||
|
||||
def _wait_for_app_boot(page: Page) -> None:
|
||||
"""Wait until the landed page's deferred module graph has EXECUTED.
|
||||
|
||||
``to_have_url`` passes the moment the redirect navigation COMMITS, while
|
||||
``document.readyState`` is still ``interactive`` — the page's
|
||||
``<script type="module">`` (app.js plus everything it imports) can still
|
||||
be in flight, so a handler the page wires up in JS does not exist yet.
|
||||
A caller that submits straight away (``#composer.requestSubmit()`` in the
|
||||
thinking suites) then fires the form's NATIVE GET submit: the browser
|
||||
navigates to ``/?message=…``, the conversation is never sent, and the
|
||||
story's first assertion fails against an empty chat. ``load`` fires after
|
||||
every deferred module has run, so this makes an interactive page a real
|
||||
precondition of ``login`` for every suite — and it is a no-op once the
|
||||
page has settled, so no suite pays a wait twice.
|
||||
"""
|
||||
page.wait_for_load_state("load")
|
||||
|
||||
Reference in New Issue
Block a user