diff --git a/feature/calculator/build.gradle.kts b/feature/calculator/build.gradle.kts index bf1e84b9..32c84c63 100644 --- a/feature/calculator/build.gradle.kts +++ b/feature/calculator/build.gradle.kts @@ -25,10 +25,14 @@ plugins { android { namespace = "com.sadellie.unitto.feature.calculator" + testOptions.unitTests.isIncludeAndroidResources = true } dependencies { testImplementation(libs.junit) + testImplementation(libs.org.robolectric) + androidTestImplementation(libs.androidx.compose.ui.test.junit4) + debugImplementation(libs.androidx.compose.ui.test.manifest) implementation(project(":data:common")) implementation(project(":data:userprefs")) diff --git a/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt b/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt new file mode 100644 index 00000000..9f1d1be0 --- /dev/null +++ b/feature/calculator/src/androidTest/java/com/sadellie/unitto/feature/calculator/CalculatorScreenTest.kt @@ -0,0 +1,101 @@ +/* + * Unitto is a unit converter for Android + * Copyright (c) 2023 Elshan Agaev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sadellie.unitto.feature.calculator + +import androidx.activity.ComponentActivity +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.swipeDown +import com.sadellie.unitto.core.base.R +import org.junit.Rule +import org.junit.Test + +class CalculatorScreenTest { + @get:Rule + val composeTestRule = createAndroidComposeRule() + + @Test + fun loading_showLoadingKeyboard(): Unit = with(composeTestRule) { + setContent { + CalculatorScreen( + uiState = CalculatorUIState.Loading, + navigateToMenu = {}, + navigateToSettings = {}, + addTokens = {}, + clearInput = {}, + deleteTokens = {}, + onCursorChange = {}, + toggleCalculatorMode = {}, + evaluate = {}, + clearHistory = {} + ) + } + + onNodeWithTag("loading").assertExists() + } + + @Test + fun ready_showRealKeyboard(): Unit = with(composeTestRule) { + setContent { + CalculatorScreen( + uiState = CalculatorUIState.Ready(), + navigateToMenu = {}, + navigateToSettings = {}, + addTokens = {}, + clearInput = {}, + deleteTokens = {}, + onCursorChange = {}, + toggleCalculatorMode = {}, + evaluate = {}, + clearHistory = {} + ) + } + + onNodeWithTag("loading").assertDoesNotExist() + onNodeWithTag("ready").assertExists() + } + + @Test + fun ready_swipeForHistory(): Unit = with(composeTestRule) { + setContent { + CalculatorScreen( + uiState = CalculatorUIState.Ready(), + navigateToMenu = {}, + navigateToSettings = {}, + addTokens = {}, + clearInput = {}, + deleteTokens = {}, + onCursorChange = {}, + toggleCalculatorMode = {}, + evaluate = {}, + clearHistory = {} + ) + } + + onNodeWithTag("inputBox") + .performTouchInput { swipeDown() } + onNodeWithTag("historyButton") + .performClick() + onNodeWithText(composeTestRule.activity.getString(R.string.calculator_clear_history_support)) + .assertExists() + } +} diff --git a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt index 5f6bb70b..d50325d7 100644 --- a/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt +++ b/feature/calculator/src/main/java/com/sadellie/unitto/feature/calculator/CalculatorScreen.kt @@ -58,6 +58,8 @@ import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.testTag import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview @@ -84,30 +86,57 @@ internal fun CalculatorRoute( navigateToSettings: () -> Unit, viewModel: CalculatorViewModel = hiltViewModel() ) { - val uiState = viewModel.uiState.collectAsStateWithLifecycle().value + val uiState = viewModel.uiState.collectAsStateWithLifecycle() + CalculatorScreen( + uiState = uiState.value, + navigateToMenu = navigateToMenu, + navigateToSettings = navigateToSettings, + addTokens = viewModel::addTokens, + clearInput = viewModel::clearInput, + deleteTokens = viewModel::deleteTokens, + onCursorChange = viewModel::onCursorChange, + toggleCalculatorMode = viewModel::toggleCalculatorMode, + evaluate = viewModel::evaluate, + clearHistory = viewModel::clearHistory + ) +} + +@Composable +internal fun CalculatorScreen( + uiState: CalculatorUIState, + navigateToMenu: () -> Unit, + navigateToSettings: () -> Unit, + addTokens: (String) -> Unit, + clearInput: () -> Unit, + deleteTokens: () -> Unit, + onCursorChange: (TextRange) -> Unit, + toggleCalculatorMode: () -> Unit, + evaluate: () -> Unit, + clearHistory: () -> Unit +) { when (uiState) { is CalculatorUIState.Loading -> Loading( navigateToMenu = navigateToMenu, navigateToSettings = navigateToSettings, ) - is CalculatorUIState.Ready -> CalculatorScreen( + is CalculatorUIState.Ready -> Ready( uiState = uiState, navigateToMenu = navigateToMenu, navigateToSettings = navigateToSettings, - addSymbol = viewModel::addTokens, - clearSymbols = viewModel::clearInput, - deleteSymbol = viewModel::deleteTokens, - onCursorChange = viewModel::onCursorChange, - toggleAngleMode = viewModel::toggleCalculatorMode, - evaluate = viewModel::evaluate, - clearHistory = viewModel::clearHistory + addSymbol = addTokens, + clearSymbols = clearInput, + deleteSymbol = deleteTokens, + onCursorChange = onCursorChange, + toggleAngleMode = toggleCalculatorMode, + evaluate = evaluate, + clearHistory = clearHistory ) } } @Composable -private fun CalculatorScreen( +private fun Ready( uiState: CalculatorUIState.Ready, navigateToMenu: () -> Unit, navigateToSettings: () -> Unit, @@ -137,7 +166,8 @@ private fun CalculatorScreen( Icons.Default.Delete, stringResource(R.string.calculator_clear_history_title) ) - } + }, + modifier = Modifier.semantics { testTag = "historyButton" } ) } else { SettingsButton(navigateToSettings) @@ -202,7 +232,8 @@ private fun CalculatorScreen( // Input Column( - Modifier + modifier = Modifier + .semantics { testTag = "inputBox" } .offset(y = dragDp) .height(textBoxHeight) .background( @@ -286,6 +317,7 @@ private fun CalculatorScreen( // Keyboard CalculatorKeyboard( modifier = Modifier + .semantics { testTag = "ready" } .offset(y = dragDp + textBoxHeight) .height(keyboardHeight) .fillMaxWidth() @@ -337,7 +369,7 @@ private fun CalculatorScreen( } @Composable -private fun Loading( +internal fun Loading( navigateToMenu: () -> Unit, navigateToSettings: () -> Unit, ) { @@ -401,6 +433,7 @@ private fun Loading( // Keyboard CalculatorKeyboardLoading( modifier = Modifier + .semantics { testTag = "loading" } .offset(y = maxHeight * 0.25f) .height(maxHeight * 0.75f) .fillMaxWidth() @@ -446,11 +479,11 @@ private fun PreviewCalculatorScreen() { ), navigateToMenu = {}, navigateToSettings = {}, - addSymbol = {}, - clearSymbols = {}, - deleteSymbol = {}, + addTokens = {}, + clearInput = {}, + deleteTokens = {}, onCursorChange = {}, - toggleAngleMode = {}, + toggleCalculatorMode = {}, evaluate = {}, clearHistory = {} )