/**
* 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 android.content.res.ColorStateList;
import android.graphics.Color;
import android.text.InputType;
import android.text.InputFilter;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.widget.EditText;
import com.facebook.react.bridge.CatalystInstance;
import com.facebook.react.bridge.ReactTestHelper;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.JSApplicationCausedNativeException;
import com.facebook.react.bridge.JavaOnlyMap;
import com.facebook.react.uimanager.ReactStylesDiffMap;
import com.facebook.react.uimanager.DisplayMetricsHolder;
import com.facebook.react.views.text.DefaultStyleValuesUtil;
import com.facebook.react.uimanager.ThemedReactContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.powermock.modules.junit4.rule.PowerMockRule;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import static org.fest.assertions.api.Assertions.assertThat;
/**
* Verify {@link EditText} view property being applied properly by {@link ReactTextInputManager}
*/
@RunWith(RobolectricTestRunner.class)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
public class ReactTextInputPropertyTest {
@Rule
public PowerMockRule rule = new PowerMockRule();
private ReactApplicationContext mContext;
private CatalystInstance mCatalystInstanceMock;
private ThemedReactContext mThemedContext;
private ReactTextInputManager mManager;
@Before
public void setup() {
mContext = new ReactApplicationContext(RuntimeEnvironment.application);
mCatalystInstanceMock = ReactTestHelper.createMockCatalystInstance();
mContext.initializeWithInstance(mCatalystInstanceMock);
mThemedContext = new ThemedReactContext(mContext, mContext);
mManager = new ReactTextInputManager();
DisplayMetricsHolder.setWindowDisplayMetrics(new DisplayMetrics());
}
public ReactStylesDiffMap buildStyles(Object... keysAndValues) {
return new ReactStylesDiffMap(JavaOnlyMap.of(keysAndValues));
}
@Test
public void testAutoCorrect() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isZero();
mManager.updateProperties(view, buildStyles("autoCorrect", true));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isZero();
mManager.updateProperties(view, buildStyles("autoCorrect", false));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isNotZero();
mManager.updateProperties(view, buildStyles("autoCorrect", null));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isZero();
}
@Test
public void testAutoCapitalize() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_WORDS).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS).isZero();
mManager.updateProperties(
view,
buildStyles("autoCapitalize", InputType.TYPE_TEXT_FLAG_CAP_SENTENCES));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_WORDS).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS).isZero();
mManager.updateProperties(
view,
buildStyles("autoCapitalize", InputType.TYPE_TEXT_FLAG_CAP_WORDS));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_WORDS).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS).isZero();
mManager.updateProperties(
view,
buildStyles("autoCapitalize", InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_WORDS).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS).isNotZero();
mManager.updateProperties(
view,
buildStyles("autoCapitalize", InputType.TYPE_CLASS_TEXT));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_SENTENCES).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_WORDS).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS).isZero();
}
@Test
public void testPlaceholder() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getHint()).isNull();
mManager.updateProperties(view, buildStyles("placeholder", "sometext"));
assertThat(view.getHint()).isEqualTo("sometext");
mManager.updateProperties(view, buildStyles("placeholder", null));
assertThat(view.getHint()).isNull();
}
@Test
public void testEditable() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.isEnabled()).isTrue();
mManager.updateProperties(view, buildStyles("editable", false));
assertThat(view.isEnabled()).isFalse();
mManager.updateProperties(view, buildStyles("editable", null));
assertThat(view.isEnabled()).isTrue();
mManager.updateProperties(view, buildStyles("editable", false));
assertThat(view.isEnabled()).isFalse();
mManager.updateProperties(view, buildStyles("editable", true));
assertThat(view.isEnabled()).isTrue();
}
@Test
public void testPlaceholderTextColor() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
final ColorStateList defaultPlaceholderColorStateList =
DefaultStyleValuesUtil.getDefaultTextColorHint(
view.getContext());
ColorStateList colors = view.getHintTextColors();
assertThat(colors).isEqualTo(defaultPlaceholderColorStateList);
mManager.updateProperties(view, buildStyles("placeholderTextColor", null));
colors = view.getHintTextColors();
assertThat(colors).isEqualTo(defaultPlaceholderColorStateList);
mManager.updateProperties(view, buildStyles("placeholderTextColor", Color.RED));
colors = view.getHintTextColors();
assertThat(colors.getDefaultColor()).isEqualTo(Color.RED);
mManager.updateProperties(view, buildStyles("placeholderTextColor", null));
colors = view.getHintTextColors();
assertThat(colors).isEqualTo(defaultPlaceholderColorStateList);
}
@Test
public void testMultiline() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isZero();
mManager.updateProperties(view, buildStyles("multiline", false));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isZero();
mManager.updateProperties(view, buildStyles("multiline", true));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isNotZero();
mManager.updateProperties(view, buildStyles("multiline", null));
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isZero();
}
@Test
public void testNumLines() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getMinLines()).isEqualTo(1);
mManager.updateProperties(view, buildStyles("numberOfLines", 5));
assertThat(view.getMinLines()).isEqualTo(5);
mManager.updateProperties(view, buildStyles("numberOfLines", 4));
assertThat(view.getMinLines()).isEqualTo(4);
}
@Test
public void testKeyboardType() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isZero();
mManager.updateProperties(view, buildStyles("keyboardType", "text"));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isZero();
mManager.updateProperties(view, buildStyles("keyboardType", "numeric"));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isNotZero();
mManager.updateProperties(view, buildStyles("keyboardType", "email-address"));
assertThat(view.getInputType() & InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS).isNotZero();
mManager.updateProperties(view, buildStyles("keyboardType", null));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isZero();
}
@Test
public void testPasswordInput() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD).isZero();
mManager.updateProperties(view, buildStyles("secureTextEntry", false));
assertThat(view.getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD).isZero();
mManager.updateProperties(view, buildStyles("secureTextEntry", true));
assertThat(view.getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD).isNotZero();
mManager.updateProperties(view, buildStyles("secureTextEntry", null));
assertThat(view.getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD).isZero();
}
@Test
public void testIncrementalInputTypeUpdates() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
mManager.updateProperties(view, buildStyles());
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isZero();
mManager.updateProperties(view, buildStyles("multiline", true));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isZero();
mManager.updateProperties(view, buildStyles("autoCorrect", false));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isNotZero();
mManager.updateProperties(view, buildStyles("keyboardType", "NUMERIC"));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isNotZero();
mManager.updateProperties(view, buildStyles("multiline", null));
assertThat(view.getInputType() & InputType.TYPE_CLASS_NUMBER).isNotZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_MULTI_LINE).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_AUTO_CORRECT).isZero();
assertThat(view.getInputType() & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS).isNotZero();
}
@Test
public void testTextAlign() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
int defaultGravity = view.getGravity();
int defaultHorizontalGravity = defaultGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
int defaultVerticalGravity = defaultGravity & Gravity.VERTICAL_GRAVITY_MASK;
// Theme
assertThat(view.getGravity()).isNotEqualTo(Gravity.NO_GRAVITY);
// TextAlign
mManager.updateProperties(view, buildStyles("textAlign", "left"));
assertThat(view.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK).isEqualTo(Gravity.LEFT);
mManager.updateProperties(view, buildStyles("textAlign", "right"));
assertThat(view.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK).isEqualTo(Gravity.RIGHT);
mManager.updateProperties(view, buildStyles("textAlign", "center"));
assertThat(view.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK).isEqualTo(Gravity.CENTER_HORIZONTAL);
mManager.updateProperties(view, buildStyles("textAlign", null));
assertThat(view.getGravity() & Gravity.HORIZONTAL_GRAVITY_MASK).isEqualTo(defaultHorizontalGravity);
// TextAlignVertical
mManager.updateProperties(view, buildStyles("textAlignVertical", "top"));
assertThat(view.getGravity() & Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.TOP);
mManager.updateProperties(view, buildStyles("textAlignVertical", "bottom"));
assertThat(view.getGravity() & Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.BOTTOM);
mManager.updateProperties(view, buildStyles("textAlignVertical", "center"));
assertThat(view.getGravity() & Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.CENTER_VERTICAL);
mManager.updateProperties(view, buildStyles("textAlignVertical", null));
assertThat(view.getGravity() & Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(defaultVerticalGravity);
// TextAlign + TextAlignVertical
mManager.updateProperties(
view,
buildStyles("textAlign", "center", "textAlignVertical", "center"));
assertThat(view.getGravity()).isEqualTo(Gravity.CENTER);
mManager.updateProperties(
view,
buildStyles("textAlign", "right", "textAlignVertical", "bottom"));
assertThat(view.getGravity()).isEqualTo(Gravity.RIGHT | Gravity.BOTTOM);
mManager.updateProperties(
view,
buildStyles("textAlign", null, "textAlignVertical", null));
assertThat(view.getGravity()).isEqualTo(defaultGravity);
}
@Test
public void testMaxLength() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
InputFilter[] filters = new InputFilter[] { new InputFilter.AllCaps() };
view.setFilters(filters);
mManager.setMaxLength(view, null);
assertThat(view.getFilters()).isEqualTo(filters);
}
@Test
public void testSelection() {
ReactEditText view = mManager.createViewInstance(mThemedContext);
view.setText("Need some text to select something...");
mManager.updateProperties(view, buildStyles());
assertThat(view.getSelectionStart()).isEqualTo(0);
assertThat(view.getSelectionEnd()).isEqualTo(0);
JavaOnlyMap selection = JavaOnlyMap.of("start", 5, "end", 10);
mManager.updateProperties(view, buildStyles("selection", selection));
assertThat(view.getSelectionStart()).isEqualTo(5);
assertThat(view.getSelectionEnd()).isEqualTo(10);
mManager.updateProperties(view, buildStyles("selection", null));
assertThat(view.getSelectionStart()).isEqualTo(5);
assertThat(view.getSelectionEnd()).isEqualTo(10);
}
}