1
+ /*
2
+ * Copyright 2000-2025 Vaadin Ltd.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5
+ * use this file except in compliance with the License. You may obtain a copy of
6
+ * the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+ * License for the specific language governing permissions and limitations under
14
+ * the License.
15
+ */
16
+ package com .vaadin .flow .server .communication .rpc ;
17
+
18
+ import java .util .Arrays ;
19
+ import java .util .List ;
20
+
21
+ import tools .jackson .databind .JsonNode ;
22
+ import tools .jackson .databind .node .ArrayNode ;
23
+ import tools .jackson .databind .node .ObjectNode ;
24
+ import org .junit .After ;
25
+ import org .junit .Assert ;
26
+ import org .junit .Before ;
27
+ import org .junit .Test ;
28
+
29
+ import com .vaadin .flow .component .ClientCallable ;
30
+ import com .vaadin .flow .component .Component ;
31
+ import com .vaadin .flow .component .EventData ;
32
+ import com .vaadin .flow .component .Tag ;
33
+ import com .vaadin .flow .component .UI ;
34
+ import com .vaadin .flow .internal .JacksonUtils ;
35
+ import com .vaadin .flow .server .MockServletServiceSessionSetup ;
36
+ import com .vaadin .flow .server .VaadinService ;
37
+ import com .vaadin .flow .server .VaadinSession ;
38
+ import com .vaadin .flow .shared .JsonConstants ;
39
+ import com .vaadin .tests .util .MockDeploymentConfiguration ;
40
+ import com .vaadin .tests .util .MockUI ;
41
+
42
+ /**
43
+ * Tests for @ClientCallable method support with bean and collection parameters
44
+ * and return values.
45
+ */
46
+ public class ClientCallableBeanSupportTest {
47
+
48
+ private MockServletServiceSessionSetup mocks ;
49
+ private UI ui ;
50
+ private PublishedServerEventHandlerRpcHandler handler ;
51
+
52
+ // Test bean classes
53
+ public static class SimpleBean {
54
+ public String name ;
55
+ public int value ;
56
+ public boolean active ;
57
+
58
+ public SimpleBean () {
59
+ }
60
+
61
+ public SimpleBean (String name , int value , boolean active ) {
62
+ this .name = name ;
63
+ this .value = value ;
64
+ this .active = active ;
65
+ }
66
+ }
67
+
68
+ public static class NestedBean {
69
+ public String title ;
70
+ public SimpleBean simple ;
71
+
72
+ public NestedBean () {
73
+ }
74
+
75
+ public NestedBean (String title , SimpleBean simple ) {
76
+ this .title = title ;
77
+ this .simple = simple ;
78
+ }
79
+ }
80
+
81
+ // Test component with @ClientCallable methods
82
+ @ Tag (Tag .DIV )
83
+ public static class ComponentWithClientCallableMethods extends Component {
84
+ private SimpleBean receivedBean ;
85
+ private List <SimpleBean > receivedList ;
86
+ private NestedBean receivedNestedBean ;
87
+ private List <Integer > receivedIntegerList ;
88
+
89
+ @ ClientCallable
90
+ public void handleSimpleBean (@ EventData ("bean" ) SimpleBean bean ) {
91
+ this .receivedBean = bean ;
92
+ }
93
+
94
+ @ ClientCallable
95
+ public void handleBeanList (@ EventData ("list" ) List <SimpleBean > list ) {
96
+ this .receivedList = list ;
97
+ }
98
+
99
+ @ ClientCallable
100
+ public void handleNestedBean (@ EventData ("nested" ) NestedBean nested ) {
101
+ this .receivedNestedBean = nested ;
102
+ }
103
+
104
+ @ ClientCallable
105
+ public void handleIntegerList (
106
+ @ EventData ("integers" ) List <Integer > integers ) {
107
+ this .receivedIntegerList = integers ;
108
+ }
109
+
110
+ @ ClientCallable
111
+ public SimpleBean returnSimpleBean () {
112
+ return new SimpleBean ("returned" , 42 , true );
113
+ }
114
+
115
+ @ ClientCallable
116
+ public List <SimpleBean > returnBeanList () {
117
+ return Arrays .asList (new SimpleBean ("first" , 1 , true ),
118
+ new SimpleBean ("second" , 2 , false ));
119
+ }
120
+
121
+ @ ClientCallable
122
+ public NestedBean returnNestedBean () {
123
+ return new NestedBean ("outer" , new SimpleBean ("inner" , 100 , false ));
124
+ }
125
+
126
+ @ ClientCallable
127
+ public List <Integer > returnIntegerList () {
128
+ return Arrays .asList (10 , 20 , 30 );
129
+ }
130
+
131
+ // Getters for test verification
132
+ public SimpleBean getReceivedBean () {
133
+ return receivedBean ;
134
+ }
135
+
136
+ public List <SimpleBean > getReceivedList () {
137
+ return receivedList ;
138
+ }
139
+
140
+ public NestedBean getReceivedNestedBean () {
141
+ return receivedNestedBean ;
142
+ }
143
+
144
+ public List <Integer > getReceivedIntegerList () {
145
+ return receivedIntegerList ;
146
+ }
147
+ }
148
+
149
+ @ Before
150
+ public void setUp () throws Exception {
151
+ mocks = new MockServletServiceSessionSetup ();
152
+ VaadinService service = mocks .getService ();
153
+ service .init ();
154
+
155
+ ui = new MockUI ();
156
+ VaadinSession .setCurrent (mocks .getSession ());
157
+ UI .setCurrent (ui );
158
+
159
+ handler = new PublishedServerEventHandlerRpcHandler ();
160
+ }
161
+
162
+ @ After
163
+ public void tearDown () {
164
+ mocks .cleanup ();
165
+ }
166
+
167
+ @ Test
168
+ public void testSimpleBeanParameter () throws Exception {
169
+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
170
+ ui .add (component );
171
+
172
+ // Create JSON for SimpleBean parameter
173
+ ObjectNode beanJson = JacksonUtils .createObjectNode ();
174
+ beanJson .put ("name" , "TestBean" );
175
+ beanJson .put ("value" , 123 );
176
+ beanJson .put ("active" , true );
177
+
178
+ // Create parameters array
179
+ ArrayNode params = JacksonUtils .createArrayNode ();
180
+ params .add (beanJson );
181
+
182
+ // Invoke the method directly
183
+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
184
+ component .getClass (), "handleSimpleBean" , params , -1 );
185
+
186
+ // Verify the bean was properly deserialized
187
+ SimpleBean received = component .getReceivedBean ();
188
+ Assert .assertNotNull ("Bean should be received" , received );
189
+ Assert .assertEquals ("TestBean" , received .name );
190
+ Assert .assertEquals (123 , received .value );
191
+ Assert .assertTrue (received .active );
192
+ }
193
+
194
+ @ Test
195
+ public void testBeanListParameter () throws Exception {
196
+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
197
+ ui .add (component );
198
+
199
+ // Create JSON array with bean objects
200
+ ArrayNode beanArray = JacksonUtils .createArrayNode ();
201
+
202
+ ObjectNode bean1 = JacksonUtils .createObjectNode ();
203
+ bean1 .put ("name" , "First" );
204
+ bean1 .put ("value" , 1 );
205
+ bean1 .put ("active" , true );
206
+ beanArray .add (bean1 );
207
+
208
+ ObjectNode bean2 = JacksonUtils .createObjectNode ();
209
+ bean2 .put ("name" , "Second" );
210
+ bean2 .put ("value" , 2 );
211
+ bean2 .put ("active" , false );
212
+ beanArray .add (bean2 );
213
+
214
+ // Create parameters array
215
+ ArrayNode params = JacksonUtils .createArrayNode ();
216
+ params .add (beanArray );
217
+
218
+ // Invoke the method directly
219
+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
220
+ component .getClass (), "handleBeanList" , params , -1 );
221
+
222
+ // Verify the list was properly deserialized
223
+ List <SimpleBean > received = component .getReceivedList ();
224
+ Assert .assertNotNull ("List should be received" , received );
225
+ Assert .assertEquals ("Should have 2 beans" , 2 , received .size ());
226
+
227
+ Assert .assertEquals ("First" , received .get (0 ).name );
228
+ Assert .assertEquals (1 , received .get (0 ).value );
229
+ Assert .assertTrue (received .get (0 ).active );
230
+
231
+ Assert .assertEquals ("Second" , received .get (1 ).name );
232
+ Assert .assertEquals (2 , received .get (1 ).value );
233
+ Assert .assertFalse (received .get (1 ).active );
234
+ }
235
+
236
+ @ Test
237
+ public void testNestedBeanParameter () throws Exception {
238
+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
239
+ ui .add (component );
240
+
241
+ // Create JSON for nested bean
242
+ ObjectNode innerBean = JacksonUtils .createObjectNode ();
243
+ innerBean .put ("name" , "InnerBean" );
244
+ innerBean .put ("value" , 456 );
245
+ innerBean .put ("active" , false );
246
+
247
+ ObjectNode nestedBean = JacksonUtils .createObjectNode ();
248
+ nestedBean .put ("title" , "OuterBean" );
249
+ nestedBean .set ("simple" , innerBean );
250
+
251
+ // Create parameters array
252
+ ArrayNode params = JacksonUtils .createArrayNode ();
253
+ params .add (nestedBean );
254
+
255
+ // Invoke the method directly
256
+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
257
+ component .getClass (), "handleNestedBean" , params , -1 );
258
+
259
+ // Verify the nested bean was properly deserialized
260
+ NestedBean received = component .getReceivedNestedBean ();
261
+ Assert .assertNotNull ("Nested bean should be received" , received );
262
+ Assert .assertEquals ("OuterBean" , received .title );
263
+ Assert .assertNotNull ("Inner bean should be present" , received .simple );
264
+ Assert .assertEquals ("InnerBean" , received .simple .name );
265
+ Assert .assertEquals (456 , received .simple .value );
266
+ Assert .assertFalse (received .simple .active );
267
+ }
268
+
269
+ @ Test
270
+ public void testIntegerListParameter () throws Exception {
271
+ ComponentWithClientCallableMethods component = new ComponentWithClientCallableMethods ();
272
+ ui .add (component );
273
+
274
+ // Create JSON array with integers
275
+ ArrayNode intArray = JacksonUtils .createArrayNode ();
276
+ intArray .add (10 );
277
+ intArray .add (20 );
278
+ intArray .add (30 );
279
+
280
+ // Create parameters array
281
+ ArrayNode params = JacksonUtils .createArrayNode ();
282
+ params .add (intArray );
283
+
284
+ // Invoke the method directly
285
+ PublishedServerEventHandlerRpcHandler .invokeMethod (component ,
286
+ component .getClass (), "handleIntegerList" , params , -1 );
287
+
288
+ // Verify the integer list was properly deserialized
289
+ List <Integer > received = component .getReceivedIntegerList ();
290
+ Assert .assertNotNull ("Integer list should be received" , received );
291
+ Assert .assertEquals ("Should have 3 integers" , 3 , received .size ());
292
+ Assert .assertEquals (Integer .valueOf (10 ), received .get (0 ));
293
+ Assert .assertEquals (Integer .valueOf (20 ), received .get (1 ));
294
+ Assert .assertEquals (Integer .valueOf (30 ), received .get (2 ));
295
+ }
296
+ }
0 commit comments