Skip to content

Commit db48005

Browse files
Add GTest utility methods to test C++ Turbo Module EventEmitters (#54060)
Summary: Changelog: [Internal] Adds a test case to assert Turbo Module eventEmitter properties in GTests Differential Revision: D83280050
1 parent 588233c commit db48005

File tree

4 files changed

+78
-4
lines changed

4 files changed

+78
-4
lines changed

packages/react-native/ReactCommon/react/bridging/EventEmitter.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,27 @@ class EventSubscription {
2828
EventSubscription(const EventSubscription&) = delete;
2929
EventSubscription& operator=(const EventSubscription&) = delete;
3030

31+
void remove() {
32+
remove_();
33+
}
34+
3135
private:
3236
friend Bridging<EventSubscription>;
33-
3437
std::function<void()> remove_;
3538
};
3639

3740
template <>
3841
struct Bridging<EventSubscription> {
42+
static EventSubscription fromJs(
43+
jsi::Runtime& rt,
44+
const jsi::Object& value,
45+
const std::shared_ptr<CallInvoker>& jsInvoker) {
46+
auto listener = bridging::fromJs<AsyncCallback<>>(
47+
rt, value.getProperty(rt, "remove"), jsInvoker);
48+
return EventSubscription(
49+
[listener = std::move(listener)]() mutable { listener(); });
50+
}
51+
3952
static jsi::Object toJs(
4053
jsi::Runtime& rt,
4154
const EventSubscription& eventSubscription,

packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ class JSI_EXPORT TurboModule : public jsi::HostObject {
9191
size_t count);
9292
};
9393
std::unordered_map<std::string, MethodMetadata> methodMap_;
94+
95+
friend class TurboModuleTestFixtureInternal;
9496
std::unordered_map<std::string, std::shared_ptr<IAsyncEventEmitter>>
9597
eventEmitterMap_;
9698

packages/react-native/ReactCommon/react/nativemodule/core/tests/TurboModuleTestFixture.h

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#pragma once
99

1010
#include <ReactCommon/TestCallInvoker.h>
11+
#include <ReactCommon/TurboModule.h>
1112
#include <gtest/gtest.h>
1213
#include <hermes/hermes.h>
1314
#include <react/bridging/Bridging.h>
@@ -16,10 +17,25 @@
1617
#include <optional>
1718

1819
namespace facebook::react {
19-
class TurboModule;
20+
21+
class TurboModuleTestFixtureInternal {
22+
public:
23+
static bool containsEventEmitter(
24+
TurboModule& turboModule,
25+
const std::string& eventEmitterName) {
26+
return turboModule.eventEmitterMap_.contains(eventEmitterName);
27+
}
28+
29+
static const std::shared_ptr<IAsyncEventEmitter> getEventEmitter(
30+
TurboModule& turboModule,
31+
const std::string& eventEmitterName) {
32+
return turboModule.eventEmitterMap_.at(eventEmitterName);
33+
}
34+
};
2035

2136
template <typename T, typename... Args>
22-
class TurboModuleTestFixture : public ::testing::Test {
37+
class TurboModuleTestFixture : public TurboModuleTestFixtureInternal,
38+
public ::testing::Test {
2339
static_assert(
2440
std::is_base_of<TurboModule, T>::value,
2541
"T must be derived from TurboModule");
@@ -54,6 +70,29 @@ class TurboModuleTestFixture : public ::testing::Test {
5470
}));
5571
}
5672

73+
template <typename... EventType, typename Listener>
74+
EventSubscription addEventEmitterListener(
75+
jsi::Runtime& rt,
76+
const std::string& eventEmitterName,
77+
Listener&& listener) {
78+
EXPECT_TRUE(containsEventEmitter(*module_, eventEmitterName));
79+
auto listenJs = bridging::toJs(
80+
rt,
81+
[listener = std::forward<Listener>(listener)](
82+
const EventType&... event) { listener(event...); },
83+
jsInvoker_);
84+
std::shared_ptr<AsyncEventEmitter<EventType...>> eventEmitter =
85+
std::static_pointer_cast<AsyncEventEmitter<EventType...>>(
86+
getEventEmitter(*module_, eventEmitterName));
87+
jsi::Object eventEmitterJs = bridging::toJs(rt, *eventEmitter, jsInvoker_);
88+
auto eventSubscriptionJs =
89+
jsi::Object(eventEmitterJs.asFunction(rt)
90+
.callWithThis(rt, eventEmitterJs, listenJs)
91+
.asObject(rt));
92+
return bridging::fromJs<EventSubscription>(
93+
rt, eventSubscriptionJs, jsInvoker_);
94+
}
95+
5796
void TearDown() override {
5897
module_ = nullptr;
5998
jsInvoker_ = nullptr;

packages/rn-tester/NativeCxxModuleExample/tests/NativeCxxModuleExampleTests.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#include <NativeCxxModuleExample/NativeCxxModuleExample.h>
99
#include <ReactCommon/TurboModuleTestFixture.h>
1010
#include <gtest/gtest.h>
11-
#include <list>
1211
#include <memory>
1312
#include <optional>
1413
#include <vector>
@@ -172,4 +171,25 @@ TEST_F(
172171
EXPECT_EQ(module_->getWithWithOptionalArgs(*runtime_, false), false);
173172
}
174173

174+
TEST_F(NativeCxxModuleExampleTests, VoidFunEmitsEvents) {
175+
int onPressCalled = 0;
176+
std::string onClickCalled;
177+
auto onPressSubscription = addEventEmitterListener<>(
178+
*runtime_, "onPress", [&]() { onPressCalled++; });
179+
addEventEmitterListener<std::string>(
180+
*runtime_, "onClick", [&](const std::string& event) {
181+
onClickCalled = event;
182+
});
183+
module_->voidFunc(*runtime_);
184+
jsInvoker_->flushQueue();
185+
EXPECT_EQ(onPressCalled, 1);
186+
EXPECT_EQ(onClickCalled, "value from callback on click!");
187+
188+
onPressSubscription.remove();
189+
jsInvoker_->flushQueue();
190+
191+
module_->voidFunc(*runtime_);
192+
jsInvoker_->flushQueue();
193+
EXPECT_EQ(onPressCalled, 1);
194+
}
175195
} // namespace facebook::react

0 commit comments

Comments
 (0)