├── .circleci └── config.yml ├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml └── dictionaries │ └── dsvor.xml ├── LICENSE ├── README.md ├── _config.yml ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle.kts ├── test-app ├── README.md ├── build.gradle.kts └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── avito │ │ └── android │ │ └── ui │ │ └── test │ │ ├── AppBarScreen.kt │ │ ├── AppBarTest.kt │ │ ├── BackgroundDrawableScreen.kt │ │ ├── BackgroundDrawableTest.kt │ │ ├── ButtonsOverRecyclerScreen.kt │ │ ├── ButtonsOverRecyclerTest.kt │ │ ├── ButtonsOverRecyclerWithCollapsingToolbarTest.kt │ │ ├── ButtonsScreen.kt │ │ ├── ButtonsTest.kt │ │ ├── DisplayedWithTextTest.kt │ │ ├── DistantViewOnScrollScreen.kt │ │ ├── EditTextScreen.kt │ │ ├── EditTextTest.kt │ │ ├── GodRuleChain.kt │ │ ├── IdenticalCellsRecyclerScreen.kt │ │ ├── IdenticalCellsRecyclerTest.kt │ │ ├── InterceptorTest.kt │ │ ├── LongRecyclerScreen.kt │ │ ├── LongRecyclerTest.kt │ │ ├── MovingButtonScreen.kt │ │ ├── MovingButtonTest.kt │ │ ├── OverflowMenuScreen.kt │ │ ├── OverflowMenuTest.kt │ │ ├── ReadTextTest.kt │ │ ├── RecyclerAsLayoutScreen.kt │ │ ├── RecyclerAsLayoutTest.kt │ │ ├── RecyclerInRecyclerLayoutScreen.kt │ │ ├── RecyclerInRecyclerTest.kt │ │ ├── RecyclerWithSingleLongItemScreen.kt │ │ ├── RecyclerWithSingleLongItemTest.kt │ │ ├── Screen.kt │ │ ├── ScreenRule.kt │ │ ├── ScrollViewScrollToEndTest.kt │ │ ├── StatefulRecyclerViewAdapterScreen.kt │ │ ├── StatefulRecyclerViewAdapterTest.kt │ │ ├── SwipeRefreshScreen.kt │ │ ├── SwipeRefreshTest.kt │ │ ├── TabLayoutScreen.kt │ │ ├── TabLayoutTest.kt │ │ ├── TextElementsScreen.kt │ │ ├── TextElementsTest.kt │ │ ├── UITestRunner.kt │ │ ├── ViewPagerScreen.kt │ │ ├── ViewPagerTest.kt │ │ ├── VisibilityScreen.kt │ │ └── VisibilityTest.kt │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── avito │ │ └── android │ │ └── ui │ │ ├── AppBarActivity.kt │ │ ├── ButtonsActivity.kt │ │ ├── ButtonsOverRecyclerActivity.kt │ │ ├── ButtonsOverRecyclerWithCollapsingToolbarActivity.kt │ │ ├── DistantViewOnScrollActivity.kt │ │ ├── DrawableActivity.kt │ │ ├── EditTextActivity.kt │ │ ├── IdenticalCellsRecyclerActivity.kt │ │ ├── LongRecyclerActivity.kt │ │ ├── MovingButtonActivity.kt │ │ ├── OverflowMenuActivity.kt │ │ ├── RecyclerAsLayoutActivity.kt │ │ ├── RecyclerInRecyclerActivity.kt │ │ ├── RecyclerWithLongItemsActivity.kt │ │ ├── StatefulRecyclerViewAdapterActivity.kt │ │ ├── SwipeRefreshActivity.kt │ │ ├── TabLayoutActivity.kt │ │ ├── TextElementActivity.kt │ │ ├── View.kt │ │ ├── ViewPagerActivity.kt │ │ └── VisibilityActivity.kt │ └── res │ ├── drawable │ └── ic_check_black_24dp.xml │ ├── layout │ ├── activity_app_bar.xml │ ├── activity_buttons.xml │ ├── activity_buttons_over_recycler.xml │ ├── activity_buttons_over_recycler_with_collapsing_toolbar.xml │ ├── activity_distant_view_on_scroll.xml │ ├── activity_drawable.xml │ ├── activity_edittext.xml │ ├── activity_moving_button.xml │ ├── activity_overflow.xml │ ├── activity_recycler.xml │ ├── activity_swipe_refresh.xml │ ├── activity_tab_layout.xml │ ├── activity_text_elements.xml │ ├── activity_view_pager.xml │ ├── activity_visibility.xml │ ├── cell.xml │ ├── cell_with_edit_text.xml │ ├── cell_with_inner_recycler.xml │ ├── cell_with_multiple_text_views.xml │ ├── cell_with_text_input.xml │ ├── long_item.xml │ ├── long_recycler.xml │ ├── view_pager_even_item.xml │ └── view_pager_odd_item.xml │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ └── values │ └── color.xml ├── ui-testing-core ├── build.gradle.kts ├── lint.xml └── src │ ├── main │ ├── AndroidManifest.xml │ └── java │ │ ├── androidx │ │ └── test │ │ │ └── espresso │ │ │ ├── action │ │ │ └── SwipeDirection.kt │ │ │ └── assertion │ │ │ ├── HumanReadables.kt │ │ │ └── isDoesntExistAssertion.kt │ │ └── com │ │ └── avito │ │ └── android │ │ └── test │ │ ├── Device.kt │ │ ├── Intents.kt │ │ ├── InteractionContext.kt │ │ ├── UITestConfig.kt │ │ ├── Waiter.kt │ │ ├── action │ │ ├── Actions.kt │ │ ├── ActionsDriver.kt │ │ ├── ActionsImpl.kt │ │ ├── InteractionContextMatcherActions.kt │ │ ├── InteractionContextPositionActions.kt │ │ ├── OnDescendantMatcherListItemActions.kt │ │ ├── OnDescendantPositionListItemActions.kt │ │ ├── OnViewActionsDriver.kt │ │ └── WebElementActions.kt │ │ ├── checks │ │ ├── Checks.kt │ │ ├── ChecksDriver.kt │ │ ├── ChecksImpl.kt │ │ ├── InteractionContextMatcherChecksDriver.kt │ │ ├── InteractionContextPositionChecksDriver.kt │ │ ├── LabelChecks.kt │ │ ├── LabelChecksImpl.kt │ │ ├── OnDescendantMatcherListItemChecksDriver.kt │ │ ├── OnDescendantPositionListItemChecksDriver.kt │ │ ├── OnViewChecksDriver.kt │ │ ├── PasswordFieldChecks.kt │ │ ├── TextFieldErrorChecks.kt │ │ ├── TextFieldHintChecks.kt │ │ └── WebElementChecks.kt │ │ ├── element │ │ └── field │ │ │ ├── TextFieldAction.kt │ │ │ ├── TextFieldChecks.kt │ │ │ ├── TextFieldElement.kt │ │ │ └── actions │ │ │ └── TypeText.kt │ │ ├── espresso │ │ ├── EspressoActions.kt │ │ ├── action │ │ │ ├── ActionOnClickableElement.kt │ │ │ ├── ActionOnEnabledElement.kt │ │ │ ├── ActionOnLongClickableElement.kt │ │ │ ├── CollapseAppBarAction.kt │ │ │ ├── GroupedViewAction.kt │ │ │ ├── OrientationChangeAction.kt │ │ │ ├── RecyclerSpanCountAction.kt │ │ │ ├── RecyclerViewHorizontalOffsetAction.kt │ │ │ ├── RecyclerViewItemsCountAction.kt │ │ │ ├── RecyclerViewVerticalOffsetAction.kt │ │ │ ├── TextViewReadAction.kt │ │ │ ├── ToolbarReadMenuItemsAction.kt │ │ │ ├── ViewGetHeightAction.kt │ │ │ ├── ViewGetTranslationYAction.kt │ │ │ ├── ViewPagersFlipAction.kt │ │ │ ├── ViewPagersSelectAction.kt │ │ │ ├── WaitForIdleAction.kt │ │ │ ├── click │ │ │ │ ├── ClickAction.kt │ │ │ │ └── Event.kt │ │ │ ├── recycler │ │ │ │ ├── RecyclerViewActions.kt │ │ │ │ ├── RecyclerViewScroll.kt │ │ │ │ └── ViewHolderItemMatching.kt │ │ │ └── scroll │ │ │ │ ├── Scroll.kt │ │ │ │ └── ScrollToIfPossibleAction.kt │ │ └── assertion │ │ │ └── ViewExistsAssertion.kt │ │ ├── interceptor │ │ ├── HumanReadableInterceptor.kt │ │ └── Interceptor.kt │ │ ├── internal │ │ ├── Cache.kt │ │ ├── SQLiteDB.kt │ │ └── SharedPreferences.kt │ │ ├── matcher │ │ ├── AlphaMatcher.kt │ │ ├── AvitoPositionAssertions.kt │ │ ├── CanBeClickedMatcher.kt │ │ ├── CanBeLongClickedMatcher.kt │ │ ├── CollapsingToolbarTitleMatcher.kt │ │ ├── CompoundDrawableMatcher.kt │ │ ├── DrawableBackgroundMatcher.kt │ │ ├── DrawableMatcher.kt │ │ ├── DrawableMatcherActionIcon.kt │ │ ├── DrawableMatcherCheckableImageView.kt │ │ ├── DrawableMatcherImageButton.kt │ │ ├── FocusableInTouchModeMatcher.kt │ │ ├── HintMatcher.kt │ │ ├── ImageShownMatcher.kt │ │ ├── IsAssignableFromMatcher.kt │ │ ├── IsRefreshingMatcher.kt │ │ ├── NoViewMatcher.kt │ │ ├── RecyclerViewMatcher.kt │ │ ├── StringPatternMatcher.kt │ │ ├── TabLayoutSelectMatcher.kt │ │ ├── TabLayoutTabsCountMatcher.kt │ │ ├── TextInputLayoutErrorMatcher.kt │ │ ├── TextInputLayoutHintMatcher.kt │ │ ├── TextInputPasswordVisibilityMatcher.kt │ │ ├── TextViewLinesMatcher.kt │ │ ├── ToolbarSubTitleResMatcher.kt │ │ ├── ToolbarSubtitleMatcher.kt │ │ ├── ToolbarTitleMatcher.kt │ │ ├── ToolbarTitleResMatcher.kt │ │ ├── UniversalCheckedMatcher.kt │ │ ├── ViewGroupMatcher.kt │ │ ├── ViewMatchers.kt │ │ ├── ViewPagersSelectMatcher.kt │ │ ├── ViewPagersTabsCountMatcher.kt │ │ └── WithHintEndingMatcher.kt │ │ ├── page_object │ │ ├── Alert.kt │ │ ├── AppBarElement.kt │ │ ├── FloatingViewElement.kt │ │ ├── ImageViewElement.kt │ │ ├── KeyboardElement.kt │ │ ├── ListElement.kt │ │ ├── PageObjectElement.kt │ │ ├── ProgressBarElement.kt │ │ ├── RatingBarElement.kt │ │ ├── SnackbarElement.kt │ │ ├── SwipeRefreshElement.kt │ │ ├── SwitchElement.kt │ │ ├── TabLayoutElement.kt │ │ ├── TextElement.kt │ │ ├── TextInputElement.kt │ │ ├── ToolbarElement.kt │ │ ├── ToolbarMenuElement.kt │ │ ├── ViewPagerElement.kt │ │ └── WebView.kt │ │ ├── screenshot │ │ ├── ScreenshotConsumer.kt │ │ ├── ScreenshotProvider.kt │ │ ├── ScreenshotRule.kt │ │ └── SuppressScreenshot.kt │ │ └── util │ │ ├── AppendableDescription.kt │ │ ├── ClickTypeRule.kt │ │ ├── Context.kt │ │ ├── Drawables.kt │ │ ├── HumanReadables.kt │ │ ├── Instrumentations.kt │ │ ├── Reflection.kt │ │ └── View.kt │ └── test │ └── java │ └── com │ └── avito │ └── android │ └── test │ └── util │ └── ReflectionTests.kt └── ui-testing-maps ├── build.gradle.kts ├── lint.xml └── src └── main ├── AndroidManifest.xml └── java └── com.avito.android.test.maps ├── Exception.kt ├── GoogleMapActions.kt ├── GoogleMapChecks.kt ├── GoogleMapFragmentElement.kt ├── GoogleMapMatchers.kt └── provider ├── FragmentGoogleMapProvider.kt └── GoogleMapProvider.kt /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle/ 3 | *build/ 4 | local.properties 5 | **/.idea/* 6 | !.idea/codeStyles 7 | !.idea/inspectionProfiles 8 | !.idea/dictionaries 9 | **/gen/* 10 | captures/* -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/dictionaries/dsvor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | avito 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Dmitriy Voronin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ARCHIVED] Avito Android UI testing library 2 | 3 | See https://github.com/avito-tech/avito-android for this library as part of a bigger project. 4 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | android.useAndroidX=true 2 | android.enableJetifier=false 3 | minSdk=18 4 | targetSdk=28 5 | # versions 6 | androidGradlePluginVersion=3.5.0 7 | kotlinVersion=1.3.50 8 | playServicesVersion=17.0.0 9 | materialVersion=1.0.0 10 | junitVersion=4.12 11 | # jetpack 12 | appcompatVersion=1.0.2 13 | recyclerViewVersion=1.0.0 14 | # test versions 15 | androidXTestVersion=1.2.0 16 | espressoVersion=3.1.0 17 | uiAutomatorVersion=2.2.0 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/avito-tech/android-ui-testing/3fa67bd425cb7ae38b2c4687e8e8ee9e6104ff2e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | val kotlinVersion: String by settings 2 | val androidGradlePluginVersion: String by settings 3 | 4 | pluginManagement { 5 | repositories { 6 | gradlePluginPortal() 7 | google() 8 | } 9 | resolutionStrategy { 10 | eachPlugin { 11 | val pluginId = requested.id.id 12 | when { 13 | pluginId.startsWith("com.android.") -> 14 | useModule("com.android.tools.build:gradle:$androidGradlePluginVersion") 15 | 16 | pluginId.startsWith("org.jetbrains.kotlin.") -> 17 | useVersion(kotlinVersion) 18 | } 19 | } 20 | } 21 | } 22 | 23 | rootProject.buildFileName = "build.gradle.kts" 24 | 25 | include(":ui-testing-core") 26 | include(":ui-testing-maps") 27 | include(":test-app") 28 | -------------------------------------------------------------------------------- /test-app/README.md: -------------------------------------------------------------------------------- 1 | Tests for test-ui library -------------------------------------------------------------------------------- /test-app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | val targetSdk: String by project 2 | val minSdk: String by project 3 | 4 | val kotlinVersion: String by project 5 | val playServicesVersion: String by project 6 | val appcompatVersion: String by project 7 | val recyclerViewVersion: String by project 8 | val materialVersion: String by project 9 | 10 | plugins { 11 | id("com.android.application") 12 | kotlin("android") 13 | } 14 | 15 | android { 16 | compileSdkVersion(targetSdk.toInt()) 17 | 18 | defaultConfig { 19 | minSdkVersion(minSdk) 20 | targetSdkVersion(targetSdk.toInt()) 21 | testInstrumentationRunner = "com.avito.android.ui.test.UITestRunner" 22 | } 23 | 24 | buildTypes { 25 | getByName("debug") { 26 | matchingFallbacks = listOf("release") 27 | } 28 | } 29 | 30 | variantFilter { 31 | if (name == "release") { 32 | setIgnore(true) 33 | } 34 | } 35 | 36 | packagingOptions { 37 | pickFirst("protobuf.meta") 38 | } 39 | } 40 | 41 | dependencies { 42 | implementation(kotlin("stdlib", kotlinVersion)) 43 | implementation("com.google.android.gms:play-services-maps:$playServicesVersion") 44 | 45 | api("androidx.appcompat:appcompat:$appcompatVersion") 46 | api("androidx.recyclerview:recyclerview:$recyclerViewVersion") 47 | api("com.google.android.material:material:$materialVersion") 48 | 49 | androidTestImplementation(project(":ui-testing-core")) 50 | } 51 | 52 | tasks.getByName("build").dependsOn("$path:assembleAndroidTest") 53 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/AppBarScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.AppBarElement 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.test.page_object.ViewElement 7 | import com.avito.android.ui.R 8 | 9 | class AppBarScreen : PageObject() { 10 | val appBar: AppBarElement = element(withId(R.id.appbar)) 11 | val testView: ViewElement = element(withId(R.id.view_in_collapsing_toolbar)) 12 | } 13 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/AppBarTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.AppBarActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class AppBarTest { 8 | 9 | @get:Rule 10 | val rule = screenRule() 11 | 12 | @Test 13 | fun collapse_collapsesViews() { 14 | rule.launchActivity(null) 15 | 16 | rule.activity.setExpanded(true) 17 | 18 | Screen.appBarScreen.appBar.actions.collapse() 19 | Screen.appBarScreen.testView.checks.isNotCompletelyDisplayed() 20 | } 21 | 22 | @Test 23 | fun expand_showsViews() { 24 | rule.launchActivity(null) 25 | 26 | rule.activity.setExpanded(false) 27 | 28 | Screen.appBarScreen.appBar.actions.expand() 29 | Screen.appBarScreen.testView.checks.isCompletelyDisplayed() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/BackgroundDrawableScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.PageObject 5 | import com.avito.android.test.page_object.ViewElement 6 | import com.avito.android.ui.R 7 | 8 | class BackgroundDrawableScreen : PageObject() { 9 | val viewWithBackgroundRedColor: ViewElement = element(withId(R.id.background_view_color)) 10 | val viewWithBackgroundCheckIcon: ViewElement = element(withId(R.id.background_view_image)) 11 | } 12 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/BackgroundDrawableTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.DrawableActivity 4 | import com.avito.android.ui.R 5 | import org.junit.Rule 6 | import org.junit.Test 7 | 8 | class BackgroundDrawableTest { 9 | 10 | @get:Rule 11 | val rule = screenRule(launchActivity = true) 12 | 13 | @Test 14 | fun backgroundDrawable_matches_whenBackgroundIsColorResource() { 15 | Screen.backgroundDrawableScreen.viewWithBackgroundRedColor.checks.hasBackground(R.color.red) 16 | } 17 | 18 | @Test 19 | fun backgroundDrawable_matches_whenBackgroundIsIconResource() { 20 | Screen.backgroundDrawableScreen.viewWithBackgroundCheckIcon.checks 21 | .hasBackground(R.drawable.ic_check_black_24dp) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ButtonsOverRecyclerScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.widget.FrameLayout 4 | import androidx.test.espresso.matcher.ViewMatchers.hasDescendant 5 | import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom 6 | import androidx.test.espresso.matcher.ViewMatchers.withId 7 | import androidx.test.espresso.matcher.ViewMatchers.withText 8 | import com.avito.android.test.InteractionContext 9 | import com.avito.android.test.page_object.ListElement 10 | import com.avito.android.test.page_object.PageObject 11 | import com.avito.android.test.page_object.ViewElement 12 | import com.avito.android.ui.R 13 | 14 | class ButtonsOverRecyclerScreen : PageObject() { 15 | 16 | val list: List = element(withId(R.id.recycler)) 17 | 18 | class List(interactionContext: InteractionContext) : ListElement(interactionContext) { 19 | 20 | fun cellWithTitle(title: String): Cell = listElement(hasDescendant(withText(title))) 21 | fun cellAt(position: Int): Cell = 22 | listElement(isAssignableFrom(FrameLayout::class.java), position) 23 | 24 | class Cell(interactionContext: InteractionContext) : ViewElement(interactionContext) { 25 | val title: ViewElement = element(withId(R.id.title)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ButtonsOverRecyclerTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.action.SwipeDirections 4 | import com.avito.android.ui.ButtonsOverRecyclerActivity 5 | import org.hamcrest.Matchers.equalTo 6 | import org.hamcrest.Matchers.greaterThan 7 | import org.junit.Rule 8 | import org.junit.Test 9 | 10 | class ButtonsOverRecyclerTest { 11 | 12 | @get:Rule 13 | val rule = screenRule(launchActivity = true) 14 | 15 | @Test 16 | fun listElement_swipe_RecyclerViewBehindButtons() { 17 | with(Screen.buttonsOverRecycler.list) { 18 | actions.swipe(SwipeDirections.BOTTOM_TO_TOP) 19 | checks.firstVisiblePosition(greaterThan(0)) 20 | // on some devices swipe to top may end on half way 21 | repeat(3) { actions.swipe(SwipeDirections.TOP_TO_BOTTOM) } 22 | checks.firstVisiblePosition(equalTo(0)) 23 | } 24 | } 25 | 26 | @Test 27 | fun listElement_elementClicked_whenThereIsOverlappedButton() { 28 | Screen.buttonsOverRecycler.list.cellAt(60).click() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ButtonsOverRecyclerWithCollapsingToolbarTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.ButtonsOverRecyclerWithCollapsingToolbarActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class ButtonsOverRecyclerWithCollapsingToolbarTest { 8 | 9 | @get:Rule 10 | val rule = screenRule(launchActivity = true) 11 | 12 | @Test 13 | fun listElement_elementClicked_whenThereIsOverlappedButtonInScreenWithCollapsingToolbar() { 14 | Screen.buttonsOverRecycler.list.cellAt(90).click() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ButtonsScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import androidx.test.espresso.matcher.ViewMatchers.withText 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.test.page_object.ViewElement 7 | import com.avito.android.ui.R 8 | 9 | class ButtonsScreen : PageObject() { 10 | val enabledButton: ViewElement = element(withId(R.id.button_enabled)) 11 | val enabledButtonClickIndicatorView: ViewElement = element( 12 | withId(R.id.button_enabled_clicked_text_view) 13 | ) 14 | val enabledButtonLongClickIndicatorView: ViewElement = element( 15 | withId(R.id.button_enabled_long_clicked_text_view) 16 | ) 17 | val disabledButton: ViewElement = element(withId(R.id.button_disabled)) 18 | val nonClickableButton: ViewElement = element(withId(R.id.button_non_clickable)) 19 | 20 | val clickableContainerInnerButton: ViewElement = element(withText("Non clickable inside clickable")) 21 | val clickableContainerIndicator: ViewElement = element(withId(R.id.clickable_container_indicator)) 22 | 23 | val nonLongClickableButton: ViewElement = element(withId(R.id.button_non_long_clickable)) 24 | val nonLongClickableButtonIndicator: ViewElement = element( 25 | withId(R.id.button_non_long_clickable_clicked_text_view) 26 | ) 27 | 28 | val longClickableContainerInnerButton: ViewElement = element(withText("Non long-clickable inside long-clickable")) 29 | val longClickableContainerIndicator: ViewElement = element(withId(R.id.long_clickable_container_indicator)) 30 | } -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/DisplayedWithTextTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.IdenticalCellsRecyclerActivity 4 | import org.hamcrest.Matchers 5 | import org.junit.Rule 6 | import org.junit.Test 7 | 8 | class DisplayedWithTextTest { 9 | 10 | @get:Rule 11 | val rule = screenRule() 12 | 13 | @Test 14 | fun canAssertText_onFoundItem() { 15 | rule.launchActivity(IdenticalCellsRecyclerActivity.intent(arrayListOf("test string"))) 16 | 17 | Screen.identicalCellsRecycler.list.cellAt(position = 0) 18 | .title.checks.displayedWithText("test string") 19 | } 20 | 21 | @Test 22 | fun canAssertTextWithMatcher_onFoundItem() { 23 | rule.launchActivity(IdenticalCellsRecyclerActivity.intent(arrayListOf("test string"))) 24 | 25 | Screen.identicalCellsRecycler.list.cellAt(position = 0) 26 | .title.checks.displayedWithText(Matchers.startsWith("test")) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/DistantViewOnScrollScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.PageObject 5 | import com.avito.android.test.page_object.ViewElement 6 | import com.avito.android.ui.R 7 | 8 | class DistantViewOnScrollScreen : PageObject() { 9 | val scroll: ViewElement = ViewElement(withId(R.id.scroll)) 10 | val view: ViewElement = element(withId(R.id.view)) 11 | } 12 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/EditTextScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.element.field.TextFieldElement 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.ui.R 7 | 8 | class EditTextScreen : PageObject() { 9 | val editText: TextFieldElement = element(withId(R.id.edit_text)) 10 | val editText1: TextFieldElement = element(withId(R.id.edit_text1)) 11 | val phoneNumberText: TextFieldElement = element(withId(R.id.phone_number_edit_text1)) 12 | } 13 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/EditTextTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.test.Device 4 | import com.avito.android.ui.EditTextActivity 5 | import org.junit.Rule 6 | import org.junit.Test 7 | 8 | class EditTextTest { 9 | 10 | @get:Rule 11 | val rule = screenRule() 12 | 13 | @Test 14 | fun findsKeyboard_whenKeyboardIsOpenedByActivityManifest() = with(rule) { 15 | launchActivity(null) 16 | 17 | Device.keyboard.checks.isDisplayed(activity) 18 | } 19 | 20 | @Test 21 | fun writesToInputWithFormatting() = with(rule) { 22 | launchActivity(null) 23 | 24 | Screen.editTextScreen.phoneNumberText.write("9261418698") 25 | Screen.editTextScreen.phoneNumberText.checks.displayedWithText("(926) 141-8698") 26 | } 27 | 28 | @Test 29 | fun writesCyrillicText() = with(rule) { 30 | launchActivity(null) 31 | 32 | Screen.editTextScreen.editText1.write("Кирилл и Мефодий не изобретали кириллицу") 33 | Screen.editTextScreen.editText1.checks.displayedWithText("Кирилл и Мефодий не изобретали кириллицу") 34 | } 35 | 36 | @Test 37 | fun writesLongText() = with(rule) { 38 | launchActivity(null) 39 | 40 | val superLongText = StringBuilder() 41 | .apply { 42 | (0..100000) 43 | .forEach { _ -> 44 | append("a") 45 | } 46 | } 47 | .toString() 48 | 49 | Screen.editTextScreen.editText.write(superLongText) 50 | Screen.editTextScreen.editText.checks.displayedWithText(superLongText) 51 | } 52 | 53 | @Test 54 | fun writesEmptyText() = with(rule) { 55 | launchActivity(null) 56 | 57 | Screen.editTextScreen.editText.write("") 58 | Screen.editTextScreen.editText.checks.displayedWithText("") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/GodRuleChain.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.app.Activity 4 | import android.app.Instrumentation 5 | import android.content.Intent 6 | import androidx.test.rule.ActivityTestRule 7 | import org.junit.rules.RuleChain 8 | import org.junit.rules.TestRule 9 | import org.junit.runner.Description 10 | import org.junit.runners.model.Statement 11 | 12 | /** 13 | * RuleChain with exposed methods and fields from it's rules 14 | */ 15 | class GodRuleChain(private val chain: RuleChain) : 16 | TestRule { 17 | 18 | @Suppress("UNCHECKED_CAST") 19 | private val rules = chain.javaClass.getDeclaredField("rulesStartingWithInnerMost") 20 | .apply { isAccessible = true }.get(chain) as List 21 | 22 | private val activityTestRule = rules.filterIsInstance>().first() 23 | 24 | val activity: T 25 | get() = activityTestRule.activity 26 | 27 | val activityResult: Instrumentation.ActivityResult 28 | get() = activityTestRule.activityResult 29 | 30 | fun runOnUiThread(runnable: () -> Unit) = activityTestRule.runOnUiThread(runnable) 31 | 32 | fun launchActivity(intent: Intent?): T = activityTestRule.launchActivity(intent) 33 | 34 | fun launchActivity(func: (Intent) -> Intent): T = 35 | activityTestRule.launchActivity(func(Intent(Intent.ACTION_MAIN))) 36 | 37 | override fun apply(base: Statement?, description: Description?): Statement { 38 | return chain.apply(base, description) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/IdenticalCellsRecyclerScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.widget.FrameLayout 4 | import androidx.test.espresso.matcher.ViewMatchers.hasDescendant 5 | import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom 6 | import androidx.test.espresso.matcher.ViewMatchers.withId 7 | import androidx.test.espresso.matcher.ViewMatchers.withText 8 | import com.avito.android.test.InteractionContext 9 | import com.avito.android.test.page_object.ListElement 10 | import com.avito.android.test.page_object.PageObject 11 | import com.avito.android.test.page_object.ViewElement 12 | import com.avito.android.ui.R 13 | 14 | class IdenticalCellsRecyclerScreen : PageObject() { 15 | 16 | val list: List = element(withId(R.id.recycler)) 17 | 18 | class List(interactionContext: InteractionContext) : ListElement(interactionContext) { 19 | 20 | fun cellWithTitle(title: String): Cell = listElement(hasDescendant(withText(title))) 21 | fun cellAt(position: Int): Cell = 22 | listElement(isAssignableFrom(FrameLayout::class.java), position) 23 | 24 | class Cell(interactionContext: InteractionContext) : ViewElement(interactionContext) { 25 | val title: ViewElement = element(withId(R.id.title)) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/IdenticalCellsRecyclerTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.IdenticalCellsRecyclerActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class IdenticalCellsRecyclerTest { 8 | 9 | @get:Rule 10 | val rule = screenRule() 11 | 12 | @Test 13 | fun typedItemWithMatcher_foundSecondItem_withAllDifferentValues() { 14 | rule.launchActivity(IdenticalCellsRecyclerActivity.intent(arrayListOf("1", "2", "3"))) 15 | 16 | Screen.identicalCellsRecycler.list.cellWithTitle("2").title.checks.displayedWithText("2") 17 | } 18 | 19 | @Test 20 | fun typedItemWithMatcher_failedToFailValues_withIdenticalValues() { 21 | rule.launchActivity(IdenticalCellsRecyclerActivity.intent(arrayListOf("2", "2", "2"))) 22 | 23 | Screen.identicalCellsRecycler.list.cellWithTitle("2").title.checks.displayedWithText("2") 24 | } 25 | 26 | @Test 27 | fun typedItemAtPosition_foundSecondItem_withAllDifferentValues() { 28 | rule.launchActivity(IdenticalCellsRecyclerActivity.intent(arrayListOf("1", "2", "3"))) 29 | 30 | Screen.identicalCellsRecycler.list.cellAt(position = 1).title.checks.displayedWithText("2") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/InterceptorTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.test.UITestConfig 4 | import com.avito.android.test.interceptor.HumanReadableActionInterceptor 5 | import com.avito.android.test.interceptor.HumanReadableAssertionInterceptor 6 | import com.avito.android.ui.VisibilityActivity 7 | import org.junit.Assert 8 | import org.junit.Rule 9 | import org.junit.Test 10 | 11 | class InterceptorTest { 12 | 13 | @get:Rule 14 | val rule = screenRule(true) 15 | 16 | @Test 17 | fun actionInterceptor_intercepts_clickAction() { 18 | var intercepted = "" 19 | 20 | UITestConfig.actionInterceptors += HumanReadableActionInterceptor { intercepted = it } 21 | Screen.visibility.label.click() 22 | 23 | Assert.assertEquals("single click on clickable element on enabled element on AppCompatTextView(id=text;text=Test)", intercepted) 24 | } 25 | 26 | @Test 27 | fun assertionInterceptor_intercepts_visibleCheck() { 28 | var intercepted = "" 29 | 30 | UITestConfig.assertionInterceptors += HumanReadableAssertionInterceptor { intercepted = it } 31 | 32 | Screen.visibility.label.checks.isVisible() 33 | 34 | Assert.assertEquals( 35 | "Check view has effective visibility=VISIBLE on AppCompatTextView(id=text;text=Test)", 36 | intercepted 37 | ) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/LongRecyclerScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.ListElement 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.ui.R 7 | 8 | class LongRecyclerScreen : PageObject() { 9 | val list: ListElement = element(withId(R.id.recycler)) 10 | } 11 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/LongRecyclerTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.LongRecyclerActivity 4 | import com.avito.android.ui.RecyclerAsLayoutActivity 5 | import org.hamcrest.Matchers.greaterThan 6 | import org.junit.Rule 7 | import org.junit.Test 8 | 9 | class LongRecyclerTest { 10 | 11 | @get:Rule 12 | val rule = screenRule() 13 | 14 | private fun givenItemsList(n: Int) = (0..n).asSequence().map { "label_$it" }.toList() 15 | 16 | @Test 17 | fun textInput_isAccessible() { 18 | 19 | val itemsCount = 500 20 | 21 | rule.launchActivity( 22 | RecyclerAsLayoutActivity.intent( 23 | arrayListOf().apply { addAll(givenItemsList(itemsCount)) } 24 | ) 25 | ) 26 | 27 | Screen.longRecycler.list.actions.smoothScrollToPosition(itemsCount) 28 | Thread.sleep(1000L) 29 | 30 | Screen.longRecycler.list.checks.firstVisiblePosition(greaterThan(400)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/MovingButtonScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers 4 | import com.avito.android.test.page_object.PageObject 5 | import com.avito.android.test.page_object.ViewElement 6 | import com.avito.android.ui.R 7 | 8 | class MovingButtonScreen : PageObject() { 9 | val movedButton: ViewElement = element(ViewMatchers.withId(R.id.moving_button)) 10 | val movedButtonClickIndicatorView: ViewElement = 11 | element(ViewMatchers.withId(R.id.moving_button_clicked_text_view)) 12 | } -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/MovingButtonTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.MovingButtonActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class MovingButtonTest { 8 | 9 | @get:Rule 10 | val rule = screenRule(launchActivity = true) 11 | 12 | @Test 13 | fun movingButton_clicked() { 14 | Screen.movingButton.movedButton.click() 15 | Screen.movingButton.movedButtonClickIndicatorView.checks.isDisplayed() 16 | } 17 | } -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/OverflowMenuScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.PageObject 5 | import com.avito.android.test.page_object.ToolbarElement 6 | import com.avito.android.test.page_object.ViewElement 7 | import com.avito.android.ui.R 8 | 9 | class OverflowMenuScreen : PageObject() { 10 | 11 | val toolbar: Toolbar = element() 12 | val label: ViewElement = element(withId(R.id.text)) 13 | 14 | class Toolbar : ToolbarElement() { 15 | val menuItem = actionMenuItem("check") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/OverflowMenuTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.view.MenuItem 4 | import androidx.test.espresso.NoMatchingViewException 5 | import com.avito.android.ui.OverflowMenuActivity 6 | import org.junit.Rule 7 | import org.junit.Test 8 | import org.junit.rules.ExpectedException 9 | 10 | class OverflowMenuTest { 11 | 12 | @get:Rule 13 | val rule = screenRule() 14 | 15 | @get:Rule 16 | val exception: ExpectedException = ExpectedException.none() 17 | 18 | @Test 19 | fun menuItem_isClickable_inActionMenu() { 20 | val label = "ACTION CLICKED" 21 | rule.launchActivity(OverflowMenuActivity.intent(MenuItem.SHOW_AS_ACTION_ALWAYS, label)) 22 | 23 | Screen.overflow.toolbar.menuItem.actions.click() 24 | Screen.overflow.label.checks.displayedWithText(label) 25 | } 26 | 27 | @Test 28 | fun menuItem_isClickable_inOverflowMenu() { 29 | val label = "OVERFLOW CLICKED" 30 | rule.launchActivity(OverflowMenuActivity.intent(MenuItem.SHOW_AS_ACTION_NEVER, label)) 31 | 32 | Screen.overflow.toolbar.menuItem.actions.click() 33 | Screen.overflow.label.checks.displayedWithText(label) 34 | } 35 | 36 | @Test 37 | fun menuItem_notFound_inOverflowMenuWithNoAutoClick() { 38 | rule.launchActivity( 39 | OverflowMenuActivity.intent( 40 | MenuItem.SHOW_AS_ACTION_NEVER, 41 | "doesn't matter" 42 | ) 43 | ) 44 | 45 | exception.expect(NoMatchingViewException::class.java) 46 | exception.expectMessage("No views in hierarchy found matching: with text") 47 | Screen.overflow.toolbar.menuItem.disableOverflowMenuAutoOpen().actions.click() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/RecyclerAsLayoutScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.InteractionContext 5 | import com.avito.android.test.element.field.TextFieldElement 6 | import com.avito.android.test.page_object.ListElement 7 | import com.avito.android.test.page_object.PageObject 8 | import com.avito.android.test.page_object.TextInputElement 9 | import com.avito.android.test.page_object.ViewElement 10 | import com.avito.android.ui.R 11 | 12 | class RecyclerAsLayoutScreen : PageObject() { 13 | 14 | val list: List = element(withId(R.id.recycler)) 15 | 16 | class List(interactionContext: InteractionContext) : ListElement(interactionContext) { 17 | val inputField: TextInputElement = listElement(withId(R.id.input_layout)) 18 | val editText: TextFieldElement = listElement(withId(R.id.edit_text)) 19 | val label: ViewElement = listElement(withId(R.id.title)) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/RecyclerAsLayoutTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.RecyclerAsLayoutActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class RecyclerAsLayoutTest { 8 | 9 | @get:Rule 10 | val rule = screenRule() 11 | 12 | @Test 13 | fun label_isAccessible() { 14 | rule.launchActivity( 15 | RecyclerAsLayoutActivity.intent( 16 | arrayListOf( 17 | "input", 18 | "editText", 19 | "label" 20 | ) 21 | ) 22 | ) 23 | 24 | Screen.recyclerAsLayout.list.label.checks.withText("label") 25 | } 26 | 27 | @Test 28 | fun editText_isAccessible() { 29 | rule.launchActivity( 30 | RecyclerAsLayoutActivity.intent( 31 | arrayListOf( 32 | "input", 33 | "editText", 34 | "label" 35 | ) 36 | ) 37 | ) 38 | 39 | Screen.recyclerAsLayout.list.editText.checks.withHintText("editText") 40 | } 41 | 42 | @Test 43 | fun textInput_isAccessible() { 44 | rule.launchActivity( 45 | RecyclerAsLayoutActivity.intent( 46 | arrayListOf( 47 | "input", 48 | "editText", 49 | "label" 50 | ) 51 | ) 52 | ) 53 | 54 | Screen.recyclerAsLayout.list.inputField.checks.withHintText("input") 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/RecyclerInRecyclerLayoutScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.widget.FrameLayout 4 | import androidx.test.espresso.matcher.ViewMatchers 5 | import androidx.test.espresso.matcher.ViewMatchers.hasDescendant 6 | import androidx.test.espresso.matcher.ViewMatchers.withId 7 | import androidx.test.espresso.matcher.ViewMatchers.withText 8 | import com.avito.android.test.InteractionContext 9 | import com.avito.android.test.page_object.ListElement 10 | import com.avito.android.test.page_object.PageObject 11 | import com.avito.android.test.page_object.ViewElement 12 | import com.avito.android.ui.R 13 | 14 | class RecyclerInRecyclerLayoutScreen : PageObject() { 15 | 16 | val list: List = element(withId(R.id.recycler)) 17 | 18 | class List(interactionContext: InteractionContext) : ListElement(interactionContext) { 19 | 20 | val horizontalList: InnerList = listElement(withId(R.id.inner_recycler)) 21 | 22 | class InnerList(interactionContext: InteractionContext) : ListElement(interactionContext) { 23 | 24 | fun cellWithTitle(title: String): Cell = listElement(hasDescendant(withText(title))) 25 | 26 | fun cellAt(position: Int): Cell = listElement( 27 | ViewMatchers.isAssignableFrom(FrameLayout::class.java), 28 | position 29 | ) 30 | 31 | class Cell(interactionContext: InteractionContext) : ViewElement(interactionContext) { 32 | val title: ViewElement = element(withId(R.id.title)) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/RecyclerInRecyclerTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.RecyclerInRecyclerActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class RecyclerInRecyclerTest { 8 | 9 | @get:Rule 10 | val rule = screenRule() 11 | 12 | @Test 13 | fun typedItemAtPosition_foundFirstValue() { 14 | rule.launchActivity(RecyclerInRecyclerActivity.intent(arrayListOf("0", "1", "2"))) 15 | 16 | Screen.recyclerInRecycler.list.horizontalList.cellAt(position = 0) 17 | .title.checks.displayedWithText("0") 18 | } 19 | 20 | @Test 21 | fun typedItemWithMatcher_foundFirstValue() { 22 | rule.launchActivity(RecyclerInRecyclerActivity.intent(arrayListOf("0", "1", "2"))) 23 | 24 | Screen.recyclerInRecycler.list.horizontalList.cellWithTitle("0") 25 | .title.checks.displayedWithText("0") 26 | } 27 | 28 | @Test 29 | fun typedItemAtPosition_foundThirdValue() { 30 | rule.launchActivity(RecyclerInRecyclerActivity.intent(arrayListOf("0", "1", "2"))) 31 | 32 | Screen.recyclerInRecycler.list.horizontalList.cellAt(position = 2) 33 | .title.checks.displayedWithText("2") 34 | } 35 | 36 | @Test 37 | fun typedItemWithMatcher_foundThirdValue() { 38 | rule.launchActivity(RecyclerInRecyclerActivity.intent(arrayListOf("0", "1", "2"))) 39 | 40 | Screen.recyclerInRecycler.list.horizontalList.cellWithTitle("2") 41 | .title.checks.displayedWithText("2") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/RecyclerWithSingleLongItemScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.ListElement 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.ui.R 7 | 8 | class RecyclerWithSingleLongItemScreen : PageObject() { 9 | val list: ListElement = element(withId(R.id.recycler)) 10 | } 11 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/Screen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | object Screen { 4 | 5 | val distantViewOnScroll: DistantViewOnScrollScreen 6 | get() = DistantViewOnScrollScreen() 7 | 8 | val visibility: VisibilityScreen 9 | get() = VisibilityScreen() 10 | 11 | val overflow: OverflowMenuScreen 12 | get() = OverflowMenuScreen() 13 | 14 | val swipeRefresh: SwipeRefreshScreen 15 | get() = SwipeRefreshScreen() 16 | 17 | val textsElements: TextElementsScreen 18 | get() = TextElementsScreen() 19 | 20 | val buttons: ButtonsScreen 21 | get() = ButtonsScreen() 22 | 23 | val movingButton: MovingButtonScreen 24 | get() = MovingButtonScreen() 25 | 26 | val appBarScreen: AppBarScreen 27 | get() = AppBarScreen() 28 | 29 | val identicalCellsRecycler: IdenticalCellsRecyclerScreen 30 | get() = IdenticalCellsRecyclerScreen() 31 | 32 | val buttonsOverRecycler: ButtonsOverRecyclerScreen 33 | get() = ButtonsOverRecyclerScreen() 34 | 35 | val statefulRecyclerViewAdapterScreen: StatefulRecyclerViewAdapterScreen 36 | get() = StatefulRecyclerViewAdapterScreen() 37 | 38 | val recyclerAsLayout: RecyclerAsLayoutScreen 39 | get() = RecyclerAsLayoutScreen() 40 | 41 | val viewPagerScreen: ViewPagerScreen 42 | get() = ViewPagerScreen() 43 | 44 | val longRecycler: LongRecyclerScreen 45 | get() = LongRecyclerScreen() 46 | 47 | val recyclerInRecycler: RecyclerInRecyclerLayoutScreen 48 | get() = RecyclerInRecyclerLayoutScreen() 49 | 50 | val editTextScreen: EditTextScreen 51 | get() = EditTextScreen() 52 | 53 | val tabLayoutScreen: TabLayoutScreen 54 | get() = TabLayoutScreen() 55 | 56 | val backgroundDrawableScreen: BackgroundDrawableScreen 57 | get() = BackgroundDrawableScreen() 58 | 59 | val recyclerWithSingleLongItemScreen: RecyclerWithSingleLongItemScreen 60 | get() = RecyclerWithSingleLongItemScreen() 61 | } 62 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ScreenRule.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.app.Activity 4 | import androidx.test.rule.ActivityTestRule 5 | import org.junit.rules.RuleChain 6 | 7 | inline fun screenRule(launchActivity: Boolean = false): GodRuleChain = 8 | GodRuleChain( 9 | RuleChain.emptyRuleChain() 10 | .around(ActivityTestRule(T::class.java, true, launchActivity)) 11 | ) 12 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ScrollViewScrollToEndTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.DistantViewOnScrollActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class ScrollViewScrollToEndTest { 8 | 9 | @get:Rule 10 | val rule = screenRule(launchActivity = true) 11 | 12 | @Test 13 | fun isVisible_viewIsNotDisplayed() { 14 | Screen.distantViewOnScroll.view.checks.isNotDisplayed() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/StatefulRecyclerViewAdapterScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.hasDescendant 4 | import androidx.test.espresso.matcher.ViewMatchers.withId 5 | import androidx.test.espresso.matcher.ViewMatchers.withText 6 | import com.avito.android.test.InteractionContext 7 | import com.avito.android.test.page_object.ListElement 8 | import com.avito.android.test.page_object.PageObject 9 | import com.avito.android.test.page_object.ViewElement 10 | import com.avito.android.ui.R 11 | 12 | class StatefulRecyclerViewAdapterScreen : PageObject() { 13 | 14 | val list: List = element(withId(R.id.recycler)) 15 | 16 | class List(override val interactionContext: InteractionContext) : ListElement(interactionContext) { 17 | 18 | fun cellWithTitle(title: String): Cell = listElement(hasDescendant(withText(title))) 19 | 20 | fun cellWithTitleCreatedByRecyclerViewInteractionContext(title: String): ViewElement = 21 | listElement( 22 | hasDescendant(withText(title)) 23 | ) 24 | 25 | class Cell(interactionContext: InteractionContext) : ViewElement(interactionContext) { 26 | val title: ViewElement = element(withId(R.id.title)) 27 | val title2: ViewElement = element(withId(R.id.title2)) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/SwipeRefreshScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.ListElement 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.test.page_object.SwipeRefreshElement 7 | import com.avito.android.ui.R 8 | 9 | class SwipeRefreshScreen : PageObject() { 10 | val list: ListElement = element(withId(R.id.swipe_refresh)) 11 | val swipeRefreshElement: SwipeRefreshElement = element(withId(R.id.swipe_refresh)) 12 | } 13 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/SwipeRefreshTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.SwipeRefreshActivity 4 | import org.hamcrest.CoreMatchers.equalTo 5 | import org.hamcrest.MatcherAssert.assertThat 6 | import org.junit.Rule 7 | import org.junit.Test 8 | 9 | class SwipeRefreshTest { 10 | 11 | @get:Rule 12 | val rule = screenRule() 13 | 14 | @Test 15 | fun refresh_onPullToRefresh_listAction() { 16 | rule.launchActivity(null) 17 | 18 | Screen.swipeRefresh.list.actions.pullToRefresh() 19 | 20 | Screen.swipeRefresh.swipeRefreshElement.checks.isRefreshing() 21 | assertThat(rule.activity.refreshedTimes, equalTo(1)) 22 | } 23 | 24 | @Test 25 | fun refresh_onPullToRefresh_swipeRefreshAction() { 26 | rule.launchActivity(null) 27 | 28 | Screen.swipeRefresh.swipeRefreshElement.actions.pullToRefresh() 29 | 30 | assertThat(rule.activity.refreshedTimes, equalTo(1)) 31 | } 32 | 33 | @Test 34 | fun stop_refreshing_by_intention() { 35 | rule.launchActivity(null) 36 | 37 | Screen.swipeRefresh.swipeRefreshElement.actions.pullToRefresh() 38 | 39 | rule.activity.postAndStopRefreshing() 40 | 41 | Screen.swipeRefresh.swipeRefreshElement.checks.isNotRefreshing() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/TabLayoutScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.PageObject 5 | import com.avito.android.test.page_object.TabLayoutElement 6 | import com.avito.android.ui.R 7 | 8 | class TabLayoutScreen : PageObject() { 9 | val tabs: TabLayoutElement = element(withId(R.id.tabs)) 10 | } 11 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/TabLayoutTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import com.avito.android.ui.TabLayoutActivity 4 | import org.junit.Rule 5 | import org.junit.Test 6 | 7 | class TabLayoutTest { 8 | 9 | @get:Rule 10 | val rule = screenRule(launchActivity = true) 11 | 12 | @Test 13 | fun tabsCountIs1000() { 14 | Screen.tabLayoutScreen.tabs.checks.withTabsCount(1000) 15 | } 16 | 17 | @Test 18 | fun selectTab500_tabIsDisplayed() { 19 | Screen.tabLayoutScreen.tabs.select(500) 20 | Screen.tabLayoutScreen.tabs.checks.withSelectedPosition(500) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/TextElementsScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.page_object.TextElement 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.ui.R 7 | 8 | class TextElementsScreen : PageObject() { 9 | val textView: TextElement = element(withId(R.id.text_view)) 10 | val textViewLong: TextElement = element(withId(R.id.text_view_long)) 11 | } -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/UITestRunner.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.content.Intent 4 | import androidx.test.runner.AndroidJUnitRunner 5 | 6 | class UITestRunner : AndroidJUnitRunner() { 7 | 8 | override fun onStart() { 9 | super.onStart() 10 | targetContext.sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/ViewPagerScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers.withId 4 | import com.avito.android.test.InteractionContext 5 | import com.avito.android.test.page_object.PageObject 6 | import com.avito.android.test.page_object.ViewElement 7 | import com.avito.android.test.page_object.ViewPagerElement 8 | import com.avito.android.ui.R 9 | 10 | class ViewPagerScreen : PageObject() { 11 | 12 | val pager: Pager = element(withId(R.id.view_pager)) 13 | 14 | class Pager(interactionContext: InteractionContext) : ViewPagerElement(interactionContext) { 15 | val currentEvenPage: EvenPage = currentPageElement(withId(R.id.even_page_root)) 16 | val currentOddPage: OddPage = currentPageElement(withId(R.id.odd_page_root)) 17 | } 18 | 19 | class EvenPage(interactionContext: InteractionContext) : ViewElement(interactionContext) { 20 | val label: ViewElement = element(withId(R.id.view_pager_even_label)) 21 | } 22 | 23 | class OddPage(interactionContext: InteractionContext) : ViewElement(interactionContext) { 24 | val label: ViewElement = element(withId(R.id.view_pager_odd_label)) 25 | } 26 | } -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/VisibilityScreen.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import androidx.test.espresso.matcher.ViewMatchers 4 | import com.avito.android.test.page_object.PageObject 5 | import com.avito.android.test.page_object.ViewElement 6 | import com.avito.android.ui.R 7 | 8 | class VisibilityScreen : PageObject() { 9 | val label: ViewElement = element(ViewMatchers.withId(R.id.text)) 10 | } 11 | -------------------------------------------------------------------------------- /test-app/src/androidTest/java/com/avito/android/ui/test/VisibilityTest.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui.test 2 | 3 | import android.view.View 4 | import com.avito.android.ui.R 5 | import com.avito.android.ui.VisibilityActivity 6 | import junit.framework.AssertionFailedError 7 | import org.junit.Rule 8 | import org.junit.Test 9 | import org.junit.rules.ExpectedException 10 | 11 | class VisibilityTest { 12 | 13 | @get:Rule 14 | val rule = screenRule() 15 | 16 | @get:Rule 17 | val exception: ExpectedException = ExpectedException.none() 18 | 19 | @Test 20 | fun isVisible_success_forVisibleElement() { 21 | rule.launchActivity(null) 22 | 23 | Screen.visibility.label.checks.isVisible() 24 | } 25 | 26 | @Test 27 | fun isVisible_fail_forInVisibleElement() { 28 | rule.launchActivity(null) 29 | 30 | rule.runOnUiThread { 31 | rule.activity.findViewById(R.id.text).visibility = View.INVISIBLE 32 | } 33 | exception.expect(AssertionFailedError::class.java) 34 | exception.expectMessage( 35 | "'view has effective visibility=VISIBLE' " + 36 | "doesn't match the selected view." 37 | ) 38 | Screen.visibility.label.checks.isVisible() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /test-app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test-app/src/main/java/com/avito/android/ui/AppBarActivity.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.appcompat.widget.Toolbar 6 | import com.google.android.material.appbar.AppBarLayout 7 | 8 | class AppBarActivity : AppCompatActivity() { 9 | 10 | private lateinit var appBarLayout: AppBarLayout 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_app_bar) 15 | appBarLayout = findViewById(R.id.appbar) 16 | val toolbar = findViewById(R.id.toolbar) 17 | setSupportActionBar(toolbar) 18 | } 19 | 20 | fun setExpanded(isExpanded: Boolean) { 21 | appBarLayout.handler.post { 22 | appBarLayout.setExpanded(isExpanded) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test-app/src/main/java/com/avito/android/ui/ButtonsOverRecyclerActivity.kt: -------------------------------------------------------------------------------- 1 | package com.avito.android.ui 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.Button 8 | import android.widget.TextView 9 | import androidx.appcompat.app.AppCompatActivity 10 | import androidx.recyclerview.widget.LinearLayoutManager 11 | import androidx.recyclerview.widget.RecyclerView 12 | 13 | class ButtonsOverRecyclerActivity : AppCompatActivity() { 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | setContentView(R.layout.activity_buttons_over_recycler) 18 | 19 | val data = (1..99).map { it.toString() }.toList() 20 | 21 | findViewById(R.id.recycler).apply { 22 | layoutManager = LinearLayoutManager(this@ButtonsOverRecyclerActivity) 23 | adapter = Adapter(data) 24 | } 25 | 26 | findViewById