diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 0cbe6db7d..e3266ea0a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -176,6 +176,9 @@ dependencies { androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + testImplementation("org.mockito:mockito-core:5.4.0") + testImplementation("org.mockito.kotlin:mockito-kotlin:5.0.0") + implementation("androidx.browser:browser:1.5.0") implementation("androidx.profileinstaller:profileinstaller:1.3.1") diff --git a/app/src/main/java/com/jerboa/Utils.kt b/app/src/main/java/com/jerboa/Utils.kt index 03a70e8cf..952c9fb46 100644 --- a/app/src/main/java/com/jerboa/Utils.kt +++ b/app/src/main/java/com/jerboa/Utils.kt @@ -691,42 +691,44 @@ data class InputField( ) fun validatePostName( + ctx: Context, name: String, ): InputField { return if (name.isEmpty()) { InputField( - label = "Title required", + label = ctx.getString(R.string.title_required), hasError = true, ) } else if (name.length < 3) { InputField( - label = "Title must be > 3 chars", + label = ctx.getString(R.string.title_min_3_chars), hasError = true, ) } else if (name.length >= MAX_POST_TITLE_LENGTH) { InputField( - label = "Title cannot be > 200 chars", + label = ctx.getString(R.string.title_less_than_200_chars), hasError = true, ) } else { InputField( - label = "Title", + label = ctx.getString(R.string.title), hasError = false, ) } } fun validateUrl( + ctx: Context, url: String, ): InputField { return if (url.isNotEmpty() && !PatternsCompat.WEB_URL.matcher(url).matches()) { InputField( - label = "Invalid Url", + label = ctx.getString(R.string.url_invalid), hasError = true, ) } else { InputField( - label = "Url", + label = ctx.getString(R.string.url), hasError = false, ) } diff --git a/app/src/main/java/com/jerboa/ui/components/post/create/CreatePostActivity.kt b/app/src/main/java/com/jerboa/ui/components/post/create/CreatePostActivity.kt index 5b44468e5..ab5f5d839 100644 --- a/app/src/main/java/com/jerboa/ui/components/post/create/CreatePostActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/post/create/CreatePostActivity.kt @@ -84,8 +84,8 @@ fun CreatePostActivity( } var isUploadingImage by rememberSaveable { mutableStateOf(false) } - val nameField = validatePostName(name) - val urlField = validateUrl(url) + val nameField = validatePostName(ctx, name) + val urlField = validateUrl(ctx, url) val formValid = !nameField.hasError && !urlField.hasError && (selectedCommunity !== null) LaunchedEffect(initialUrl) { diff --git a/app/src/main/java/com/jerboa/ui/components/post/edit/PostEditActivity.kt b/app/src/main/java/com/jerboa/ui/components/post/edit/PostEditActivity.kt index 7e82f5cb3..0f9506546 100644 --- a/app/src/main/java/com/jerboa/ui/components/post/edit/PostEditActivity.kt +++ b/app/src/main/java/com/jerboa/ui/components/post/edit/PostEditActivity.kt @@ -68,8 +68,8 @@ fun PostEditActivity( } var isUploadingImage by rememberSaveable { mutableStateOf(false) } - val nameField = validatePostName(name) - val urlField = validateUrl(url) + val nameField = validatePostName(ctx, name) + val urlField = validateUrl(ctx, url) val formValid = !nameField.hasError && !urlField.hasError Scaffold( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index abab2ff5b..b7919a857 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -303,7 +303,13 @@ TopWeek TopYear %1$s %2$s ago + Title + Title cannot be more than 200 characters + Title must be at least three characters + Title required Back + URL + Invalid URL Login first Upvote Downvote diff --git a/app/src/test/java/com/jerboa/UtilsKtTest.kt b/app/src/test/java/com/jerboa/UtilsKtTest.kt index e4d7304cc..34a4ffb33 100644 --- a/app/src/test/java/com/jerboa/UtilsKtTest.kt +++ b/app/src/test/java/com/jerboa/UtilsKtTest.kt @@ -1,12 +1,20 @@ package com.jerboa +import android.content.Context import androidx.compose.ui.unit.dp import com.jerboa.api.API import com.jerboa.ui.theme.SMALL_PADDING import junitparams.JUnitParamsRunner +import junitparams.Parameters import org.junit.Assert.* +import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock import org.ocpsoft.prettytime.PrettyTime import java.time.Duration import java.time.Instant @@ -15,6 +23,11 @@ import java.util.Locale @RunWith(JUnitParamsRunner::class) class UtilsKtTest { + + @JvmField + @Rule + val rule: MockitoRule = MockitoJUnit.rule() + @Test fun testCalculateCommentOffset() { assertEquals(0.dp, calculateCommentOffset(0, 0)) @@ -83,20 +96,31 @@ class UtilsKtTest { @Test fun testValidatePostName() { - assertTrue(validatePostName("").hasError) - assertTrue(validatePostName("a").hasError) - assertTrue(validatePostName("a".repeat(MAX_POST_TITLE_LENGTH)).hasError) + val ctx = mock { + on { getString(anyInt()) } doReturn "" + } + + assertTrue(validatePostName(ctx, "").hasError) + assertTrue(validatePostName(ctx, "a").hasError) + assertTrue(validatePostName(ctx, "a".repeat(MAX_POST_TITLE_LENGTH)).hasError) - assertFalse(validatePostName("totally fine").hasError) - assertFalse(validatePostName("a".repeat(MAX_POST_TITLE_LENGTH - 1)).hasError) + assertFalse(validatePostName(ctx, "totally fine").hasError) + assertFalse(validatePostName(ctx, "a".repeat(MAX_POST_TITLE_LENGTH - 1)).hasError) } @Test fun testValidateUrl() { - assertTrue(validateUrl("nonsense").hasError) + val ctx = mock { + on { getString(R.string.url) } doReturn "url" + on { getString(R.string.url_invalid) } doReturn "url_invalid" + } + + assertTrue(validateUrl(ctx, "nonsense").hasError) + assertSame("url_invalid", validateUrl(ctx, "nonsense").label) - assertFalse(validateUrl("").hasError) - assertFalse(validateUrl("https://example.com").hasError) + assertFalse(validateUrl(ctx, "").hasError) + assertFalse(validateUrl(ctx, "https://example.com").hasError) + assertSame("url", validateUrl(ctx, "https://example.com").label) } @Test @@ -154,15 +178,20 @@ class UtilsKtTest { } @Test - fun testSiFormat() { - assertEquals("0", siFormat(0)) - assertEquals("1K", siFormat(1000)) - assertEquals("1.1K", siFormat(1100)) - assertEquals("1M", siFormat(1000000)) - assertEquals("1.2M", siFormat(1234500)) - assertEquals("12M", siFormat(12345000)) + @Parameters(method = "siFormatCases") + fun testSiFormat(expected: String, input: Int) { + assertEquals(expected, siFormat(input)) } + fun siFormatCases() = listOf( + listOf("0", 0), + listOf("1K", 1000), + listOf("1.1K", 1100), + listOf("1M", 1000000), + listOf("1.2M", 1234500), + listOf("12M", 12345000), + ) + @Test fun testParseUrl() { val cases = mapOf(