mirror of
https://github.com/nesquena/hermes-webui.git
synced 2026-05-25 03:00:23 +00:00
8c803c0a07
1. test_issue1362_codex_oauth_onboarding.py::test_anthropic_onboarding_setup_allows_linked_oauth_without_api_key
Pre-existing env-collision bug, surfaced when HERMES_WEBUI_SKIP_ONBOARDING=1
is in the test runner env (set by hosting providers and by isolated test
harnesses). `apply_onboarding_setup()` short-circuits without writing the
config file when SKIP_ONBOARDING is set, but the test asserts the file was
written, so it fails with FileNotFoundError on read_text().
Fix: `monkeypatch.delenv("HERMES_WEBUI_SKIP_ONBOARDING", raising=False)` —
matches the convention already used in test_issue1499_keyless_onboarding.py
and test_issue1500_lmstudio_env_var_alignment.py.
2. test_issue1800_file_html_interactions.py::test_media_html_inline_keeps_csp_sandbox
Slicing-based source-string assertion (4000-char window after `def _handle_media`)
broke because PR #2044's MEDIA_ALLOWED_ROOTS parsing was inserted earlier in
the function and pushed the CSP block to offset 4211. Widened window to 5000.
Assertion content is structural (CSP sandbox string present), not positional.
87 lines
3.6 KiB
Python
87 lines
3.6 KiB
Python
"""Regression coverage for issue #1800 file-picker and HTML-open interactions."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from pathlib import Path
|
|
|
|
|
|
REPO = Path(__file__).resolve().parents[1]
|
|
INDEX_HTML = (REPO / "static" / "index.html").read_text(encoding="utf-8")
|
|
BOOT_JS = (REPO / "static" / "boot.js").read_text(encoding="utf-8")
|
|
UI_JS = (REPO / "static" / "ui.js").read_text(encoding="utf-8")
|
|
STYLE_CSS = (REPO / "static" / "style.css").read_text(encoding="utf-8")
|
|
ROUTES_PY = (REPO / "api" / "routes.py").read_text(encoding="utf-8")
|
|
|
|
|
|
def _slice_after(source: str, needle: str, chars: int = 900) -> str:
|
|
idx = source.find(needle)
|
|
assert idx >= 0, f"{needle!r} not found"
|
|
return source[idx : idx + chars]
|
|
|
|
|
|
def test_attach_button_is_non_submit_button():
|
|
"""Attach must not act like a submit button in browser/container shells."""
|
|
m = re.search(r"<button[^>]*id=\"btnAttach\"[^>]*>", INDEX_HTML)
|
|
assert m, "btnAttach button not found"
|
|
assert 'type="button"' in m.group(0)
|
|
|
|
|
|
def test_file_input_is_visually_hidden_not_display_none():
|
|
"""Hidden file inputs are more consistently opened by user-gesture clicks."""
|
|
m = re.search(r"<input[^>]*id=\"fileInput\"[^>]*>", INDEX_HTML)
|
|
assert m, "fileInput not found"
|
|
tag = m.group(0)
|
|
assert "file-input-visually-hidden" in tag
|
|
assert "display:none" not in tag
|
|
rule = _slice_after(STYLE_CSS, ".file-input-visually-hidden", 240)
|
|
assert "position:absolute" in rule
|
|
assert "opacity:0" in rule
|
|
|
|
|
|
def test_attach_click_prevents_default_and_opens_picker():
|
|
body = _slice_after(BOOT_JS, "$('btnAttach').onclick", 300)
|
|
assert "preventDefault" in body
|
|
assert "$('fileInput').value=''" in body
|
|
assert "$('fileInput').click()" in body
|
|
|
|
|
|
def test_html_chat_attachment_opens_sandboxed_inline_raw_file():
|
|
"""Uploaded .html attachments render as an openable link, not an inert badge."""
|
|
body = _slice_after(UI_JS, "function _renderAttachmentHtml", 900)
|
|
assert "_HTML_EXTS.test(fname)" in body
|
|
assert "inline=1" in body
|
|
assert "target=\"_blank\"" in body
|
|
assert "rel=\"noopener\"" in body
|
|
assert "msg-file-badge--html" in body
|
|
|
|
|
|
def test_html_media_open_full_uses_inline_new_tab_not_download():
|
|
"""MEDIA: HTML preview's Open full page link should open a browser view."""
|
|
body = _slice_after(UI_JS, "function loadHtmlInline", 1800)
|
|
assert "'&inline=1'" in body
|
|
assert "target=\"_blank\"" in body
|
|
assert "rel=\"noopener\"" in body
|
|
normal_open = next(line for line in body.splitlines() if "html-open-link" in line)
|
|
assert "download=" not in normal_open
|
|
|
|
|
|
def test_media_html_inline_keeps_csp_sandbox():
|
|
"""api/media may serve HTML inline only behind a CSP sandbox."""
|
|
# Slice widened to 5000 (was 4000) after PR #2044 added MEDIA_ALLOWED_ROOTS
|
|
# parsing earlier in _handle_media, which pushed the CSP block past the
|
|
# original window. The assertion is structural, not positional.
|
|
body = _slice_after(ROUTES_PY, "def _handle_media", 5000)
|
|
assert 'html_inline_ok = inline_preview and mime == "text/html"' in body
|
|
assert 'csp = "sandbox allow-scripts" if html_inline_ok else None' in body
|
|
assert "csp=csp" in body
|
|
assert "allow-same-origin" not in body
|
|
|
|
|
|
def test_sandboxed_file_responses_do_not_send_x_frame_options():
|
|
"""X-Frame-Options: DENY would block the sandbox iframe preview."""
|
|
body = _slice_after(ROUTES_PY, "def _serve_file_bytes", 1800)
|
|
csp_branch = body[body.find("if csp:") : body.find("else:", body.find("if csp:"))]
|
|
assert "Content-Security-Policy" in csp_branch
|
|
assert 'send_header("X-Frame-Options"' not in csp_branch
|