afv-library/plugins/builder/salesforce-development/scripts/test/test_ui_modes.py
2026-08-10 16:05:50 +00:00

278 lines
14 KiB
Python

#!/usr/bin/env python3
"""Surface-by-mode contracts for ambient Salesforce plugin UI."""
from __future__ import annotations
import io
import json
import os
import tempfile
import unittest
from contextlib import redirect_stdout
from pathlib import Path
from unittest import mock
from _test_support import load_module, strip_ansi
SCRIPTS = Path(__file__).resolve().parent.parent
PLUGIN_ROOT = SCRIPTS.parent
SFX = load_module(SCRIPTS / "sf_context.py", "ui_modes_context")
PLUGIN_JSON = PLUGIN_ROOT / ".claude-plugin/plugin.json"
STATE = {
"currentStage": "Build",
"reason": "Create source metadata.",
"stages": [
{"name": "Connect", "status": "complete"},
{"name": "Project", "status": "complete"},
{"name": "Build", "status": "current"},
{"name": "Test", "status": "future"},
{"name": "Deploy", "status": "future"},
{"name": "Observe", "status": "future"},
],
"context": {"project": "acme", "orgAlias": "dev", "orgStatus": "unprobed"},
}
class UiModeContracts(unittest.TestCase):
def test_manifest_declares_one_string_option_with_full_default(self):
plugin = json.loads(PLUGIN_JSON.read_text(encoding="utf-8"))
config = plugin["userConfig"]
self.assertEqual(set(config), {"ui_mode"})
self.assertEqual(config["ui_mode"]["type"], "string")
self.assertEqual(config["ui_mode"]["default"], "full")
self.assertIn("full", config["ui_mode"]["description"])
self.assertIn("compact", config["ui_mode"]["description"])
self.assertIn("plain", config["ui_mode"]["description"])
self.assertIn("off", config["ui_mode"]["description"])
def test_missing_empty_and_invalid_values_fail_safe_to_full(self):
for raw, expected in ((None, "full"), ("", "full"), ("bogus", "full"),
("FULL", "full"), ("compact", "compact"),
("plain", "plain"), ("off", "off")):
env = {} if raw is None else {"CLAUDE_PLUGIN_OPTION_UI_MODE": raw}
with self.subTest(raw=raw), mock.patch.dict(os.environ, env, clear=True):
self.assertEqual(SFX._ui_mode(), expected)
def test_ambient_surface_mode_matrix_and_no_color_orthogonality(self):
full = "\x1b[32mFULL ART ●◉○\x1b[0m"
cases = {}
for mode in ("full", "compact", "plain", "off"):
with mock.patch.dict(os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": mode}, clear=True):
cases[mode] = SFX._ambient_surface(full, STATE, project_name="acme")
self.assertEqual(cases["full"], full)
self.assertIsNone(cases["off"])
self.assertNotIn("FULL ART", cases["compact"])
self.assertIn("salesforce-development", cases["compact"])
self.assertIn("Build", cases["compact"])
self.assertLessEqual(max(map(SFX._terminal_cell_width, cases["compact"].splitlines())), 80)
self.assertNotIn("\x1b", cases["plain"])
self.assertNotRegex(cases["plain"], r"[●◉○]")
self.assertIn("Current stage: Build", cases["plain"])
self.assertIn("Reached: Connect, Project", cases["plain"])
self.assertIn("No evidence: Build, Test, Deploy, Observe", cases["plain"])
with mock.patch.dict(os.environ, {
"CLAUDE_PLUGIN_OPTION_UI_MODE": "full", "NO_COLOR": "1"
}, clear=True):
# NO_COLOR affects renderers, not mode selection or ambient policy.
self.assertEqual(SFX._ui_mode(), "full")
self.assertIsNotNone(SFX._ambient_surface(strip_ansi(full), STATE, project_name="acme"))
def capture_detect(self, mode: str, *, source: str = "startup", session_title: str = "") -> dict:
with tempfile.TemporaryDirectory() as td:
root = Path(td)
(root / "sfdx-project.json").write_text(
'{"packageDirectories":[{"path":"force-app","default":true}]}',
encoding="utf-8",
)
old = Path.cwd()
os.chdir(root)
try:
payload = io.StringIO(json.dumps({
"source": source, "session_id": "ui-mode", "session_title": session_title,
}))
out = io.StringIO()
with mock.patch.dict(os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": mode}, clear=True), \
mock.patch.object(SFX, "_derive_journey_state", return_value=STATE), \
mock.patch.object(SFX, "project_meta", return_value={"name": "acme"}), \
mock.patch.object(SFX, "project_stats", return_value={
key: 0 for key in (
"apex_src", "apex_test", "triggers", "lwc",
"aura", "objects", "permsets", "flows",
)
}), \
mock.patch.object(SFX, "_configured_target_alias", return_value="dev"), \
mock.patch.object(SFX, "_record_welcomed") as welcomed, \
mock.patch.object(SFX, "_record_entered") as entered, \
mock.patch.object(SFX, "_record_rail_signature") as signature, \
mock.patch.object(SFX.sys, "stdin", payload), redirect_stdout(out):
self.assertEqual(SFX.cmd_detect(), 0)
if source == "compact" or mode == "off":
welcomed.assert_not_called()
entered.assert_not_called()
signature.assert_not_called()
else:
welcomed.assert_called_once_with("ui-mode")
entered.assert_called_once_with("ui-mode")
signature.assert_called_once_with("ui-mode", STATE)
return json.loads(out.getvalue())
finally:
os.chdir(old)
def test_session_start_title_is_bounded_project_only_and_respects_user_title(self):
startup = self.capture_detect("full")
self.assertEqual(startup["sessionTitle"], "SF · acme")
self.assertLessEqual(SFX._terminal_cell_width(startup["sessionTitle"]), 60)
self.assertNotIn("dev", startup["sessionTitle"])
self.assertNotIn("sessionTitle", self.capture_detect(
"full", source="resume", session_title="My hand-named session"
))
self.assertNotIn("sessionTitle", self.capture_detect("full", source="clear"))
self.assertNotIn("sessionTitle", self.capture_detect("off"))
def test_only_session_start_handler_has_a_status_message(self):
plugin = json.loads(PLUGIN_JSON.read_text(encoding="utf-8"))
handlers = []
for event, blocks in plugin["hooks"].items():
for block in blocks:
for handler in block.get("hooks", []):
if "statusMessage" in handler:
handlers.append((event, handler["statusMessage"]))
self.assertEqual(handlers, [(
"SessionStart", "Loading local Salesforce project context…"
)])
def test_session_start_context_is_invariant_and_off_only_hides_visible_ambient_ui(self):
results = {mode: self.capture_detect(mode) for mode in ("full", "compact", "plain", "off")}
contexts = {
result["hookSpecificOutput"]["additionalContext"] for result in results.values()
}
self.assertEqual(len(contexts), 1)
self.assertIn("skills first", contexts.pop().lower())
self.assertIn(SFX.BANNER_WORDMARK, strip_ansi(results["full"]["systemMessage"]))
self.assertNotIn(SFX.BANNER_WORDMARK, results["compact"]["systemMessage"])
self.assertIn("Current stage: Build", results["plain"]["systemMessage"])
self.assertNotIn("systemMessage", results["off"])
def test_off_does_not_claim_or_mark_a_hidden_ambient_prompt_surface(self):
with tempfile.TemporaryDirectory() as td:
root = Path(td)
root.joinpath("sfdx-project.json").write_text("{}", encoding="utf-8")
old_cwd = Path.cwd()
old_markers = SFX._WELCOME_MARKER_DIR
old_runtime = SFX._PROMPT_RUNTIME_DIR
os.chdir(root)
SFX._WELCOME_MARKER_DIR = root / "markers"
SFX._PROMPT_RUNTIME_DIR = root / "runtime"
try:
payload = {
"prompt": "add a field to Account",
"session_id": "ui-off",
"prompt_id": "prompt-1",
}
context = SFX._prompt_context(payload, rotate_fallback=False)
out = io.StringIO()
with mock.patch.dict(
os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": "off"}, clear=True), \
mock.patch.object(
SFX, "_resolve_position_and_org", return_value=(STATE, {"alias": "dev"})), \
mock.patch.object(SFX, "project_meta", return_value={"name": "acme"}), \
redirect_stdout(out):
self.assertEqual(SFX.cmd_orientation_paint(
payload=payload, prompt_context=context
), 0)
self.assertEqual(json.loads(out.getvalue()), {"continue": True})
self.assertFalse(SFX._rail_painted_this_turn(context))
self.assertFalse(SFX._welcomed_this_session("ui-off"))
self.assertFalse(SFX._entered_this_session("ui-off"))
self.assertIsNone(SFX._last_rail_signature("ui-off"))
finally:
SFX._PROMPT_RUNTIME_DIR = old_runtime
SFX._WELCOME_MARKER_DIR = old_markers
os.chdir(old_cwd)
def test_off_wayfinder_keeps_model_note_without_claim_or_signature(self):
with tempfile.TemporaryDirectory() as td:
root = Path(td)
root.joinpath("sfdx-project.json").write_text("{}", encoding="utf-8")
old_cwd = Path.cwd()
old_markers = SFX._WELCOME_MARKER_DIR
old_runtime = SFX._PROMPT_RUNTIME_DIR
os.chdir(root)
SFX._WELCOME_MARKER_DIR = root / "markers"
SFX._PROMPT_RUNTIME_DIR = root / "runtime"
try:
payload = {
"tool_input": {"command": "sf config set target-org dev"},
"session_id": "wayfinder-off",
"prompt_id": "prompt-1",
}
org = {
"alias": "dev", "edition": "Developer", "apiVersion": "65.0",
"username": "dev@example.com", "instanceUrl": "https://example.com",
}
out = io.StringIO()
with mock.patch.dict(
os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": "off"}, clear=True), \
mock.patch.object(SFX, "get_target_org_detailed", return_value=("dev", "")), \
mock.patch.object(SFX, "resolve_org_info", return_value=org), \
mock.patch.object(SFX, "project_meta", return_value={"name": "acme"}), \
mock.patch.object(SFX, "project_stats", return_value={}), \
mock.patch.object(SFX, "git_status_line", return_value=""), \
mock.patch.object(SFX, "_derive_journey_state", return_value=STATE), \
redirect_stdout(out):
self.assertEqual(SFX.cmd_wayfinder(payload=payload), 0)
result = json.loads(out.getvalue())
self.assertIn("Target org is now 'dev'", result[
"hookSpecificOutput"]["additionalContext"])
self.assertNotIn("systemMessage", result)
context = SFX._prompt_context(payload, rotate_fallback=False)
self.assertFalse(SFX._rail_painted_this_turn(context))
self.assertIsNone(SFX._last_rail_signature("wayfinder-off"))
finally:
SFX._PROMPT_RUNTIME_DIR = old_runtime
SFX._WELCOME_MARKER_DIR = old_markers
os.chdir(old_cwd)
def test_resolution_trace_is_ambient_but_keeps_evidence_side_effects(self):
payload = {"tool_input": {"skill": "salesforce-development:platform-apex-generate"}}
results = {}
for mode in ("full", "compact", "plain", "off"):
out = io.StringIO()
with mock.patch.dict(os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": mode}, clear=True), \
mock.patch.object(SFX, "_read_hook_payload", return_value=payload), \
redirect_stdout(out):
self.assertEqual(SFX.cmd_resolution_trace(), 0)
results[mode] = json.loads(out.getvalue())
self.assertIn("systemMessage", results["full"])
self.assertIn("systemMessage", results["compact"])
self.assertNotIn("\x1b", results["plain"]["systemMessage"])
self.assertNotIn("systemMessage", results["off"])
def test_explicit_readiness_and_safety_renderers_ignore_ui_mode(self):
report = {"tools": [{
"name": "Salesforce CLI", "status": "critical",
"message": "Install the CLI before continuing.",
}]}
readiness = []
for mode in ("full", "compact", "plain", "off"):
with mock.patch.dict(os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": mode}, clear=True):
readiness.append(SFX.render_readiness_text(report))
self.assertEqual(len(set(readiness)), 1)
self.assertIn("BLOCKED", readiness[0])
self.assertIn("Install the CLI", readiness[0])
def test_explicit_journey_output_is_identical_in_every_mode(self):
outputs = []
for mode in ("full", "compact", "plain", "off"):
out = io.StringIO()
with mock.patch.dict(os.environ, {"CLAUDE_PLUGIN_OPTION_UI_MODE": mode}, clear=True), \
mock.patch.object(SFX, "_journey_state", return_value=STATE), redirect_stdout(out):
self.assertEqual(SFX.cmd_journey([]), 0)
outputs.append(out.getvalue())
self.assertEqual(len(set(outputs)), 1)
self.assertIn("current: Build", outputs[0])
if __name__ == "__main__":
unittest.main(verbosity=2)