From 6f0982b8d961bcafe2b441834e7a9fa89212315e Mon Sep 17 00:00:00 2001 From: Marian Jureczko Date: Wed, 27 Nov 2024 19:41:12 +0100 Subject: [PATCH] #17 in progress - espresso tests for main screen --- app/build.gradle | 20 +++- .../poszukiwacz/AbstractUITest.kt | 53 +++++++++ .../poszukiwacz/CustomArrangersDetector.kt | 35 ++++++ .../poszukiwacz/CustomTestRunner.kt | 0 .../res}/arranger.properties | 0 .../poszukiwacz/MainScreenTest.kt | 107 ++++++++++++++++++ .../poszukiwacz/TestPortsModule.kt | 46 ++++++++ .../pl/marianjureczko/poszukiwacz/UiTest.kt | 5 + .../poszukiwacz/MainScreenTest.kt | 2 +- .../pl/marianjureczko/poszukiwacz/UiTest.kt | 47 +------- .../poszukiwacz/screen/main/MainScreen.kt | 2 - .../poszukiwacz/screen/main/MainScreenBody.kt | 49 +++++--- .../poszukiwacz/screen/main/MainState.kt | 3 +- .../poszukiwacz/screen/main/MainViewModel.kt | 5 +- .../TreasureEditorScreenBody.kt | 13 ++- .../poszukiwacz/shared/di/PortsModule.kt | 7 ++ .../poszukiwacz/shared/di/SingletonModule.kt | 6 - .../ui/components/AbstractDialog.kt | 9 +- .../ui/components/EmbeddedButton.kt | 20 +++- .../ui/components/EnterTextDialog.kt | 33 +++++- .../poszukiwacz/ui/components/TopBar.kt | 10 +- .../poszukiwacz/ui/components/YesNoDialog.kt | 9 +- .../poszukiwacz/model/HunterPathArranger.kt | 12 -- .../poszukiwacz/model/RouteArranger.kt | 49 -------- .../model/TreasuresProgressArranger.kt | 18 --- .../screen/main/MainViewModelTest.kt | 8 +- .../AddTreasureDescriptionToRouteTest.kt | 4 +- .../poszukiwacz/TestStoragePort.kt | 6 + .../testShared/resources/arranger.properties | 2 + build.gradle | 1 + 30 files changed, 403 insertions(+), 178 deletions(-) create mode 100644 app/src/androidTest/java/pl/marianjureczko/poszukiwacz/AbstractUITest.kt create mode 100644 app/src/androidTest/java/pl/marianjureczko/poszukiwacz/CustomArrangersDetector.kt rename app/src/{androidTestKalinowiceCustomDebug => androidTest}/java/pl/marianjureczko/poszukiwacz/CustomTestRunner.kt (100%) rename app/src/{test/resources => androidTest/res}/arranger.properties (100%) create mode 100644 app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt create mode 100644 app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/TestPortsModule.kt create mode 100644 app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/UiTest.kt delete mode 100644 app/src/test/java/pl/marianjureczko/poszukiwacz/model/HunterPathArranger.kt delete mode 100644 app/src/test/java/pl/marianjureczko/poszukiwacz/model/RouteArranger.kt delete mode 100644 app/src/test/java/pl/marianjureczko/poszukiwacz/model/TreasuresProgressArranger.kt rename app/src/{test => testShared}/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt (93%) create mode 100644 app/src/testShared/resources/arranger.properties diff --git a/app/build.gradle b/app/build.gradle index a79510d..83cff26 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -87,8 +87,13 @@ android { sourceSets { androidTest { manifest.srcFile 'src/androidTest/AndroidManifest.xml' - java.srcDirs = ['src/androidTest/java'] + java.srcDirs = ['src/androidTest/java', 'src/testSharedClone/java'] res.srcDirs = ['src/androidTest/res'] + resources.srcDirs = ['src/androidTest/resources', 'src/testSharedClone/resources'] + } + test { + java.srcDirs += 'src/testShared/java' + resources.srcDirs += 'src/testShared/resources' } } compileOptions { @@ -148,7 +153,7 @@ dependencies { implementation 'com.google.accompanist:accompanist-permissions:0.30.1' implementation 'commons-io:commons-io:2.11.0' implementation 'com.mapbox.maps:android:10.18.0' - implementation 'com.facebook.android:facebook-share:latest.release' + implementation 'com.facebook.android:facebook-share:17.0.2' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'org.apache.commons:commons-math3:3.6.1' @@ -157,23 +162,28 @@ dependencies { testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2' testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.2' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.8.2' - testImplementation group: 'com.ocadotechnology.gembus', name: 'test-arranger', version: '1.4.9' + testImplementation("com.ocadotechnology.gembus:test-arranger:1.6.4-SNAPSHOT") { + exclude group: "dk.brics.automaton", module: "automaton" + } testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.26.3' testImplementation group: 'org.mockito', name: 'mockito-core', version: '5.12.0' testImplementation group: 'org.mockito', name: 'mockito-junit-jupiter', version: '4.4.0' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.0' testImplementation 'org.mockito.kotlin:mockito-kotlin:5.4.0' + androidTestImplementation("com.ocadotechnology.gembus:test-arranger:1.6.4-SNAPSHOT") { + exclude group: "dk.brics.automaton", module: "automaton" + } androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test:core:1.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation "androidx.test.espresso:espresso-intents:3.5.1" - androidTestImplementation group: 'org.mockito', name: 'mockito-core', version: '4.4.0' - androidTestImplementation 'org.mockito:mockito-inline:2.13.0' + androidTestImplementation group: 'org.mockito', name: 'mockito-android', version: '5.12.0' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" androidTestImplementation 'androidx.test:rules:1.5.0' androidTestImplementation 'androidx.test:runner:1.5.2' androidTestImplementation "com.google.dagger:hilt-android-testing:${hilt_version}" + kaptAndroidTest "com.google.dagger:hilt-android-compiler:${hilt_version}" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" diff --git a/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/AbstractUITest.kt b/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/AbstractUITest.kt new file mode 100644 index 0000000..2e7ec98 --- /dev/null +++ b/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/AbstractUITest.kt @@ -0,0 +1,53 @@ +package pl.marianjureczko.poszukiwacz + +import android.Manifest +import androidx.compose.ui.test.SemanticsNodeInteraction +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.rule.GrantPermissionRule +import dagger.hilt.android.testing.HiltAndroidRule +import org.junit.Before +import org.junit.Rule +import pl.marianjureczko.poszukiwacz.activity.main.MainActivity + +abstract class AbstractUITest { + @get:Rule(order = 0) + val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) + val composeRule = createAndroidComposeRule() + + @get:Rule(order = 3) + val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.CAMERA, + Manifest.permission.RECORD_AUDIO + ) + val context = InstrumentationRegistry.getInstrumentation().targetContext + + @Before + fun init() { + hiltRule.inject() + } + + fun performClick(contentDescription: String) { + composeRule + .onNodeWithContentDescription(contentDescription) + .assertExists() + .performClick() + composeRule.waitForIdle() + } + + fun getNode(contentDescription: String): SemanticsNodeInteraction { + return composeRule.onNodeWithContentDescription(contentDescription) + } + + fun assertImageIsDisplayed(drawableId: Int) { + composeRule.onNodeWithTag(drawableId.toString()) + .assertIsDisplayed() + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/CustomArrangersDetector.kt b/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/CustomArrangersDetector.kt new file mode 100644 index 0000000..077d99f --- /dev/null +++ b/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/CustomArrangersDetector.kt @@ -0,0 +1,35 @@ +package pl.marianjureczko.poszukiwacz + +import androidx.test.platform.app.InstrumentationRegistry +import dalvik.system.DexFile +import org.junit.Test + + +class CustomArrangersDetector { + + @Test + fun findArrangerConstructors() { + val testClasses = getTestApkClasses() + val customArrangers = convertToComaSeparatedCustomArrangers(testClasses) + println("### $customArrangers ###") + } + + private fun convertToComaSeparatedCustomArrangers(classes: List): String { + return classes + .filter { c -> c.endsWith("Arranger") && !c.endsWith(".Arranger") && !c.endsWith(".CustomArranger") } + .joinToString(separator = ",") + } + + private fun getTestApkClasses(): List { + try { + val testApkPath: String = InstrumentationRegistry.getInstrumentation() + .getContext() + .getApplicationInfo() + .sourceDir + val dexFile = DexFile(testApkPath) + return dexFile.entries().asSequence().toList() + } catch (e: Exception) { + throw RuntimeException("Failed to retrieve test APK classes", e) + } + } +} \ No newline at end of file diff --git a/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/CustomTestRunner.kt b/app/src/androidTest/java/pl/marianjureczko/poszukiwacz/CustomTestRunner.kt similarity index 100% rename from app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/CustomTestRunner.kt rename to app/src/androidTest/java/pl/marianjureczko/poszukiwacz/CustomTestRunner.kt diff --git a/app/src/test/resources/arranger.properties b/app/src/androidTest/res/arranger.properties similarity index 100% rename from app/src/test/resources/arranger.properties rename to app/src/androidTest/res/arranger.properties diff --git a/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt b/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt new file mode 100644 index 0000000..f948d3b --- /dev/null +++ b/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt @@ -0,0 +1,107 @@ +package pl.marianjureczko.poszukiwacz + +import androidx.compose.ui.test.SemanticsNodeInteraction +import androidx.compose.ui.test.assertTextEquals +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import org.junit.After +import org.junit.Test +import pl.marianjureczko.poszukiwacz.model.Route +import pl.marianjureczko.poszukiwacz.model.RouteArranger +import pl.marianjureczko.poszukiwacz.screen.main.CONFIRM_ROUTE_NAME_BUTTON +import pl.marianjureczko.poszukiwacz.screen.main.DELETE_ROUTE_BUTTON +import pl.marianjureczko.poszukiwacz.screen.main.EDIT_ROUTE_BUTTON +import pl.marianjureczko.poszukiwacz.screen.main.ENTER_ROUTE_NAME_TITLE +import pl.marianjureczko.poszukiwacz.screen.main.NEW_ROUTE_BUTTON +import pl.marianjureczko.poszukiwacz.screen.main.ROUTE_NAME_TEXT_EDIT +import pl.marianjureczko.poszukiwacz.screen.treasureseditor.TREASURE_ITEM_ROW +import pl.marianjureczko.poszukiwacz.screen.treasureseditor.TREASURE_ITEM_TEXT +import pl.marianjureczko.poszukiwacz.shared.di.PortsModule +import pl.marianjureczko.poszukiwacz.ui.components.TOPBAR_SCREEN_TITLE +import pl.marianjureczko.poszukiwacz.ui.components.YES_BUTTON +import java.time.LocalDateTime +import java.time.ZoneOffset + + +@UninstallModules(PortsModule::class) +@HiltAndroidTest +class MainScreenTest : UiTest() { + + var route: Route = getRouteFromStorage() + + @After + fun restoreRoute() { + if(TestPortsModule.storage.routes.isEmpty()) { + val newRoute = RouteArranger.routeWithoutTipFiles() + TestPortsModule.storage.routes[newRoute.name] = newRoute + } + route = getRouteFromStorage() + } + + @Test + fun shouldGoToTreasureEditorScreen_whenCreatingNewRoute() { + //given + val button: SemanticsNodeInteraction = getNode(NEW_ROUTE_BUTTON) + + //then + button.assertTextEquals(context.getString(R.string.new_route_button)) + button.performClick() + composeRule.waitForIdle() + + val dialogTitle = getNode(ENTER_ROUTE_NAME_TITLE) + dialogTitle.assertTextEquals(context.getString(R.string.route_name_prompt)) + + val nameInput = getNode(ROUTE_NAME_TEXT_EDIT) + val routeName = "TEST_" + LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + nameInput.assertExists() + .performTextInput(routeName) + composeRule.waitForIdle() + + performClick(CONFIRM_ROUTE_NAME_BUTTON) + + getNode(TOPBAR_SCREEN_TITLE) + .assertTextEquals("${context.getString(R.string.route)} $routeName") + getNode(TREASURE_ITEM_ROW) + .assertDoesNotExist() + } + + @Test + fun shouldGoToTreasureEditorScreen_whenClickOnEditButton() { + //given +// +// composeRule.setContent { +// // Your composable function that renders the screen +// MainScreen { }() +// } + composeRule.waitForIdle() + + //when + performClick(EDIT_ROUTE_BUTTON + route.name) + + //then + val screenTitle: SemanticsNodeInteraction = getNode(TOPBAR_SCREEN_TITLE) + screenTitle.assertTextEquals("${context.getString(R.string.route)} ${route.name}") + route.treasures.forEach { treasure -> + val treasureText: SemanticsNodeInteraction = getNode("$TREASURE_ITEM_TEXT ${treasure.id}") + treasureText.assertTextEquals(treasure.prettyName()) + } + } + + @Test + fun shouldRemoveRoute_whenClickingRemoveButton() { + //given + composeRule.waitForIdle() + + //when + performClick(DELETE_ROUTE_BUTTON + route.name) + performClick(YES_BUTTON) + + //then + getNode(EDIT_ROUTE_BUTTON + route.name) + .assertDoesNotExist() + } + + private fun getRouteFromStorage() = TestPortsModule.storage.routes.values.first() +} \ No newline at end of file diff --git a/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/TestPortsModule.kt b/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/TestPortsModule.kt new file mode 100644 index 0000000..2834f1f --- /dev/null +++ b/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/TestPortsModule.kt @@ -0,0 +1,46 @@ +package pl.marianjureczko.poszukiwacz + +import com.google.android.gms.location.FusedLocationProviderClient +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import org.mockito.Mockito.mock +import pl.marianjureczko.poszukiwacz.shared.port.CameraPort +import pl.marianjureczko.poszukiwacz.shared.port.LocationPort +import pl.marianjureczko.poszukiwacz.shared.port.StorageHelper +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object TestPortsModule { + + val storage = TestStoragePort() + val locationClient = mock() + val location = mock() + val camera = mock() + + @Singleton + @Provides + fun fusedLocationClient(): FusedLocationProviderClient { + return locationClient + } + + @Singleton + @Provides + fun locationPort(): LocationPort { + return location + } + + @Singleton + @Provides + fun photoPort(): CameraPort { + return camera + } + + @Singleton + @Provides + fun storageHelper(): StorageHelper { + return storage + } +} \ No newline at end of file diff --git a/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/UiTest.kt b/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/UiTest.kt new file mode 100644 index 0000000..4ddcd89 --- /dev/null +++ b/app/src/androidTestClassic/java/pl/marianjureczko/poszukiwacz/UiTest.kt @@ -0,0 +1,5 @@ +package pl.marianjureczko.poszukiwacz + +open class UiTest: AbstractUITest() { + +} \ No newline at end of file diff --git a/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt b/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt index 6d8ad80..85e6ae2 100644 --- a/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt +++ b/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/MainScreenTest.kt @@ -16,7 +16,7 @@ class MainScreenTest : UiTest() { @Test - fun shouldShowGuideWithImages() { + fun shouldGuideViewsWithImages_whenArrowIsClicked() { //given // composeRule.waitForIdle() val text: SemanticsNodeInteraction = getNode(GUIDE_TEXT) diff --git a/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/UiTest.kt b/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/UiTest.kt index 89477c4..a3913b7 100644 --- a/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/UiTest.kt +++ b/app/src/androidTestKalinowiceCustomDebug/java/pl/marianjureczko/poszukiwacz/UiTest.kt @@ -1,55 +1,10 @@ package pl.marianjureczko.poszukiwacz -import androidx.compose.ui.test.SemanticsNodeInteraction -import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsEnabled -import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.onNodeWithContentDescription -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.performClick -import androidx.test.platform.app.InstrumentationRegistry -import androidx.test.rule.GrantPermissionRule -import dagger.hilt.android.testing.HiltAndroidRule -import org.junit.Before -import org.junit.Rule -import pl.marianjureczko.poszukiwacz.activity.main.MainActivity import pl.marianjureczko.poszukiwacz.screen.main.START_BUTTON -open class UiTest { - @get:Rule(order = 0) - val hiltRule = HiltAndroidRule(this) - - @get:Rule(order = 1) - val composeRule = createAndroidComposeRule() - - @get:Rule(order = 3) - val permissionRule: GrantPermissionRule = GrantPermissionRule.grant( - android.Manifest.permission.ACCESS_FINE_LOCATION, - android.Manifest.permission.CAMERA - ) - val context = InstrumentationRegistry.getInstrumentation().targetContext - - @Before - fun init() { - hiltRule.inject() - } - - fun performClick(contentDescription: String) { - composeRule - .onNodeWithContentDescription(contentDescription) - .assertExists() - .performClick() - } - - fun getNode(contentDescription: String): SemanticsNodeInteraction { - return composeRule.onNodeWithContentDescription(contentDescription) - .assertExists() - } - - fun assertImageIsDisplayed(drawableId: Int) { - composeRule.onNodeWithTag(drawableId.toString()) - .assertIsDisplayed() - } +open class UiTest: AbstractUITest() { protected fun goToSearching() { var buttonDisabled = true diff --git a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreen.kt b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreen.kt index 1ad9675..b1c26b5 100644 --- a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreen.kt +++ b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreen.kt @@ -6,7 +6,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource import androidx.navigation.NavController -import com.google.accompanist.permissions.ExperimentalPermissionsApi import pl.marianjureczko.poszukiwacz.R import pl.marianjureczko.poszukiwacz.permissions.RequirementsForNavigation import pl.marianjureczko.poszukiwacz.shared.GoToSearching @@ -14,7 +13,6 @@ import pl.marianjureczko.poszukiwacz.shared.GoToTreasureEditor import pl.marianjureczko.poszukiwacz.ui.components.TopBar import pl.marianjureczko.poszukiwacz.ui.handlePermissionWithExitOnDenied -@OptIn(ExperimentalPermissionsApi::class) @SuppressLint("UnusedMaterialScaffoldPaddingParameter") @Composable fun MainScreen( diff --git a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreenBody.kt b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreenBody.kt index 8190ccb..14dcbad 100644 --- a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreenBody.kt +++ b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainScreenBody.kt @@ -27,7 +27,6 @@ import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import pl.marianjureczko.poszukiwacz.R import pl.marianjureczko.poszukiwacz.model.Route -import pl.marianjureczko.poszukiwacz.shared.DeleteRoute import pl.marianjureczko.poszukiwacz.shared.GoToSearching import pl.marianjureczko.poszukiwacz.shared.GoToTreasureEditor import pl.marianjureczko.poszukiwacz.ui.components.AdvertBanner @@ -36,6 +35,13 @@ import pl.marianjureczko.poszukiwacz.ui.components.EnterTextDialog import pl.marianjureczko.poszukiwacz.ui.components.LargeButton import pl.marianjureczko.poszukiwacz.ui.components.YesNoDialog +const val NEW_ROUTE_BUTTON = "New route" +const val CONFIRM_ROUTE_NAME_BUTTON = "Confirm route name" +const val ENTER_ROUTE_NAME_TITLE = "Enter route name" +const val ROUTE_NAME_TEXT_EDIT = "Edit route name" +const val EDIT_ROUTE_BUTTON = "Edit route button" +const val DELETE_ROUTE_BUTTON = "Delete route button" + @Composable fun MainScreenBody(goToTreasureEditor: GoToTreasureEditor, goToSearching: GoToSearching) { val viewModel: MainViewModel = hiltViewModel() @@ -46,11 +52,11 @@ fun MainScreenBody(goToTreasureEditor: GoToTreasureEditor, goToSearching: GoToSe modifier = Modifier.weight(0.99f) ) { items(state.routes) { route -> - RouteItem(route, viewModel, { viewModel.deleteRoute(route) }, goToTreasureEditor, goToSearching) + RouteItem(route, viewModel, goToTreasureEditor, goToSearching) } } Spacer(modifier = Modifier.weight(0.01f)) - LargeButton(R.string.new_route_button) { + LargeButton(R.string.new_route_button, description = NEW_ROUTE_BUTTON) { viewModel.openNewRouteDialog() } val context = LocalContext.current @@ -60,7 +66,10 @@ fun MainScreenBody(goToTreasureEditor: GoToTreasureEditor, goToSearching: GoToSe EnterTextDialog( visible = state.newRoute.showDialog, hideIt = { viewModel.hideNewRouteDialog() }, - title = R.string.route_name_prompt + title = R.string.route_name_prompt, + buttonDescription = CONFIRM_ROUTE_NAME_BUTTON, + textFieldDescription = ROUTE_NAME_TEXT_EDIT, + titleDescription = ENTER_ROUTE_NAME_TITLE ) { routeName -> viewModel.createNewRouteByName(routeName, goToTreasureEditor) } YesNoDialog( state.showOverrideRouteDialog, @@ -69,6 +78,16 @@ fun MainScreenBody(goToTreasureEditor: GoToTreasureEditor, goToSearching: GoToSe ) { viewModel.replaceRouteWithNewOne(state.newRoute.routeName, goToTreasureEditor) } + YesNoDialog( + state = viewModel.state.value.deleteConfirmation.showDialog, + hideIt = { viewModel.hideConfirmDeleteDialog() }, + titleString = viewModel.state.value.deleteConfirmation.confirmationPrompt, + onYes = { + viewModel.state.value.deleteConfirmation.deleteCandidate?.let { route -> + viewModel.deleteRoute(route) + } + } + ) AdvertBanner() } } @@ -77,7 +96,6 @@ fun MainScreenBody(goToTreasureEditor: GoToTreasureEditor, goToSearching: GoToSe fun RouteItem( item: Route, viewModel: MainViewModel, - onDelete: DeleteRoute, goToTreasureEditor: GoToTreasureEditor, goToSearching: GoToSearching ) { @@ -102,16 +120,17 @@ fun RouteItem( .fillMaxWidth() .weight(0.5f) ) - EmbeddedButton(Icons.TwoTone.Edit) { goToTreasureEditor(item.name) } - EmbeddedButton(Icons.TwoTone.Share) { print("TODO") } - EmbeddedButton(Icons.TwoTone.Delete) { viewModel.openConfirmDeleteDialog(item.name) } - - YesNoDialog( - state = viewModel.state.value.deleteConfirmation.showDialog, - hideIt = { viewModel.hideConfirmDeleteDialog() }, - titleString = viewModel.state.value.deleteConfirmation.confirmationPrompt, - onYes = onDelete - ) + EmbeddedButton( + imageVector = Icons.TwoTone.Edit, + description = EDIT_ROUTE_BUTTON + item.name + ) { goToTreasureEditor(item.name) } + EmbeddedButton(imageVector = Icons.TwoTone.Share) { print("TODO") } + EmbeddedButton( + imageVector = Icons.TwoTone.Delete, + description = DELETE_ROUTE_BUTTON + item.name + ) { + viewModel.openConfirmDeleteDialog(item) + } } } } \ No newline at end of file diff --git a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainState.kt b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainState.kt index caf7718..0aa3dd9 100644 --- a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainState.kt +++ b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainState.kt @@ -11,7 +11,8 @@ data class MainState( data class DeleteConfirmation( val showDialog: Boolean = false, - val confirmationPrompt: String = "" + val confirmationPrompt: String = "", + val deleteCandidate: Route? = null ) data class NewRoute( diff --git a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModel.kt b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModel.kt index cd7536f..7b1f579 100644 --- a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModel.kt +++ b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModel.kt @@ -40,10 +40,11 @@ class MainViewModel @Inject constructor( _state.value = _state.value.copy(showOverrideRouteDialog = false) } - fun openConfirmDeleteDialog(routeName: String) { + fun openConfirmDeleteDialog(route: Route) { val newValue = _state.value.deleteConfirmation.copy( showDialog = true, - confirmationPrompt = resources.getString(R.string.route_remove_prompt, routeName) + confirmationPrompt = resources.getString(R.string.route_remove_prompt, route.name), + deleteCandidate = route ) _state.value = _state.value.copy(deleteConfirmation = newValue) } diff --git a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/treasureseditor/TreasureEditorScreenBody.kt b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/treasureseditor/TreasureEditorScreenBody.kt index 5c58726..e5da57a 100644 --- a/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/treasureseditor/TreasureEditorScreenBody.kt +++ b/app/src/classic/java/pl/marianjureczko/poszukiwacz/screen/treasureseditor/TreasureEditorScreenBody.kt @@ -35,6 +35,8 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -59,6 +61,9 @@ import pl.marianjureczko.poszukiwacz.ui.components.AdvertBanner import pl.marianjureczko.poszukiwacz.ui.components.EmbeddedButton import pl.marianjureczko.poszukiwacz.ui.components.YesNoDialog +const val TREASURE_ITEM_ROW = "Treasure row" +const val TREASURE_ITEM_TEXT = "Treasure" + @Composable fun TreasureEditorScreenBody( state: TreasureEditorState, @@ -135,6 +140,7 @@ fun TreasureItem( modifier = Modifier .padding(8.dp) .fillMaxWidth() + .semantics { contentDescription = TREASURE_ITEM_ROW } ) { Text( text = treasure.prettyName(), @@ -143,6 +149,7 @@ fun TreasureItem( modifier = Modifier .fillMaxWidth() .weight(0.5f) + .semantics { contentDescription = "$TREASURE_ITEM_TEXT ${treasure.id}" } ) DoPhotoButton( treasure, @@ -160,7 +167,7 @@ fun TreasureItem( showOverrideSoundTipDialog, showSoundRecordingDialog ) - EmbeddedButton(Icons.TwoTone.Delete) { removeTreasure(treasure.id) } + EmbeddedButton(imageVector = Icons.TwoTone.Delete) { removeTreasure(treasure.id) } } } } @@ -183,7 +190,7 @@ private fun RecordSoundTipButton( } val permissionErrorMsg = stringResource(R.string.recording_permission_not_granted) val context = LocalContext.current - EmbeddedButton(Icons.TwoTone.Mic) { + EmbeddedButton(imageVector = Icons.TwoTone.Mic) { if (recordingPermissionGranted) { if (state.overrideSoundTipQuestionProvider(treasure)) { showOverrideSoundTipDialog() @@ -216,7 +223,7 @@ private fun DoPhotoButton( launchDoPhoto() } - EmbeddedButton(Icons.TwoTone.CameraAlt) { + EmbeddedButton(imageVector = Icons.TwoTone.CameraAlt) { if (state.overridePhotoQuestionProvider(treasure)) { showOverridePhotoDialog() } else { diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/PortsModule.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/PortsModule.kt index e346ffd..9902d83 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/PortsModule.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/PortsModule.kt @@ -11,12 +11,19 @@ import dagger.hilt.components.SingletonComponent import kotlinx.coroutines.CoroutineDispatcher import pl.marianjureczko.poszukiwacz.shared.port.CameraPort import pl.marianjureczko.poszukiwacz.shared.port.LocationPort +import pl.marianjureczko.poszukiwacz.shared.port.StorageHelper import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) object PortsModule { + @Singleton + @Provides + fun storageHelper(@ApplicationContext appContext: Context): StorageHelper { + return StorageHelper(appContext) + } + @Singleton @Provides fun fusedLocationClient(@ApplicationContext context: Context): FusedLocationProviderClient { diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/SingletonModule.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/SingletonModule.kt index e6f6605..aacc395 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/SingletonModule.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/shared/di/SingletonModule.kt @@ -63,12 +63,6 @@ object SingletonModule { return PhotoHelper(appContext, storageHelper) } - @Singleton - @Provides - fun storageHelper(@ApplicationContext appContext: Context): StorageHelper { - return StorageHelper(appContext) - } - @Singleton @Provides fun resources(@ApplicationContext appContext: Context): Resources { diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/AbstractDialog.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/AbstractDialog.kt index 50d9adb..8949c3c 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/AbstractDialog.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/AbstractDialog.kt @@ -10,6 +10,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.colorResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import pl.marianjureczko.poszukiwacz.R @@ -21,6 +23,7 @@ fun AbstractDialog( title: Int?, titleString: String? = null, text: @Composable (() -> Unit)? = null, + titleDescription : String = "", buttons: @Composable () -> Unit ) { if (visible) { @@ -30,7 +33,11 @@ fun AbstractDialog( modifier = Modifier.border(width = 1.dp, color = colorResource(R.color.colorPrimary)), onDismissRequest = { hideIt() }, title = { Text( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier + .fillMaxWidth() + .semantics { + contentDescription = titleDescription + }, text = titleToShow, style = MaterialTheme.typography.h5, textAlign = TextAlign.Center diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EmbeddedButton.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EmbeddedButton.kt index 2ca09e7..a54e0cc 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EmbeddedButton.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EmbeddedButton.kt @@ -11,20 +11,34 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp +private const val EMBEDDED_BUTTON = "Embedded button" + @Composable -fun EmbeddedButton(imageVector: ImageVector, colorFilter: ColorFilter? = null, onClick: () -> Unit) { +fun EmbeddedButton( + modifier: Modifier = Modifier, + imageVector: ImageVector, + description: String = EMBEDDED_BUTTON, + colorFilter: ColorFilter? = null, + onClick: () -> Unit +) { TextButton( onClick = onClick, colors = ButtonDefaults.buttonColors(backgroundColor = Color.Transparent), contentPadding = PaddingValues(0.dp), - modifier = Modifier.width(35.dp), + modifier = modifier + .width(35.dp) + .semantics { + contentDescription = description + }, content = { Image( imageVector = imageVector, contentDescription = null, - modifier = Modifier.background(color = Color.Transparent), + modifier = modifier.background(color = Color.Transparent), colorFilter = colorFilter ) } diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EnterTextDialog.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EnterTextDialog.kt index 7ce5cb2..fe3d17b 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EnterTextDialog.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/EnterTextDialog.kt @@ -1,5 +1,6 @@ package pl.marianjureczko.poszukiwacz.ui.components +import android.util.Log import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -23,6 +24,8 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import pl.marianjureczko.poszukiwacz.R @@ -33,6 +36,9 @@ fun EnterTextDialog( visible: Boolean, hideIt: () -> Unit, title: Int, + textFieldDescription: String = "", + buttonDescription: String = "", + titleDescription: String = "", onClick: (text: String) -> Unit ) { val text = remember { mutableStateOf("") } @@ -42,11 +48,16 @@ fun EnterTextDialog( visible, { text.value = ""; hideIt() }, title, + titleDescription = titleDescription, text = { Column { TextField( + modifier = Modifier + .focusRequester(focusRequester) + .semantics { + contentDescription = textFieldDescription + }, textStyle = MaterialTheme.typography.body1, - modifier = Modifier.focusRequester(focusRequester), shape = RoundedCornerShape(0), value = text.value, onValueChange = { text.value = it } @@ -62,7 +73,11 @@ fun EnterTextDialog( ) { Button( shape = Shapes.large, - modifier = Modifier.width(140.dp), + modifier = Modifier + .width(140.dp) + .semantics { + contentDescription = buttonDescription + }, colors = ButtonDefaults.buttonColors(backgroundColor = Color.LightGray), onClick = { hideIt() @@ -77,11 +92,19 @@ fun EnterTextDialog( ) if (visible) { LaunchedEffect(key1 = "on start") { - focusRequester.requestFocus() + try { + focusRequester.requestFocus() + } catch (e: Exception) { + Log.e("EnterTextDialog", "Ignoring error, we can go on without focus: ${e.message}", ) + } } DisposableEffect(key1 = "on dispose") { onDispose { - focusManager.clearFocus() + try { + focusManager.clearFocus() + } catch (e: Exception) { + Log.e("EnterTextDialog", "Ignoring error, we can go on without clearing focus: ${e.message}", ) + } } } } @@ -90,5 +113,5 @@ fun EnterTextDialog( @Preview(showBackground = true, apiLevel = 31) @Composable fun EnterTextDialogPreview() { - EnterTextDialog(true, {}, R.string.app_name, {}) + EnterTextDialog(true, {}, R.string.app_name) {} } \ No newline at end of file diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/TopBar.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/TopBar.kt index 1739e03..13d0b6f 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/TopBar.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/TopBar.kt @@ -25,12 +25,16 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.navigation.NavController import pl.marianjureczko.poszukiwacz.R +const val TOPBAR_SCREEN_TITLE = "Screen title" + @Composable fun TopBar(navController: NavController, title: String, onClickOnGuide: () -> Unit, onClickOnFacebook: () -> Unit) { val showMenu = remember { mutableStateOf(false) } @@ -38,8 +42,8 @@ fun TopBar(navController: NavController, title: String, onClickOnGuide: () -> Un navigationIcon = { if (navController.previousBackStackEntry != null) { EmbeddedButton( - Icons.Outlined.ArrowBack, - ColorFilter.tint(Color.White) + imageVector = Icons.Outlined.ArrowBack, + colorFilter = ColorFilter.tint(Color.White) ) { navController.navigateUp() } } }, @@ -59,6 +63,7 @@ fun TopBar(navController: NavController, title: String, onClickOnGuide: () -> Un }, title = { + // TODO t: check replacing Row with Box(maxWidth, Image->left, Text->center) Row( horizontalArrangement = Arrangement.Start, verticalAlignment = Alignment.CenterVertically, @@ -73,6 +78,7 @@ fun TopBar(navController: NavController, title: String, onClickOnGuide: () -> Un contentScale = ContentScale.FillHeight ) Text( + modifier = Modifier.semantics { contentDescription = TOPBAR_SCREEN_TITLE }, text = title, color = Color.White, style = MaterialTheme.typography.h6.copy(fontFamily = FontFamily.Default), diff --git a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/YesNoDialog.kt b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/YesNoDialog.kt index 0f8245e..4fc5f8d 100644 --- a/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/YesNoDialog.kt +++ b/app/src/main/java/pl/marianjureczko/poszukiwacz/ui/components/YesNoDialog.kt @@ -10,10 +10,14 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import pl.marianjureczko.poszukiwacz.R +const val YES_BUTTON = "Yes" + @Composable fun YesNoDialog( state: Boolean, @@ -36,7 +40,10 @@ fun YesNoDialog( onYes() hideIt() }) { - Text(stringResource(R.string.yes)) + Text( + modifier = Modifier.semantics { contentDescription = YES_BUTTON}, + text = stringResource(R.string.yes) + ) } TextButton(onClick = { hideIt() }) { Text(stringResource(R.string.no)) diff --git a/app/src/test/java/pl/marianjureczko/poszukiwacz/model/HunterPathArranger.kt b/app/src/test/java/pl/marianjureczko/poszukiwacz/model/HunterPathArranger.kt deleted file mode 100644 index 8912512..0000000 --- a/app/src/test/java/pl/marianjureczko/poszukiwacz/model/HunterPathArranger.kt +++ /dev/null @@ -1,12 +0,0 @@ -package pl.marianjureczko.poszukiwacz.model - -import com.ocadotechnology.gembus.test.CustomArranger -import com.ocadotechnology.gembus.test.some - -class HunterPathArranger : CustomArranger() { - override fun instance(): HunterPath { - val instance = super.instance() - instance.addLocation(some()) - return instance - } -} \ No newline at end of file diff --git a/app/src/test/java/pl/marianjureczko/poszukiwacz/model/RouteArranger.kt b/app/src/test/java/pl/marianjureczko/poszukiwacz/model/RouteArranger.kt deleted file mode 100644 index bcd6f1b..0000000 --- a/app/src/test/java/pl/marianjureczko/poszukiwacz/model/RouteArranger.kt +++ /dev/null @@ -1,49 +0,0 @@ -package pl.marianjureczko.poszukiwacz.model - -import com.ocadotechnology.gembus.test.CustomArranger -import com.ocadotechnology.gembus.test.some -import com.ocadotechnology.gembus.test.someObjects -import com.ocadotechnology.gembus.test.someString -import pl.marianjureczko.poszukiwacz.shared.port.StorageHelper -import java.io.File - -class RouteArranger : CustomArranger() { - companion object { - - fun saveWithTreasureDescription(treasureDescription: TreasureDescription, storageHelper: StorageHelper): Route { - val route = Route(someString(), mutableListOf(treasureDescription)) - storageHelper.save(route) - return route - } - - fun savedWithTipFiles(storageHelper: StorageHelper): Route { - val route = some() - route.treasures.forEach { t -> - t.instantiatePhotoFile(storageHelper).createNewFile() - t.tipFileName = storageHelper.newSoundFile() - File(t.tipFileName).createNewFile() - } - storageHelper.save(route) - return route - } - - fun routeWithoutTipFiles(): Route { - val route = some() - route.treasures.forEach { t -> - t.photoFileName = null - t.tipFileName = null - } - return route - } - - fun withNameAndTreasureWithQrCode(name:String, qrCode: String): Route { - val route = some().copy(name = name) - route.treasures.first().qrCode = qrCode - return route - } - } - - override fun instance(): Route { - return Route(some(), someObjects(3).toMutableList()) - } -} \ No newline at end of file diff --git a/app/src/test/java/pl/marianjureczko/poszukiwacz/model/TreasuresProgressArranger.kt b/app/src/test/java/pl/marianjureczko/poszukiwacz/model/TreasuresProgressArranger.kt deleted file mode 100644 index b4a7f5a..0000000 --- a/app/src/test/java/pl/marianjureczko/poszukiwacz/model/TreasuresProgressArranger.kt +++ /dev/null @@ -1,18 +0,0 @@ -package pl.marianjureczko.poszukiwacz.model - -import com.ocadotechnology.gembus.test.CustomArranger -import com.ocadotechnology.gembus.test.some -import com.ocadotechnology.gembus.test.someString - -class TreasuresProgressArranger : CustomArranger() { - override fun instance(): TreasuresProgress { - val instance = super.instance() - val treasureDescription = some() - instance.routeName = someString() - instance.collect(treasureDescription) - instance.collect(some()) - instance.addCommemorativePhoto(treasureDescription, someString()) -// instance.hunterPath.addLocation(Coordinates(some() % 180, some() % 90)) - return instance - } -} \ No newline at end of file diff --git a/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModelTest.kt b/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModelTest.kt index edc7566..76133eb 100644 --- a/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModelTest.kt +++ b/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/screen/main/MainViewModelTest.kt @@ -1,6 +1,7 @@ package pl.marianjureczko.poszukiwacz.screen.main import android.content.res.Resources +import com.ocadotechnology.gembus.test.some import com.ocadotechnology.gembus.test.someString import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Assertions.assertFalse @@ -11,6 +12,7 @@ import org.mockito.Mockito.mock import pl.marianjureczko.poszukiwacz.R import pl.marianjureczko.poszukiwacz.TestStoragePort import pl.marianjureczko.poszukiwacz.eq +import pl.marianjureczko.poszukiwacz.model.Route import pl.marianjureczko.poszukiwacz.shared.GoToTreasureEditor class MainViewModelTest { @@ -67,13 +69,13 @@ class MainViewModelTest { fun shouldSetShowDialogAndPromptTextInState_whenRequestingOpeningDeleteConfirmationDialog() { //given val viewModel = MainViewModel(storage, resources) - val routeName = someString() + val route = some() val expectedPrompt = someString() - given(resources.getString(eq(R.string.route_remove_prompt), eq(routeName))) + given(resources.getString(eq(R.string.route_remove_prompt), eq(route.name))) .willReturn(expectedPrompt) //when - viewModel.openConfirmDeleteDialog(routeName) + viewModel.openConfirmDeleteDialog(route) //then val actual = viewModel.state.value.deleteConfirmation diff --git a/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/usecase/AddTreasureDescriptionToRouteTest.kt b/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/usecase/AddTreasureDescriptionToRouteTest.kt index 2f23c7e..3a59871 100644 --- a/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/usecase/AddTreasureDescriptionToRouteTest.kt +++ b/app/src/testClassic/java/pl/marianjureczko/poszukiwacz/usecase/AddTreasureDescriptionToRouteTest.kt @@ -3,11 +3,9 @@ package pl.marianjureczko.poszukiwacz.usecase import com.ocadotechnology.gembus.test.some import com.ocadotechnology.gembus.test.someString import org.assertj.core.api.Assertions.assertThat - import org.junit.jupiter.api.Test import pl.marianjureczko.poszukiwacz.TestStoragePort import pl.marianjureczko.poszukiwacz.model.Route -import pl.marianjureczko.poszukiwacz.model.RouteArranger import pl.marianjureczko.poszukiwacz.model.TreasureDescription import pl.marianjureczko.poszukiwacz.shared.Coordinates import pl.marianjureczko.poszukiwacz.testhelpers.assertRouteContainsTreasureWith @@ -40,7 +38,7 @@ class AddTreasureDescriptionToRouteTest { fun shouldAddTreasureDescription_whenAddingSecondTreasureToRoute() { //given val treasure = some() - val route = RouteArranger.saveWithTreasureDescription(treasure, storage) + val route = pl.marianjureczko.poszukiwacz.model.RouteArranger.saveWithTreasureDescription(treasure, storage) //when val actualRoute = useCase(route, coordinates) diff --git a/app/src/test/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt b/app/src/testShared/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt similarity index 93% rename from app/src/test/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt rename to app/src/testShared/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt index 6013767..f0dedc8 100644 --- a/app/src/test/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt +++ b/app/src/testShared/java/pl/marianjureczko/poszukiwacz/TestStoragePort.kt @@ -19,6 +19,12 @@ class TestStoragePort : StorageHelper(mock()) { routes[route.name] = route } + fun clear() { + routes.clear() + requestedTipRemovals.clear() + progresses.clear() + } + override fun loadAll(): MutableList = routes.values.toMutableList() override fun loadRoute(name: String): Route = routes[name]!! override fun save(route: Route) { diff --git a/app/src/testShared/resources/arranger.properties b/app/src/testShared/resources/arranger.properties new file mode 100644 index 0000000..ed1daa4 --- /dev/null +++ b/app/src/testShared/resources/arranger.properties @@ -0,0 +1,2 @@ +arranger.root=pl.marianjureczko.poszukiwacz +arranger.android.customArrangers=pl.marianjureczko.poszukiwacz.model.HunterPathArranger, pl.marianjureczko.poszukiwacz.model.RouteArranger, pl.marianjureczko.poszukiwacz.model.TreasuresProgressArranger diff --git a/build.gradle b/build.gradle index 43e26fe..abdf764 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,7 @@ buildscript { allprojects { repositories { + mavenLocal() google() jcenter() mavenCentral()