1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-1.1 OR LicenseRef-Slint-commercial |
3 | |
4 | #define CATCH_CONFIG_MAIN |
5 | #include "catch2/catch.hpp" |
6 | |
7 | #include <slint.h> |
8 | #include <slint-interpreter.h> |
9 | |
10 | SCENARIO("Value API" ) |
11 | { |
12 | using namespace slint::interpreter; |
13 | Value value; |
14 | |
15 | REQUIRE(value.type() == Value::Type::Void); |
16 | |
17 | SECTION("Construct a string" ) |
18 | { |
19 | REQUIRE(!value.to_string().has_value()); |
20 | slint::SharedString cpp_str("Hello World" ); |
21 | value = Value(cpp_str); |
22 | REQUIRE(value.type() == Value::Type::String); |
23 | |
24 | auto string_opt = value.to_string(); |
25 | REQUIRE(string_opt.has_value()); |
26 | REQUIRE(string_opt.value() == "Hello World" ); |
27 | } |
28 | |
29 | SECTION("Construct a number" ) |
30 | { |
31 | REQUIRE(!value.to_number().has_value()); |
32 | const double number = 42.0; |
33 | value = Value(number); |
34 | REQUIRE(value.type() == Value::Type::Number); |
35 | |
36 | auto number_opt = value.to_number(); |
37 | REQUIRE(number_opt.has_value()); |
38 | REQUIRE(number_opt.value() == number); |
39 | |
40 | Value v2 = 42; |
41 | REQUIRE(v2.type() == Value::Type::Number); |
42 | REQUIRE(v2 == value); |
43 | REQUIRE(*v2.to_number() == number); |
44 | } |
45 | |
46 | SECTION("Construct a bool" ) |
47 | { |
48 | REQUIRE(!value.to_bool().has_value()); |
49 | value = Value(true); |
50 | REQUIRE(value.type() == Value::Type::Bool); |
51 | |
52 | auto bool_opt = value.to_bool(); |
53 | REQUIRE(bool_opt.has_value()); |
54 | REQUIRE(bool_opt.value() == true); |
55 | } |
56 | |
57 | SECTION("Construct an array" ) |
58 | { |
59 | REQUIRE(!value.to_array().has_value()); |
60 | slint::SharedVector<Value> array { Value(42.0), Value(true) }; |
61 | value = Value(array); |
62 | REQUIRE(value.type() == Value::Type::Model); |
63 | |
64 | auto array_opt = value.to_array(); |
65 | REQUIRE(array_opt.has_value()); |
66 | |
67 | auto extracted_array = array_opt.value(); |
68 | REQUIRE(extracted_array.size() == 2); |
69 | REQUIRE(extracted_array[0].to_number().value() == 42); |
70 | REQUIRE(extracted_array[1].to_bool().value()); |
71 | } |
72 | |
73 | SECTION("Construct a brush" ) |
74 | { |
75 | REQUIRE(!value.to_brush().has_value()); |
76 | slint::Brush brush(slint::Color::from_rgb_uint8(255, 0, 255)); |
77 | value = Value(brush); |
78 | REQUIRE(value.type() == Value::Type::Brush); |
79 | |
80 | auto brush_opt = value.to_brush(); |
81 | REQUIRE(brush_opt.has_value()); |
82 | REQUIRE(brush_opt.value() == brush); |
83 | } |
84 | |
85 | SECTION("Construct a struct" ) |
86 | { |
87 | REQUIRE(!value.to_struct().has_value()); |
88 | slint::interpreter::Struct struc; |
89 | value = Value(struc); |
90 | REQUIRE(value.type() == Value::Type::Struct); |
91 | |
92 | auto struct_opt = value.to_struct(); |
93 | REQUIRE(struct_opt.has_value()); |
94 | } |
95 | |
96 | SECTION("Construct an image" ) |
97 | { |
98 | REQUIRE(!value.to_image().has_value()); |
99 | slint::Image image = slint::Image::load_from_path( |
100 | SOURCE_DIR "/../../../logo/slint-logo-square-light-128x128.png" ); |
101 | REQUIRE(image.size().width == 128); |
102 | value = Value(image); |
103 | REQUIRE(value.type() == Value::Type::Image); |
104 | |
105 | auto image2 = value.to_image(); |
106 | REQUIRE(image2.has_value()); |
107 | REQUIRE(image2->size().width == 128); |
108 | REQUIRE(image == *image2); |
109 | } |
110 | |
111 | SECTION("Construct a model" ) |
112 | { |
113 | // And test that it is properly destroyed when the value is destroyed |
114 | struct M : slint::VectorModel<Value> |
115 | { |
116 | bool *destroyed; |
117 | explicit M(bool *destroyed) : destroyed(destroyed) { } |
118 | void play() |
119 | { |
120 | this->push_back(Value(4.)); |
121 | this->set_row_data(0, Value(9.)); |
122 | } |
123 | ~M() { *destroyed = true; } |
124 | }; |
125 | bool destroyed = false; |
126 | auto m = std::make_shared<M>(&destroyed); |
127 | { |
128 | Value value(m); |
129 | REQUIRE(value.type() == Value::Type::Model); |
130 | REQUIRE(!destroyed); |
131 | m->play(); |
132 | m = nullptr; |
133 | REQUIRE(!destroyed); |
134 | // play a bit with the value to test the copy and move |
135 | Value v2 = value; |
136 | Value v3 = std::move(v2); |
137 | REQUIRE(!destroyed); |
138 | } |
139 | REQUIRE(destroyed); |
140 | } |
141 | |
142 | SECTION("Compare Values" ) |
143 | { |
144 | Value str1 { slint::SharedString("Hello1" ) }; |
145 | Value str2 { slint::SharedString("Hello2" ) }; |
146 | Value fl1 { 10. }; |
147 | Value fl2 { 12. }; |
148 | |
149 | REQUIRE(str1 == str1); |
150 | REQUIRE(str1 != str2); |
151 | REQUIRE(str1 != fl2); |
152 | REQUIRE(fl1 == fl1); |
153 | REQUIRE(fl1 != fl2); |
154 | REQUIRE(Value() == Value()); |
155 | REQUIRE(Value() != str1); |
156 | REQUIRE(str1 == slint::SharedString("Hello1" )); |
157 | REQUIRE(str1 != slint::SharedString("Hello2" )); |
158 | REQUIRE(slint::SharedString("Hello2" ) == str2); |
159 | REQUIRE(fl1 != slint::SharedString("Hello2" )); |
160 | REQUIRE(fl2 == 12.); |
161 | } |
162 | } |
163 | |
164 | SCENARIO("Struct API" ) |
165 | { |
166 | using namespace slint::interpreter; |
167 | Struct struc; |
168 | |
169 | REQUIRE(!struc.get_field("not_there" )); |
170 | |
171 | struc.set_field("field_a" , Value(slint::SharedString("Hallo" ))); |
172 | |
173 | auto value_opt = struc.get_field("field_a" ); |
174 | REQUIRE(value_opt.has_value()); |
175 | auto value = value_opt.value(); |
176 | REQUIRE(value.to_string().has_value()); |
177 | REQUIRE(value.to_string().value() == "Hallo" ); |
178 | |
179 | int count = 0; |
180 | for (auto [k, value] : struc) { |
181 | REQUIRE(count == 0); |
182 | count++; |
183 | REQUIRE(k == "field-a" ); |
184 | REQUIRE(value.to_string().value() == "Hallo" ); |
185 | } |
186 | |
187 | struc.set_field("field_b" , Value(slint::SharedString("World" ))); |
188 | std::map<std::string, slint::SharedString> map; |
189 | for (auto [k, value] : struc) |
190 | map[std::string(k)] = *value.to_string(); |
191 | |
192 | REQUIRE(map |
193 | == std::map<std::string, slint::SharedString> { |
194 | { "field-a" , slint::SharedString("Hallo" ) }, |
195 | { "field-b" , slint::SharedString("World" ) } }); |
196 | } |
197 | |
198 | SCENARIO("Struct Iterator Constructor" ) |
199 | { |
200 | using namespace slint::interpreter; |
201 | |
202 | std::vector<std::pair<std::string_view, Value>> values = { { "field_a" , Value(true) }, |
203 | { "field_b" , Value(42.0) } }; |
204 | |
205 | Struct struc(values.begin(), values.end()); |
206 | |
207 | REQUIRE(!struc.get_field("foo" ).has_value()); |
208 | REQUIRE(struc.get_field("field_a" ).has_value()); |
209 | REQUIRE(struc.get_field("field_a" ).value().to_bool().value()); |
210 | REQUIRE(struc.get_field("field_b" ).value().to_number().value() == 42.0); |
211 | } |
212 | |
213 | SCENARIO("Struct Initializer List Constructor" ) |
214 | { |
215 | using namespace slint::interpreter; |
216 | |
217 | Struct struc({ { "field_a" , Value(true) }, { "field_b" , Value(42.0) } }); |
218 | |
219 | REQUIRE(!struc.get_field("foo" ).has_value()); |
220 | REQUIRE(struc.get_field("field_a" ).has_value()); |
221 | REQUIRE(struc.get_field("field_a" ).value().to_bool().value()); |
222 | REQUIRE(struc.get_field("field_b" ).value().to_number().value() == 42.0); |
223 | } |
224 | |
225 | SCENARIO("Struct empty field iteration" ) |
226 | { |
227 | using namespace slint::interpreter; |
228 | Struct struc; |
229 | REQUIRE(struc.begin() == struc.end()); |
230 | } |
231 | |
232 | SCENARIO("Struct field iteration" ) |
233 | { |
234 | using namespace slint::interpreter; |
235 | |
236 | Struct struc({ { "field_a" , Value(true) }, { "field_b" , Value(42.0) } }); |
237 | |
238 | auto it = struc.begin(); |
239 | auto end = struc.end(); |
240 | REQUIRE(it != end); |
241 | |
242 | auto check_valid_entry = [](const auto &key, const auto &value) -> bool { |
243 | if (key == "field-a" ) |
244 | return value == Value(true); |
245 | if (key == "field-b" ) |
246 | return value == Value(42.0); |
247 | return false; |
248 | }; |
249 | |
250 | std::set<std::string> seen_fields; |
251 | |
252 | for (; it != end; ++it) { |
253 | const auto [key, value] = *it; |
254 | REQUIRE(check_valid_entry(key, value)); |
255 | auto value_inserted = seen_fields.insert(std::string(key)).second; |
256 | REQUIRE(value_inserted); |
257 | } |
258 | } |
259 | |
260 | SCENARIO("Component Compiler" ) |
261 | { |
262 | using namespace slint::interpreter; |
263 | using namespace slint; |
264 | |
265 | ComponentCompiler compiler; |
266 | |
267 | SECTION("configure include paths" ) |
268 | { |
269 | SharedVector<SharedString> in_paths; |
270 | in_paths.push_back("path1" ); |
271 | in_paths.push_back("path2" ); |
272 | compiler.set_include_paths(in_paths); |
273 | |
274 | auto out_paths = compiler.include_paths(); |
275 | REQUIRE(out_paths.size() == 2); |
276 | REQUIRE(out_paths[0] == "path1" ); |
277 | REQUIRE(out_paths[1] == "path2" ); |
278 | } |
279 | |
280 | SECTION("configure style" ) |
281 | { |
282 | REQUIRE(compiler.style() == "" ); |
283 | compiler.set_style("fluent" ); |
284 | REQUIRE(compiler.style() == "fluent" ); |
285 | } |
286 | |
287 | SECTION("configure translation domain" ) |
288 | { |
289 | // Make sure this compiles. |
290 | compiler.set_translation_domain("cpptests" ); |
291 | } |
292 | |
293 | SECTION("Compile failure from source" ) |
294 | { |
295 | auto result = compiler.build_from_source("Syntax Error!!" , "" ); |
296 | REQUIRE_FALSE(result.has_value()); |
297 | } |
298 | |
299 | SECTION("Compile from source" ) |
300 | { |
301 | auto result = compiler.build_from_source("export component Dummy {}" , "" ); |
302 | REQUIRE(result.has_value()); |
303 | } |
304 | |
305 | SECTION("Compile failure from path" ) |
306 | { |
307 | auto result = compiler.build_from_path(SOURCE_DIR "/file-not-there.slint" ); |
308 | REQUIRE_FALSE(result.has_value()); |
309 | auto diags = compiler.diagnostics(); |
310 | |
311 | REQUIRE(diags.size() == 1); |
312 | REQUIRE(diags[0].message.starts_with("Could not load" )); |
313 | REQUIRE(diags[0].line == 0); |
314 | REQUIRE(diags[0].column == 0); |
315 | } |
316 | |
317 | SECTION("Compile from path" ) |
318 | { |
319 | auto result = compiler.build_from_path(SOURCE_DIR "/test.slint" ); |
320 | REQUIRE(result.has_value()); |
321 | } |
322 | } |
323 | |
324 | SCENARIO("Component Definition Properties" ) |
325 | { |
326 | using namespace slint::interpreter; |
327 | using namespace slint; |
328 | |
329 | ComponentCompiler compiler; |
330 | auto comp_def = *compiler.build_from_source( |
331 | "export component Dummy { in property <string> test; callback dummy; }" , "" ); |
332 | auto properties = comp_def.properties(); |
333 | REQUIRE(properties.size() == 1); |
334 | REQUIRE(properties[0].property_name == "test" ); |
335 | REQUIRE(properties[0].property_type == Value::Type::String); |
336 | |
337 | auto callback_names = comp_def.callbacks(); |
338 | REQUIRE(callback_names.size() == 1); |
339 | REQUIRE(callback_names[0] == "dummy" ); |
340 | |
341 | auto instance = comp_def.create(); |
342 | ComponentDefinition new_comp_def = instance->definition(); |
343 | auto new_props = new_comp_def.properties(); |
344 | REQUIRE(new_props.size() == 1); |
345 | REQUIRE(new_props[0].property_name == "test" ); |
346 | REQUIRE(new_props[0].property_type == Value::Type::String); |
347 | } |
348 | |
349 | SCENARIO("Component Definition Properties / Two-way bindings" ) |
350 | { |
351 | using namespace slint::interpreter; |
352 | using namespace slint; |
353 | |
354 | ComponentCompiler compiler; |
355 | auto comp_def = *compiler.build_from_source( |
356 | "export component Dummy { in-out property <string> test <=> sub_object.test; " |
357 | " sub_object := Rectangle { property <string> test; }" |
358 | "}" , |
359 | "" ); |
360 | auto properties = comp_def.properties(); |
361 | REQUIRE(properties.size() == 1); |
362 | REQUIRE(properties[0].property_name == "test" ); |
363 | REQUIRE(properties[0].property_type == Value::Type::String); |
364 | } |
365 | |
366 | SCENARIO("Invoke callback" ) |
367 | { |
368 | using namespace slint::interpreter; |
369 | using namespace slint; |
370 | |
371 | ComponentCompiler compiler; |
372 | |
373 | SECTION("valid" ) |
374 | { |
375 | auto result = compiler.build_from_source( |
376 | "export component Dummy { callback some_callback(string, int) -> string; }" , "" ); |
377 | REQUIRE(result.has_value()); |
378 | auto instance = result->create(); |
379 | std::string local_string = "_string_on_the_stack_" ; |
380 | REQUIRE(instance->set_callback("some_callback" , [local_string](auto args) { |
381 | SharedString arg1 = *args[0].to_string(); |
382 | int arg2 = int(*args[1].to_number()); |
383 | std::string res = std::string(arg1) + ":" + std::to_string(arg2) + local_string; |
384 | return Value(SharedString(res)); |
385 | })); |
386 | Value args[] = { SharedString("Hello" ), 42. }; |
387 | auto res = instance->invoke("some_callback" , args); |
388 | REQUIRE(res.has_value()); |
389 | REQUIRE(*res->to_string() == SharedString("Hello:42_string_on_the_stack_" )); |
390 | } |
391 | |
392 | SECTION("invalid" ) |
393 | { |
394 | auto result = compiler.build_from_source( |
395 | "export component Dummy { callback foo(string, int) -> string; }" , "" ); |
396 | REQUIRE(result.has_value()); |
397 | auto instance = result->create(); |
398 | REQUIRE(!instance->set_callback("bar" , [](auto) { return Value(); })); |
399 | Value args[] = { SharedString("Hello" ), 42. }; |
400 | auto res = instance->invoke("bar" , args); |
401 | REQUIRE(!res.has_value()); |
402 | } |
403 | } |
404 | |
405 | SCENARIO("Array between .slint and C++" ) |
406 | { |
407 | using namespace slint::interpreter; |
408 | using namespace slint; |
409 | |
410 | ComponentCompiler compiler; |
411 | |
412 | auto result = compiler.build_from_source( |
413 | "export component Dummy { in-out property <[int]> array: [1, 2, 3]; }" , "" ); |
414 | REQUIRE(result.has_value()); |
415 | auto instance = result->create(); |
416 | |
417 | SECTION(".slint to C++" ) |
418 | { |
419 | auto maybe_array = instance->get_property("array" ); |
420 | REQUIRE(maybe_array.has_value()); |
421 | REQUIRE(maybe_array->type() == Value::Type::Model); |
422 | |
423 | auto array = *maybe_array; |
424 | REQUIRE(array.to_array() == slint::SharedVector<Value> { Value(1.), Value(2.), Value(3.) }); |
425 | } |
426 | |
427 | SECTION("C++ to .slint" ) |
428 | { |
429 | slint::SharedVector<Value> cpp_array { Value(4.), Value(5.), Value(6.) }; |
430 | |
431 | instance->set_property("array" , Value(cpp_array)); |
432 | auto maybe_array = instance->get_property("array" ); |
433 | REQUIRE(maybe_array.has_value()); |
434 | REQUIRE(maybe_array->type() == Value::Type::Model); |
435 | |
436 | auto actual_array = *maybe_array; |
437 | REQUIRE(actual_array.to_array() == cpp_array); |
438 | } |
439 | } |
440 | |
441 | SCENARIO("Angle between .slint and C++" ) |
442 | { |
443 | using namespace slint::interpreter; |
444 | using namespace slint; |
445 | |
446 | ComponentCompiler compiler; |
447 | |
448 | auto result = compiler.build_from_source( |
449 | "export component Dummy { in-out property <angle> the_angle: " |
450 | "0.25turn; out property <bool> test: the_angle == 0.5turn; }" , |
451 | "" ); |
452 | REQUIRE(result.has_value()); |
453 | auto instance = result->create(); |
454 | |
455 | SECTION("Read property" ) |
456 | { |
457 | auto angle_value = instance->get_property("the-angle" ); |
458 | REQUIRE(angle_value.has_value()); |
459 | REQUIRE(angle_value->type() == Value::Type::Number); |
460 | auto angle = angle_value->to_number(); |
461 | REQUIRE(angle.has_value()); |
462 | REQUIRE(*angle == 90); |
463 | } |
464 | SECTION("Write property" ) |
465 | { |
466 | REQUIRE(!*instance->get_property("test" )->to_bool()); |
467 | bool ok = instance->set_property("the_angle" , 180.); |
468 | REQUIRE(ok); |
469 | REQUIRE(*instance->get_property("the_angle" )->to_number() == 180); |
470 | REQUIRE(*instance->get_property("test" )->to_bool()); |
471 | } |
472 | } |
473 | |
474 | SCENARIO("Component Definition Name" ) |
475 | { |
476 | using namespace slint::interpreter; |
477 | using namespace slint; |
478 | |
479 | ComponentCompiler compiler; |
480 | auto comp_def = *compiler.build_from_source("export component IHaveAName { }" , "" ); |
481 | REQUIRE(comp_def.name() == "IHaveAName" ); |
482 | } |
483 | |
484 | SCENARIO("Send key events" ) |
485 | { |
486 | using namespace slint::interpreter; |
487 | using namespace slint; |
488 | |
489 | ComponentCompiler compiler; |
490 | auto comp_def = compiler.build_from_source(R"( |
491 | export component Dummy { |
492 | forward-focus: scope; |
493 | out property <string> result; |
494 | scope := FocusScope { |
495 | key-pressed(event) => { |
496 | if (event.text != Key.Shift && event.text != Key.Control) { |
497 | result += event.text; |
498 | } |
499 | return accept; |
500 | } |
501 | } |
502 | } |
503 | )" , |
504 | "" ); |
505 | REQUIRE(comp_def.has_value()); |
506 | auto instance = comp_def->create(); |
507 | slint::testing::send_keyboard_string_sequence(&*instance, "Hello keys!" ); |
508 | REQUIRE(*instance->get_property("result" )->to_string() == "Hello keys!" ); |
509 | } |
510 | |
511 | SCENARIO("Global properties" ) |
512 | { |
513 | using namespace slint::interpreter; |
514 | using namespace slint; |
515 | |
516 | ComponentCompiler compiler; |
517 | |
518 | auto result = compiler.build_from_source( |
519 | R"( |
520 | export global The-Global { |
521 | in-out property <string> the-property: "€€€"; |
522 | pure callback to_uppercase(string)->string; |
523 | public function ff() -> string { return the-property; } |
524 | } |
525 | export component Dummy { |
526 | out property <string> result: The-Global.to_uppercase("abc"); |
527 | } |
528 | )" , |
529 | "" ); |
530 | for (auto &&x : compiler.diagnostics()) |
531 | std::cerr << x.message << std::endl; |
532 | REQUIRE(result.has_value()); |
533 | auto component_definition = *result; |
534 | |
535 | SECTION("Globals introspection" ) |
536 | { |
537 | auto globals = component_definition.globals(); |
538 | REQUIRE(globals.size() == 1); |
539 | REQUIRE(globals[0] == "The-Global" ); |
540 | |
541 | REQUIRE(!component_definition.global_properties("not there" ).has_value()); |
542 | |
543 | REQUIRE(component_definition.global_properties("The_Global" ).has_value()); |
544 | REQUIRE(component_definition.global_properties("The-Global" ).has_value()); |
545 | |
546 | auto properties = *component_definition.global_properties("The-Global" ); |
547 | REQUIRE(properties.size() == 1); |
548 | REQUIRE(properties[0].property_name == "the-property" ); |
549 | REQUIRE(properties[0].property_type == Value::Type::String); |
550 | |
551 | auto callbacks = *component_definition.global_callbacks("The-Global" ); |
552 | REQUIRE(callbacks.size() == 1); |
553 | REQUIRE(callbacks[0] == "to_uppercase" ); |
554 | } |
555 | |
556 | auto instance = component_definition.create(); |
557 | |
558 | SECTION("Invalid read" ) |
559 | { |
560 | REQUIRE(!instance->get_global_property("the - global" , "the-property" ).has_value()); |
561 | REQUIRE(!instance->get_global_property("The-Global" , "the property" ).has_value()); |
562 | } |
563 | SECTION("Invalid set" ) |
564 | { |
565 | REQUIRE(!instance->set_global_property("the - global" , "the-property" , 5.)); |
566 | REQUIRE(!instance->set_global_property("The-Global" , "the property" , 5.)); |
567 | REQUIRE(!instance->set_global_property("The-Global" , "the-property" , 5.)); |
568 | } |
569 | SECTION("get property" ) |
570 | { |
571 | auto value = instance->get_global_property("The_Global" , "the-property" ); |
572 | REQUIRE(value.has_value()); |
573 | REQUIRE(value->to_string().has_value()); |
574 | REQUIRE(value->to_string().value() == "€€€" ); |
575 | } |
576 | SECTION("set property" ) |
577 | { |
578 | REQUIRE(instance->set_global_property("The-Global" , "the-property" , SharedString("§§§" ))); |
579 | auto value = instance->get_global_property("The-Global" , "the_property" ); |
580 | REQUIRE(value.has_value()); |
581 | REQUIRE(value->to_string().has_value()); |
582 | REQUIRE(value->to_string().value() == "§§§" ); |
583 | } |
584 | SECTION("set/invoke callback" ) |
585 | { |
586 | REQUIRE(instance->set_global_callback("The-Global" , "to_uppercase" , [](auto args) { |
587 | std::string arg1(*args[0].to_string()); |
588 | std::transform(arg1.begin(), arg1.end(), arg1.begin(), toupper); |
589 | return SharedString(arg1); |
590 | })); |
591 | auto result = instance->get_property("result" ); |
592 | REQUIRE(result.has_value()); |
593 | REQUIRE(result->to_string().has_value()); |
594 | REQUIRE(result->to_string().value() == "ABC" ); |
595 | |
596 | Value args[] = { SharedString("Hello" ) }; |
597 | auto res = instance->invoke_global("The_Global" , "to-uppercase" , args); |
598 | REQUIRE(res.has_value()); |
599 | REQUIRE(*res->to_string() == SharedString("HELLO" )); |
600 | } |
601 | SECTION("callback errors" ) |
602 | { |
603 | REQUIRE(!instance->set_global_callback("TheGlobal" , "to_uppercase" , |
604 | [](auto) { return Value {}; })); |
605 | REQUIRE(!instance->set_global_callback("The-Global" , "touppercase" , |
606 | [](auto) { return Value {}; })); |
607 | REQUIRE(!instance->invoke_global("TheGlobal" , "touppercase" , {})); |
608 | REQUIRE(!instance->invoke_global("The-Global" , "touppercase" , {})); |
609 | } |
610 | SECTION("invoke function" ) |
611 | { |
612 | REQUIRE(instance->set_global_property("The-Global" , "the-property" , SharedString("&&&" ))); |
613 | auto res = instance->invoke_global("The_Global" , "ff" , {}); |
614 | REQUIRE(res.has_value()); |
615 | REQUIRE(*res->to_string() == SharedString("&&&" )); |
616 | } |
617 | } |
618 | |