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

202 lines
7.8 KiB
Python

from __future__ import annotations
from pathlib import Path
LOGS_JS = Path("src/opensquilla/gateway/static/js/views/logs.js")
LOGS_CSS = Path("src/opensquilla/gateway/static/css/views/logs.css")
CONFIG_JS = Path("src/opensquilla/gateway/static/js/views/config.js")
CONFIG_CSS = Path("src/opensquilla/gateway/static/css/views/config.css")
CONFIG_EXAMPLE = Path("opensquilla.toml.example")
def test_logs_view_describes_configurable_debug_logging() -> None:
source = LOGS_JS.read_text(encoding="utf-8")
assert "Gateway file logging is configurable" in source
assert "logs.status" in source
assert "Raw turn-call capture is enabled by" in source
assert "opensquilla diagnostics on --raw" in source
assert "OPENSQUILLA_LOG_DIR" in source
assert "OPENSQUILLA_TURN_CALL_LOG=1" in source
def test_config_view_explains_debug_file_logging_fields() -> None:
source = CONFIG_JS.read_text(encoding="utf-8")
assert "'debug'" in source
assert "Security-sensitive developer mode" in source
assert "'diagnostics_enabled'" in source
assert "Default standard diagnostics mode" in source
assert "'log_file_enabled'" in source
assert "'log_level'" in source
assert "'log_file_max_bytes'" in source
assert "'log_file_backup_count'" in source
def test_logs_mobile_toolbar_keeps_level_filters_compact() -> None:
css = LOGS_CSS.read_text(encoding="utf-8")
levels_start = css.index(".lg-levels__row {")
levels_rule = css[levels_start : css.index("}", levels_start)]
assert "flex-wrap: wrap" in levels_rule
level_button_start = css.index(".lg-level-btn {")
level_button_rule = css[
level_button_start : css.index("}", level_button_start)
]
assert "min-height: 40px" in level_button_rule
mobile_start = css.index("@media (max-width: 720px)")
mobile_block = css[mobile_start:]
mobile_levels_wrap_start = mobile_block.index(".lg-levels {")
mobile_levels_wrap_rule = mobile_block[
mobile_levels_wrap_start : mobile_block.index("}", mobile_levels_wrap_start)
]
mobile_levels_start = mobile_block.index(".lg-levels__row {")
mobile_levels_rule = mobile_block[
mobile_levels_start : mobile_block.index("}", mobile_levels_start)
]
mobile_button_start = mobile_block.index(".lg-level-btn {")
mobile_button_rule = mobile_block[
mobile_button_start : mobile_block.index("}", mobile_button_start)
]
assert "width: 100%" in mobile_levels_wrap_rule
assert "flex-direction: column" in mobile_levels_wrap_rule
assert "align-items: stretch" in mobile_levels_wrap_rule
assert "gap: 6px" in mobile_levels_wrap_rule
assert "width: 100%" in mobile_levels_rule
assert "flex-wrap: wrap" in mobile_levels_rule
assert "min-width: 0" in mobile_levels_rule
assert "overflow: visible" in mobile_levels_rule
assert "overflow-x: auto" not in mobile_levels_rule
assert "mask-image: linear-gradient" not in mobile_levels_rule
assert "padding-inline-end" not in mobile_levels_rule
assert "flex: 0 0 auto" in mobile_button_rule
assert ".lg-search-wrap" in mobile_block
assert "width: 100%" in mobile_block
assert "min-width: 0" in mobile_block
assert ".lg-toggle { width: 100%; min-height: 40px; }" in mobile_block
def test_logs_auto_follow_toggle_keeps_touch_friendly_hit_area() -> None:
css = LOGS_CSS.read_text(encoding="utf-8")
toggle_rule = css[css.index(".lg-toggle {") : css.index("}", css.index(".lg-toggle {"))]
track_start = css.index(".lg-toggle__track {")
track_rule = css[track_start : css.index("}", track_start)]
thumb_start = css.index(".lg-toggle__thumb {")
thumb_rule = css[thumb_start : css.index("}", thumb_start)]
assert "min-height: 40px" in toggle_rule
assert "width: 40px; height: 22px" in track_rule
assert "width: 18px; height: 18px" in thumb_rule
assert "input:focus-visible + .lg-toggle__track" in css
def test_logs_search_keeps_touch_friendly_hit_area() -> None:
css = LOGS_CSS.read_text(encoding="utf-8")
start = css.index(".lg-search-input {")
rule = css[start : css.index("}", start)]
assert "min-height: 40px" in rule
def test_logs_header_actions_keep_touch_friendly_hit_area() -> None:
css = LOGS_CSS.read_text(encoding="utf-8")
start = css.index(".lg-stage__actions .btn {")
rule = css[start : css.index("}", start)]
assert "min-height: 40px" in rule
def test_logs_lines_wrap_at_words_before_breaking_long_tokens() -> None:
css = LOGS_CSS.read_text(encoding="utf-8")
line_start = css.index(".lg-line {")
line_rule = css[line_start : css.index("}", line_start)]
msg_start = css.index(".lg-line__msg {")
msg_rule = css[msg_start : css.index("}", msg_start)]
assert "white-space: pre-wrap" in line_rule
assert "min-width: 0" in line_rule
assert "word-break: break-all" not in line_rule
assert "min-width: 0" in msg_rule
assert "word-break: normal" in msg_rule
assert "overflow-wrap: break-word" in msg_rule
mobile_start = css.index("@media (max-width: 720px)")
mobile_block = css[mobile_start:]
mobile_msg_start = mobile_block.index(".lg-line__msg {")
mobile_msg_rule = mobile_block[
mobile_msg_start : mobile_block.index("}", mobile_msg_start)
]
assert "grid-column: 1 / -1" in mobile_msg_rule
def test_config_mobile_tabs_scroll_instead_of_wrapping() -> None:
css = CONFIG_CSS.read_text(encoding="utf-8")
mobile_start = css.index("@media (max-width: 760px)")
mobile_block = css[mobile_start:]
assert ".cfg-tabs" in mobile_block
assert "flex-wrap: nowrap" in mobile_block
assert "overflow-x: auto" in mobile_block
assert "overflow-y: hidden" in mobile_block
assert "scroll-snap-type: x proximity" in mobile_block
assert ".cfg-tab" in mobile_block
assert "flex: 0 0 auto" in mobile_block
assert "min-height: 40px" in mobile_block
help_rule = css[css.index(".cfg-help-btn {") : css.index("}", css.index(".cfg-help-btn {"))]
assert "min-width: 40px" in help_rule
assert "min-height: 40px" in help_rule
def test_example_config_lists_debug_file_logging_controls() -> None:
source = CONFIG_EXAMPLE.read_text(encoding="utf-8")
assert "log_file_enabled" in source
assert "log_level" in source
assert "log_file_max_bytes" in source
assert "log_file_backup_count" in source
assert "diagnostics_enabled enables standard diagnostics" in source
assert "OPENSQUILLA_TURN_CALL_LOG=1" in source
def test_logs_poll_does_not_overlap_or_fail_silently() -> None:
source = LOGS_JS.read_text(encoding="utf-8")
assert "let _pollInFlight = false;" in source
assert "let _pollErrorShown = false;" in source
start = source.index("async function _poll()")
end = source.index(" function _guessLevel", start)
body = source[start:end]
assert "if (!_el || _pollInFlight) return;" in body
assert "_pollInFlight = true;" in body
assert "Log refresh failed" in body
assert "_pollErrorShown = true;" in body
assert "_pollInFlight = false;" in body
assert "finally" in body
def test_config_view_resets_mode_and_preserves_unsaved_yaml_draft() -> None:
source = CONFIG_JS.read_text(encoding="utf-8")
render_start = source.index("function render(el)")
render_end = source.index(" function destroy()", render_start)
render_body = source[render_start:render_end]
destroy_start = render_end
destroy_end = source.index(" async function _loadData()", destroy_start)
destroy_body = source[destroy_start:destroy_end]
assert "_setMode(_mode);" in render_body
assert "_mode = 'form';" in destroy_body
assert "let _yamlDraft = '';" in source
assert "let _yamlDirty = false;" in source
assert "_bindYamlDraftTracking();" in source
assert "_yamlDraft = e.target.value;" in source
assert "_yamlDirty = _yamlDraft !== _yamlText;" in source
assert "_yamlDirty ? _yamlDraft : _yamlText" in source