/* * 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 #include #include #include #include "TestComponent.h" using namespace facebook::react; class ShadowNodeTest : public ::testing::Test { protected: ShadowNodeTest() : eventDispatcher_(std::shared_ptr()), componentDescriptor_(TestComponentDescriptor({eventDispatcher_})) { /* * The structure: * * * * * * * * * */ auto props = std::make_shared(); auto traits = TestShadowNode::BaseTraits(); auto familyAA = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 11, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeAA_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyAA, traits); auto familyABA = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 12, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeABA_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyABA, traits); auto familyABB = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 13, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeABB_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyABB, traits); auto nodeABChildren = std::make_shared( SharedShadowNodeList{nodeABA_, nodeABB_}); auto familyAB = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 15, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeAB_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ nodeABChildren, }, familyAB, traits); auto familyAC = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 16, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeAC_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyAC, traits); auto nodeAChildren = std::make_shared( SharedShadowNodeList{nodeAA_, nodeAB_, nodeAC_}); auto familyA = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 17, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeA_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ nodeAChildren, }, familyA, traits); auto familyZ = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 18, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); nodeZ_ = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, familyZ, traits); } std::shared_ptr eventDispatcher_; std::shared_ptr nodeA_; std::shared_ptr nodeAA_; std::shared_ptr nodeABA_; std::shared_ptr nodeABB_; std::shared_ptr nodeAB_; std::shared_ptr nodeAC_; std::shared_ptr nodeZ_; TestComponentDescriptor componentDescriptor_; SurfaceId surfaceId_ = 1; }; TEST_F(ShadowNodeTest, handleShadowNodeCreation) { EXPECT_FALSE(nodeZ_->getSealed()); EXPECT_STREQ(nodeZ_->getComponentName(), "Test"); EXPECT_EQ(nodeZ_->getTag(), 18); EXPECT_EQ(nodeZ_->getSurfaceId(), surfaceId_); EXPECT_EQ(nodeZ_->getEventEmitter(), nullptr); EXPECT_EQ(nodeZ_->getChildren().size(), 0); } TEST_F(ShadowNodeTest, handleSealRecusive) { nodeZ_->sealRecursive(); EXPECT_TRUE(nodeZ_->getSealed()); EXPECT_TRUE(nodeZ_->getProps()->getSealed()); } TEST_F(ShadowNodeTest, handleShadowNodeSimpleCloning) { auto nodeARevision2 = std::make_shared(*nodeA_, ShadowNodeFragment{}); EXPECT_STREQ(nodeA_->getComponentName(), nodeARevision2->getComponentName()); EXPECT_EQ(nodeA_->getTag(), nodeARevision2->getTag()); EXPECT_EQ(nodeA_->getSurfaceId(), nodeARevision2->getSurfaceId()); EXPECT_EQ(nodeA_->getEventEmitter(), nodeARevision2->getEventEmitter()); } TEST_F(ShadowNodeTest, handleShadowNodeMutation) { auto nodeABChildren = nodeAB_->getChildren(); EXPECT_EQ(nodeABChildren.size(), 2); EXPECT_EQ(nodeABChildren.at(0), nodeABA_); EXPECT_EQ(nodeABChildren.at(1), nodeABB_); auto nodeABArevision2 = std::make_shared(*nodeABA_, ShadowNodeFragment{}); nodeAB_->replaceChild(*nodeABA_, nodeABArevision2); nodeABChildren = nodeAB_->getChildren(); EXPECT_EQ(nodeABChildren.size(), 2); EXPECT_EQ(nodeABChildren.at(0), nodeABArevision2); EXPECT_EQ(nodeABChildren.at(1), nodeABB_); // Seal the entire tree. nodeAB_->sealRecursive(); EXPECT_TRUE(nodeAB_->getSealed()); EXPECT_TRUE(nodeABArevision2->getSealed()); EXPECT_TRUE(nodeABB_->getSealed()); } TEST_F(ShadowNodeTest, handleCloneFunction) { auto nodeABClone = nodeAB_->clone({}); // Those two nodes are *not* same. EXPECT_NE(nodeAB_, nodeABClone); // `secondNodeClone` is an instance of `TestShadowNode`. EXPECT_NE( std::dynamic_pointer_cast(nodeABClone), nullptr); // Both nodes have same content. EXPECT_EQ(nodeAB_->getTag(), nodeABClone->getTag()); EXPECT_EQ(nodeAB_->getSurfaceId(), nodeABClone->getSurfaceId()); EXPECT_EQ(nodeAB_->getProps(), nodeABClone->getProps()); } TEST_F(ShadowNodeTest, handleState) { auto family = std::make_shared( ShadowNodeFamilyFragment{ /* .tag = */ 9, /* .surfaceId = */ surfaceId_, /* .eventEmitter = */ nullptr, }, eventDispatcher_, componentDescriptor_); auto traits = TestShadowNode::BaseTraits(); auto props = std::make_shared(); auto fragment = ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ {}}; auto const initialState = componentDescriptor_.createInitialState(fragment, family); auto firstNode = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ initialState}, family, traits); auto secondNode = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ initialState}, family, traits); auto thirdNode = std::make_shared( ShadowNodeFragment{ /* .props = */ props, /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), /* .state = */ initialState}, family, traits); TestShadowNode::ConcreteState::Shared _state = std::static_pointer_cast( initialState); _state->updateState(TestState{42}); thirdNode->setStateData({9001}); // State object are compared by pointer, not by value. EXPECT_EQ(firstNode->getState(), secondNode->getState()); EXPECT_NE(firstNode->getState(), thirdNode->getState()); secondNode->setStateData(TestState{42}); EXPECT_NE(firstNode->getState(), secondNode->getState()); // State cannot be changed for sealed shadow node. secondNode->sealRecursive(); EXPECT_DEATH_IF_SUPPORTED( { secondNode->setStateData(TestState{42}); }, "Attempt to mutate a sealed object."); }