Optional sections: checkpoint tracker, admin/roles, login page
- dashboard.html: each section sits in OPTIONAL:<name> BEGIN/END blocks in the style, markup and script, with sample answers so a page with no bot shows it working. Core change: setUser records the role on <body>, so CSS hides admin-only parts from operators. - templates/sections.py keeps the chosen sections and deletes the rest, markers included; it stops on a broken marker. - templates/sections.md: what each section needs from the host (events, actions, desktop slot snippets, the role re-check, the login hand-off). - SKILL.md step 5 asks one multi-select question and runs sections.py. - Page check: checkpoint states, admin visibility by role and assign/remove, login theme and hand-off to auth or the stub. Cases skip on a generated page without the section; the template must carry all three. The harness signs in when the page has a login screen. - tests/test_sections.py: no sections leave no trace, and that page still passes the page check. - Eval runner: a-empty-sections variant (all three) and per-section checks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3eb3fdf00e
commit
9a5b95448d
10 files changed
+536
-10
No files matched your search
@@ -0,0 +1,55 @@
|
||||
"""Optional sections: the strip tool keeps the chosen sections and leaves nothing of the rest."""
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
TEMPLATES = ROOT / "rpa-dashboard-no-ai" / "templates"
|
||||
spec = importlib.util.spec_from_file_location("sections", TEMPLATES / "sections.py")
|
||||
sections = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(sections)
|
||||
|
||||
TEMPLATE = (TEMPLATES / "dashboard.html").read_text(encoding="utf-8")
|
||||
# Text each optional section brings into the page (markup, script or style).
|
||||
TRACES = {
|
||||
"checkpoints": re.compile(r"checkpoint|cpstate|cp-", re.I),
|
||||
"admin": re.compile(r"admin", re.I),
|
||||
"login": re.compile(r"login_|sign in|sign out|logout", re.I),
|
||||
}
|
||||
|
||||
|
||||
def test_no_sections_leave_nothing_behind():
|
||||
html = sections.keep(TEMPLATE, [])
|
||||
for name, trace in TRACES.items():
|
||||
assert not trace.search(html), f"{name} left behind: {trace.search(html)[0]!r}"
|
||||
assert "OPTIONAL" not in html
|
||||
|
||||
|
||||
@pytest.mark.parametrize("chosen", [["checkpoints"], ["admin"], ["login"], ["checkpoints", "admin", "login"]])
|
||||
def test_chosen_sections_are_kept_and_the_rest_removed(chosen):
|
||||
html = sections.keep(TEMPLATE, chosen)
|
||||
for name, trace in TRACES.items():
|
||||
assert bool(trace.search(html)) == (name in chosen), name
|
||||
assert "OPTIONAL" not in html
|
||||
|
||||
|
||||
def test_unknown_section_is_refused():
|
||||
with pytest.raises(SystemExit, match="checkpoints, admin, login"):
|
||||
sections.keep(TEMPLATE, ["charts"])
|
||||
|
||||
|
||||
def test_page_without_sections_passes_the_page_check(tmp_path):
|
||||
fill = {"APP_NAME": "T", "APP_SUBTITLE": "", "LOGO_SRC": "", "API_BASE": "",
|
||||
"STEPS_JSON": json.dumps(["Start", "Work", "Done"]),
|
||||
"REPORT_COLUMNS_JSON": json.dumps([{"key": "item", "label": "Item"}, {"key": "status", "label": "Status"}])}
|
||||
page_file = tmp_path / "dashboard.html"
|
||||
page_file.write_text(re.sub(r"\{\{(\w+)\}\}", lambda m: fill[m[1]], sections.keep(TEMPLATE, [])), encoding="utf-8")
|
||||
r = subprocess.run([sys.executable, "-m", "pytest", str(ROOT / "tests" / "page_check"), "-q", "-p", "no:cacheprovider"],
|
||||
env={**os.environ, "PAGE_CHECK_HTML": str(page_file)}, capture_output=True, text=True)
|
||||
assert r.returncode == 0, r.stdout[-2000:]
|
||||
Reference in new issue
Block a user