/** * Copyright (c) 2013-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. */ package com.facebook.react.tests; import java.util.Arrays; import java.util.List; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; import com.facebook.react.ReactRootView; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.modules.systeminfo.AndroidInfoModule; import com.facebook.react.uimanager.PixelUtil; import com.facebook.react.uimanager.UIImplementation; import com.facebook.react.uimanager.UIImplementationProvider; import com.facebook.react.uimanager.UIManagerModule; import com.facebook.react.uimanager.ViewManager; import com.facebook.react.views.text.ReactRawTextManager; import com.facebook.react.views.text.ReactTextViewManager; import com.facebook.react.views.view.ReactViewManager; import com.facebook.react.testing.FakeWebSocketModule; import com.facebook.react.testing.ReactIntegrationTestCase; import com.facebook.react.testing.ReactTestHelper; /** * Test case for basic {@link UIManagerModule} functionality. */ public class CatalystUIManagerTestCase extends ReactIntegrationTestCase { private interface UIManagerTestModule extends JavaScriptModule { void renderFlexTestApplication(int rootTag); void renderFlexWithTextApplication(int rootTag); void renderAbsolutePositionTestApplication(int rootTag); void renderAbsolutePositionBottomRightTestApplication(int rootTag); void renderCenteredTextViewTestApplication(int rootTag, String text); void renderUpdatePositionInListTestApplication(int rootTag); void flushUpdatePositionInList(); } private UIManagerTestModule jsModule; private UIManagerModule uiManager; private int inPixelRounded(int val) { return Math.round(PixelUtil.toPixelFromDIP(val)); } private boolean isWithinRange(float value, float lower, float upper) { return value >= lower && value <= upper; } private ReactRootView createRootView() { ReactRootView rootView = new ReactRootView(getContext()); final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); rootView.setLayoutParams( new FrameLayout.LayoutParams(metrics.widthPixels, metrics.heightPixels)); uiManager.addMeasuredRootView(rootView); // We add the root view by posting to the main thread so wait for that to complete so that the // root view tag is added to the view waitForIdleSync(); return rootView; } @Override protected void setUp() throws Exception { super.setUp(); List<ViewManager> viewManagers = Arrays.<ViewManager>asList( new ReactViewManager(), new ReactTextViewManager(), new ReactRawTextManager()); uiManager = new UIManagerModule( getContext(), viewManagers, new UIImplementationProvider()); UiThreadUtil.runOnUiThread(new Runnable() { @Override public void run() { uiManager.onHostResume(); } }); waitForIdleSync(); jsModule = ReactTestHelper.catalystInstanceBuilder(this) .addNativeModule(uiManager) .addNativeModule(new AndroidInfoModule()) .addNativeModule(new FakeWebSocketModule()) .addJSModule(UIManagerTestModule.class) .build() .getJSModule(UIManagerTestModule.class); } public void testFlexUIRendered() { FrameLayout rootView = createRootView(); jsModule.renderFlexTestApplication(rootView.getId()); waitForBridgeAndUIIdle(); assertEquals(1, rootView.getChildCount()); ViewGroup container = getViewByTestId(rootView, "container"); assertEquals(inPixelRounded(200), container.getWidth()); assertEquals(inPixelRounded(200), container.getHeight()); assertEquals(2, container.getChildCount()); View child0 = container.getChildAt(0); assertEquals(inPixelRounded(100), child0.getWidth()); assertEquals(inPixelRounded(200), child0.getHeight()); View child1 = container.getChildAt(1); assertEquals(inPixelRounded(100), child1.getWidth()); assertEquals(inPixelRounded(200), child1.getHeight()); } // TODO t13583009 // Breaks OSS CI but runs fine locally // Find what could be different and make the test independent of env // public void testFlexWithTextViews() { // FrameLayout rootView = createRootView(); // jsModule.renderFlexWithTextApplication(rootView.getId()); // waitForBridgeAndUIIdle(); // // assertEquals(1, rootView.getChildCount()); // // ViewGroup container = getViewByTestId(rootView, "container"); // assertEquals(inPixelRounded(300), container.getHeight()); // assertEquals(1, container.getChildCount()); // // ViewGroup row = (ViewGroup) container.getChildAt(0); // assertEquals(inPixelRounded(300), row.getHeight()); // assertEquals(2, row.getChildCount()); // // // Text measurement adds padding that isn't completely dependent on density so we can't easily // // get an exact value here // float approximateExpectedTextHeight = inPixelRounded(19); // View leftText = row.getChildAt(0); // assertTrue( // isWithinRange( // leftText.getHeight(), // approximateExpectedTextHeight - PixelUtil.toPixelFromDIP(1), // approximateExpectedTextHeight + PixelUtil.toPixelFromDIP(1))); // assertEquals(row.getWidth() / 2 - inPixelRounded(20), leftText.getWidth()); // assertEquals(inPixelRounded(290), (leftText.getTop() + leftText.getHeight())); // // View rightText = row.getChildAt(1); // assertTrue( // isWithinRange( // rightText.getHeight(), // approximateExpectedTextHeight - PixelUtil.toPixelFromDIP(1), // approximateExpectedTextHeight + PixelUtil.toPixelFromDIP(1))); // assertEquals(leftText.getWidth(), rightText.getWidth()); // assertEquals(leftText.getTop(), rightText.getTop()); // assertEquals(leftText.getWidth() + inPixelRounded(30), rightText.getLeft()); // } public void testAbsolutePositionUIRendered() { FrameLayout rootView = createRootView(); jsModule.renderAbsolutePositionTestApplication(rootView.getId()); waitForBridgeAndUIIdle(); assertEquals(1, rootView.getChildCount()); View absoluteView = getViewByTestId(rootView, "absolute"); assertEquals(inPixelRounded(50), absoluteView.getWidth()); assertEquals(inPixelRounded(60), absoluteView.getHeight()); assertEquals(inPixelRounded(10), absoluteView.getLeft()); assertEquals(inPixelRounded(15), absoluteView.getTop()); } public void testUpdatePositionInList() { FrameLayout rootView = createRootView(); jsModule.renderUpdatePositionInListTestApplication(rootView.getId()); waitForBridgeAndUIIdle(); ViewGroup containerView = getViewByTestId(rootView, "container"); View c0 = containerView.getChildAt(0); View c1 = containerView.getChildAt(1); View c2 = containerView.getChildAt(2); assertEquals(inPixelRounded(10), c0.getHeight()); assertEquals(inPixelRounded(0), c0.getTop()); assertEquals(inPixelRounded(10), c1.getHeight()); assertEquals(inPixelRounded(10), c1.getTop()); assertEquals(inPixelRounded(10), c2.getHeight()); assertEquals(inPixelRounded(20), c2.getTop()); // Let's make the second elements 50px height instead of 10px jsModule.flushUpdatePositionInList(); waitForBridgeAndUIIdle(); assertEquals(inPixelRounded(10), c0.getHeight()); assertEquals(inPixelRounded(0), c0.getTop()); assertEquals(inPixelRounded(50), c1.getHeight()); assertEquals(inPixelRounded(10), c1.getTop()); assertEquals(inPixelRounded(10), c2.getHeight()); assertEquals(inPixelRounded(60), c2.getTop()); } public void testAbsolutePositionBottomRightUIRendered() { FrameLayout rootView = createRootView(); jsModule.renderAbsolutePositionBottomRightTestApplication(rootView.getId()); waitForBridgeAndUIIdle(); assertEquals(1, rootView.getChildCount()); ViewGroup containerView = getViewByTestId(rootView, "container"); View absoluteView = containerView.getChildAt(0); assertEquals(inPixelRounded(50), absoluteView.getWidth()); assertEquals(inPixelRounded(60), absoluteView.getHeight()); assertEquals(inPixelRounded(100 - 50 - 10), Math.round(absoluteView.getLeft())); assertEquals(inPixelRounded(100 - 60 - 15), Math.round(absoluteView.getTop())); } public void _testCenteredText(String text) { ReactRootView rootView = new ReactRootView(getContext()); int rootTag = uiManager.addMeasuredRootView(rootView); jsModule.renderCenteredTextViewTestApplication(rootTag, text); waitForBridgeAndUIIdle(); TextView textView = getViewByTestId(rootView, "text"); // text view should be centered String msg = "text `" + text + "` is not centered"; assertTrue(msg, textView.getLeft() > 0.1); assertTrue(msg, textView.getTop() > 0.1); assertEquals( msg, (int) Math.ceil((inPixelRounded(200) - textView.getWidth()) * 0.5f), textView.getLeft()); assertEquals( msg, (int) Math.ceil((inPixelRounded(100) - textView.getHeight()) * 0.5f), textView.getTop()); } public void testCenteredTextCases() { String[] cases = new String[] { "test", "with whitespace", }; for (String text : cases) { _testCenteredText(text); } } }