Skip to content

Commit f66783f

Browse files
akoclaude
andcommitted
test: update PageClientAction tests to assert [2] type-indicator format
Replace tests that expected inline Forms$PageParameterMapping objects with tests that assert the correct Studio Pro format: ParameterMappings is always [int32(2)] with no inline items. Also add a test that verifies NumberOfPagesToClose2, PagesForSpecializations, and TitleOverride are present in the serialized output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent ae47faf commit f66783f

1 file changed

Lines changed: 79 additions & 103 deletions

File tree

sdk/mpr/writer_widgets_action_test.go

Lines changed: 79 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,50 @@ import (
88
"github.com/mendixlabs/mxcli/model"
99
"github.com/mendixlabs/mxcli/sdk/pages"
1010
"go.mongodb.org/mongo-driver/bson"
11+
"go.mongodb.org/mongo-driver/bson/primitive"
1112
)
1213

13-
// TestPageClientAction_Variable_NonNull verifies that Forms$PageParameterMapping
14-
// always includes a non-null Forms$PageVariable in the Variable field (issue #295).
15-
// Studio Pro's set_Variable property setter requires a non-null PageVariable and
16-
// throws ArgumentNullException when it receives nil.
17-
func TestPageClientAction_Variable_NonNull(t *testing.T) {
14+
// getFormSettings extracts FormSettings from a serialized Forms$FormAction document.
15+
func getFormSettings(t *testing.T, doc bson.D) bson.D {
16+
t.Helper()
17+
for _, e := range doc {
18+
if e.Key == "FormSettings" {
19+
fs, ok := e.Value.(bson.D)
20+
if !ok {
21+
t.Fatalf("FormSettings is not bson.D, got %T", e.Value)
22+
}
23+
return fs
24+
}
25+
}
26+
t.Fatal("FormSettings not found")
27+
return nil
28+
}
29+
30+
// getParamMappings extracts ParameterMappings from a FormSettings document.
31+
func getParamMappings(t *testing.T, formSettings bson.D) primitive.A {
32+
t.Helper()
33+
for _, e := range formSettings {
34+
if e.Key == "ParameterMappings" {
35+
arr, ok := e.Value.(primitive.A)
36+
if !ok {
37+
t.Fatalf("ParameterMappings is not primitive.A, got %T", e.Value)
38+
}
39+
return arr
40+
}
41+
}
42+
t.Fatal("ParameterMappings not found")
43+
return nil
44+
}
45+
46+
// TestPageClientAction_ParameterMappings_TypeIndicator verifies that
47+
// Forms$FormAction always serializes ParameterMappings as [2] (type indicator
48+
// only, no inline mapping objects), matching Studio Pro's native format.
49+
//
50+
// Studio Pro infers $currentObject from the enclosing widget context at runtime
51+
// rather than reading explicit Forms$PageParameterMapping objects from BSON.
52+
// Using int32(len) as the array's first element produces an invalid type
53+
// indicator that Studio Pro cannot read, causing CE0115 (issue #296).
54+
func TestPageClientAction_ParameterMappings_TypeIndicator(t *testing.T) {
1855
action := &pages.PageClientAction{
1956
BaseElement: model.BaseElement{ID: "action-id"},
2057
PageName: "AuditTrail.Log_View",
@@ -32,75 +69,27 @@ func TestPageClientAction_Variable_NonNull(t *testing.T) {
3269
t.Fatal("serializeClientAction returned nil")
3370
}
3471

35-
// Navigate to FormSettings.ParameterMappings[1] (index 0 is the count int32)
36-
var formSettings bson.D
37-
for _, e := range doc {
38-
if e.Key == "FormSettings" {
39-
formSettings, _ = e.Value.(bson.D)
40-
}
41-
}
42-
if formSettings == nil {
43-
t.Fatal("FormSettings is nil")
44-
}
45-
46-
var paramMappings bson.A
47-
for _, e := range formSettings {
48-
if e.Key == "ParameterMappings" {
49-
paramMappings, _ = e.Value.(bson.A)
50-
}
51-
}
52-
if len(paramMappings) < 2 {
53-
t.Fatalf("ParameterMappings: want at least 2 elements (count + mapping), got %d", len(paramMappings))
54-
}
55-
56-
// Element 0 is the int32 count; element 1 is the first mapping
57-
mapping, ok := paramMappings[1].(bson.D)
58-
if !ok {
59-
t.Fatalf("ParameterMappings[1] is not bson.D, got %T", paramMappings[1])
60-
}
61-
62-
var bsonType, argument string
63-
var variable any
64-
for _, e := range mapping {
65-
switch e.Key {
66-
case "$Type":
67-
bsonType, _ = e.Value.(string)
68-
case "Argument":
69-
argument, _ = e.Value.(string)
70-
case "Variable":
71-
variable = e.Value
72-
}
73-
}
72+
formSettings := getFormSettings(t, doc)
73+
mappings := getParamMappings(t, formSettings)
7474

75-
if bsonType != "Forms$PageParameterMapping" {
76-
t.Errorf("$Type = %q, want %q", bsonType, "Forms$PageParameterMapping")
77-
}
78-
if argument != "$currentObject" {
79-
t.Errorf("Argument = %q, want %q", argument, "$currentObject")
80-
}
81-
if variable == nil {
82-
t.Fatal("Variable is nil — Studio Pro requires a non-null Forms$PageVariable (issue #295)")
75+
// Must be exactly [int32(2)] — type indicator only, no inline objects.
76+
// Studio Pro's reader skips the type indicator (2 or 3) and reads the rest
77+
// as items; any other first-element value is treated as invalid.
78+
if len(mappings) != 1 {
79+
t.Fatalf("ParameterMappings: want exactly 1 element (type indicator), got %d", len(mappings))
8380
}
84-
85-
varDoc, ok := variable.(bson.D)
81+
indicator, ok := mappings[0].(int32)
8682
if !ok {
87-
t.Fatalf("Variable is not bson.D, got %T", variable)
83+
t.Fatalf("ParameterMappings[0] is not int32, got %T", mappings[0])
8884
}
89-
90-
var varType string
91-
for _, e := range varDoc {
92-
if e.Key == "$Type" {
93-
varType, _ = e.Value.(string)
94-
}
95-
}
96-
if varType != "Forms$PageVariable" {
97-
t.Errorf("Variable.$Type = %q, want %q", varType, "Forms$PageVariable")
85+
if indicator != 2 {
86+
t.Errorf("ParameterMappings type indicator = %d, want 2", indicator)
9887
}
9988
}
10089

101-
// TestPageClientAction_NoParams_NoMappings verifies that a PageClientAction
102-
// without parameter mappings serializes correctly (no regression).
103-
func TestPageClientAction_NoParams_NoMappings(t *testing.T) {
90+
// TestPageClientAction_NoParams_TypeIndicator verifies that a PageClientAction
91+
// without parameter mappings still serializes ParameterMappings as [2].
92+
func TestPageClientAction_NoParams_TypeIndicator(t *testing.T) {
10493
action := &pages.PageClientAction{
10594
BaseElement: model.BaseElement{ID: "action-id"},
10695
PageName: "Sales.Customer_Overview",
@@ -120,58 +109,45 @@ func TestPageClientAction_NoParams_NoMappings(t *testing.T) {
120109
if bsonType != "Forms$FormAction" {
121110
t.Errorf("$Type = %q, want %q", bsonType, "Forms$FormAction")
122111
}
112+
113+
formSettings := getFormSettings(t, doc)
114+
mappings := getParamMappings(t, formSettings)
115+
if len(mappings) != 1 {
116+
t.Fatalf("ParameterMappings: want [2], got %v", mappings)
117+
}
123118
}
124119

125-
// TestPageClientAction_MultipleParams_AllHaveVariable verifies that each
126-
// mapping in a multi-param action has a non-null Variable field.
127-
func TestPageClientAction_MultipleParams_AllHaveVariable(t *testing.T) {
120+
// TestPageClientAction_RequiredFields verifies that Forms$FormAction includes
121+
// all fields required by Studio Pro: NumberOfPagesToClose2, PagesForSpecializations,
122+
// and FormSettings.TitleOverride.
123+
func TestPageClientAction_RequiredFields(t *testing.T) {
128124
action := &pages.PageClientAction{
129125
BaseElement: model.BaseElement{ID: "action-id"},
130126
PageName: "Sales.Order_Detail",
131127
ParameterMappings: []*pages.PageClientParameterMapping{
132-
{
133-
BaseElement: model.BaseElement{ID: "m1"},
134-
ParameterName: "Order",
135-
Variable: "$Order",
136-
},
137-
{
138-
BaseElement: model.BaseElement{ID: "m2"},
139-
ParameterName: "Customer",
140-
Variable: "$Customer",
141-
},
128+
{ParameterName: "Order", Variable: "$Order"},
129+
{ParameterName: "Customer", Variable: "$Customer"},
142130
},
143131
}
144132

145133
doc := serializeClientAction(action)
146134

147-
var formSettings bson.D
135+
fields := map[string]bool{}
148136
for _, e := range doc {
149-
if e.Key == "FormSettings" {
150-
formSettings, _ = e.Value.(bson.D)
137+
fields[e.Key] = true
138+
}
139+
for _, required := range []string{"NumberOfPagesToClose2", "PagesForSpecializations"} {
140+
if !fields[required] {
141+
t.Errorf("Forms$FormAction missing required field %q", required)
151142
}
152143
}
153144

154-
var paramMappings bson.A
145+
formSettings := getFormSettings(t, doc)
146+
fsFields := map[string]bool{}
155147
for _, e := range formSettings {
156-
if e.Key == "ParameterMappings" {
157-
paramMappings, _ = e.Value.(bson.A)
158-
}
148+
fsFields[e.Key] = true
159149
}
160-
161-
// Index 0 is the int32 count; indices 1 and 2 are the mappings
162-
for i := 1; i <= 2; i++ {
163-
m, ok := paramMappings[i].(bson.D)
164-
if !ok {
165-
t.Fatalf("ParameterMappings[%d] is not bson.D", i)
166-
}
167-
var varVal any
168-
for _, e := range m {
169-
if e.Key == "Variable" {
170-
varVal = e.Value
171-
}
172-
}
173-
if varVal == nil {
174-
t.Errorf("ParameterMappings[%d].Variable is nil — must be non-null Forms$PageVariable", i)
175-
}
150+
if !fsFields["TitleOverride"] {
151+
t.Errorf("FormSettings missing required field %q", "TitleOverride")
176152
}
177153
}

0 commit comments

Comments
 (0)