diff --git a/README.md b/README.md index 9ec770bf5..7afce517b 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Kaspresso is a great framework for UI testing. Based on [Espresso](https://developer.android.com/training/testing/espresso) and [UI Automator](https://developer.android.com/training/testing/ui-automator), Kaspresso provides a wide range of additional amazing features, such as: * 100% stability, no flakiness. -* *[WIP] Jetpack Compose support.* +* Jetpack Compose support [since version 1.4]. * Significantly faster execution of UI Automator commands. With Kaspresso, some UI Automator commands run **10 times faster**! * Excellent readability due to human DSL. @@ -252,7 +252,7 @@ dependencies { androidTestImplementation 'com.kaspersky.android-components:kaspresso:' # Allure support androidTestImplementation "com.kaspersky.android-components:kaspresso-allure-support:" - # Jetpack Compose support + # Jetpack Compose support (since version 1.4) androidTestImplementation "com.kaspersky.android-components:kaspresso-compose-support:" } ``` diff --git a/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt b/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt index fc3ec2536..c535ff951 100644 --- a/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt +++ b/compose-support/src/main/java/com/kaspersky/components/composesupport/config/ComposeConfig.kt @@ -3,7 +3,6 @@ package com.kaspersky.components.composesupport.config import com.kaspersky.components.composesupport.interceptors.behavior.SemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.behavior.impl.autoscroll.AutoScrollSemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.behavior.impl.elementloader.ElementLoaderSemanticsBehaviorInterceptor -import com.kaspersky.components.composesupport.interceptors.behavior.impl.failure.FailureLoggingSemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.behavior.impl.flakysafety.FlakySafeSemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.behavior.impl.systemsafety.SystemDialogSafetySemanticsBehaviorInterceptor import com.kaspersky.components.composesupport.interceptors.watcher.SemanticsWatcherInterceptor @@ -42,15 +41,13 @@ class ComposeConfig { adbServer ), ElementLoaderSemanticsBehaviorInterceptor(libLogger, elementLoaderParams), - FlakySafeSemanticsBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingSemanticsBehaviorInterceptor(libLogger) + FlakySafeSemanticsBehaviorInterceptor(flakySafetyParams, libLogger) ) } else { mutableListOf( AutoScrollSemanticsBehaviorInterceptor(libLogger, autoScrollParams), ElementLoaderSemanticsBehaviorInterceptor(libLogger, elementLoaderParams), - FlakySafeSemanticsBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingSemanticsBehaviorInterceptor(libLogger) + FlakySafeSemanticsBehaviorInterceptor(flakySafetyParams, libLogger) ) } } diff --git a/compose-support/src/main/java/com/kaspersky/components/composesupport/interceptors/behavior/impl/failure/FailureLoggingSemanticsBehaviorInterceptor.kt b/compose-support/src/main/java/com/kaspersky/components/composesupport/interceptors/behavior/impl/failure/FailureLoggingSemanticsBehaviorInterceptor.kt index 7ce1c44d3..0397f762f 100644 --- a/compose-support/src/main/java/com/kaspersky/components/composesupport/interceptors/behavior/impl/failure/FailureLoggingSemanticsBehaviorInterceptor.kt +++ b/compose-support/src/main/java/com/kaspersky/components/composesupport/interceptors/behavior/impl/failure/FailureLoggingSemanticsBehaviorInterceptor.kt @@ -12,8 +12,9 @@ import io.github.kakaocup.compose.intercept.operation.ComposeAssertion * The implementation of [SemanticsBehaviorInterceptor] and [FailureLoggingProvider] interfaces. * Provides failure logging functionality for [ComposeInteraction.perform] and [ComposeInteraction.check] calls. * - * By default, this interceptor is not used in Kaspresso. - * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly + * Important! + * By default, the interceptor is not used in Kaspresso because this one pollutes logs by error messages that may confuse a user. + * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly. */ class FailureLoggingSemanticsBehaviorInterceptor( logger: UiTestLogger diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingDataBehaviorInterceptor.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingDataBehaviorInterceptor.kt index e04816c9c..b3c1db6e4 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingDataBehaviorInterceptor.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingDataBehaviorInterceptor.kt @@ -10,8 +10,9 @@ import com.kaspersky.kaspresso.logger.UiTestLogger * The implementation of [DataBehaviorInterceptor] and [FailureLoggingProvider] interfaces. * Provides failure logging functionality for [DataInteraction.check] calls. * - * By default, this interceptor is not used in Kaspresso. - * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly + * Important! + * By default, the interceptor is not used in Kaspresso because this one pollutes logs by error messages that may confuse a user. + * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly. */ class FailureLoggingDataBehaviorInterceptor( logger: UiTestLogger diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingViewBehaviorInterceptor.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingViewBehaviorInterceptor.kt index 2a3070c6f..d80ac5b32 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingViewBehaviorInterceptor.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingViewBehaviorInterceptor.kt @@ -10,8 +10,9 @@ import com.kaspersky.kaspresso.logger.UiTestLogger * The implementation of [ViewBehaviorInterceptor] and [FailureLoggingProvider] interfaces. * Provides failure logging functionality for [ViewInteraction.perform] and [ViewInteraction.check] calls. * - * By default, this interceptor is not used in Kaspresso. - * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly + * Important! + * By default, the interceptor is not used in Kaspresso because this one pollutes logs by error messages that may confuse a user. + * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly. */ class FailureLoggingViewBehaviorInterceptor( logger: UiTestLogger diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingWebBehaviorInterceptor.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingWebBehaviorInterceptor.kt index 6c6263933..a3c865de7 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingWebBehaviorInterceptor.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behavior/impl/failure/FailureLoggingWebBehaviorInterceptor.kt @@ -10,8 +10,9 @@ import com.kaspersky.kaspresso.logger.UiTestLogger * The implementation of [WebBehaviorInterceptor] and [FailureLoggingProvider] interfaces. * Provides failure logging functionality for [Web.WebInteraction.perform] and [Web.WebInteraction.check] calls. * - * By default, this interceptor is not used in Kaspresso. - * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly + * Important! + * By default, the interceptor is not used in Kaspresso because this one pollutes logs by error messages that may confuse a user. + * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly. */ class FailureLoggingWebBehaviorInterceptor( logger: UiTestLogger diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingDeviceBehaviorInterceptor.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingDeviceBehaviorInterceptor.kt index 69694a166..ea74d76a1 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingDeviceBehaviorInterceptor.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingDeviceBehaviorInterceptor.kt @@ -12,8 +12,9 @@ import com.kaspersky.kaspresso.logger.UiTestLogger * The implementation of [DeviceBehaviorInterceptor] and [FailureLoggingProvider] interfaces. * Provides failure logging functionality for [UiDeviceInteraction.perform] and [UiDeviceInteraction.check] calls. * - * By default, this interceptor is not used in Kaspresso. - * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly + * Important! + * By default, the interceptor is not used in Kaspresso because this one pollutes logs by error messages that may confuse a user. + * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly. */ class FailureLoggingDeviceBehaviorInterceptor( logger: UiTestLogger diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingObjectBehaviorInterceptor.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingObjectBehaviorInterceptor.kt index f160cc171..4c2ca5489 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingObjectBehaviorInterceptor.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/interceptors/behaviorkautomator/impl/failure/FailureLoggingObjectBehaviorInterceptor.kt @@ -12,8 +12,9 @@ import com.kaspersky.kaspresso.logger.UiTestLogger * The implementation of [ObjectBehaviorInterceptor] and [FailureLoggingProvider] interfaces. * Provides failure logging functionality for [UiObjectInteraction.perform] and [UiObjectInteraction.check] calls. * - * By default, this interceptor is not used in Kaspresso. - * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly + * Important! + * By default, the interceptor is not used in Kaspresso because this one pollutes logs by error messages that may confuse a user. + * If you desire to change result log (especially in case of an error) we recommend to use [FailureLoggingProvider] directly. */ class FailureLoggingObjectBehaviorInterceptor( logger: UiTestLogger diff --git a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt index 420323249..3e48532fb 100644 --- a/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt +++ b/kaspresso/src/main/kotlin/com/kaspersky/kaspresso/kaspresso/Kaspresso.kt @@ -71,9 +71,6 @@ import com.kaspersky.kaspresso.interceptors.behavior.ViewBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behavior.WebBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behavior.impl.autoscroll.AutoScrollViewBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behavior.impl.autoscroll.AutoScrollWebBehaviorInterceptor -import com.kaspersky.kaspresso.interceptors.behavior.impl.failure.FailureLoggingDataBehaviorInterceptor -import com.kaspersky.kaspresso.interceptors.behavior.impl.failure.FailureLoggingViewBehaviorInterceptor -import com.kaspersky.kaspresso.interceptors.behavior.impl.failure.FailureLoggingWebBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behavior.impl.flakysafety.FlakySafeDataBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behavior.impl.flakysafety.FlakySafeViewBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behavior.impl.flakysafety.FlakySafeWebBehaviorInterceptor @@ -84,8 +81,6 @@ import com.kaspersky.kaspresso.interceptors.behaviorkautomator.DeviceBehaviorInt import com.kaspersky.kaspresso.interceptors.behaviorkautomator.ObjectBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.autoscroll.AutoScrollObjectBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.elementloader.ElementLoaderObjectBehaviorInterceptor -import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.failure.FailureLoggingDeviceBehaviorInterceptor -import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.failure.FailureLoggingObjectBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.flakysafety.FlakySafeDeviceBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.flakysafety.FlakySafeObjectBehaviorInterceptor import com.kaspersky.kaspresso.interceptors.behaviorkautomator.impl.systemsafety.SystemDialogSafetyDeviceBehaviorInterceptor @@ -832,12 +827,10 @@ data class Kaspresso( instrumentalDependencyProviderFactory.getInterceptorProvider(instrumentation), adbServer ), - FlakySafeViewBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingViewBehaviorInterceptor(libLogger) + FlakySafeViewBehaviorInterceptor(flakySafetyParams, libLogger) ) else mutableListOf( AutoScrollViewBehaviorInterceptor(autoScrollParams, libLogger), - FlakySafeViewBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingViewBehaviorInterceptor(libLogger) + FlakySafeViewBehaviorInterceptor(flakySafetyParams, libLogger) ) if (!::dataBehaviorInterceptors.isInitialized) dataBehaviorInterceptors = @@ -847,11 +840,9 @@ data class Kaspresso( instrumentalDependencyProviderFactory.getInterceptorProvider(instrumentation), adbServer ), - FlakySafeDataBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingDataBehaviorInterceptor(libLogger) + FlakySafeDataBehaviorInterceptor(flakySafetyParams, libLogger) ) else mutableListOf( - FlakySafeDataBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingDataBehaviorInterceptor(libLogger) + FlakySafeDataBehaviorInterceptor(flakySafetyParams, libLogger) ) if (!::webBehaviorInterceptors.isInitialized) webBehaviorInterceptors = @@ -863,14 +854,12 @@ data class Kaspresso( instrumentalDependencyProviderFactory.getInterceptorProvider(instrumentation), adbServer ), - FlakySafeWebBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingWebBehaviorInterceptor(libLogger) + FlakySafeWebBehaviorInterceptor(flakySafetyParams, libLogger) ) } else { mutableListOf( AutoScrollWebBehaviorInterceptor(autoScrollParams, libLogger), - FlakySafeWebBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingWebBehaviorInterceptor(libLogger) + FlakySafeWebBehaviorInterceptor(flakySafetyParams, libLogger) ) } @@ -882,8 +871,7 @@ data class Kaspresso( adbServer ), ElementLoaderObjectBehaviorInterceptor(libLogger, elementLoaderParams), - FlakySafeObjectBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingObjectBehaviorInterceptor(libLogger) + FlakySafeObjectBehaviorInterceptor(flakySafetyParams, libLogger) ) if (!::deviceBehaviorInterceptors.isInitialized) deviceBehaviorInterceptors = mutableListOf( @@ -892,8 +880,7 @@ data class Kaspresso( instrumentalDependencyProviderFactory.getInterceptorProvider(instrumentation), adbServer ), - FlakySafeDeviceBehaviorInterceptor(flakySafetyParams, libLogger), - FailureLoggingDeviceBehaviorInterceptor(libLogger) + FlakySafeDeviceBehaviorInterceptor(flakySafetyParams, libLogger) ) if (!::stepWatcherInterceptors.isInitialized) stepWatcherInterceptors = mutableListOf( diff --git a/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyScreen.kt b/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyScreen.kt index 1508384a5..f7e1e4097 100644 --- a/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyScreen.kt +++ b/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyScreen.kt @@ -1,6 +1,11 @@ package com.kaspersky.kaspresso.composesupport.sample.features.flaky -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.Button diff --git a/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyViewModel.kt b/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyViewModel.kt index 41fbe121d..406a17c4f 100644 --- a/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyViewModel.kt +++ b/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/flaky/SimpleFlakyViewModel.kt @@ -4,7 +4,10 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.plus private const val TIMEOUT = 1_000L diff --git a/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/main/MainScreen.kt b/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/main/MainScreen.kt index 01b295894..8f43e95f4 100644 --- a/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/main/MainScreen.kt +++ b/samples/kaspresso-compose-support-sample/src/main/kotlin/com/kaspersky/kaspresso/composesupport/sample/features/main/MainScreen.kt @@ -1,6 +1,11 @@ package com.kaspersky.kaspresso.composesupport.sample.features.main -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.material.Button import androidx.compose.material.MaterialTheme import androidx.compose.material.Text diff --git a/static-analysis/config/detekt/config.yml b/static-analysis/config/detekt/config.yml index f1d0a8d2a..13eb3bf88 100644 --- a/static-analysis/config/detekt/config.yml +++ b/static-analysis/config/detekt/config.yml @@ -281,7 +281,7 @@ formatting: active: true autoCorrect: true NoWildcardImports: - active: false + active: true PackageName: active: false autoCorrect: true @@ -619,4 +619,4 @@ style: VarCouldBeVal: active: false WildcardImport: - active: false + active: true diff --git a/wiki/10_Jetpack-Compose.md b/wiki/10_Jetpack-Compose.md index 588195df6..38d7e92c4 100644 --- a/wiki/10_Jetpack-Compose.md +++ b/wiki/10_Jetpack-Compose.md @@ -115,6 +115,40 @@ I/KASPRESSO: ___________________________________________________________________ I/KASPRESSO: TEST STEP: "3. Click on the Second button" in ComposeSimpleFlakyTest ``` +## Caveats +Remember, that Jetpack Compose and all relative tools are developing. +It means Jetpack Compose is not learned very well and some things can be unexpected after "Old fashioned View World" experience. +Let me show the interesting case. + +For example, this code +```kotlin +composeSimpleFlakyScreen(composeTestRule) { + firstButton { + performClick() + } +} +``` +can be the source of flakiness behavior if `firstButton` is located in non visible for a user area +(you just need to scroll to see the element). + +But, this code will always work stably: +```kotlin +composeSimpleFlakyScreen(composeTestRule) { + firstButton { + assertIsDisplayed() + performClick() + } +} +``` + +The explanation is in the nature of SemanticsNode Tree and Jetpack Compose. `firstButton` is a Node and presented in the Tree. +It means that `performClick()` may work and nothing bad doesn't happen. But, `firstButton` is not visible physically and a real click doesn't occur. +Such behavior causes the crash of a test a little bit later.
+But, `assertIsDisplayed()` check doesn't pass on the first try (we don't see the element on the screen) and +launches work of all Interceptors including Autoscroll interceptor which scrolls the Screen to the desired element. + +Please, [share your experience](https://github.com/KasperskyLab/Kaspresso/issues/new) to help other developers. + ## What else ### Configuration @@ -175,6 +209,8 @@ All information about Robolectric support is available [here](./08_Kaspresso-Rob ### Compose is compatible with all sweet Kaspresso extensions Sweet Kaspresso extensions means using of the such constructions as -- `flakySafely`, -- `continuously`, -- etc. +- `flakySafely` +- `continuously` + +The support of some constructions is in progress: [issue-317](https://github.com/KasperskyLab/Kaspresso/issues/317). +