1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 |
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 = |
331 | *compiler.build_from_source("export component Dummy { in property <string> test; " |
332 | "callback dummy; public function my-fun() {} }" , |
333 | "" ); |
334 | auto properties = comp_def.properties(); |
335 | REQUIRE(properties.size() == 1); |
336 | REQUIRE(properties[0].property_name == "test" ); |
337 | REQUIRE(properties[0].property_type == Value::Type::String); |
338 | |
339 | auto callback_names = comp_def.callbacks(); |
340 | REQUIRE(callback_names.size() == 1); |
341 | REQUIRE(callback_names[0] == "dummy" ); |
342 | |
343 | auto function_names = comp_def.functions(); |
344 | REQUIRE(function_names.size() == 1); |
345 | REQUIRE(function_names[0] == "my-fun" ); |
346 | |
347 | auto instance = comp_def.create(); |
348 | ComponentDefinition new_comp_def = instance->definition(); |
349 | auto new_props = new_comp_def.properties(); |
350 | REQUIRE(new_props.size() == 1); |
351 | REQUIRE(new_props[0].property_name == "test" ); |
352 | REQUIRE(new_props[0].property_type == Value::Type::String); |
353 | } |
354 | |
355 | SCENARIO("Component Definition Properties / Two-way bindings" ) |
356 | { |
357 | using namespace slint::interpreter; |
358 | using namespace slint; |
359 | |
360 | ComponentCompiler compiler; |
361 | auto comp_def = *compiler.build_from_source( |
362 | "export component Dummy { in-out property <string> test <=> sub_object.test; " |
363 | " sub_object := Rectangle { property <string> test; }" |
364 | "}" , |
365 | "" ); |
366 | auto properties = comp_def.properties(); |
367 | REQUIRE(properties.size() == 1); |
368 | REQUIRE(properties[0].property_name == "test" ); |
369 | REQUIRE(properties[0].property_type == Value::Type::String); |
370 | } |
371 | |
372 | SCENARIO("Invoke callback" ) |
373 | { |
374 | using namespace slint::interpreter; |
375 | using namespace slint; |
376 | |
377 | ComponentCompiler compiler; |
378 | |
379 | SECTION("valid" ) |
380 | { |
381 | auto result = compiler.build_from_source( |
382 | "export component Dummy { callback some_callback(string, int) -> string; }" , "" ); |
383 | REQUIRE(result.has_value()); |
384 | auto instance = result->create(); |
385 | std::string local_string = "_string_on_the_stack_" ; |
386 | REQUIRE(instance->set_callback("some_callback" , [local_string](auto args) { |
387 | SharedString arg1 = *args[0].to_string(); |
388 | int arg2 = int(*args[1].to_number()); |
389 | std::string res = std::string(arg1) + ":" + std::to_string(arg2) + local_string; |
390 | return Value(SharedString(res)); |
391 | })); |
392 | Value args[] = { SharedString("Hello" ), 42. }; |
393 | auto res = instance->invoke("some_callback" , args); |
394 | REQUIRE(res.has_value()); |
395 | REQUIRE(*res->to_string() == SharedString("Hello:42_string_on_the_stack_" )); |
396 | } |
397 | |
398 | SECTION("invalid" ) |
399 | { |
400 | auto result = compiler.build_from_source( |
401 | "export component Dummy { callback foo(string, int) -> string; }" , "" ); |
402 | REQUIRE(result.has_value()); |
403 | auto instance = result->create(); |
404 | REQUIRE(!instance->set_callback("bar" , [](auto) { return Value(); })); |
405 | Value args[] = { SharedString("Hello" ), 42. }; |
406 | auto res = instance->invoke("bar" , args); |
407 | REQUIRE(!res.has_value()); |
408 | } |
409 | } |
410 | |
411 | SCENARIO("Array between .slint and C++" ) |
412 | { |
413 | using namespace slint::interpreter; |
414 | using namespace slint; |
415 | |
416 | ComponentCompiler compiler; |
417 | |
418 | auto result = compiler.build_from_source( |
419 | "export component Dummy { in-out property <[int]> array: [1, 2, 3]; }" , "" ); |
420 | REQUIRE(result.has_value()); |
421 | auto instance = result->create(); |
422 | |
423 | SECTION(".slint to C++" ) |
424 | { |
425 | auto maybe_array = instance->get_property("array" ); |
426 | REQUIRE(maybe_array.has_value()); |
427 | REQUIRE(maybe_array->type() == Value::Type::Model); |
428 | |
429 | auto array = *maybe_array; |
430 | REQUIRE(array.to_array() == slint::SharedVector<Value> { Value(1.), Value(2.), Value(3.) }); |
431 | } |
432 | |
433 | SECTION("C++ to .slint" ) |
434 | { |
435 | slint::SharedVector<Value> cpp_array { Value(4.), Value(5.), Value(6.) }; |
436 | |
437 | instance->set_property("array" , Value(cpp_array)); |
438 | auto maybe_array = instance->get_property("array" ); |
439 | REQUIRE(maybe_array.has_value()); |
440 | REQUIRE(maybe_array->type() == Value::Type::Model); |
441 | |
442 | auto actual_array = *maybe_array; |
443 | REQUIRE(actual_array.to_array() == cpp_array); |
444 | } |
445 | } |
446 | |
447 | SCENARIO("Angle between .slint and C++" ) |
448 | { |
449 | using namespace slint::interpreter; |
450 | using namespace slint; |
451 | |
452 | ComponentCompiler compiler; |
453 | |
454 | auto result = compiler.build_from_source( |
455 | "export component Dummy { in-out property <angle> the_angle: " |
456 | "0.25turn; out property <bool> test: the_angle == 0.5turn; }" , |
457 | "" ); |
458 | REQUIRE(result.has_value()); |
459 | auto instance = result->create(); |
460 | |
461 | SECTION("Read property" ) |
462 | { |
463 | auto angle_value = instance->get_property("the-angle" ); |
464 | REQUIRE(angle_value.has_value()); |
465 | REQUIRE(angle_value->type() == Value::Type::Number); |
466 | auto angle = angle_value->to_number(); |
467 | REQUIRE(angle.has_value()); |
468 | REQUIRE(*angle == 90); |
469 | } |
470 | SECTION("Write property" ) |
471 | { |
472 | REQUIRE(!*instance->get_property("test" )->to_bool()); |
473 | bool ok = instance->set_property("the_angle" , 180.); |
474 | REQUIRE(ok); |
475 | REQUIRE(*instance->get_property("the_angle" )->to_number() == 180); |
476 | REQUIRE(*instance->get_property("test" )->to_bool()); |
477 | } |
478 | } |
479 | |
480 | SCENARIO("Component Definition Name" ) |
481 | { |
482 | using namespace slint::interpreter; |
483 | using namespace slint; |
484 | |
485 | ComponentCompiler compiler; |
486 | auto comp_def = *compiler.build_from_source("export component IHaveAName { }" , "" ); |
487 | REQUIRE(comp_def.name() == "IHaveAName" ); |
488 | } |
489 | |
490 | SCENARIO("Send key events" ) |
491 | { |
492 | using namespace slint::interpreter; |
493 | using namespace slint; |
494 | |
495 | ComponentCompiler compiler; |
496 | auto comp_def = compiler.build_from_source(R"( |
497 | export component Dummy { |
498 | forward-focus: scope; |
499 | out property <string> result; |
500 | scope := FocusScope { |
501 | key-pressed(event) => { |
502 | if (event.text != Key.Shift && event.text != Key.Control) { |
503 | result += event.text; |
504 | } |
505 | return accept; |
506 | } |
507 | } |
508 | } |
509 | )" , |
510 | "" ); |
511 | REQUIRE(comp_def.has_value()); |
512 | auto instance = comp_def->create(); |
513 | slint::private_api::testing::send_keyboard_string_sequence(&*instance, "Hello keys!" ); |
514 | REQUIRE(*instance->get_property("result" )->to_string() == "Hello keys!" ); |
515 | } |
516 | |
517 | SCENARIO("Global properties" ) |
518 | { |
519 | using namespace slint::interpreter; |
520 | using namespace slint; |
521 | |
522 | ComponentCompiler compiler; |
523 | |
524 | auto result = compiler.build_from_source( |
525 | R"( |
526 | export global The-Global { |
527 | in-out property <string> the-property: "€€€"; |
528 | pure callback to_uppercase(string)->string; |
529 | public function ff() -> string { return the-property; } |
530 | } |
531 | export component Dummy { |
532 | out property <string> result: The-Global.to_uppercase("abc"); |
533 | } |
534 | )" , |
535 | "" ); |
536 | for (auto &&x : compiler.diagnostics()) |
537 | std::cerr << x.message << std::endl; |
538 | REQUIRE(result.has_value()); |
539 | auto component_definition = *result; |
540 | |
541 | SECTION("Globals introspection" ) |
542 | { |
543 | auto globals = component_definition.globals(); |
544 | REQUIRE(globals.size() == 1); |
545 | REQUIRE(globals[0] == "The-Global" ); |
546 | |
547 | REQUIRE(!component_definition.global_properties("not there" ).has_value()); |
548 | |
549 | REQUIRE(component_definition.global_properties("The_Global" ).has_value()); |
550 | REQUIRE(component_definition.global_properties("The-Global" ).has_value()); |
551 | |
552 | auto properties = *component_definition.global_properties("The-Global" ); |
553 | REQUIRE(properties.size() == 1); |
554 | REQUIRE(properties[0].property_name == "the-property" ); |
555 | REQUIRE(properties[0].property_type == Value::Type::String); |
556 | |
557 | auto callbacks = *component_definition.global_callbacks("The-Global" ); |
558 | REQUIRE(callbacks.size() == 1); |
559 | REQUIRE(callbacks[0] == "to_uppercase" ); |
560 | |
561 | auto functions = *component_definition.global_functions("The-Global" ); |
562 | REQUIRE(functions.size() == 1); |
563 | REQUIRE(functions[0] == "ff" ); |
564 | } |
565 | |
566 | auto instance = component_definition.create(); |
567 | |
568 | SECTION("Invalid read" ) |
569 | { |
570 | REQUIRE(!instance->get_global_property("the - global" , "the-property" ).has_value()); |
571 | REQUIRE(!instance->get_global_property("The-Global" , "the property" ).has_value()); |
572 | } |
573 | SECTION("Invalid set" ) |
574 | { |
575 | REQUIRE(!instance->set_global_property("the - global" , "the-property" , 5.)); |
576 | REQUIRE(!instance->set_global_property("The-Global" , "the property" , 5.)); |
577 | REQUIRE(!instance->set_global_property("The-Global" , "the-property" , 5.)); |
578 | } |
579 | SECTION("get property" ) |
580 | { |
581 | auto value = instance->get_global_property("The_Global" , "the-property" ); |
582 | REQUIRE(value.has_value()); |
583 | REQUIRE(value->to_string().has_value()); |
584 | REQUIRE(value->to_string().value() == "€€€" ); |
585 | } |
586 | SECTION("set property" ) |
587 | { |
588 | REQUIRE(instance->set_global_property("The-Global" , "the-property" , SharedString("§§§" ))); |
589 | auto value = instance->get_global_property("The-Global" , "the_property" ); |
590 | REQUIRE(value.has_value()); |
591 | REQUIRE(value->to_string().has_value()); |
592 | REQUIRE(value->to_string().value() == "§§§" ); |
593 | } |
594 | SECTION("set/invoke callback" ) |
595 | { |
596 | REQUIRE(instance->set_global_callback("The-Global" , "to_uppercase" , [](auto args) { |
597 | std::string arg1(*args[0].to_string()); |
598 | std::transform(arg1.begin(), arg1.end(), arg1.begin(), toupper); |
599 | return SharedString(arg1); |
600 | })); |
601 | auto result = instance->get_property("result" ); |
602 | REQUIRE(result.has_value()); |
603 | REQUIRE(result->to_string().has_value()); |
604 | REQUIRE(result->to_string().value() == "ABC" ); |
605 | |
606 | Value args[] = { SharedString("Hello" ) }; |
607 | auto res = instance->invoke_global("The_Global" , "to-uppercase" , args); |
608 | REQUIRE(res.has_value()); |
609 | REQUIRE(*res->to_string() == SharedString("HELLO" )); |
610 | } |
611 | SECTION("callback errors" ) |
612 | { |
613 | REQUIRE(!instance->set_global_callback("TheGlobal" , "to_uppercase" , |
614 | [](auto) { return Value {}; })); |
615 | REQUIRE(!instance->set_global_callback("The-Global" , "touppercase" , |
616 | [](auto) { return Value {}; })); |
617 | REQUIRE(!instance->invoke_global("TheGlobal" , "touppercase" , {})); |
618 | REQUIRE(!instance->invoke_global("The-Global" , "touppercase" , {})); |
619 | } |
620 | SECTION("invoke function" ) |
621 | { |
622 | REQUIRE(instance->set_global_property("The-Global" , "the-property" , SharedString("&&&" ))); |
623 | auto res = instance->invoke_global("The_Global" , "ff" , {}); |
624 | REQUIRE(res.has_value()); |
625 | REQUIRE(*res->to_string() == SharedString("&&&" )); |
626 | } |
627 | } |
628 | |