/** * Copyright (c) 2015-present, Facebook, Inc. * * 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) { RawProps raw; raw["nativeID"] = "abc"; 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 node = std::make_shared( ShadowNodeFragment { .tag = 9, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList() }, nullptr ); ASSERT_FALSE(node->getSealed()); ASSERT_STREQ(node->getComponentName().c_str(), "Test"); ASSERT_EQ(node->getTag(), 9); ASSERT_EQ(node->getRootTag(), 1); ASSERT_EQ(node->getEventEmitter(), nullptr); ASSERT_EQ(node->getChildren().size(), 0); ASSERT_STREQ(node->getProps()->nativeId.c_str(), "testNativeID"); node->sealRecursive(); ASSERT_TRUE(node->getSealed()); ASSERT_TRUE(node->getProps()->getSealed()); } TEST(ShadowNodeTest, handleShadowNodeSimpleCloning) { auto node = std::make_shared( ShadowNodeFragment { .tag = 9, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList() }, nullptr ); auto node2 = std::make_shared(node, ShadowNodeFragment {}); ASSERT_STREQ(node->getComponentName().c_str(), "Test"); ASSERT_EQ(node->getTag(), 9); ASSERT_EQ(node->getRootTag(), 1); ASSERT_EQ(node->getEventEmitter(), nullptr); } TEST(ShadowNodeTest, handleShadowNodeMutation) { auto props = std::make_shared(); auto node1 = std::make_shared(ShadowNodeFragment {.tag = 1, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); auto node2 = std::make_shared(ShadowNodeFragment {.tag = 2, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); auto node3 = std::make_shared(ShadowNodeFragment {.tag = 3, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); 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 firstNode = std::make_shared(ShadowNodeFragment {.tag = 9, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); // The shadow node is not clonable if `cloneFunction` is not provided, ASSERT_DEATH_IF_SUPPORTED(firstNode->clone({}), "cloneFunction_"); auto secondNode = std::make_shared( ShadowNodeFragment { .tag = 9, .rootTag = 1, .props = std::make_shared(), .children = ShadowNode::emptySharedShadowNodeSharedList() }, [](const SharedShadowNode &shadowNode, const ShadowNodeFragment &fragment) { return std::make_shared( std::static_pointer_cast(shadowNode), fragment ); } ); auto secondNodeClone = secondNode->clone({}); // Those two nodes are *not* same. ASSERT_NE(secondNode, secondNodeClone); // `secondNodeClone` is an instance of `TestShadowNode`. ASSERT_NE(std::dynamic_pointer_cast(secondNodeClone), nullptr); // Both nodes have same content. ASSERT_EQ(secondNode->getTag(), secondNodeClone->getTag()); ASSERT_EQ(secondNode->getRootTag(), secondNodeClone->getRootTag()); ASSERT_EQ(secondNode->getProps(), secondNodeClone->getProps()); } TEST(ShadowNodeTest, handleLocalData) { 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, .rootTag = 1, .props = props, .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); auto secondNode = std::make_shared(ShadowNodeFragment {.tag = 9, .rootTag = 1, .props = props, .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); auto thirdNode = std::make_shared(ShadowNodeFragment {.tag = 9, .rootTag = 1, .props = props, .children = ShadowNode::emptySharedShadowNodeSharedList()}, nullptr); firstNode->setLocalData(localData42); secondNode->setLocalData(localData42); thirdNode->setLocalData(localDataOver9000); // LocalData object are compared by pointer, not by value. ASSERT_EQ(*firstNode, *secondNode); ASSERT_NE(*firstNode, *thirdNode); secondNode->setLocalData(anotherLocalData42); ASSERT_NE(*firstNode, *secondNode); // LocalData cannot be changed for sealed shadow node. secondNode->sealRecursive(); ASSERT_ANY_THROW(secondNode->setLocalData(localDataOver9000)); }