// Copyright 2016 The Nomulus Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package google.registry.ui.forms; import static com.google.common.collect.Range.atLeast; import static com.google.common.collect.Range.atMost; import static com.google.common.collect.Range.closed; import static com.google.common.truth.Truth.assertThat; import static org.hamcrest.Matchers.equalTo; import com.google.common.base.CharMatcher; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Range; import com.google.common.testing.NullPointerTester; import com.google.re2j.Pattern; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Unit tests for {@link FormField}. */ @RunWith(JUnit4.class) public class FormFieldTest { private enum ICanHazEnum { LOL, CAT } @Rule public final ExpectedException thrown = ExpectedException.none(); @Test public void testConvert_nullString_notPresent() { assertThat(FormField.named("lol").build().convert(null)).isAbsent(); } @Test public void testConvert_emptyString_returnsEmpty() { assertThat(FormField.named("lol").build().convert("").get()).isEmpty(); } @Test public void testWithDefault_hasValue_returnsValue() { assertThat(FormField.named("lol").withDefault("default").build().convert("return me!")) .hasValue("return me!"); } @Test public void testWithDefault_nullValue_returnsDefault() { assertThat(FormField.named("lol").withDefault("default").build().convert(null)) .hasValue("default"); } @Test public void testEmptyToNull_emptyString_notPresent() { assertThat(FormField.named("lol").emptyToNull().build().convert("")).isAbsent(); } @Test public void testEmptyToNullRequired_emptyString_throwsFfe() { thrown.expect(equalTo(new FormFieldException("This field is required.").propagate("lol"))); FormField.named("lol").emptyToNull().required().build().convert(""); } @Test public void testEmptyToNull_typeMismatch() { thrown.expect(IllegalStateException.class); FormField.named("lol", Object.class).emptyToNull(); } @Test public void testNamedLong() { assertThat(FormField.named("lol", Long.class).build().convert(666L)).hasValue(666L); } @Test public void testUppercased() { FormField field = FormField.named("lol").uppercased().build(); assertThat(field.convert(null)).isAbsent(); assertThat(field.convert("foo")).hasValue("FOO"); assertThat(field.convert("BAR")).hasValue("BAR"); } @Test public void testLowercased() { FormField field = FormField.named("lol").lowercased().build(); assertThat(field.convert(null)).isAbsent(); assertThat(field.convert("foo")).hasValue("foo"); assertThat(field.convert("BAR")).hasValue("bar"); } @Test public void testIn_passesThroughNull() { FormField field = FormField.named("lol") .in(ImmutableSet.of("foo", "bar")) .build(); assertThat(field.convert(null)).isAbsent(); } @Test public void testIn_valueIsContainedInSet() { FormField field = FormField.named("lol") .in(ImmutableSet.of("foo", "bar")) .build(); assertThat(field.convert("foo")).hasValue("foo"); assertThat(field.convert("bar")).hasValue("bar"); } @Test public void testIn_valueMissingFromSet() { FormField field = FormField.named("lol") .in(ImmutableSet.of("foo", "bar")) .build(); thrown.expect(equalTo(new FormFieldException("Unrecognized value.").propagate("lol"))); field.convert("omfg"); } @Test public void testRange_hasLowerBound_nullValue_passesThrough() { assertThat(FormField.named("lol").range(atLeast(5)).build().convert(null)).isAbsent(); } @Test public void testRange_minimum_stringLengthEqualToMinimum_doesNothing() { assertThat(FormField.named("lol").range(atLeast(5)).build().convert("hello")).hasValue("hello"); } @Test public void testRange_minimum_stringLengthShorterThanMinimum_throwsFfe() { thrown.expect(FormFieldException.class); thrown.expectMessage("Number of characters (3) not in range [4"); FormField.named("lol").range(atLeast(4)).build().convert("lol"); } @Test public void testRange_noLowerBound_nullValue_passThrough() { assertThat(FormField.named("lol").range(atMost(5)).build().convert(null)).isAbsent(); } @Test public void testRange_maximum_stringLengthEqualToMaximum_doesNothing() { assertThat(FormField.named("lol").range(atMost(5)).build().convert("hello")).hasValue("hello"); } @Test public void testRange_maximum_stringLengthShorterThanMaximum_throwsFfe() { thrown.expect(FormFieldException.class); thrown.expectMessage("Number of characters (6) not in range"); FormField.named("lol").range(atMost(5)).build().convert("omgomg"); } @Test public void testRange_numericTypes() { FormField.named("lol", Byte.class).range(closed(5, 10)).build().convert((byte) 7); FormField.named("lol", Short.class).range(closed(5, 10)).build().convert((short) 7); FormField.named("lol", Integer.class).range(closed(5, 10)).build().convert(7); FormField.named("lol", Long.class).range(closed(5, 10)).build().convert(7L); FormField.named("lol", Float.class).range(closed(5, 10)).build().convert(7F); FormField.named("lol", Double.class).range(closed(5, 10)).build().convert(7D); } @Test public void testRange_typeMismatch() { thrown.expect(IllegalStateException.class); FormField.named("lol", Object.class).range(atMost(5)); } @Test public void testMatches_matches_doesNothing() { assertThat(FormField.named("lol").matches(Pattern.compile("[a-z]+")).build().convert("abc")) .hasValue("abc"); } @Test public void testMatches_mismatch_throwsFfeAndShowsDefaultErrorMessageWithPattern() { thrown.expect(equalTo(new FormFieldException("Must match pattern: [a-z]+").propagate("lol"))); FormField.named("lol").matches(Pattern.compile("[a-z]+")).build().convert("123abc456"); } @Test public void testMatches_typeMismatch() { thrown.expect(IllegalStateException.class); FormField.named("lol", Object.class).matches(Pattern.compile(".")); } @Test public void testRetains() { assertThat( FormField.named("lol") .retains(CharMatcher.anyOf("0123456789")) .build() .convert(" 123 1593-43 453 45 4 4 \t")) .hasValue("1231593434534544"); } @Test public void testCast() { assertThat( FormField.named("lol") .transform( Integer.class, new Function() { @Override public Integer apply(String input) { return Integer.parseInt(input); } }) .build() .convert("123")) .hasValue(123); } @Test public void testCast_twice() { assertThat( FormField.named("lol") .transform( Object.class, new Function() { @Override public Integer apply(String input) { return Integer.parseInt(input); } }) .transform(String.class, Functions.toStringFunction()) .build() .convert("123")) .hasValue("123"); } @Test public void testAsList_null_notPresent() { assertThat(FormField.named("lol").asList().build().convert(null)).isAbsent(); } @Test public void testAsList_empty_returnsEmpty() { assertThat(FormField.named("lol").asList().build().convert(ImmutableList.of()).get()) .isEmpty(); } @Test public void testAsListEmptyToNullRequired_empty_throwsFfe() { thrown.expect(equalTo(new FormFieldException("This field is required.").propagate("lol"))); FormField.named("lol") .asList() .emptyToNull() .required() .build() .convert(ImmutableList.of()); } @Test public void testListEmptyToNull_empty_notPresent() { assertThat(FormField.named("lol") .asList() .emptyToNull() .build() .convert(ImmutableList.of())) .isAbsent(); } @Test public void testAsEnum() { FormField omgField = FormField.named("omg") .asEnum(ICanHazEnum.class) .build(); assertThat(omgField.convert("LOL").get()).isEqualTo(ICanHazEnum.LOL); assertThat(omgField.convert("CAT").get()).isEqualTo(ICanHazEnum.CAT); } @Test public void testAsEnum_lowercase_works() { FormField omgField = FormField.named("omg") .asEnum(ICanHazEnum.class) .build(); assertThat(omgField.convert("lol").get()).isEqualTo(ICanHazEnum.LOL); assertThat(omgField.convert("cat").get()).isEqualTo(ICanHazEnum.CAT); } @Test public void testAsEnum_badInput_throwsFfe() { FormField omgField = FormField.named("omg") .asEnum(ICanHazEnum.class) .build(); thrown.expect(equalTo(new FormFieldException("Enum ICanHazEnum does not contain 'helo'") .propagate("omg"))); omgField.convert("helo"); } @Test public void testSplitList() { FormField> field = FormField.named("lol") .asList(Splitter.on(',').omitEmptyStrings()) .build(); assertThat(field.convert("oh,my,goth").get()) .containsExactly("oh", "my", "goth") .inOrder(); assertThat(field.convert("").get()).isEmpty(); assertThat(field.convert(null)).isAbsent(); } @Test public void testSplitSet() { FormField> field = FormField.named("lol") .uppercased() .asSet(Splitter.on(',').omitEmptyStrings()) .build(); assertThat(field.convert("oh,my,goth").get()) .containsExactly("OH", "MY", "GOTH") .inOrder(); assertThat(field.convert("").get()).isEmpty(); assertThat(field.convert(null)).isAbsent(); } @Test public void testAsList() { assertThat( FormField.named("lol").asList().build().convert(ImmutableList.of("lol", "cat", "")).get()) .containsExactly("lol", "cat", "").inOrder(); } @Test public void testAsList_trimmedEmptyToNullOnItems() { assertThat(FormField.named("lol") .trimmed() .emptyToNull() .matches(Pattern.compile("[a-z]+")) .asList() .range(closed(1, 2)) .build() .convert(ImmutableList.of("lol\n", "\tcat ")) .get()) .containsExactly("lol", "cat").inOrder(); } @Test public void testAsList_nullElements_getIgnored() { assertThat(FormField.named("lol") .emptyToNull() .asList() .build() .convert(ImmutableList.of("omg", "")) .get()) .containsExactly("omg"); } @Test public void testAsListRequiredElements_nullElement_throwsFfeWithIndex() { thrown.expect(equalTo(new FormFieldException("This field is required.") .propagate(1) .propagate("lol"))); FormField.named("lol") .emptyToNull() .required() .asList() .build() .convert(ImmutableList.of("omg", "")); } @Test public void testMapAsListRequiredElements_nullElement_throwsFfeWithIndexAndKey() { thrown.expect(equalTo(new FormFieldException("This field is required.") .propagate("cat") .propagate(0) .propagate("lol"))); FormField.mapNamed("lol") .transform(String.class, new Function, String>() { @Override public String apply(Map input) { return FormField.named("cat") .emptyToNull() .required() .build() .extractUntyped(input) .get(); }}) .asList() .build() .convert(ImmutableList.>of( ImmutableMap.of("cat", ""))); } @Test public void testAsListTrimmed_typeMismatch() { FormField.named("lol").trimmed().asList(); thrown.expect(IllegalStateException.class); FormField.named("lol").asList().trimmed(); } @Test public void testAsMatrix() { assertThat( FormField.named("lol", Integer.class) .transform(new Function() { @Override public Integer apply(Integer input) { return input * 2; }}) .asList() .asList() .build() .convert(Lists.cartesianProduct(ImmutableList.of( ImmutableList.of(1, 2), ImmutableList.of(3, 4)))) .get()) .containsExactly( ImmutableList.of(2, 6), ImmutableList.of(2, 8), ImmutableList.of(4, 6), ImmutableList.of(4, 8)).inOrder(); } @Test public void testAsSet() { assertThat(FormField.named("lol") .asSet() .build() .convert(ImmutableList.of("lol", "cat", "cat")) .get()) .containsExactly("lol", "cat"); } @Test public void testTrimmed() { assertThat(FormField.named("lol").trimmed().build().convert(" \thello \t\n")).hasValue("hello"); } @Test public void testTrimmed_typeMismatch() { thrown.expect(IllegalStateException.class); FormField.named("lol", Object.class).trimmed(); } @Test public void testAsBuilder() { FormField field = FormField.named("omg").uppercased().build(); assertThat(field.name()).isEqualTo("omg"); assertThat(field.convert("hello")).hasValue("HELLO"); field = field.asBuilder().build(); assertThat(field.name()).isEqualTo("omg"); assertThat(field.convert("hello")).hasValue("HELLO"); } @Test public void testAsBuilderNamed() { FormField field = FormField.named("omg").uppercased().build(); assertThat(field.name()).isEqualTo("omg"); assertThat(field.convert("hello")).hasValue("HELLO"); field = field.asBuilderNamed("bog").build(); assertThat(field.name()).isEqualTo("bog"); assertThat(field.convert("hello")).hasValue("HELLO"); } @Test public void testNullness() { NullPointerTester tester = new NullPointerTester() .setDefault(Class.class, Object.class) .setDefault(Function.class, Functions.identity()) .setDefault(Pattern.class, Pattern.compile(".")) .setDefault(Range.class, Range.all()) .setDefault(Map.class, ImmutableMap.of()) .setDefault(Set.class, ImmutableSet.of()) .setDefault(String.class, "hello.com"); tester.testAllPublicStaticMethods(FormField.class); tester.testAllPublicInstanceMethods(FormField.named("lol")); tester.testAllPublicInstanceMethods(FormField.named("lol").build()); } }