Panels are a new kernel rendering tier between surfaces and block renderers, enabling composable companion views (e.g. notes panel inside chat). This version ships the plumbing layer: - Manifest validation for panels field (provider map + consumer array) - Soft-dep warning at install time for unresolved panel consumers - PanelMeta struct + resolvePanels() resolves consumers at surface load - window.__PANELS__ injected into base template - sw.panels SDK module (open/close/toggle/isOpen/isAvailable/list/active) - Lazy JS loading pipeline with module caching - 10 new tests (6 validation + 4 resolve) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
package pages
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"armature/store"
|
|
)
|
|
|
|
func TestResolvePanels_HappyPath(t *testing.T) {
|
|
// Provider package with a panels map
|
|
provider := &store.PackageRegistration{
|
|
ID: "notes",
|
|
Title: "Notes",
|
|
Enabled: true,
|
|
Source: "extension",
|
|
Manifest: map[string]any{
|
|
"panels": map[string]any{
|
|
"reference": map[string]any{
|
|
"entry": "js/panels/reference.js",
|
|
"title": "Notes Reference",
|
|
"icon": "📝",
|
|
"description": "Searchable note list",
|
|
"min_width": float64(280),
|
|
"default_width": float64(400),
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Consumer package that wants notes.reference
|
|
consumer := &store.PackageRegistration{
|
|
ID: "chat",
|
|
Title: "Chat",
|
|
Enabled: true,
|
|
Source: "extension",
|
|
Manifest: map[string]any{
|
|
"panels": []any{"notes.reference"},
|
|
},
|
|
}
|
|
|
|
e := testEngine(t, provider, consumer)
|
|
panels := e.resolvePanels(context.Background(), consumer.Manifest)
|
|
|
|
if len(panels) != 1 {
|
|
t.Fatalf("expected 1 panel, got %d", len(panels))
|
|
}
|
|
p := panels[0]
|
|
if p.PanelID != "notes.reference" {
|
|
t.Errorf("expected panel_id 'notes.reference', got %q", p.PanelID)
|
|
}
|
|
if p.PackageID != "notes" {
|
|
t.Errorf("expected package_id 'notes', got %q", p.PackageID)
|
|
}
|
|
if p.Entry != "js/panels/reference.js" {
|
|
t.Errorf("expected entry 'js/panels/reference.js', got %q", p.Entry)
|
|
}
|
|
if p.Title != "Notes Reference" {
|
|
t.Errorf("expected title 'Notes Reference', got %q", p.Title)
|
|
}
|
|
if p.Icon != "📝" {
|
|
t.Errorf("expected icon '📝', got %q", p.Icon)
|
|
}
|
|
if p.MinWidth != 280 {
|
|
t.Errorf("expected min_width 280, got %d", p.MinWidth)
|
|
}
|
|
if p.DefaultWidth != 400 {
|
|
t.Errorf("expected default_width 400, got %d", p.DefaultWidth)
|
|
}
|
|
}
|
|
|
|
func TestResolvePanels_ProviderMissing(t *testing.T) {
|
|
// Consumer references a package that doesn't exist
|
|
consumer := &store.PackageRegistration{
|
|
ID: "chat",
|
|
Title: "Chat",
|
|
Enabled: true,
|
|
Source: "extension",
|
|
Manifest: map[string]any{
|
|
"panels": []any{"nonexistent.panel"},
|
|
},
|
|
}
|
|
|
|
e := testEngine(t, consumer)
|
|
panels := e.resolvePanels(context.Background(), consumer.Manifest)
|
|
|
|
if len(panels) != 0 {
|
|
t.Errorf("expected 0 panels for missing provider, got %d", len(panels))
|
|
}
|
|
}
|
|
|
|
func TestResolvePanels_ProviderDisabled(t *testing.T) {
|
|
provider := &store.PackageRegistration{
|
|
ID: "notes",
|
|
Title: "Notes",
|
|
Enabled: false, // disabled
|
|
Source: "extension",
|
|
Manifest: map[string]any{
|
|
"panels": map[string]any{
|
|
"reference": map[string]any{
|
|
"entry": "js/panels/reference.js",
|
|
"title": "Notes Reference",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
consumer := &store.PackageRegistration{
|
|
ID: "chat",
|
|
Title: "Chat",
|
|
Enabled: true,
|
|
Source: "extension",
|
|
Manifest: map[string]any{
|
|
"panels": []any{"notes.reference"},
|
|
},
|
|
}
|
|
|
|
e := testEngine(t, provider, consumer)
|
|
panels := e.resolvePanels(context.Background(), consumer.Manifest)
|
|
|
|
if len(panels) != 0 {
|
|
t.Errorf("expected 0 panels for disabled provider, got %d", len(panels))
|
|
}
|
|
}
|
|
|
|
func TestResolvePanels_NoPanelsField(t *testing.T) {
|
|
consumer := &store.PackageRegistration{
|
|
ID: "chat",
|
|
Title: "Chat",
|
|
Enabled: true,
|
|
Source: "extension",
|
|
Manifest: map[string]any{},
|
|
}
|
|
|
|
e := testEngine(t, consumer)
|
|
panels := e.resolvePanels(context.Background(), consumer.Manifest)
|
|
|
|
if panels != nil {
|
|
t.Errorf("expected nil panels for manifest without panels field, got %v", panels)
|
|
}
|
|
}
|