/** * 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.uimanager; import android.view.View; import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.JavaOnlyArray; import com.facebook.react.bridge.JavaOnlyMap; import com.facebook.react.uimanager.annotations.ReactProp; import com.facebook.react.uimanager.annotations.ReactPropGroup; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Rule; import org.junit.Test; import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.modules.junit4.rule.PowerMockRule; import org.robolectric.RobolectricTestRunner; import static org.fest.assertions.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; /** * Test updating view through {@link ViewManager} with {@link ReactProp} and {@link ReactPropGroup} * annotations. */ @RunWith(RobolectricTestRunner.class) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) public class ReactPropAnnotationSetterTest { @Rule public PowerMockRule rule = new PowerMockRule(); public interface ViewManagerUpdatesReceiver { void onBooleanSetterCalled(boolean value); void onIntSetterCalled(int value); void onDoubleSetterCalled(double value); void onFloatSetterCalled(float value); void onStringSetterCalled(String value); void onBoxedBooleanSetterCalled(Boolean value); void onBoxedIntSetterCalled(Integer value); void onArraySetterCalled(ReadableArray value); void onMapSetterCalled(ReadableMap value); void onFloatGroupPropSetterCalled(int index, float value); void onIntGroupPropSetterCalled(int index, int value); void onBoxedIntGroupPropSetterCalled(int index, Integer value); } private class ViewManagerUnderTest extends ViewManager<View, ReactShadowNode> { final ViewManagerUpdatesReceiver mViewManagerUpdatesReceiver; private ViewManagerUnderTest(ViewManagerUpdatesReceiver viewManagerUpdatesReceiver) { mViewManagerUpdatesReceiver = viewManagerUpdatesReceiver; } @Override public String getName() { return "RedpandasLivestreamVideoView"; } @Override public ReactShadowNode createShadowNodeInstance() { fail("This method should not be executed as a part of this test"); return null; } @Override public Class<? extends ReactShadowNode> getShadowNodeClass() { return ReactShadowNode.class; } @Override protected View createViewInstance(ThemedReactContext reactContext) { fail("This method should not be executed as a part of this test"); return null; } @Override public void updateExtraData(View root, Object extraData) { fail("This method should not be executed as a part of this test"); } @ReactProp(name = "boolProp") public void setBoolProp(View v, boolean value) { mViewManagerUpdatesReceiver.onBooleanSetterCalled(value); } @ReactProp(name = "boolPropWithDefault", defaultBoolean = true) public void setBoolPropWithDefault(View v, boolean value) { mViewManagerUpdatesReceiver.onBooleanSetterCalled(value); } @ReactProp(name = "intProp") public void setIntProp(View v, int value) { mViewManagerUpdatesReceiver.onIntSetterCalled(value); } @ReactProp(name = "intPropWithDefault", defaultInt = 7168) public void setIntPropWithDefault(View v, int value) { mViewManagerUpdatesReceiver.onIntSetterCalled(value); } @ReactProp(name = "floatProp") public void setFloatProp(View v, float value) { mViewManagerUpdatesReceiver.onFloatSetterCalled(value); } @ReactProp(name = "floatPropWithDefault", defaultFloat = 14.0f) public void setFloatPropWithDefault(View v, float value) { mViewManagerUpdatesReceiver.onFloatSetterCalled(value); } @ReactProp(name = "doubleProp") public void setDoubleProp(View v, double value) { mViewManagerUpdatesReceiver.onDoubleSetterCalled(value); } @ReactProp(name = "doublePropWithDefault", defaultDouble = -88.0) public void setDoublePropWithDefault(View v, double value) { mViewManagerUpdatesReceiver.onDoubleSetterCalled(value); } @ReactProp(name = "stringProp") public void setStringProp(View v, String value) { mViewManagerUpdatesReceiver.onStringSetterCalled(value); } @ReactProp(name = "boxedBoolProp") public void setBoxedBoolProp(View v, Boolean value) { mViewManagerUpdatesReceiver.onBoxedBooleanSetterCalled(value); } @ReactProp(name = "boxedIntProp") public void setBoxedIntProp(View v, Integer value) { mViewManagerUpdatesReceiver.onBoxedIntSetterCalled(value); } @ReactProp(name = "arrayProp") public void setArrayProp(View v, ReadableArray value) { mViewManagerUpdatesReceiver.onArraySetterCalled(value); } @ReactProp(name = "mapProp") public void setMapProp(View v, ReadableMap value) { mViewManagerUpdatesReceiver.onMapSetterCalled(value); } @ReactPropGroup(names = { "floatGroupPropFirst", "floatGroupPropSecond", }) public void setFloatGroupProp(View v, int index, float value) { mViewManagerUpdatesReceiver.onFloatGroupPropSetterCalled(index, value); } @ReactPropGroup(names = { "floatGroupPropWithDefaultFirst", "floatGroupPropWithDefaultSecond", }, defaultFloat = -100.0f) public void setFloatGroupPropWithDefault(View v, int index, float value) { mViewManagerUpdatesReceiver.onFloatGroupPropSetterCalled(index, value); } @ReactPropGroup(names = { "intGroupPropFirst", "intGroupPropSecond" }) public void setIntGroupProp(View v, int index, int value) { mViewManagerUpdatesReceiver.onIntGroupPropSetterCalled(index, value); } @ReactPropGroup(names = { "intGroupPropWithDefaultFirst", "intGroupPropWithDefaultSecond" }, defaultInt = 555) public void setIntGroupPropWithDefault(View v, int index, int value) { mViewManagerUpdatesReceiver.onIntGroupPropSetterCalled(index, value); } @ReactPropGroup(names = { "boxedIntGroupPropFirst", "boxedIntGroupPropSecond", }) public void setBoxedIntGroupProp(View v, int index, Integer value) { mViewManagerUpdatesReceiver.onBoxedIntGroupPropSetterCalled(index, value); } } public static ReactStylesDiffMap buildStyles(Object... keysAndValues) { return new ReactStylesDiffMap(JavaOnlyMap.of(keysAndValues)); } private ViewManagerUnderTest mViewManager; private ViewManagerUpdatesReceiver mUpdatesReceiverMock; @Before public void setup() { mUpdatesReceiverMock = mock(ViewManagerUpdatesReceiver.class); mViewManager = new ViewManagerUnderTest(mUpdatesReceiverMock); } @Test public void testBooleanSetter() { mViewManager.updateProperties(null, buildStyles("boolProp", true)); verify(mUpdatesReceiverMock).onBooleanSetterCalled(true); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boolProp", false)); verify(mUpdatesReceiverMock).onBooleanSetterCalled(false); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boolProp", null)); verify(mUpdatesReceiverMock).onBooleanSetterCalled(false); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boolPropWithDefault", false)); verify(mUpdatesReceiverMock).onBooleanSetterCalled(false); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boolPropWithDefault", null)); verify(mUpdatesReceiverMock).onBooleanSetterCalled(true); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testIntSetter() { mViewManager.updateProperties(null, buildStyles("intProp", 13)); verify(mUpdatesReceiverMock).onIntSetterCalled(13); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intProp", null)); verify(mUpdatesReceiverMock).onIntSetterCalled(0); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intPropWithDefault", -1)); verify(mUpdatesReceiverMock).onIntSetterCalled(-1); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intPropWithDefault", null)); verify(mUpdatesReceiverMock).onIntSetterCalled(7168); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testDoubleSetter() { mViewManager.updateProperties(null, buildStyles("doubleProp", 13.0)); verify(mUpdatesReceiverMock).onDoubleSetterCalled(13.0); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("doubleProp", null)); verify(mUpdatesReceiverMock).onDoubleSetterCalled(0.0); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("doublePropWithDefault", -1.0)); verify(mUpdatesReceiverMock).onDoubleSetterCalled(-1.0); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("doublePropWithDefault", null)); verify(mUpdatesReceiverMock).onDoubleSetterCalled(-88.0); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testFloatSetter() { mViewManager.updateProperties(null, buildStyles("floatProp", 13.0)); verify(mUpdatesReceiverMock).onFloatSetterCalled(13.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("floatProp", null)); verify(mUpdatesReceiverMock).onFloatSetterCalled(0.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("floatPropWithDefault", -1.0)); verify(mUpdatesReceiverMock).onFloatSetterCalled(-1.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("floatPropWithDefault", null)); verify(mUpdatesReceiverMock).onFloatSetterCalled(14.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testStringSetter() { mViewManager.updateProperties(null, buildStyles("stringProp", "someRandomString")); verify(mUpdatesReceiverMock).onStringSetterCalled("someRandomString"); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("stringProp", null)); verify(mUpdatesReceiverMock).onStringSetterCalled(null); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testBoxedBooleanSetter() { mViewManager.updateProperties(null, buildStyles("boxedBoolProp", true)); verify(mUpdatesReceiverMock).onBoxedBooleanSetterCalled(Boolean.TRUE); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boxedBoolProp", false)); verify(mUpdatesReceiverMock).onBoxedBooleanSetterCalled(Boolean.FALSE); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boxedBoolProp", null)); verify(mUpdatesReceiverMock).onBoxedBooleanSetterCalled(null); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testBoxedIntSetter() { mViewManager.updateProperties(null, buildStyles("boxedIntProp", 55)); verify(mUpdatesReceiverMock).onBoxedIntSetterCalled(Integer.valueOf(55)); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boxedIntProp", null)); verify(mUpdatesReceiverMock).onBoxedIntSetterCalled(null); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testArraySetter() { ReadableArray array = new JavaOnlyArray(); mViewManager.updateProperties(null, buildStyles("arrayProp", array)); verify(mUpdatesReceiverMock).onArraySetterCalled(array); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("arrayProp", null)); verify(mUpdatesReceiverMock).onArraySetterCalled(null); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testMapSetter() { ReadableMap map = new JavaOnlyMap(); mViewManager.updateProperties(null, buildStyles("mapProp", map)); verify(mUpdatesReceiverMock).onMapSetterCalled(map); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("mapProp", null)); verify(mUpdatesReceiverMock).onMapSetterCalled(null); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testFloatGroupSetter() { mViewManager.updateProperties(null, buildStyles("floatGroupPropFirst", 11.0)); verify(mUpdatesReceiverMock).onFloatGroupPropSetterCalled(0, 11.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("floatGroupPropSecond", -111.0)); verify(mUpdatesReceiverMock).onFloatGroupPropSetterCalled(1, -111.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("floatGroupPropSecond", null)); verify(mUpdatesReceiverMock).onFloatGroupPropSetterCalled(1, 0.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("floatGroupPropWithDefaultFirst", null)); verify(mUpdatesReceiverMock).onFloatGroupPropSetterCalled(0, -100.0f); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testIntGroupSetter() { mViewManager.updateProperties(null, buildStyles("intGroupPropFirst", -7)); verify(mUpdatesReceiverMock).onIntGroupPropSetterCalled(0, -7); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intGroupPropSecond", -77)); verify(mUpdatesReceiverMock).onIntGroupPropSetterCalled(1, -77); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intGroupPropSecond", null)); verify(mUpdatesReceiverMock).onIntGroupPropSetterCalled(1, 0); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intGroupPropWithDefaultFirst", 5)); verify(mUpdatesReceiverMock).onIntGroupPropSetterCalled(0, 5); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intGroupPropWithDefaultFirst", null)); verify(mUpdatesReceiverMock).onIntGroupPropSetterCalled(0, 555); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("intGroupPropWithDefaultSecond", null)); verify(mUpdatesReceiverMock).onIntGroupPropSetterCalled(1, 555); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test public void testStringGroupSetter() { mViewManager.updateProperties(null, buildStyles("boxedIntGroupPropFirst", -7)); verify(mUpdatesReceiverMock).onBoxedIntGroupPropSetterCalled(0, -7); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boxedIntGroupPropSecond", 12345)); verify(mUpdatesReceiverMock).onBoxedIntGroupPropSetterCalled(1, 12345); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); mViewManager.updateProperties(null, buildStyles("boxedIntGroupPropSecond", null)); verify(mUpdatesReceiverMock).onBoxedIntGroupPropSetterCalled(1, null); verifyNoMoreInteractions(mUpdatesReceiverMock); reset(mUpdatesReceiverMock); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateBoolPropWithMap() { mViewManager.updateProperties(null, buildStyles("boolProp", new JavaOnlyMap())); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateStringPropWithDouble() { mViewManager.updateProperties(null, buildStyles("stringProp", 14.5)); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateDoublePropWithString() { mViewManager.updateProperties(null, buildStyles("doubleProp", "hello")); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateIntPropWithDouble() { mViewManager.updateProperties(null, buildStyles("intProp", -7.4)); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateArrayPropWithBool() { mViewManager.updateProperties(null, buildStyles("arrayProp", false)); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateMapPropWithArray() { mViewManager.updateProperties(null, buildStyles("mapProp", new JavaOnlyArray())); } @Test(expected = JSApplicationIllegalArgumentException.class) public void testFailToUpdateBoxedIntPropWithBoxedDouble() { mViewManager.updateProperties(null, buildStyles("boxedIntProp", Double.valueOf(1))); } }