Files
2026-07-13 13:12:00 +08:00

170 lines
6.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""E2E: the Settings → Appearance *code* font size drives Monaco and persists.
The code-font controls live on the Settings page (``pages/SettingsPage.tsx``,
``UiCodeFontSizeControl`` / ``UiCodeFontFamilyControl``) as two rows labelled
"Code font size" and "Code font family": a segmented pill (```` / value /
``+``) under a ``role="group"`` labelled "Code font size", plus a free-text
family input. Stepping the size writes the px choice to
``localStorage["omnigent:code-font-size"]``.
Unlike the chrome font (which rides the ``--ui-font-scale`` CSS variable), the
code editor (Monaco) and terminal (xterm) are fixed-pixel widgets: they read a
concrete ``fontSize`` at construction and are updated imperatively via the
in-module pub/sub in ``lib/codeFontPreferences.ts``. Monaco applies the size as
an inline ``font-size`` on its ``.view-lines`` element, so opening a non-markdown
file and reading that computed size is the portable signal that the preference
reached a real, mounted editor. The default is 13px; the range is 1024px, so
the ```` / ``+`` buttons disable at the bounds.
No LLM turn is involved — the file is seeded via the filesystem PUT endpoint and
the size is a pure client-side preference.
"""
from __future__ import annotations
import shutil
from collections.abc import Iterator
from pathlib import Path
import httpx
import pytest
from playwright.sync_api import Locator, Page, expect
STORAGE_KEY = "omnigent:code-font-size"
# The seeded file lands in ``<cwd>/<session_id>/`` (the agent spec uses
# ``os_env.cwd: .``); mirror test_file_autosave.py's per-session cleanup.
_REPO_ROOT = Path(__file__).resolve().parents[2]
_PY_PATH = "code_font_module.py"
_PY_CONTENT = 'def greet(name):\n return "hello " + name\n'
def _seed_file(base_url: str, session_id: str, path: str, content: str) -> None:
resp = httpx.put(
f"{base_url}/v1/sessions/{session_id}/resources/environments/default/filesystem/{path}",
json={"content": content, "encoding": "utf-8"},
timeout=10.0,
)
resp.raise_for_status()
@pytest.fixture
def seeded_python(seeded_session: tuple[str, str]) -> Iterator[tuple[str, str]]:
base_url, session_id = seeded_session
_seed_file(base_url, session_id, _PY_PATH, _PY_CONTENT)
try:
yield (base_url, session_id)
finally:
shutil.rmtree(_REPO_ROOT / session_id, ignore_errors=True)
def _stored_size(page: Page) -> str | None:
"""The persisted code-font size preference, or None when unset (default 13)."""
return page.evaluate(f"() => window.localStorage.getItem('{STORAGE_KEY}')")
def _open_appearance(page: Page, base_url: str) -> None:
"""Navigate to the Settings Appearance section and wait for the code control."""
page.goto(f"{base_url}/settings/appearance")
expect(page.get_by_role("group", name="Code font size", exact=True)).to_be_visible(
timeout=30_000
)
def _open_monaco(page: Page, base_url: str, session_id: str) -> Locator:
"""Open the seeded Python file in Monaco and return the file-viewer locator."""
page.goto(f"{base_url}/c/{session_id}?file={_PY_PATH}")
file_viewer = page.locator('[data-testid="file-viewer"]:visible')
expect(file_viewer).to_be_visible(timeout=30_000)
# Non-markdown files render in Monaco (lazy-loaded); wait until it has laid
# out the file so the inline font-size is present on .view-lines.
expect(file_viewer.locator(".monaco-editor")).to_be_visible(timeout=20_000)
expect(file_viewer.locator(".view-lines")).to_contain_text("greet", timeout=10_000)
return file_viewer
def _monaco_font_size(file_viewer: Locator) -> str:
"""The computed ``font-size`` Monaco applied to its ``.view-lines`` element."""
return file_viewer.locator(".view-lines").first.evaluate("el => getComputedStyle(el).fontSize")
def test_code_font_size_defaults_apply_to_monaco(
page: Page, seeded_python: tuple[str, str]
) -> None:
"""A fresh context opens Monaco at the 13px code-font default, nothing stored."""
base_url, session_id = seeded_python
file_viewer = _open_monaco(page, base_url, session_id)
# No stored preference → 13px default reaches the mounted editor.
assert _stored_size(page) is None, "expected no persisted code-font size on a fresh load"
assert _monaco_font_size(file_viewer) == "13px", "Monaco did not open at the code-font default"
def test_code_font_size_step_applies_to_monaco_and_persists(
page: Page, seeded_python: tuple[str, str]
) -> None:
"""Stepping the size in Settings re-fonts a Monaco editor and survives reload.
Steps the code-font size up in Settings, then opens the file: the mounted
Monaco editor reads the new size at construction and renders its
``.view-lines`` at that px. A reload restores it (persisted + re-applied), so
the editor never flashes back to the default.
"""
base_url, session_id = seeded_python
_open_appearance(page, base_url)
value = page.get_by_test_id("code-font-size-input")
increase = page.get_by_test_id("code-font-size-inc")
# Fresh context → default 13px, nothing stored.
expect(value).to_have_value("13")
assert _stored_size(page) is None
# → 15px: two steps up. The value and storage move together.
increase.click()
increase.click()
expect(value).to_have_value("15")
assert _stored_size(page) == "15"
# The editor picks up the stepped size (read at construction).
file_viewer = _open_monaco(page, base_url, session_id)
assert _monaco_font_size(file_viewer) == "15px", "Monaco did not reflect the stepped size"
# The choice survives a full reload.
page.reload()
file_viewer = page.locator('[data-testid="file-viewer"]:visible')
expect(file_viewer.locator(".view-lines")).to_contain_text("greet", timeout=30_000)
assert _monaco_font_size(file_viewer) == "15px", "size was not restored after reload"
def test_code_font_size_steppers_clamp_at_bounds(
page: Page, seeded_session: tuple[str, str]
) -> None:
"""The ```` / ``+`` buttons disable at the 10px min and 24px max."""
base_url, _session_id = seeded_session
# Seed the max before the app boots so the "+" button renders disabled.
page.goto(base_url)
page.evaluate(f"() => window.localStorage.setItem('{STORAGE_KEY}', '24')")
_open_appearance(page, base_url)
value = page.get_by_test_id("code-font-size-input")
decrease = page.get_by_test_id("code-font-size-dec")
increase = page.get_by_test_id("code-font-size-inc")
# At the 24px max, only "+" is disabled.
expect(value).to_have_value("24")
expect(increase).to_be_disabled()
expect(decrease).to_be_enabled()
# Hold "" down to the 10px min; there it flips to "" disabled, "+" enabled.
for _ in range(16):
if decrease.is_disabled():
break
decrease.click()
expect(value).to_have_value("10")
expect(decrease).to_be_disabled()
expect(increase).to_be_enabled()