diff --git a/.github/workflows/android.yml b/.github/workflows/Main.yml similarity index 95% rename from .github/workflows/android.yml rename to .github/workflows/Main.yml index e29ab87b..5615a79d 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/Main.yml @@ -1,14 +1,9 @@ -name: Android CI +name: Android Main CI on: push: branches: - main - - dev - paths-ignore: - - '**.md' - - 'media/**' - pull_request: paths-ignore: - '**.md' - 'media/**' diff --git a/.github/workflows/PR.yml b/.github/workflows/PR.yml new file mode 100644 index 00000000..40342fdb --- /dev/null +++ b/.github/workflows/PR.yml @@ -0,0 +1,53 @@ +name: Android PR CI + +on: + pull_request: + paths-ignore: + - '**.md' + - 'media/**' + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2.5.0 + + - name: set up JDK 17 + uses: actions/setup-java@v3.6.0 + with: + java-version: '17' + distribution: 'adopt' + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2.4.2 + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew assembleDebug testDebugUnitTest jvmTest + + - name: Verify database migration + run: chmod +x ./scripts/sqldelight/verifyDatabaseMigration && ./scripts/sqldelight/verifyDatabaseMigration + + - name: Upload the test report + if: failure() + uses: actions/upload-artifact@v3.1.1 + with: + name: TestReport + path: | + feature-monster-compendium/build/reports/tests + data/build/reports/tests + domain/build/reports/tests + + - name: Upload an APK + uses: actions/upload-artifact@v3.1.1 + with: + name: app-debug.apk + path: app/build/outputs/apk/debug/app-debug.apk + + - name: Upload an APK metadata JSON + uses: actions/upload-artifact@v3.1.1 + with: + name: output-metadata.json + path: app/build/outputs/apk/debug/output-metadata.json diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bbb20a26..082ef8a2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -143,4 +143,7 @@ dependencies { implementation(libs.koin.compose) testImplementation(libs.bundles.unittest) testImplementation(libs.koin.test) + androidTestImplementation(libs.bundles.instrumentedtest) + androidTestImplementation(libs.compose.ui.test) + debugImplementation(libs.compose.ui.test.manifest) } diff --git a/app/src/androidTest/kotlin/br/alexandregpereira/hunter/app/folder/FolderListTest.kt b/app/src/androidTest/kotlin/br/alexandregpereira/hunter/app/folder/FolderListTest.kt new file mode 100644 index 00000000..96970999 --- /dev/null +++ b/app/src/androidTest/kotlin/br/alexandregpereira/hunter/app/folder/FolderListTest.kt @@ -0,0 +1,47 @@ +package br.alexandregpereira.hunter.app.folder + +import androidx.compose.ui.test.assertIsNotDisplayed +import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.longClick +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.onRoot +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.printToLog +import androidx.test.espresso.Espresso +import br.alexandregpereira.hunter.app.MainScreen +import org.junit.Rule +import org.junit.Test + +class FolderListTest { + + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun folderDeletion() { + composeTestRule.setContent { + MainScreen() + } + + composeTestRule.waitUntil(timeoutMillis = 5000) { + composeTestRule.onNodeWithText("Aboleth").isDisplayed() + } + composeTestRule.onNodeWithText("Aboleth").performClick() + composeTestRule.waitUntil(timeoutMillis = 5000) { + composeTestRule.onNodeWithContentDescription("MonsterOptions").isDisplayed() + } + composeTestRule.onNodeWithContentDescription("MonsterOptions").performClick() + composeTestRule.onNodeWithText("Add to Folder").performClick() + composeTestRule.onNodeWithText("Folder name").performTextInput("Folder Test") + composeTestRule.onNodeWithText("Save").performClick() + Espresso.pressBack() + composeTestRule.onNodeWithText("Folders").performClick() + composeTestRule.onNodeWithText("Folder Test").performTouchInput { longClick() } + composeTestRule.onNodeWithText("Delete").performClick() + composeTestRule.onNodeWithText("Folder Test").assertIsNotDisplayed() + } +} \ No newline at end of file diff --git a/feature/folder-list/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/folder/list/FolderListStateHolder.kt b/feature/folder-list/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/folder/list/FolderListStateHolder.kt index bb04f1df..5d7b9b52 100644 --- a/feature/folder-list/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/folder/list/FolderListStateHolder.kt +++ b/feature/folder-list/state-holder/src/commonMain/kotlin/br/alexandregpereira/hunter/folder/list/FolderListStateHolder.kt @@ -82,14 +82,16 @@ class FolderListStateHolder internal constructor( fun onItemSelectionDeleteClick() { analytics.trackItemSelectionDeleteClick() - onItemSelectionClose() flowOf(state.value.itemSelection) .map { it.toList() } .map { folderNames -> removeMonsterFolders(folderNames).collect() } .flowOn(dispatcher) - .onEach { loadMonsterFolders() } + .onEach { + onItemSelectionClose() + loadMonsterFolders() + } .launchIn(scope) } diff --git a/feature/monster-detail/android/src/main/kotlin/br/alexandregpereira/hunter/detail/ui/MonsterTitleCompose.kt b/feature/monster-detail/android/src/main/kotlin/br/alexandregpereira/hunter/detail/ui/MonsterTitleCompose.kt index 6f2163e7..4a3f6f6b 100644 --- a/feature/monster-detail/android/src/main/kotlin/br/alexandregpereira/hunter/detail/ui/MonsterTitleCompose.kt +++ b/feature/monster-detail/android/src/main/kotlin/br/alexandregpereira/hunter/detail/ui/MonsterTitleCompose.kt @@ -29,6 +29,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clipToBounds +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp @@ -77,7 +79,7 @@ fun MonsterTitleCompose( end = contentPadding.calculateEndPadding(LayoutDirection.Rtl), top = contentPadding.calculateTopPadding(), bottom = contentPadding.calculateBottomPadding() - ), + ).semantics { contentDescription = "MonsterOptions" }, onOptionsClicked ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ee17357d..8f872fad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,6 +35,8 @@ coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil_compose compose-activity = { module = "androidx.activity:activity-compose", version.ref = "compose_activity" } compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" } compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } +compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" } +compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest", version.ref = "compose" } compose-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } compose-util = { module = "androidx.compose.ui:ui-util", version.ref = "compose" }