personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chat: cover SSR markdown bootstrap in browser

+132
+132
apps/chat/tests/test_markdown_bootstrap_browser.py
··· 1 + # SPDX-License-Identifier: AGPL-3.0-only 2 + # Copyright (c) 2026 sol pbc 3 + 4 + from __future__ import annotations 5 + 6 + import contextlib 7 + import json 8 + import os 9 + import shutil 10 + import subprocess 11 + import threading 12 + from collections.abc import Iterator 13 + from datetime import datetime 14 + from pathlib import Path 15 + from typing import Any 16 + 17 + import pytest 18 + from werkzeug.serving import make_server 19 + 20 + from convey import create_app 21 + from convey.chat_stream import append_chat_event 22 + 23 + playwright_sync = pytest.importorskip("playwright.sync_api") 24 + sync_playwright = playwright_sync.sync_playwright 25 + PlaywrightError = playwright_sync.Error 26 + 27 + 28 + def _ms(year: int, month: int, day: int, hour: int, minute: int) -> int: 29 + return int(datetime(year, month, day, hour, minute).timestamp() * 1000) 30 + 31 + 32 + def _copytree_tracked(src: Path, dst: Path) -> None: 33 + result = subprocess.run( 34 + ["git", "ls-files", "."], 35 + cwd=str(src), 36 + capture_output=True, 37 + text=True, 38 + check=True, 39 + ) 40 + for rel in result.stdout.splitlines(): 41 + if not rel: 42 + continue 43 + src_file = src / rel 44 + dst_file = dst / rel 45 + dst_file.parent.mkdir(parents=True, exist_ok=True) 46 + if src_file.is_symlink(): 47 + os.symlink(os.readlink(src_file), dst_file) 48 + else: 49 + shutil.copy2(src_file, dst_file) 50 + 51 + 52 + @contextlib.contextmanager 53 + def _isolated_app_env(journal: Path) -> Iterator[Path]: 54 + previous = os.environ.get("SOLSTONE_JOURNAL") 55 + os.environ["SOLSTONE_JOURNAL"] = str(journal.resolve()) 56 + try: 57 + yield journal 58 + finally: 59 + if previous is None: 60 + os.environ.pop("SOLSTONE_JOURNAL", None) 61 + else: 62 + os.environ["SOLSTONE_JOURNAL"] = previous 63 + 64 + 65 + @contextlib.contextmanager 66 + def _running_chat_server(journal: Path) -> Iterator[str]: 67 + app = create_app(str(journal)) 68 + server = make_server("127.0.0.1", 0, app, threaded=True) 69 + thread = threading.Thread(target=server.serve_forever, daemon=True) 70 + thread.start() 71 + try: 72 + yield f"http://127.0.0.1:{server.server_port}" 73 + finally: 74 + server.shutdown() 75 + thread.join(timeout=5) 76 + 77 + 78 + def _write_localhost_auth_config(journal: Path) -> None: 79 + config_path = journal / "config" / "journal.json" 80 + if config_path.exists(): 81 + config = json.loads(config_path.read_text(encoding="utf-8")) 82 + else: 83 + config = {} 84 + config.setdefault("setup", {})["completed_at"] = 1 85 + config.setdefault("convey", {})["trust_localhost"] = True 86 + config_path.parent.mkdir(parents=True, exist_ok=True) 87 + config_path.write_text( 88 + json.dumps(config, indent=2, sort_keys=True) + "\n", 89 + encoding="utf-8", 90 + ) 91 + 92 + 93 + def _launch_chromium(playwright: Any) -> Any: 94 + try: 95 + return playwright.chromium.launch() 96 + except PlaywrightError as exc: 97 + message = str(exc) 98 + if "Executable doesn't exist" in message or "playwright install" in message: 99 + pytest.skip("Playwright chromium is not installed") 100 + raise 101 + 102 + 103 + def test_ssr_talent_markdown_bootstraps_to_rendered_dom(tmp_path, monkeypatch): 104 + journal = tmp_path / "journal" 105 + _copytree_tracked(Path("tests/fixtures/journal").resolve(), journal) 106 + monkeypatch.setenv("SOLSTONE_JOURNAL", str(journal)) 107 + _write_localhost_auth_config(journal) 108 + 109 + day = "20990102" 110 + append_chat_event( 111 + "talent_finished", 112 + ts=_ms(2099, 1, 2, 9, 3), 113 + use_id="use-md-browser-1", 114 + name="exec", 115 + summary="**done**", 116 + ) 117 + 118 + with _isolated_app_env(journal): 119 + with _running_chat_server(journal) as base_url: 120 + with sync_playwright() as playwright: 121 + browser = _launch_chromium(playwright) 122 + try: 123 + page = browser.new_page() 124 + page.goto(f"{base_url}/app/chat/{day}", wait_until="domcontentloaded") 125 + 126 + assert ( 127 + page.locator(".chat-talent-card-detail--markdown strong").count() 128 + > 0 129 + ) 130 + assert page.locator('[data-markdown="1"]').count() == 0 131 + finally: 132 + browser.close()