feat: precipitate grid presentation (row/column/region) into skill contract
All checks were successful
技能自动化发布 / release (push) Successful in 9s

This commit is contained in:
2026-09-20 21:08:23 +08:00
parent a125b00285
commit a70da6a076
18 changed files with 432 additions and 77 deletions

View File

@@ -736,6 +736,7 @@ class TestActionsManifest(unittest.TestCase):
"afterFailure",
"cellPresentation",
"CELL_PRESENTATION.md",
"整行",
"labelColumn",
"selectionMode",
"勾选驱动",

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Cell Presentation 契约:schema + 文档 + 示例。"""
"""Cell / Grid Presentation 契约:schema + 文档 + 示例。"""
from __future__ import annotations
import json
@@ -9,31 +9,34 @@ import unittest
from util.runtime_paths import get_skill_root
def _load_schema() -> dict:
path = os.path.join(
get_skill_root(),
"assets",
"schemas",
"skill-cell-presentation.schema.json",
)
with open(path, encoding="utf-8") as f:
def _skill_path(*parts: str) -> str:
return os.path.join(get_skill_root(), *parts)
def _load_json(*parts: str) -> dict:
with open(_skill_path(*parts), encoding="utf-8") as f:
return json.load(f)
def _load_example() -> dict:
path = os.path.join(
get_skill_root(),
"assets",
"examples",
"skill-cell-presentation.example.json",
)
with open(path, encoding="utf-8") as f:
return json.load(f)
def _load_cell_schema() -> dict:
return _load_json("assets", "schemas", "skill-cell-presentation.schema.json")
def _load_cell_example() -> dict:
return _load_json("assets", "examples", "skill-cell-presentation.example.json")
def _load_grid_schema() -> dict:
return _load_json("assets", "schemas", "skill-grid-presentation.schema.json")
def _load_grid_example() -> dict:
return _load_json("assets", "examples", "skill-grid-presentation.example.json")
class TestCellPresentationContract(unittest.TestCase):
def test_schema_file_exists_and_parses(self) -> None:
schema = _load_schema()
schema = _load_cell_schema()
self.assertEqual(schema.get("title"), "SkillCellPresentationDocument")
self.assertIs(schema.get("additionalProperties"), False)
self.assertEqual(schema["properties"]["schemaVersion"].get("const"), 1)
@@ -45,33 +48,38 @@ class TestCellPresentationContract(unittest.TestCase):
import jsonschema
except ImportError:
self.skipTest("jsonschema not installed")
jsonschema.validate(instance=_load_example(), schema=_load_schema())
jsonschema.validate(instance=_load_cell_example(), schema=_load_cell_schema())
def test_actions_schema_defines_cell_presentation_enable_block(self) -> None:
path = os.path.join(
get_skill_root(), "assets", "schemas", "skill-actions.schema.json"
)
with open(path, encoding="utf-8") as f:
actions_schema = json.load(f)
actions_schema = _load_json("assets", "schemas", "skill-actions.schema.json")
self.assertIn("cellPresentation", actions_schema["properties"])
self.assertIn("cellPresentationDeclaration", actions_schema["$defs"])
def test_docs_and_example_exist(self) -> None:
root = get_skill_root()
self.assertTrue(os.path.isfile(os.path.join(root, "references", "CELL_PRESENTATION.md")))
self.assertTrue(os.path.isfile(_skill_path("references", "CELL_PRESENTATION.md")))
self.assertTrue(
os.path.isfile(
os.path.join(root, "assets", "schemas", "skill-cell-presentation.schema.json")
_skill_path("assets", "schemas", "skill-cell-presentation.schema.json")
)
)
self.assertTrue(
os.path.isfile(
os.path.join(root, "assets", "examples", "skill-cell-presentation.example.json")
_skill_path("assets", "examples", "skill-cell-presentation.example.json")
)
)
self.assertTrue(
os.path.isfile(
_skill_path("assets", "schemas", "skill-grid-presentation.schema.json")
)
)
self.assertTrue(
os.path.isfile(
_skill_path("assets", "examples", "skill-grid-presentation.example.json")
)
)
def test_cell_presentation_md_covers_contract(self) -> None:
path = os.path.join(get_skill_root(), "references", "CELL_PRESENTATION.md")
path = _skill_path("references", "CELL_PRESENTATION.md")
with open(path, encoding="utf-8") as f:
text = f.read()
for marker in (
@@ -80,14 +88,77 @@ class TestCellPresentationContract(unittest.TestCase):
"warning",
"alert",
"_jiangchang_cell_presentation",
"_jiangchang_grid_presentation",
"cellPresentation",
"派生",
"任意 CSS",
"用途不限",
"schemaVersion",
"整行",
"整列",
"all",
"格子 > 区域 > 行 > 列",
"详情",
):
self.assertIn(marker, text, msg=f"CELL_PRESENTATION.md missing {marker!r}")
def test_grid_schema_file_exists_and_parses(self) -> None:
schema = _load_grid_schema()
self.assertEqual(schema.get("title"), "SkillGridPresentationDocument")
self.assertIs(schema.get("additionalProperties"), False)
self.assertEqual(schema["properties"]["schemaVersion"].get("const"), 1)
tones = set(schema["$defs"]["tone"].get("enum") or [])
self.assertEqual(tones, {"danger", "warning", "info", "success", "muted"})
self.assertIn("not", schema["$defs"]["ruleItem"])
def test_grid_example_validates_against_schema(self) -> None:
try:
import jsonschema
except ImportError:
self.skipTest("jsonschema not installed")
jsonschema.validate(instance=_load_grid_example(), schema=_load_grid_schema())
def test_grid_schema_rejects_all_by_all(self) -> None:
try:
import jsonschema
except ImportError:
self.skipTest("jsonschema not installed")
payload = {
"schemaVersion": 1,
"skill": "your-skill-slug",
"table": "business_records",
"rowKey": "id",
"rules": [
{
"ruleKey": "paint-everything",
"rows": "all",
"columns": "all",
"tone": "danger",
}
],
}
with self.assertRaises(jsonschema.ValidationError):
jsonschema.validate(instance=payload, schema=_load_grid_schema())
def test_internal_metadata_hides_presentation_tables(self) -> None:
from db.display_metadata import INTERNAL_METADATA_TABLES, METADATA_TABLES
self.assertIn("_jiangchang_cell_presentation", INTERNAL_METADATA_TABLES)
self.assertIn("_jiangchang_grid_presentation", INTERNAL_METADATA_TABLES)
self.assertTrue(INTERNAL_METADATA_TABLES <= METADATA_TABLES)
def test_template_actions_do_not_enable_presentation_by_default(self) -> None:
manifest = _load_json("assets", "actions.json")
self.assertNotEqual(manifest.get("cellPresentation", {}).get("enabled"), True)
def test_checklist_covers_grid_presentation(self) -> None:
path = _skill_path("development", "CHECKLIST.md")
with open(path, encoding="utf-8") as f:
text = f.read()
self.assertIn("A6-15", text)
self.assertIn("cellPresentation", text)
self.assertIn("CELL_PRESENTATION.md", text)
if __name__ == "__main__":
unittest.main()

View File

@@ -327,6 +327,11 @@ class TestDocsStandards(unittest.TestCase):
os.path.join(root, "assets", "schemas", "skill-cell-presentation.schema.json")
)
)
self.assertTrue(
os.path.isfile(
os.path.join(root, "assets", "schemas", "skill-grid-presentation.schema.json")
)
)
self.assertTrue(os.path.isfile(os.path.join(root, "tests", "test_actions_manifest.py")))
self.assertTrue(os.path.isfile(os.path.join(root, "tests", "test_data_management_contract.py")))
@@ -375,6 +380,7 @@ class TestDocsStandards(unittest.TestCase):
"表增删改与退役",
"drop_deprecated_user_table",
"孤儿物理表",
"_jiangchang_grid_presentation",
):
self.assertIn(marker, text, msg=f"SCHEMA.md missing {marker!r}")
@@ -416,6 +422,8 @@ class TestDocsStandards(unittest.TestCase):
text = self._read("assets/examples/README.md")
self.assertIn("visible-when-action.example.json", text)
self.assertIn("selection-mode-optional.action.example.json", text)
self.assertIn("skill-cell-presentation.example.json", text)
self.assertIn("skill-grid-presentation.example.json", text)
checklist = self._read("development/CHECKLIST.md")
self.assertIn("A4-7", checklist)
self.assertIn("visibleWhen", checklist)