This commit is contained in:
@@ -5,42 +5,92 @@ from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
WIDGET = ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "active-projects" / "v1"
|
||||
WIDGETS = {
|
||||
"active-projects": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "active-projects" / "v1",
|
||||
"documents": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "documents" / "v1",
|
||||
"tasks": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "tasks" / "v1",
|
||||
"finance": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "finance" / "v1",
|
||||
"crm": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "crm" / "v1",
|
||||
"discussions": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "discussions" / "v1",
|
||||
"notifications": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "notifications" / "v1",
|
||||
"scheduling": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "scheduling" / "v1",
|
||||
"inventory": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "inventory" / "v1",
|
||||
"agents": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "agents" / "v1",
|
||||
"status-page": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "status-page" / "v1",
|
||||
"project-db": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-db" / "v1",
|
||||
"notes": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "notes" / "v1",
|
||||
"project-overview": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-overview" / "v1",
|
||||
"files": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "files" / "v1",
|
||||
"proposals": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "proposals" / "v1",
|
||||
"terms": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "terms" / "v1",
|
||||
"activity": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "activity" / "v1",
|
||||
"organizations": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "organizations" / "v1",
|
||||
"gitea": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "gitea" / "v1",
|
||||
"schemes": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "schemes" / "v1",
|
||||
"task-kanban": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "task-kanban" / "v1",
|
||||
"team-workload": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "team-workload" / "v1",
|
||||
"project-timeline": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-timeline" / "v1",
|
||||
"crm-funnel": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "crm-funnel" / "v1",
|
||||
"finance-dashboard": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "finance-dashboard" / "v1",
|
||||
"proposal-approvals": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "proposal-approvals" / "v1",
|
||||
"documents-completeness": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "documents-completeness" / "v1",
|
||||
"agents-health": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "agents-health" / "v1",
|
||||
"schedule-calendar": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "schedule-calendar" / "v1",
|
||||
"acl-matrix": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "acl-matrix" / "v1",
|
||||
"inventory-warnings": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "inventory-warnings" / "v1",
|
||||
"gitea-board": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "gitea-board" / "v1",
|
||||
"schemes-progress": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "schemes-progress" / "v1",
|
||||
"notes-tree": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "notes-tree" / "v1",
|
||||
"project-db-preview": ROOT / "plugins" / "shd-mcp-plugin" / "widgets" / "project-db-preview" / "v1",
|
||||
}
|
||||
|
||||
|
||||
class WidgetArtifactTests(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.manifest = json.loads((WIDGET / "manifest.json").read_text(encoding="utf-8"))
|
||||
cls.resource = next(item for item in cls.manifest["resources"] if item["file"] == "index.html")
|
||||
cls.html = (WIDGET / "index.html").read_text(encoding="utf-8")
|
||||
cls.artifacts = {}
|
||||
for name, widget in WIDGETS.items():
|
||||
manifest = json.loads((widget / "manifest.json").read_text(encoding="utf-8"))
|
||||
resource = next(item for item in manifest["resources"] if item["file"] == "index.html")
|
||||
cls.artifacts[name] = {
|
||||
"widget": widget,
|
||||
"manifest": manifest,
|
||||
"resource": resource,
|
||||
"html": (widget / "index.html").read_text(encoding="utf-8"),
|
||||
}
|
||||
|
||||
def test_manifest_and_checksums_match_artifact(self):
|
||||
digest = hashlib.sha256(self.html.encode("utf-8")).hexdigest()
|
||||
self.assertEqual(self.resource["uri"], "ui://shd/active-projects/v1.html")
|
||||
self.assertEqual(self.resource["mediaType"], "text/html;profile=mcp-app")
|
||||
self.assertEqual(self.resource["bytes"], len(self.html.encode("utf-8")))
|
||||
self.assertEqual(self.resource["sha256"], digest)
|
||||
for name, artifact in self.artifacts.items():
|
||||
widget = artifact["widget"]
|
||||
manifest = artifact["manifest"]
|
||||
resource = artifact["resource"]
|
||||
html = artifact["html"]
|
||||
digest = hashlib.sha256(html.encode("utf-8")).hexdigest()
|
||||
self.assertEqual(resource["uri"], f"ui://shd/{name}/v1.html", name)
|
||||
self.assertEqual(resource["mediaType"], "text/html;profile=mcp-app", name)
|
||||
self.assertEqual(resource["bytes"], len(html.encode("utf-8")), name)
|
||||
self.assertEqual(resource["sha256"], digest, name)
|
||||
|
||||
checksum = (WIDGET / "SHA256SUMS").read_text(encoding="utf-8").strip().split()
|
||||
self.assertEqual(checksum, [digest, "index.html"])
|
||||
checksum = (widget / "SHA256SUMS").read_text(encoding="utf-8").strip().split()
|
||||
self.assertEqual(checksum, [digest, "index.html"], name)
|
||||
|
||||
license_path = (WIDGET / self.manifest["license"]).resolve()
|
||||
self.assertEqual(license_path, (ROOT / "LICENSE").resolve())
|
||||
source_path = (ROOT / self.manifest["source"]["path"]).resolve()
|
||||
self.assertEqual(source_path, (WIDGET / "index.html").resolve())
|
||||
license_path = (widget / manifest["license"]).resolve()
|
||||
self.assertEqual(license_path, (ROOT / "LICENSE").resolve(), name)
|
||||
source_path = (ROOT / manifest["source"]["path"]).resolve()
|
||||
self.assertEqual(source_path, (widget / "index.html").resolve(), name)
|
||||
|
||||
def test_widget_uses_portable_bridge_and_no_external_script(self):
|
||||
for marker in (
|
||||
"ui/initialize",
|
||||
"ui/notifications/initialized",
|
||||
"ui/notifications/tool-input",
|
||||
"ui/notifications/tool-result",
|
||||
"tools/call",
|
||||
"window.openai",
|
||||
):
|
||||
self.assertIn(marker, self.html, marker)
|
||||
self.assertNotIn("<script src=", self.html.lower())
|
||||
for name, artifact in self.artifacts.items():
|
||||
for marker in (
|
||||
"ui/initialize",
|
||||
"ui/notifications/initialized",
|
||||
"ui/notifications/tool-input",
|
||||
"ui/notifications/tool-result",
|
||||
"tools/call",
|
||||
"window.openai",
|
||||
):
|
||||
self.assertIn(marker, artifact["html"], f"{name}: {marker}")
|
||||
self.assertNotIn("<script src=", artifact["html"].lower(), name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user