/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "BridgingTest.h" namespace facebook::react { using namespace std::literals; TEST_F(BridgingTest, jsiTest) { jsi::Value value = true; jsi::Value string = jsi::String::createFromAscii(rt, "hello"); jsi::Value object = jsi::Object(rt); jsi::Value array = jsi::Array::createWithElements(rt, value, object); jsi::Value func = function("() => {}"); // The bridging mechanism needs to know how to copy and downcast values. EXPECT_NO_THROW(bridging::fromJs(rt, value, invoker)); EXPECT_NO_THROW(bridging::fromJs(rt, string, invoker)); EXPECT_NO_THROW(bridging::fromJs(rt, object, invoker)); EXPECT_NO_THROW(bridging::fromJs(rt, array, invoker)); EXPECT_NO_THROW(bridging::fromJs(rt, func, invoker)); // Should throw when attempting an invalid cast. EXPECT_JSI_THROW(bridging::fromJs(rt, value, invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, array, invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, object, invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, string, invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, func, invoker)); // Should be able to generically no-op convert JSI. EXPECT_NO_THROW(bridging::toJs(rt, value, invoker)); EXPECT_NO_THROW(bridging::toJs(rt, string.asString(rt), invoker)); EXPECT_NO_THROW(bridging::toJs(rt, object.asObject(rt), invoker)); EXPECT_NO_THROW(bridging::toJs(rt, array.asObject(rt).asArray(rt), invoker)); EXPECT_NO_THROW( bridging::toJs(rt, func.asObject(rt).asFunction(rt), invoker)); } TEST_F(BridgingTest, boolTest) { EXPECT_TRUE(bridging::fromJs(rt, jsi::Value(true), invoker)); EXPECT_FALSE(bridging::fromJs(rt, jsi::Value(false), invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, jsi::Value(1), invoker)); EXPECT_TRUE(bridging::toJs(rt, true).asBool()); EXPECT_FALSE(bridging::toJs(rt, false).asBool()); } TEST_F(BridgingTest, numberTest) { EXPECT_EQ(1, bridging::fromJs(rt, jsi::Value(1), invoker)); EXPECT_FLOAT_EQ(1.2f, bridging::fromJs(rt, jsi::Value(1.2), invoker)); EXPECT_DOUBLE_EQ(1.2, bridging::fromJs(rt, jsi::Value(1.2), invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, jsi::Value(true), invoker)); EXPECT_EQ(1, static_cast(bridging::toJs(rt, 1).asNumber())); EXPECT_FLOAT_EQ( 1.2f, static_cast(bridging::toJs(rt, 1.2f).asNumber())); EXPECT_DOUBLE_EQ(1.2, bridging::toJs(rt, 1.2).asNumber()); EXPECT_EQ( 42, static_cast( bridging::toJs(rt, static_cast(42)).asNumber())); EXPECT_EQ( -42, static_cast( bridging::toJs(rt, static_cast(-42)).asNumber())); EXPECT_FALSE( -42 == static_cast( bridging::toJs(rt, static_cast(-42)).asNumber())); } TEST_F(BridgingTest, stringTest) { auto string = jsi::String::createFromAscii(rt, "hello"); EXPECT_EQ("hello"s, bridging::fromJs(rt, string, invoker)); EXPECT_JSI_THROW(bridging::fromJs(rt, jsi::Value(1), invoker)); EXPECT_TRUE( jsi::String::strictEquals(rt, string, bridging::toJs(rt, "hello"))); EXPECT_TRUE( jsi::String::strictEquals(rt, string, bridging::toJs(rt, "hello"s))); EXPECT_TRUE( jsi::String::strictEquals(rt, string, bridging::toJs(rt, "hello"sv))); } TEST_F(BridgingTest, objectTest) { auto object = jsi::Object(rt); object.setProperty(rt, "foo", "bar"); auto omap = bridging::fromJs>(rt, object, invoker); auto umap = bridging::fromJs>( rt, object, invoker); EXPECT_EQ(1, omap.size()); EXPECT_EQ(1, umap.size()); EXPECT_EQ("bar"s, omap["foo"]); EXPECT_EQ("bar"s, umap["foo"]); EXPECT_EQ( "bar"s, bridging::toJs(rt, omap, invoker) .getProperty(rt, "foo") .asString(rt) .utf8(rt)); EXPECT_EQ( "bar"s, bridging::toJs(rt, umap, invoker) .getProperty(rt, "foo") .asString(rt) .utf8(rt)); } TEST_F(BridgingTest, hostObjectTest) { struct TestHostObject : public jsi::HostObject { jsi::Value get(jsi::Runtime& rt, const jsi::PropNameID& name) override { if (name.utf8(rt) == "test") { return {1}; } return jsi::Value::undefined(); } }; auto hostobject = std::make_shared(); auto object = bridging::toJs(rt, hostobject); EXPECT_EQ(1, object.getProperty(rt, "test").asNumber()); EXPECT_EQ( hostobject, bridging::fromJs(rt, object, invoker)); } TEST_F(BridgingTest, weakbjectTest) { auto object = jsi::Object(rt); auto weakobject = jsi::WeakObject(rt, object); EXPECT_TRUE(jsi::Object::strictEquals( rt, object, bridging::fromJs(rt, object, invoker) .lock(rt) .asObject(rt))); EXPECT_TRUE(jsi::Object::strictEquals( rt, object, bridging::toJs(rt, weakobject).asObject(rt))); } TEST_F(BridgingTest, arrayTest) { auto vec = std::vector({"foo"s, "bar"s}); auto array = jsi::Array::createWithElements(rt, "foo", "bar"); EXPECT_EQ( vec, bridging::fromJs>(rt, array, invoker)); auto arr = bridging::fromJs>(rt, array, invoker); EXPECT_EQ(vec[0], arr[0]); EXPECT_EQ(vec[1], arr[1]); auto pair = bridging::fromJs>(rt, array, invoker); EXPECT_EQ(vec[0], pair.first); EXPECT_EQ(vec[1], pair.second); EXPECT_EQ(vec.size(), bridging::toJs(rt, vec, invoker).size(rt)); for (size_t i = 0; i < vec.size(); i++) { EXPECT_EQ( vec[i], bridging::toJs(rt, vec, invoker) .getValueAtIndex(rt, i) .asString(rt) .utf8(rt)); } EXPECT_EQ(2, bridging::toJs(rt, std::make_pair(1, "2"), invoker).size(rt)); EXPECT_EQ(2, bridging::toJs(rt, std::make_tuple(1, "2"), invoker).size(rt)); EXPECT_EQ(2, bridging::toJs(rt, std::array{1, 2}, invoker).size(rt)); EXPECT_EQ( 2, bridging::toJs(rt, std::array{"1", "2"}, invoker) .size(rt)); EXPECT_EQ(2, bridging::toJs(rt, std::deque{1, 2}, invoker).size(rt)); EXPECT_EQ(2, bridging::toJs(rt, std::list{1, 2}, invoker).size(rt)); EXPECT_EQ( 2, bridging::toJs(rt, std::initializer_list{1, 2}, invoker).size(rt)); std::vector> headers{ {"foo", "bar"}, {"baz", "qux"}}; auto jsiHeaders = bridging::toJs(rt, headers, invoker); EXPECT_EQ(headers.size(), jsiHeaders.size(rt)); } TEST_F(BridgingTest, functionTest) { auto object = jsi::Object(rt); object.setProperty(rt, "foo", "bar"); auto lambda = [](std::map map, std::string key) { return map[key]; }; auto func = bridging::toJs(rt, lambda, invoker); EXPECT_EQ( "bar"s, func.call(rt, object, jsi::String::createFromAscii(rt, "foo")) .asString(rt) .utf8(rt)); // Should throw if not enough arguments are passed or are the wrong types. EXPECT_JSI_THROW(func.call(rt, object)); EXPECT_JSI_THROW(func.call(rt, object, jsi::Value(1))); // Test with non-capturing lambda converted to function pointer. func = bridging::toJs(rt, +lambda, invoker); EXPECT_EQ( "bar"s, func.call(rt, object, jsi::String::createFromAscii(rt, "foo")) .asString(rt) .utf8(rt)); } TEST_F(BridgingTest, syncCallbackTest) { auto fn = function("(a, b) => a + b"); auto cb = bridging::fromJs>( rt, fn, invoker); auto foo = "foo"s; EXPECT_EQ("foo1"s, cb(foo, 1)); // Tests lvalue string EXPECT_EQ("bar2", cb("bar", 2)); // Tests rvalue C string EXPECT_TRUE(fn.isFunction(rt)); // Ensure the function wasn't invalidated. } TEST_F(BridgingTest, syncCallbackImplicitBridgingTest) { { // Value auto fn = function("(a, b) => a + b"); auto cb = bridging::fromJs>( rt, fn, invoker); jsi::Value foo(jsi::String::createFromAscii(rt, "foo")); EXPECT_EQ(cb(std::move(foo), 1), "foo1"); EXPECT_EQ(cb(jsi::String::createFromAscii(rt, "bar"), 2), "bar2"); EXPECT_TRUE(fn.isFunction(rt)); } { // Object auto fn = function("(a, b) => a.obj + b"); auto cb = bridging::fromJs>( rt, fn, invoker); jsi::Object foo(rt); foo.setProperty(rt, "obj", "foo"); EXPECT_EQ(cb(std::move(foo), 1), "foo1"); EXPECT_TRUE(fn.isFunction(rt)); } { // String auto fn = function("(a, b) => a + b"); auto cb = bridging::fromJs>( rt, fn, invoker); jsi::String foo(jsi::String::createFromAscii(rt, "foo")); EXPECT_EQ(cb(std::move(foo), 1), "foo1"); EXPECT_EQ(cb(jsi::String::createFromAscii(rt, "bar"), 2), "bar2"); EXPECT_TRUE(fn.isFunction(rt)); } { // Array auto fn = function("(a, b) => a[0] + b"); auto cb = bridging::fromJs>( rt, fn, invoker); jsi::Array foo(rt, 1); foo.setValueAtIndex(rt, 0, jsi::String::createFromAscii(rt, "foo")); EXPECT_EQ(cb(std::move(foo), 1), "foo1"); EXPECT_TRUE(fn.isFunction(rt)); } } TEST_F(BridgingTest, asyncCallbackTest) { std::string output; auto func = std::function([&](auto str) { output = str; }); auto cb = bridging::fromJs>( rt, function("(func, str) => func(str)"), invoker); cb(func, "hello"); flushQueue(); // Run scheduled async work EXPECT_EQ("hello"s, output); // Test with lambda invocation cb.call([func, jsInvoker = invoker](jsi::Runtime& rt, jsi::Function& f) { f.call( rt, bridging::toJs(rt, func, jsInvoker), bridging::toJs(rt, "hello again", jsInvoker)); }); flushQueue(); EXPECT_EQ("hello again"s, output); } TEST_F(BridgingTest, asyncCallbackInvalidation) { std::string output; std::function func = [&](auto str) { output = str; }; auto jsCallback = bridging::fromJs>( rt, bridging::toJs(rt, func, invoker), invoker); jsCallback.call( [](jsi::Runtime& rt, jsi::Function& f) { f.call(rt, "hello"); }); // LongLivedObjectCollection goes away before callback is executed LongLivedObjectCollection::get(rt).clear(); flushQueue(); // Assert native callback is never invoked ASSERT_EQ(""s, output); } TEST_F(BridgingTest, asyncCallbackImplicitBridgingTest) { std::string output; auto func = std::function([&](auto str) { output = str; }); { // Value auto cb = bridging::fromJs>( rt, function("(func, a, b) => func(a + b)"), invoker); jsi::Value foo(jsi::String::createFromAscii(rt, "foo")); cb(func, std::move(foo), 1); flushQueue(); EXPECT_EQ(output, "foo1"); cb(func, jsi::String::createFromAscii(rt, "bar"), 2); flushQueue(); EXPECT_EQ(output, "bar2"); output.clear(); } { // Object auto cb = bridging::fromJs>( rt, function("(func, a, b) => func(a.obj + b)"), invoker); jsi::Object foo(rt); foo.setProperty(rt, "obj", "foo"); cb(func, std::move(foo), 1); flushQueue(); EXPECT_EQ(output, "foo1"); output.clear(); } { // String auto cb = bridging::fromJs>( rt, function("(func, a, b) => func(a + b)"), invoker); jsi::String foo(jsi::String::createFromAscii(rt, "foo")); cb(func, std::move(foo), 1); flushQueue(); EXPECT_EQ(output, "foo1"); cb(func, jsi::String::createFromAscii(rt, "bar"), 2); flushQueue(); EXPECT_EQ(output, "bar2"); output.clear(); } { // Array auto fn = function("(func, a, b) => func(a[0] + b)"); auto cb = bridging::fromJs>( rt, fn, invoker); jsi::Array foo(rt, 1); foo.setValueAtIndex(rt, 0, jsi::String::createFromAscii(rt, "foo")); cb(func, std::move(foo), 1); flushQueue(); EXPECT_EQ(output, "foo1"); output.clear(); } } TEST_F(BridgingTest, promiseTest) { auto func = function( "(promise, obj) => {" " promise.then(" " (res) => { obj.res = res; }," " (err) => { obj.err = err; }" " )" "}"); auto promise = AsyncPromise>(rt, invoker); auto output = jsi::Object(rt); func.call(rt, bridging::toJs(rt, promise, invoker), output); promise.resolve({"foo"s, "bar"s}); flushQueue(); EXPECT_EQ(1, output.getPropertyNames(rt).size(rt)); EXPECT_EQ(2, output.getProperty(rt, "res").asObject(rt).asArray(rt).size(rt)); EXPECT_NO_THROW(promise.resolve({"ignored"})); EXPECT_NO_THROW(promise.reject("ignored")); promise = AsyncPromise>(rt, invoker); output = jsi::Object(rt); func.call(rt, bridging::toJs(rt, promise, invoker), output); promise.reject("fail"); flushQueue(); EXPECT_EQ(1, output.getPropertyNames(rt).size(rt)); EXPECT_EQ( "fail"s, output.getProperty(rt, "err") .asObject(rt) .getProperty(rt, "message") .asString(rt) .utf8(rt)); EXPECT_NO_THROW(promise.resolve({"ignored"})); EXPECT_NO_THROW(promise.reject("ignored")); } using EventType = std::vector; using EventSubscriptionsWithLastEvent = std::vector>>; namespace { template void addEventSubscription( jsi::Runtime& rt, const AsyncEventEmitter& eventEmitter, EventSubscriptionsWithLastEvent& eventSubscriptionsWithListener, const std::shared_ptr& invoker) { auto eventEmitterJs = bridging::toJs(rt, eventEmitter, invoker); auto lastEvent = std::make_shared(); auto listenJs = bridging::toJs( rt, [lastEvent = lastEvent](const EventType& event) { *lastEvent = event; }, invoker); eventSubscriptionsWithListener.emplace_back(std::make_pair( jsi::Object(eventEmitterJs.asFunction(rt) .callWithThis(rt, eventEmitterJs, listenJs) .asObject(rt)), std::move(lastEvent))); } } // namespace TEST_F(BridgingTest, eventEmitterTest) { EventSubscriptionsWithLastEvent eventSubscriptionsWithListener; AsyncEventEmitter eventEmitter; EXPECT_NO_THROW(eventEmitter.emit({"one", "two", "three"})); EXPECT_EQ(0, eventSubscriptionsWithListener.size()); // register 3 JavaScript listeners to the event emitter for (int i = 0; i < 3; ++i) { addEventSubscription( rt, eventEmitter, eventSubscriptionsWithListener, invoker); } EXPECT_TRUE(eventEmitter.state_->listeners.contains(0)); EXPECT_TRUE(eventEmitter.state_->listeners.contains(1)); EXPECT_TRUE(eventEmitter.state_->listeners.contains(2)); // emit with args EXPECT_NO_THROW(eventEmitter.emit({"four", "five", "six"})); flushQueue(); // verify all listeners received the event for (const auto& [_, lastEvent] : eventSubscriptionsWithListener) { EXPECT_EQ(3, lastEvent->size()); EXPECT_EQ("four", lastEvent->at(0)); EXPECT_EQ("five", lastEvent->at(1)); EXPECT_EQ("six", lastEvent->at(2)); } // Remove 2nd eventSubscriptions eventSubscriptionsWithListener[1] .first.getPropertyAsFunction(rt, "remove") .callWithThis(rt, eventSubscriptionsWithListener[1].first); eventSubscriptionsWithListener.erase( eventSubscriptionsWithListener.begin() + 1); // Add 4th and 5th eventSubscriptions addEventSubscription( rt, eventEmitter, eventSubscriptionsWithListener, invoker); addEventSubscription( rt, eventEmitter, eventSubscriptionsWithListener, invoker); EXPECT_TRUE(eventEmitter.state_->listeners.contains(0)); EXPECT_FALSE(eventEmitter.state_->listeners.contains(1)); EXPECT_TRUE(eventEmitter.state_->listeners.contains(2)); EXPECT_TRUE(eventEmitter.state_->listeners.contains(3)); EXPECT_TRUE(eventEmitter.state_->listeners.contains(4)); // Emit more events EXPECT_NO_THROW(eventEmitter.emit({"seven", "eight", "nine"})); flushQueue(); for (const auto& [_, lastEvent] : eventSubscriptionsWithListener) { EXPECT_EQ(3, lastEvent->size()); EXPECT_EQ("seven", lastEvent->at(0)); EXPECT_EQ("eight", lastEvent->at(1)); EXPECT_EQ("nine", lastEvent->at(2)); } // clean-up the event subscriptions for (const auto& [eventSubscription, _] : eventSubscriptionsWithListener) { eventSubscription.getPropertyAsFunction(rt, "remove") .callWithThis(rt, eventSubscription); } flushQueue(); // Emit with function EXPECT_NO_THROW(eventEmitter.emit( [jsInvoker = invoker, value = {"ten", "eleven", "twelve"}](jsi::Runtime& rt) -> jsi::Value { return bridging::toJs(rt, value, jsInvoker); })); flushQueue(); // no new data as listeners had been removed for (const auto& [_, lastEvent] : eventSubscriptionsWithListener) { EXPECT_EQ(3, lastEvent->size()); EXPECT_EQ("seven", lastEvent->at(0)); EXPECT_EQ("eight", lastEvent->at(1)); EXPECT_EQ("nine", lastEvent->at(2)); } } TEST_F(BridgingTest, optionalTest) { EXPECT_EQ( 1, bridging::fromJs>(rt, jsi::Value(1), invoker)); EXPECT_EQ( 1, bridging::fromJs>( rt, std::make_optional(jsi::Value(1)), invoker)); EXPECT_EQ( "hi"s, bridging::fromJs>( rt, std::make_optional(jsi::String::createFromAscii(rt, "hi")), invoker)); EXPECT_FALSE( bridging::fromJs>(rt, jsi::Value::undefined(), invoker) .has_value()); EXPECT_FALSE( bridging::fromJs>(rt, jsi::Value::null(), invoker) .has_value()); EXPECT_TRUE(bridging::toJs(rt, std::optional(), invoker).isNull()); EXPECT_EQ(1, bridging::toJs(rt, std::optional(1), invoker).asNumber()); } TEST_F(BridgingTest, pointerTest) { auto str = "hi"s; auto unique = std::make_unique(str); auto shared = std::make_shared(str); auto weak = std::weak_ptr(shared); EXPECT_EQ(str, bridging::toJs(rt, unique, invoker).asString(rt).utf8(rt)); EXPECT_EQ(str, bridging::toJs(rt, shared, invoker).asString(rt).utf8(rt)); EXPECT_EQ(str, bridging::toJs(rt, weak, invoker).asString(rt).utf8(rt)); shared.reset(); EXPECT_TRUE(bridging::toJs(rt, weak, invoker).isNull()); } TEST_F(BridgingTest, supportTest) { // Ensure sure can convert some basic types, including primitives that can be // trivially converted to JSI values. EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs, jsi::Array>)); EXPECT_TRUE((bridging::supportsFromJs, jsi::Array&>)); EXPECT_TRUE((bridging::supportsFromJs, jsi::Array>)); EXPECT_TRUE((bridging::supportsFromJs, jsi::Array&>)); EXPECT_TRUE(( bridging:: supportsFromJs>, jsi::Array>)); EXPECT_TRUE((bridging::supportsFromJs< std::vector>, jsi::Array&>)); EXPECT_TRUE( (bridging::supportsFromJs, jsi::Object>)); EXPECT_TRUE( (bridging::supportsFromJs, jsi::Object&>)); // Ensure incompatible conversions will fail. EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs, jsi::String>)); EXPECT_FALSE((bridging::supportsFromJs, jsi::String&>)); EXPECT_FALSE((bridging::supportsFromJs, jsi::String>)); EXPECT_FALSE((bridging::supportsFromJs, jsi::String&>)); // Ensure copying and down casting JSI values is also supported. EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); EXPECT_TRUE((bridging::supportsFromJs)); // Ensure incorrect casts will fail. EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); EXPECT_FALSE((bridging::supportsFromJs)); // Ensure we can convert some basic types to JSI values. EXPECT_TRUE((bridging::supportsToJs)); EXPECT_TRUE((bridging::supportsToJs)); EXPECT_TRUE((bridging::supportsToJs)); EXPECT_TRUE((bridging::supportsToJs)); EXPECT_TRUE((bridging::supportsToJs)); EXPECT_TRUE((bridging::supportsToJs>)); EXPECT_TRUE((bridging::supportsToJs, jsi::Array>)); EXPECT_TRUE((bridging::supportsToJs>)); EXPECT_TRUE((bridging::supportsToJs, jsi::Array>)); EXPECT_TRUE((bridging::supportsToJs>)); EXPECT_TRUE( (bridging::supportsToJs, jsi::Object>)); EXPECT_TRUE((bridging::supportsToJs)); EXPECT_TRUE((bridging::supportsToJs)); // Ensure invalid conversions to JSI values are not supported. EXPECT_FALSE((bridging::supportsToJs)); EXPECT_FALSE((bridging::supportsToJs)); EXPECT_FALSE((bridging::supportsToJs)); EXPECT_FALSE((bridging::supportsToJs)); EXPECT_FALSE((bridging::supportsToJs)); EXPECT_FALSE((bridging::supportsToJs, jsi::Function>)); } TEST_F(BridgingTest, dynamicTest) { // Null auto nullFromJsResult = bridging::fromJs(rt, jsi::Value::null(), invoker); EXPECT_TRUE(nullFromJsResult.isNull()); auto nullToJsResult = bridging::toJs(rt, nullptr, invoker); EXPECT_TRUE(nullToJsResult.isNull()); // Boolean auto booleanFromJsResult = bridging::fromJs(rt, jsi::Value(true), invoker); EXPECT_TRUE(booleanFromJsResult.isBool()); EXPECT_TRUE(booleanFromJsResult.asBool()); auto booleanToJsResult = bridging::toJs(rt, true, invoker); EXPECT_TRUE(booleanToJsResult.isBool()); EXPECT_TRUE(booleanToJsResult.asBool()); // Number auto numberFromJsResult = bridging::fromJs(rt, jsi::Value(1.2), invoker); EXPECT_TRUE(numberFromJsResult.isNumber()); EXPECT_DOUBLE_EQ(1.2, numberFromJsResult.asDouble()); auto numberToJsResult = bridging::toJs(rt, 1.2, invoker); EXPECT_TRUE(numberToJsResult.isNumber()); EXPECT_DOUBLE_EQ(1.2, numberToJsResult.asNumber()); // String auto stringFromJsResult = bridging::fromJs( rt, jsi::Value(jsi::String::createFromAscii(rt, "hello")), invoker); EXPECT_TRUE(stringFromJsResult.isString()); EXPECT_EQ("hello"s, stringFromJsResult.asString()); auto stringToJsResult = bridging::toJs(rt, "hello", invoker); EXPECT_TRUE(stringToJsResult.isString()); EXPECT_EQ("hello"s, stringToJsResult.asString(rt).utf8(rt)); // Array auto arrayFromJsResult = bridging::fromJs( rt, jsi::Value(jsi::Array::createWithElements(rt, "foo", "bar")), invoker); EXPECT_TRUE(arrayFromJsResult.isArray()); EXPECT_EQ(2, arrayFromJsResult.size()); EXPECT_EQ("foo"s, arrayFromJsResult[0].asString()); EXPECT_EQ("bar"s, arrayFromJsResult[1].asString()); auto arrayToJsResult = bridging::toJs( rt, folly::dynamic::array("foo", "bar"), invoker); EXPECT_TRUE(arrayToJsResult.isObject()); EXPECT_TRUE(arrayToJsResult.asObject(rt).isArray(rt)); auto arrayToJsResultArray = arrayToJsResult.asObject(rt).asArray(rt); EXPECT_EQ(2, arrayToJsResultArray.size(rt)); EXPECT_EQ( "foo"s, arrayToJsResultArray.getValueAtIndex(rt, 0).asString(rt).utf8(rt)); EXPECT_EQ( "bar"s, arrayToJsResultArray.getValueAtIndex(rt, 1).asString(rt).utf8(rt)); // Object auto jsiObject = jsi::Object(rt); jsiObject.setProperty(rt, "foo", "bar"); auto objectFromJsResult = bridging::fromJs( rt, jsi::Value(std::move(jsiObject)), invoker); EXPECT_TRUE(objectFromJsResult.isObject()); EXPECT_EQ(1, objectFromJsResult.size()); EXPECT_EQ("bar"s, objectFromJsResult["foo"].asString()); auto objectToJsResult = bridging::toJs( rt, folly::dynamic::object("foo", "bar"), invoker); EXPECT_TRUE(objectToJsResult.isObject()); auto objectToJsResultObject = objectToJsResult.asObject(rt); EXPECT_EQ( "bar"s, objectToJsResultObject.getProperty(rt, "foo").asString(rt).utf8(rt)); // Undefined auto undefinedFromJsResult = bridging::fromJs(rt, jsi::Value::undefined(), invoker); EXPECT_TRUE(undefinedFromJsResult.isNull()); } } // namespace facebook::react