/** * Copyright (c) 2015-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.views.textinput; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import android.view.Choreographer; import android.widget.EditText; import com.facebook.react.ReactRootView; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.JavaOnlyArray; import com.facebook.react.bridge.JavaOnlyMap; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactTestHelper; import com.facebook.react.uimanager.ReactChoreographer; 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.uimanager.ViewProps; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; /** * Tests for TextInput. */ @PrepareForTest({Arguments.class, ReactChoreographer.class}) @RunWith(RobolectricTestRunner.class) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) public class TextInputTest { @Rule public PowerMockRule rule = new PowerMockRule(); private ArrayList<Choreographer.FrameCallback> mPendingChoreographerCallbacks; @Before public void setUp() { PowerMockito.mockStatic(Arguments.class, ReactChoreographer.class); ReactChoreographer choreographerMock = mock(ReactChoreographer.class); PowerMockito.when(Arguments.createMap()).thenAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { return new JavaOnlyMap(); } }); PowerMockito.when(ReactChoreographer.getInstance()).thenReturn(choreographerMock); mPendingChoreographerCallbacks = new ArrayList<>(); doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { mPendingChoreographerCallbacks .add((Choreographer.FrameCallback) invocation.getArguments()[1]); return null; } }).when(choreographerMock).postFrameCallback( any(ReactChoreographer.CallbackType.class), any(Choreographer.FrameCallback.class)); } @Test public void testPropsApplied() { UIManagerModule uiManager = getUIManagerModule(); ReactRootView rootView = new ReactRootView(RuntimeEnvironment.application); rootView.setLayoutParams(new ReactRootView.LayoutParams(100, 100)); int rootTag = uiManager.addMeasuredRootView(rootView); int textInputTag = rootTag + 1; final String hintStr = "placeholder text"; uiManager.createView( textInputTag, ReactTextInputManager.REACT_CLASS, rootTag, JavaOnlyMap.of( ViewProps.FONT_SIZE, 13.37, ViewProps.HEIGHT, 20.0, "placeholder", hintStr)); uiManager.manageChildren( rootTag, null, null, JavaOnlyArray.of(textInputTag), JavaOnlyArray.of(0), null); uiManager.onBatchComplete(); executePendingChoreographerCallbacks(); EditText editText = (EditText) rootView.getChildAt(0); assertThat(editText.getHint()).isEqualTo(hintStr); assertThat(editText.getTextSize()).isEqualTo((float) Math.ceil(13.37)); assertThat(editText.getHeight()).isEqualTo(20); } @Test public void testPropsUpdate() { UIManagerModule uiManager = getUIManagerModule(); ReactRootView rootView = new ReactRootView(RuntimeEnvironment.application); rootView.setLayoutParams(new ReactRootView.LayoutParams(100, 100)); int rootTag = uiManager.addMeasuredRootView(rootView); int textInputTag = rootTag + 1; final String hintStr = "placeholder text"; uiManager.createView( textInputTag, ReactTextInputManager.REACT_CLASS, rootTag, JavaOnlyMap.of( ViewProps.FONT_SIZE, 13.37, ViewProps.HEIGHT, 20.0, "placeholder", hintStr)); uiManager.manageChildren( rootTag, null, null, JavaOnlyArray.of(textInputTag), JavaOnlyArray.of(0), null); uiManager.onBatchComplete(); executePendingChoreographerCallbacks(); EditText editText = (EditText) rootView.getChildAt(0); assertThat(editText.getHint()).isEqualTo(hintStr); assertThat(editText.getTextSize()).isEqualTo((float) Math.ceil(13.37)); assertThat(editText.getHeight()).isEqualTo(20); final String hintStr2 = "such hint"; uiManager.updateView( textInputTag, ReactTextInputManager.REACT_CLASS, JavaOnlyMap.of( ViewProps.FONT_SIZE, 26.74, ViewProps.HEIGHT, 40.0, "placeholder", hintStr2)); uiManager.onBatchComplete(); executePendingChoreographerCallbacks(); EditText updatedEditText = (EditText) rootView.getChildAt(0); assertThat(updatedEditText.getHint()).isEqualTo(hintStr2); assertThat(updatedEditText.getTextSize()).isEqualTo((float) Math.ceil(26.74f)); assertThat(updatedEditText.getHeight()).isEqualTo(40); } private void executePendingChoreographerCallbacks() { ArrayList<Choreographer.FrameCallback> callbacks = new ArrayList<>(mPendingChoreographerCallbacks); mPendingChoreographerCallbacks.clear(); for (Choreographer.FrameCallback frameCallback : callbacks) { frameCallback.doFrame(0); } } public UIManagerModule getUIManagerModule() { ReactApplicationContext reactContext = ReactTestHelper.createCatalystContextForTest(); List<ViewManager> viewManagers = Arrays.asList( new ViewManager[] { new ReactTextInputManager(), }); UIManagerModule uiManagerModule = new UIManagerModule( reactContext, viewManagers, new UIImplementationProvider()); uiManagerModule.onHostResume(); return uiManagerModule; } }