diff --git a/app/build.gradle.kts b/app/build.gradle.kts index d7498ac217..4eb0640cee 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -40,6 +40,13 @@ android { defaultConfig { applicationId = BuildConfig.packageName vectorDrawables.useSupportLibrary = true + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunnerArguments["androidx.test.orchestrator.ENABLE"] = "true" + } + + testOptions { + execution = "ANDROIDX_TEST_ORCHESTRATOR" } androidResources { @@ -262,6 +269,8 @@ dependencies { testImplementation(projects.testing.unit) testImplementation(libs.core.tests.anroidx.arch) androidTestImplementation(projects.testing.android) + + androidTestImplementation(libs.tests.androidx.test.runner) } //sentry { diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/CleanupTest.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/CleanupTest.kt new file mode 100644 index 0000000000..f773f451dc --- /dev/null +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/CleanupTest.kt @@ -0,0 +1,38 @@ +package com.itsaky.androidide + +import android.app.Instrumentation +import android.os.ParcelFileDescriptor +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.AfterClass +import org.junit.Test +import java.io.FileInputStream +import java.io.IOException + +class CleanupTest { + companion object { + @JvmStatic + @AfterClass + fun cleanup() { + try { + val command = "rm -rf /sdcard/AndroidIDEProjects" + executeShellCommand(command) + println("Cleanup executed successfully!") + } catch (e: IOException) { + println("Failed to execute cleanup") + e.printStackTrace() + } + } + + private fun executeShellCommand(command: String): String { + val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + val pfd: ParcelFileDescriptor = instrumentation.uiAutomation.executeShellCommand(command) + val fileInputStream = FileInputStream(pfd.fileDescriptor) + return fileInputStream.bufferedReader().use { it.readText() } + } + } + + @Test + fun dummyTest() { + // This test ensures the class is executed + } +} diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/OrderedTestSuite.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/OrderedTestSuite.kt new file mode 100644 index 0000000000..16ad3a8b43 --- /dev/null +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/OrderedTestSuite.kt @@ -0,0 +1,14 @@ +package com.itsaky.androidide + +import org.junit.runner.RunWith +import org.junit.runners.Suite + +@RunWith(Suite::class) +@Suite.SuiteClasses( + WelcomeScreenTest::class, + PermissionsScreenTest::class, + ProjectBuildTestWithGroovyGradle::class, + ProjectBuildTestWithKtsGradle::class, + CleanupTest::class +) +class OrderedTestSuite \ No newline at end of file diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/PermissionsScreenTest.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/PermissionsScreenTest.kt index 7d331b71ed..cff1f3cc8f 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/PermissionsScreenTest.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/PermissionsScreenTest.kt @@ -2,11 +2,13 @@ package com.itsaky.androidide import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled import androidx.test.ext.junit.rules.activityScenarioRule +import androidx.test.platform.app.InstrumentationRegistry import com.itsaky.androidide.activities.SplashActivity import com.itsaky.androidide.screens.OnboardingScreen import com.itsaky.androidide.screens.PermissionScreen import com.itsaky.androidide.screens.SystemPermissionsScreen import com.kaspersky.kaspresso.testcases.api.testcase.TestCase +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Rule import org.junit.Test @@ -16,6 +18,11 @@ class PermissionsScreenTest : TestCase() { @get:Rule val activityRule = activityScenarioRule() + @After + fun cleanUp() { + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand("pm clear ${BuildConfig.APPLICATION_ID} && pm reset-permissions ${BuildConfig.APPLICATION_ID}") + } + @Test fun test_permissionsScreen_greenCheckMarksAppearCorrectly() = run { step("Click continue button on the Welcome Screen") { diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithGroovyGradle.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithGroovyGradle.kt index 95def227fe..a28255a24e 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithGroovyGradle.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithGroovyGradle.kt @@ -1,13 +1,18 @@ package com.itsaky.androidide import androidx.test.ext.junit.rules.activityScenarioRule +import androidx.test.platform.app.InstrumentationRegistry import com.itsaky.androidide.activities.SplashActivity +import com.itsaky.androidide.helper.initializeProjectAndCancelBuild +import com.itsaky.androidide.helper.navigateToMainScreen +import com.itsaky.androidide.helper.selectProjectTemplate import com.itsaky.androidide.screens.HomeScreen.clickCreateProjectHomeScreen import com.itsaky.androidide.screens.ProjectSettingsScreen.clickCreateProjectProjectSettings import com.itsaky.androidide.screens.ProjectSettingsScreen.selectJavaLanguage import com.itsaky.androidide.screens.ProjectSettingsScreen.selectKotlinLanguage -import com.itsaky.androidide.screens.ProjectSettingsScreen.uncheckeKotlinScript +import com.itsaky.androidide.screens.ProjectSettingsScreen.uncheckKotlinScript import com.kaspersky.kaspresso.testcases.api.testcase.TestCase +import org.junit.After import org.junit.Rule import org.junit.Test @@ -16,6 +21,11 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { @get:Rule val activityRule = activityScenarioRule() + @After + fun cleanUp() { + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand("pm clear ${BuildConfig.APPLICATION_ID} && pm reset-permissions ${BuildConfig.APPLICATION_ID}") + } + @Test fun test_projectBuild_emptyProject_java_groovyGradle() { run { @@ -26,7 +36,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_empty ) selectJavaLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -42,7 +52,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_empty ) selectKotlinLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -58,7 +68,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_basic ) selectJavaLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -74,7 +84,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_basic ) selectKotlinLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -90,7 +100,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_navigation_drawer ) selectJavaLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -106,7 +116,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_navigation_drawer ) selectKotlinLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -122,7 +132,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_navigation_tabs ) selectJavaLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -138,7 +148,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_navigation_tabs ) selectKotlinLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -154,7 +164,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_tabs ) selectJavaLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -170,7 +180,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_tabs ) selectKotlinLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -186,7 +196,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_no_AndroidX ) selectJavaLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } @@ -202,7 +212,7 @@ class ProjectBuildTestWithGroovyGradle : TestCase() { R.string.template_no_AndroidX ) selectKotlinLanguage() - uncheckeKotlinScript() + uncheckKotlinScript() clickCreateProjectProjectSettings() initializeProjectAndCancelBuild() } diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithKtsGradle.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithKtsGradle.kt index 50d893cb83..2eb6fa3feb 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithKtsGradle.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildTestWithKtsGradle.kt @@ -1,12 +1,17 @@ package com.itsaky.androidide import androidx.test.ext.junit.rules.activityScenarioRule +import androidx.test.platform.app.InstrumentationRegistry import com.itsaky.androidide.activities.SplashActivity +import com.itsaky.androidide.helper.initializeProjectAndCancelBuild +import com.itsaky.androidide.helper.navigateToMainScreen +import com.itsaky.androidide.helper.selectProjectTemplate import com.itsaky.androidide.screens.HomeScreen.clickCreateProjectHomeScreen import com.itsaky.androidide.screens.ProjectSettingsScreen.clickCreateProjectProjectSettings import com.itsaky.androidide.screens.ProjectSettingsScreen.selectJavaLanguage import com.itsaky.androidide.screens.ProjectSettingsScreen.selectKotlinLanguage import com.kaspersky.kaspresso.testcases.api.testcase.TestCase +import org.junit.After import org.junit.Rule import org.junit.Test @@ -15,6 +20,11 @@ class ProjectBuildTestWithKtsGradle : TestCase() { @get:Rule val activityRule = activityScenarioRule() + @After + fun cleanUp() { + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand("pm clear ${BuildConfig.APPLICATION_ID} && pm reset-permissions ${BuildConfig.APPLICATION_ID}") + } + @Test fun test_projectBuild_emptyProject_java() { run { diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/WelcomeScreenTest.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/WelcomeScreenTest.kt index 76c943b6dd..f252e4eb49 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/WelcomeScreenTest.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/WelcomeScreenTest.kt @@ -1,9 +1,11 @@ package com.itsaky.androidide import androidx.test.ext.junit.rules.activityScenarioRule +import androidx.test.platform.app.InstrumentationRegistry import com.itsaky.androidide.activities.SplashActivity import com.itsaky.androidide.screens.OnboardingScreen import com.kaspersky.kaspresso.testcases.api.testcase.TestCase +import org.junit.After import org.junit.Rule import org.junit.Test @@ -12,6 +14,11 @@ class WelcomeScreenTest : TestCase() { @get:Rule val activityRule = activityScenarioRule() + @After + fun cleanUp() { + InstrumentationRegistry.getInstrumentation().uiAutomation.executeShellCommand("pm clear ${BuildConfig.APPLICATION_ID} && pm reset-permissions ${BuildConfig.APPLICATION_ID}") + } + @Test fun test_welcomeScreen_itemsAppearCorrectly() { OnboardingScreen { diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildHelper.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/helper/ProjectBuildHelper.kt similarity index 95% rename from app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildHelper.kt rename to app/src/androidTest/kotlin/com/itsaky/androidide/helper/ProjectBuildHelper.kt index d54fc41aad..b70af9c4fe 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/ProjectBuildHelper.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/helper/ProjectBuildHelper.kt @@ -1,4 +1,4 @@ -package com.itsaky.androidide +package com.itsaky.androidide.helper import com.itsaky.androidide.scenarios.InitializationProjectAndCancelingBuildScenario import com.itsaky.androidide.scenarios.NavigateToMainScreenScenario diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt index 1da6fabcd0..1275b0b097 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/InitializationProjectAndCancelingBuildScenario.kt @@ -30,7 +30,7 @@ class InitializationProjectAndCancelingBuildScenario : Scenario() { } } step("Wait for the green button") { - flakySafely(180000) { + flakySafely(240000) { KView { withText(R.string.msg_project_initialized) }.isVisible() @@ -47,11 +47,11 @@ class InitializationProjectAndCancelingBuildScenario : Scenario() { } } step("Confirm that the install dialog appears and click cancel") { - flakySafely(180000) { + flakySafely(240000) { val installDialog = device.uiDevice.findObject(UiSelector().text("Do you want to install this app?")) val cancelButton = device.uiDevice.findObject(UiSelector().text("Cancel")) - if (installDialog.waitForExists(120000)) { + if (installDialog.waitForExists(180000)) { installDialog.exists() cancelButton.click() } else { diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/NavigateToMainScreenScenario.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/NavigateToMainScreenScenario.kt index 7b41f21438..aef858ea29 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/NavigateToMainScreenScenario.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/scenarios/NavigateToMainScreenScenario.kt @@ -1,5 +1,6 @@ package com.itsaky.androidide.scenarios +import androidx.test.uiautomator.UiSelector import com.itsaky.androidide.screens.InstallToolsScreen import com.itsaky.androidide.screens.OnboardingScreen import com.itsaky.androidide.screens.PermissionScreen @@ -17,42 +18,49 @@ class NavigateToMainScreenScenario : Scenario() { click() } } - step("Grant storage and install packages permissions") { - PermissionScreen { - rvPermissions { - childAt(0) { - grantButton.click() + val permissionsScreen = device.uiDevice.findObject(UiSelector().text("Permissions")) + + if (permissionsScreen.exists()) { + step("Grant storage and install packages permissions") { + PermissionScreen { + rvPermissions { + childAt(0) { + grantButton.click() + } } - } - SystemPermissionsScreen { - storagePermissionView { - click() + SystemPermissionsScreen { + storagePermissionView { + click() + } } - } - device.uiDevice.pressBack() + device.uiDevice.pressBack() - rvPermissions { - childAt(1) { - grantButton.click() + rvPermissions { + childAt(1) { + grantButton.click() + } } - } - SystemPermissionsScreen { - installPackagesPermission { - click() + SystemPermissionsScreen { + installPackagesPermission { + click() + } } - } - device.uiDevice.pressBack() - } - OnboardingScreen.nextButton { - isVisible() - isClickable() - click() + device.uiDevice.pressBack() + } + OnboardingScreen.nextButton { + isVisible() + isClickable() + click() + } } + } else { + println("skip permissions") } + step("Click continue button on the Install Tools Screen") { InstallToolsScreen.doneButton { isVisible() diff --git a/app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt b/app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt index eb95799c07..ee5201ef20 100644 --- a/app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt +++ b/app/src/androidTest/kotlin/com/itsaky/androidide/screens/ProjectSettingsScreen.kt @@ -73,7 +73,7 @@ object ProjectSettingsScreen : KScreen() { } } - fun TestContext.uncheckeKotlinScript() { + fun TestContext.uncheckKotlinScript() { step("Unselect Kotlin Script for Gradle") { kotlinScriptText { setChecked(false)