diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c20c2e9..94d9ae4 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,19 +1,16 @@
plugins {
- androidApplication
+ androidLibrary
+ kotlinAndroid
}
-val packageName = "ru.myitschool.work"
+val packageName = "ru.innovationcampus.test-lib"
android {
namespace = packageName
compileSdk = Version.Android.Sdk.compile
defaultConfig {
- applicationId = packageName
minSdk = Version.Android.Sdk.min
- targetSdk = Version.Android.Sdk.target
- versionCode = 1
- versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -25,5 +22,10 @@ android {
}
dependencies {
- defaultLibrary()
+ api(Dependencies.Kaspresso.core)
+ api(Dependencies.Kaspresso.composeSupport)
+ api(Dependencies.AndroidX.Testing.junit)
+ api(Dependencies.AndroidX.Testing.compose)
+ api(Dependencies.AndroidX.Testing.orchestrator)
+ api(Dependencies.truth)
}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
deleted file mode 100644
index 481bb43..0000000
--- a/app/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 9ee5c40..0000000
--- a/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/java/ru/innovationcampus/test/core/core/BaseTest.kt b/app/src/main/java/ru/innovationcampus/test/core/core/BaseTest.kt
new file mode 100644
index 0000000..247d668
--- /dev/null
+++ b/app/src/main/java/ru/innovationcampus/test/core/core/BaseTest.kt
@@ -0,0 +1,83 @@
+package ru.innovationcampus.test.core.core
+
+import android.app.Activity
+import android.app.Instrumentation
+import android.content.Context
+import android.content.Intent
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.core.app.ActivityScenario
+import androidx.test.espresso.Espresso
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.kaspersky.components.composesupport.config.addComposeSupport
+import com.kaspersky.kaspresso.idlewaiting.KautomatorWaitForIdleSettings
+import com.kaspersky.kaspresso.kaspresso.Kaspresso
+import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
+import com.kaspersky.kaspresso.testcases.core.testcontext.TestContext
+import java.util.Locale
+import org.junit.After
+import org.junit.Before
+import org.junit.FixMethodOrder
+import org.junit.Rule
+import org.junit.runners.MethodSorters
+import ru.innovationcampus.test.core.utils.ResultTestsData
+
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class BaseTest(
+ private val clazz: Class
+) : TestCase(kaspressoBuilder = Kaspresso.Builder.simple {
+ kautomatorWaitForIdleSettings = KautomatorWaitForIdleSettings.boost()
+}.apply { addComposeSupport() }) {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ protected lateinit var activityScenario: ActivityScenario
+ private set
+ protected lateinit var uiDevice: UiDevice
+ private set
+ protected lateinit var appContext: Context
+ private set
+ protected lateinit var instrumentation: Instrumentation
+ private set
+
+ open fun beforeTest() {}
+
+ @Before
+ fun setUp() {
+ instrumentation = InstrumentationRegistry.getInstrumentation()
+ val handler = DescriptionFailureHandler(instrumentation)
+ Espresso.setFailureHandler(handler)
+
+ uiDevice = UiDevice.getInstance(instrumentation)
+ uiDevice.pressHome()
+ Thread.sleep(1_000)
+
+ val nonLocalizedContext = instrumentation.targetContext
+ val configuration = nonLocalizedContext.resources.configuration
+ configuration.setLocale(Locale.UK)
+ appContext = nonLocalizedContext.createConfigurationContext(configuration)
+
+ val intent = Intent(appContext, clazz)
+
+ activityScenario = ActivityScenario.launch(intent)
+
+ beforeTest()
+ }
+
+ @After
+ fun reset() {
+ uiDevice.pressHome()
+ }
+
+ fun runWithInit(grade: Int, test: TestContext.() -> Unit) = run {
+ ResultTestsData.setupTest(grade)
+ try {
+ test.invoke(this)
+ ResultTestsData.successTest(grade)
+ } catch (e: Exception) {
+ ResultTestsData.putResult(instrumentation, uiDevice)
+ throw AssertionError(e.message)
+ }
+ ResultTestsData.putResult(instrumentation, uiDevice)
+ }
+}
diff --git a/app/src/main/java/ru/innovationcampus/test/core/core/DescriptionFailureHandler.kt b/app/src/main/java/ru/innovationcampus/test/core/core/DescriptionFailureHandler.kt
new file mode 100644
index 0000000..3cc070c
--- /dev/null
+++ b/app/src/main/java/ru/innovationcampus/test/core/core/DescriptionFailureHandler.kt
@@ -0,0 +1,31 @@
+package ru.innovationcampus.test.core.core
+
+import android.app.Instrumentation
+import android.view.View
+import androidx.test.espresso.FailureHandler
+import androidx.test.espresso.base.DefaultFailureHandler
+import org.hamcrest.Matcher
+import kotlin.math.min
+
+class DescriptionFailureHandler(instrumentation: Instrumentation) : FailureHandler {
+ var extraMessage = ""
+ private var delegate: DefaultFailureHandler =
+ DefaultFailureHandler(instrumentation.targetContext)
+
+ override fun handle(error: Throwable?, viewMatcher: Matcher?) {
+ // Log anything you want here
+ if (error != null) {
+ val newError = Throwable(
+
+ "$extraMessage " + error.message?.substring(
+ 0, min(
+ 1_700, error.message?.length ?: 0
+ )
+ ) + "...", error.cause
+ )
+
+ // Then delegate the error handling to the default handler which will throw an exception
+ delegate.handle(newError, viewMatcher)
+ }
+ }
+}
diff --git a/app/src/main/java/ru/innovationcampus/test/core/utils/ResultTestsData.kt b/app/src/main/java/ru/innovationcampus/test/core/utils/ResultTestsData.kt
new file mode 100644
index 0000000..25b45df
--- /dev/null
+++ b/app/src/main/java/ru/innovationcampus/test/core/utils/ResultTestsData.kt
@@ -0,0 +1,48 @@
+package ru.innovationcampus.test.core.utils
+
+import android.app.Activity
+import android.app.Instrumentation
+import android.app.Instrumentation.ActivityResult
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+
+object ResultTestsData {
+ private var grade = 0
+ private var totalTests = 0
+ private var maxGrade = 0
+ private var passTests = 0
+
+ fun setupTest(grade: Int) {
+ totalTests++
+ maxGrade += grade
+ }
+
+ fun successTest(grade: Int) {
+ passTests++
+ ResultTestsData.grade += grade
+ }
+
+
+ fun putResult(
+ instrumentation: Instrumentation,
+ uiDevice: UiDevice
+ ) {
+ uiDevice.pressHome()
+
+ val results = Bundle()
+ results.putInt("passTests", passTests)
+ results.putInt("totalTests", totalTests)
+ results.putInt("grade", grade)
+ results.putInt("maxGrade", maxGrade)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ instrumentation.addResults(results)
+ } else {
+ instrumentation.sendStatus(Activity.RESULT_OK, results)
+ }
+ Log.d("Tests", "$passTests из $totalTests тестов пройдено.")
+ Log.d("Tests", "$grade из $maxGrade баллов получено.")
+ }
+}
diff --git a/app/src/main/java/ru/innovationcampus/test/core/utils/UiTextViewExtensions.kt b/app/src/main/java/ru/innovationcampus/test/core/utils/UiTextViewExtensions.kt
new file mode 100644
index 0000000..8cc4f86
--- /dev/null
+++ b/app/src/main/java/ru/innovationcampus/test/core/utils/UiTextViewExtensions.kt
@@ -0,0 +1,17 @@
+package ru.innovationcampus.test.core.utils
+
+import com.google.common.truth.Truth.assertThat
+import com.kaspersky.components.kautomator.component.text.UiTextViewAssertions
+import com.kaspersky.components.kautomator.intercept.operation.UiOperationType
+
+fun UiTextViewAssertions.checkRegexp(regexp: String) {
+ view.check(
+ object : UiOperationType {
+ override val name = "Check text by regexp"
+ },
+ description = ""
+ ) {
+ assertThat(text).isNotEmpty()
+ assertThat(text).matches(regexp)
+ }
+}
diff --git a/app/src/main/java/ru/innovationcampus/test/core/utils/UiViewBuilderExtensions.kt b/app/src/main/java/ru/innovationcampus/test/core/utils/UiViewBuilderExtensions.kt
new file mode 100644
index 0000000..7754a54
--- /dev/null
+++ b/app/src/main/java/ru/innovationcampus/test/core/utils/UiViewBuilderExtensions.kt
@@ -0,0 +1,10 @@
+package ru.innovationcampus.test.core.utils
+
+import com.kaspersky.components.kautomator.component.common.builders.UiViewBuilder
+
+/**
+ * Используем для поиска элемента по id для compose и обычной view
+ *
+ * @param resId универсальный идентификатор
+ */
+fun UiViewBuilder.withRes(resId: String) = withSelector { res(resId) }
diff --git a/app/src/main/java/ru/innovationcampus/test/core/utils/Utils.kt b/app/src/main/java/ru/innovationcampus/test/core/utils/Utils.kt
new file mode 100644
index 0000000..af54c41
--- /dev/null
+++ b/app/src/main/java/ru/innovationcampus/test/core/utils/Utils.kt
@@ -0,0 +1,24 @@
+package ru.innovationcampus.test.core.utils
+
+import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
+import io.github.kakaocup.compose.node.element.ComposeScreen
+import io.github.kakaocup.compose.node.element.KNode
+
+/**
+ * Функцию необходимо вызывать для прогрузки Compose экрана
+ * Перед каждым открытием экрана вызывайте эту функцию, иначе экраны,
+ * написанные на Compose могут не прогрузиться.
+ */
+fun warmUpCompose(
+ semanticsProvider: SemanticsNodeInteractionsProvider
+) = BlankScreen(semanticsProvider).waitLoadingScreen.assertDoesNotExist()
+
+private class BlankScreen(
+ semanticsProvider: SemanticsNodeInteractionsProvider
+) : ComposeScreen(
+ semanticsProvider = semanticsProvider,
+ viewBuilderAction = { hasTestTag("InvalidTag") }
+) {
+ val waitLoadingScreen: KNode
+ get() = child { hasTestTag("InvalidTag") }
+}
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 07d5da9..0000000
--- a/app/src/main/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index 2b068d1..0000000
--- a/app/src/main/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 6f3b755..0000000
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
deleted file mode 100644
index 6f3b755..0000000
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
deleted file mode 100644
index c209e78..0000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
deleted file mode 100644
index b2dfe3d..0000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
deleted file mode 100644
index 4f0f1d6..0000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
deleted file mode 100644
index 62b611d..0000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
deleted file mode 100644
index 948a307..0000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
deleted file mode 100644
index 1b9a695..0000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
deleted file mode 100644
index 28d4b77..0000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9287f50..0000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
deleted file mode 100644
index aa7d642..0000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
deleted file mode 100644
index 9126ae3..0000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
deleted file mode 100644
index f8c6127..0000000
--- a/app/src/main/res/values/colors.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- #FFBB86FC
- #FF6200EE
- #FF3700B3
- #FF03DAC5
- #FF018786
- #FF000000
- #FFFFFFFF
-
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
deleted file mode 100644
index 96034ac..0000000
--- a/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
- Work
-
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
deleted file mode 100644
index 89e63d4..0000000
--- a/app/src/main/res/values/themes.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml
deleted file mode 100644
index fa0f996..0000000
--- a/app/src/main/res/xml/backup_rules.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml
deleted file mode 100644
index 9ee9997..0000000
--- a/app/src/main/res/xml/data_extraction_rules.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 860f2c8..074c663 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- androidApplication version Version.gradle apply false
- kotlinJvm version Version.Kotlin.language apply false
-}
\ No newline at end of file
+ androidLibrary version Version.gradle apply false
+ kotlinAndroid version Version.Kotlin.language apply false
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 4da3ae4..74b7a13 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -13,5 +13,5 @@ dependencyResolutionManagement {
}
}
-rootProject.name = "Work"
+rootProject.name = "TestCore"
include(":app")