/** * Copyright (c) Facebook, Inc. and its 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; TEST(ShadowNodeTest, handleProps) { const auto &raw = RawProps(folly::dynamic::object("nativeID", "abc")); auto parser = RawPropsParser(); parser.prepare(); raw.parse(parser); auto props = std::make_shared(Props(), raw); // Props are not sealed after applying raw props. ASSERT_FALSE(props->getSealed()); ASSERT_STREQ(props->nativeId.c_str(), "abc"); } TEST(ShadowNodeTest, handleShadowNodeCreation) { auto eventDispatcher = std::shared_ptr(); auto componentDescriptor = TestComponentDescriptor(eventDispatcher); auto node = std::make_shared( ShadowNodeFragment{ /* .tag = */ 9, /* .surfaceId = */ 1, /* .props = */ std::make_shared(), /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); ASSERT_FALSE(node->getSealed()); ASSERT_STREQ(node->getComponentName(), "Test"); ASSERT_EQ(node->getTag(), 9); ASSERT_EQ(node->getSurfaceId(), 1); ASSERT_EQ(node->getEventEmitter(), nullptr); ASSERT_EQ(node->getChildren().size(), 0); node->sealRecursive(); ASSERT_TRUE(node->getSealed()); ASSERT_TRUE(node->getProps()->getSealed()); } TEST(ShadowNodeTest, handleShadowNodeSimpleCloning) { auto eventDispatcher = std::shared_ptr(); auto componentDescriptor = TestComponentDescriptor(eventDispatcher); auto node = std::make_shared( ShadowNodeFragment{ /* .tag = */ 9, /* .surfaceId = */ 1, /* .props = */ std::make_shared(), /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto node2 = std::make_shared(*node, ShadowNodeFragment{}); ASSERT_STREQ(node->getComponentName(), "Test"); ASSERT_EQ(node->getTag(), 9); ASSERT_EQ(node->getSurfaceId(), 1); ASSERT_EQ(node->getEventEmitter(), nullptr); } TEST(ShadowNodeTest, handleShadowNodeMutation) { auto eventDispatcher = std::shared_ptr(); auto componentDescriptor = TestComponentDescriptor(eventDispatcher); auto props = std::make_shared(); auto node1 = std::make_shared( ShadowNodeFragment{ /* .tag = */ 1, /* .surfaceId = */ 1, /* .props = */ std::make_shared(), /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto node2 = std::make_shared( ShadowNodeFragment{ /* .tag = */ 2, /* .surfaceId = */ 1, /* .props = */ std::make_shared(), /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto node3 = std::make_shared( ShadowNodeFragment{ /* .tag = */ 3, /* .surfaceId = */ 1, /* .props = */ std::make_shared(), /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); node1->appendChild(node2); node1->appendChild(node3); auto node1Children = node1->getChildren(); ASSERT_EQ(node1Children.size(), 2); ASSERT_EQ(node1Children.at(0), node2); ASSERT_EQ(node1Children.at(1), node3); auto node4 = std::make_shared(*node2, ShadowNodeFragment{}); node1->replaceChild(node2, node4); node1Children = node1->getChildren(); ASSERT_EQ(node1Children.size(), 2); ASSERT_EQ(node1Children.at(0), node4); ASSERT_EQ(node1Children.at(1), node3); // Seal the entire tree. node1->sealRecursive(); ASSERT_TRUE(node1->getSealed()); ASSERT_TRUE(node3->getSealed()); ASSERT_TRUE(node4->getSealed()); // No more mutation after sealing. EXPECT_THROW(node4->setLocalData(nullptr), std::runtime_error); auto node5 = std::make_shared(*node4, ShadowNodeFragment{}); node5->setLocalData(nullptr); ASSERT_EQ(node5->getLocalData(), nullptr); } TEST(ShadowNodeTest, handleCloneFunction) { auto eventDispatcher = std::shared_ptr(); auto componentDescriptor = TestComponentDescriptor(eventDispatcher); auto firstNode = std::make_shared( ShadowNodeFragment{ /* .tag = */ 9, /* .surfaceId = */ 1, /* .props = */ std::make_shared(), /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto firstNodeClone = firstNode->clone({}); // Those two nodes are *not* same. ASSERT_NE(firstNode, firstNodeClone); // `secondNodeClone` is an instance of `TestShadowNode`. ASSERT_NE( std::dynamic_pointer_cast(firstNodeClone), nullptr); // Both nodes have same content. ASSERT_EQ(firstNode->getTag(), firstNodeClone->getTag()); ASSERT_EQ(firstNode->getSurfaceId(), firstNodeClone->getSurfaceId()); ASSERT_EQ(firstNode->getProps(), firstNodeClone->getProps()); } TEST(ShadowNodeTest, handleLocalData) { auto eventDispatcher = std::shared_ptr(); auto componentDescriptor = TestComponentDescriptor(eventDispatcher); auto localData42 = std::make_shared(); localData42->setNumber(42); auto anotherLocalData42 = std::make_shared(); anotherLocalData42->setNumber(42); auto localDataOver9000 = std::make_shared(); localDataOver9000->setNumber(9001); auto props = std::make_shared(); auto firstNode = std::make_shared( ShadowNodeFragment{ /* .tag = */ 9, /* .surfaceId = */ 1, /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto secondNode = std::make_shared( ShadowNodeFragment{ /* .tag = */ 9, /* .surfaceId = */ 1, /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto thirdNode = std::make_shared( ShadowNodeFragment{ /* .tag = */ 9, /* .surfaceId = */ 1, /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); firstNode->setLocalData(localData42); secondNode->setLocalData(localData42); thirdNode->setLocalData(localDataOver9000); // LocalData object are compared by pointer, not by value. ASSERT_EQ(firstNode->getLocalData(), secondNode->getLocalData()); ASSERT_NE(firstNode->getLocalData(), thirdNode->getLocalData()); secondNode->setLocalData(anotherLocalData42); ASSERT_NE(firstNode->getLocalData(), secondNode->getLocalData()); // LocalData cannot be changed for sealed shadow node. secondNode->sealRecursive(); ASSERT_ANY_THROW(secondNode->setLocalData(localDataOver9000)); } TEST(ShadowNodeTest, handleBacktracking) { /* * The structure: * * * * * * * * * */ auto eventDispatcher = std::shared_ptr(); auto componentDescriptor = TestComponentDescriptor(eventDispatcher); auto props = std::make_shared(); auto nodeAA = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto nodeABA = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto nodeABB = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto nodeABC = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto nodeABChildren = std::make_shared( SharedShadowNodeList{nodeABA, nodeABB, nodeABC}); auto nodeAB = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ nodeABChildren, }, componentDescriptor); auto nodeAC = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); auto nodeAChildren = std::make_shared( SharedShadowNodeList{nodeAA, nodeAB, nodeAC}); auto nodeA = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ nodeAChildren, }, componentDescriptor); auto nodeZ = std::make_shared( ShadowNodeFragment{ /* .tag = */ ShadowNodeFragment::tagPlaceholder(), /* .surfaceId = */ ShadowNodeFragment::surfaceIdPlaceholder(), /* .props = */ props, /* .eventEmitter = */ ShadowNodeFragment::eventEmitterPlaceholder(), /* .children = */ ShadowNode::emptySharedShadowNodeSharedList(), }, componentDescriptor); std::vector> ancestors = {}; // Negative case: auto ancestors1 = nodeZ->getAncestors(*nodeA); ASSERT_EQ(ancestors1.size(), 0); // Positive case: auto ancestors2 = nodeABC->getAncestors(*nodeA); ASSERT_EQ(ancestors2.size(), 2); ASSERT_EQ(&ancestors2[0].first.get(), nodeA.get()); ASSERT_EQ(&ancestors2[1].first.get(), nodeAB.get()); }