diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/ConsoleLogTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/ConsoleLogTest.cpp index 085389c7d71248..b8c3ba8a1b0393 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/ConsoleLogTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/ConsoleLogTest.cpp @@ -18,18 +18,30 @@ namespace facebook::react::jsinspector_modern { using namespace testing; -TEST_F(ReactInstanceIntegrationTest, ConsoleLogTest) { +TEST_P(ReactInstanceIntegrationTestWithFlags, ConsoleLog) { InSequence s; - EXPECT_CALL(getRemoteConnection(), onMessage(_)) - .Times(2) - .RetiresOnSaturation(); + EXPECT_CALL( + getRemoteConnection(), + onMessage(JsonParsed(AllOf(AtJsonPtr("/id", Eq(1)))))); EXPECT_CALL( getRemoteConnection(), onMessage(JsonParsed(AllOf( - AtJsonPtr("/params/args/0/value", Eq("Hello, World!")), - AtJsonPtr("/method", Eq("Runtime.consoleAPICalled")))))); + AtJsonPtr("/params/context/name", Eq("hermes")), + AtJsonPtr("/method", Eq("Runtime.executionContextCreated")))))); + + // Hermes console.* interception is currently explicitly disabled under the + // modern registry, and the runtime does not yet fire these events. When the + // implementation is more complete we should be able to remove this + // condition. + if (!featureFlags->enableModernCDPRegistry) { + EXPECT_CALL( + getRemoteConnection(), + onMessage(JsonParsed(AllOf( + AtJsonPtr("/params/args/0/value", Eq("Hello, World!")), + AtJsonPtr("/method", Eq("Runtime.consoleAPICalled")))))); + } EXPECT_CALL(getRemoteConnection(), onDisconnect()); @@ -37,4 +49,18 @@ TEST_F(ReactInstanceIntegrationTest, ConsoleLogTest) { run("console.log('Hello, World!');"); } +INSTANTIATE_TEST_SUITE_P( + ConsoleLogVaryingInspectorFlags, + ReactInstanceIntegrationTestWithFlags, + ::testing::Values( + FeatureFlags{ + .enableCxxInspectorPackagerConnection = true, + .enableModernCDPRegistry = true}, + FeatureFlags{ + .enableCxxInspectorPackagerConnection = false, + .enableModernCDPRegistry = false}, + FeatureFlags{ + .enableCxxInspectorPackagerConnection = true, + .enableModernCDPRegistry = false})); + } // namespace facebook::react::jsinspector_modern diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp b/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp index 35ea37e24d6fee..0dbe0445d646ae 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.cpp @@ -11,17 +11,42 @@ #include #include +#include +#include #include namespace facebook::react::jsinspector_modern { +class ReactInstanceIntegrationTestFeatureFlagsProvider + : public ReactNativeFeatureFlagsDefaults { + private: + FeatureFlags flags_; + + public: + explicit ReactInstanceIntegrationTestFeatureFlagsProvider( + FeatureFlags featureFlags) + : flags_(featureFlags) {} + + bool inspectorEnableModernCDPRegistry() override { + return flags_.enableModernCDPRegistry; + } + bool inspectorEnableCxxInspectorPackagerConnection() override { + return flags_.enableCxxInspectorPackagerConnection; + } +}; + ReactInstanceIntegrationTest::ReactInstanceIntegrationTest() : runtime(nullptr), instance(nullptr), messageQueueThread(std::make_shared()), - errorHandler(std::make_shared()) {} + errorHandler(std::make_shared()), + featureFlags(std::make_unique()) {} void ReactInstanceIntegrationTest::SetUp() { + ReactNativeFeatureFlags::override( + std::make_unique( + *featureFlags)); + auto mockRegistry = std::make_unique(); auto timerManager = std::make_shared(std::move(mockRegistry)); @@ -44,11 +69,25 @@ void ReactInstanceIntegrationTest::SetUp() { "ErrorUtils", jsi::Object::createFromHostObject(*jsiRuntime, errorHandler)); + std::shared_ptr pageTargetIfModernCDP_ = nullptr; + + if (featureFlags->enableModernCDPRegistry) { + VoidExecutor inspectorExecutor_ = [this](auto callback) { + immediateExecutor_.add(callback); + }; + MockPageTargetDelegate pageTargetDelegate_; + pageTargetIfModernCDP_ = + PageTarget::create(pageTargetDelegate_, inspectorExecutor_); + } + instance = std::make_unique( std::move(runtime_), messageQueueThread, timerManager, - std::move(jsErrorHandlingFunc)); + std::move(jsErrorHandlingFunc), + pageTargetIfModernCDP_ == nullptr ? nullptr + : pageTargetIfModernCDP_.get()); + timerManager->setRuntimeExecutor(instance->getBufferedRuntimeExecutor()); // JS Environment: @@ -56,17 +95,38 @@ void ReactInstanceIntegrationTest::SetUp() { // Inspector: auto& inspector = getInspectorInstance(); - auto pages = inspector.getPages(); - // We should now have at least a single page once the above runtime has been - // initialized. - assert(pages.size() > 0); - size_t pageId = pages.back().id; + int pageId; + if (pageTargetIfModernCDP_ != nullptr) { + // Under modern CDP, the React host is responsible for adding its page on + // startup. + pageId = inspector.addPage( + "mock-title", + "mock-vm", + [pageTargetIfModernCDP_](std::unique_ptr remote) + -> std::unique_ptr { + auto localConnection = pageTargetIfModernCDP_->connect( + std::move(remote), + { + .integrationName = "ReactInstanceIntegrationTest", + }); + return localConnection; + }, + // TODO: Allow customisation of InspectorTargetCapabilities + {}); + } else { + // Under legacy CDP, Hermes' DecoratedRuntime adds its page automatically + // within ConnectionDemux.enableDebugging. + auto pages = inspector.getPages(); + assert(pages.size() > 0); + pageId = pages.back().id; + } clientToVM_ = inspector.connect(pageId, mockRemoteConnections_.make_unique()); } void ReactInstanceIntegrationTest::TearDown() { + ReactNativeFeatureFlags::dangerouslyReset(); clientToVM_->disconnect(); } @@ -100,6 +160,7 @@ void ReactInstanceIntegrationTest::send( void ReactInstanceIntegrationTest::sendJSONString(const std::string& message) { // The runtime must be initialized and connected to before messaging clientToVM_->sendMessage(message); + messageQueueThread->flush(); } jsi::Value ReactInstanceIntegrationTest::run(const std::string& script) { diff --git a/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.h b/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.h index 804d2eec4926af..a059600b3d09b9 100644 --- a/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.h +++ b/packages/react-native/ReactCommon/jsinspector-modern/tests/ReactInstanceIntegrationTest.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include #include @@ -18,7 +19,14 @@ namespace facebook::react::jsinspector_modern { -class ReactInstanceIntegrationTest : public ::testing::Test { +using namespace ::testing; + +struct FeatureFlags { + const bool enableCxxInspectorPackagerConnection = true; + const bool enableModernCDPRegistry = true; +}; + +class ReactInstanceIntegrationTest : public Test { protected: ReactInstanceIntegrationTest(); void SetUp() override; @@ -36,6 +44,7 @@ class ReactInstanceIntegrationTest : public ::testing::Test { std::unique_ptr instance; std::shared_ptr messageQueueThread; std::shared_ptr errorHandler; + std::unique_ptr featureFlags; MockRemoteConnection& getRemoteConnection() { return *mockRemoteConnections_[0]; @@ -48,6 +57,15 @@ class ReactInstanceIntegrationTest : public ::testing::Test { bool verbose_ = false; UniquePtrFactory mockRemoteConnections_; std::unique_ptr clientToVM_; + folly::QueuedImmediateExecutor immediateExecutor_; }; +class ReactInstanceIntegrationTestWithFlags + : public ReactInstanceIntegrationTest, + public ::testing::WithParamInterface { + void SetUp() override { + featureFlags = std::make_unique(GetParam()); + ReactInstanceIntegrationTest::SetUp(); + } +}; } // namespace facebook::react::jsinspector_modern