Skip to content
This repository has been archived by the owner on May 26, 2021. It is now read-only.

Commit

Permalink
Merge pull request #203 from Vacxe/feature/view_pager_2
Browse files Browse the repository at this point in the history
Feature support for  ViewPager2
  • Loading branch information
Unlimity committed Feb 18, 2020
2 parents d50f14e + 9eb0fb9 commit 04cab97
Show file tree
Hide file tree
Showing 19 changed files with 550 additions and 4 deletions.
6 changes: 4 additions & 2 deletions buildsystem/dependencies.gradle
Expand Up @@ -12,7 +12,8 @@ ext.versions = [
espresso : '3.1.1',
espresso_rules : '1.1.1',
espresso_runner: '1.1.1',
junit : '4.12'
junit : '4.12',
viewpager2 : '1.0.0'
]

ext.libraries = [
Expand All @@ -33,7 +34,8 @@ ext.libraries = [
espresso_web : "androidx.test.espresso:espresso-web:$versions.espresso",
espresso_contrib: "androidx.test.espresso:espresso-contrib:$versions.espresso",
espresso_intents: "androidx.test.espresso:espresso-intents:$versions.espresso",
junit : "junit:junit:$versions.junit"
junit : "junit:junit:$versions.junit",
viewpager2 : "androidx.viewpager2:viewpager2:$versions.viewpager2"
]

def alias = System.getenv('BINTRAY_ALIAS')
Expand Down
1 change: 1 addition & 0 deletions kakao/build.gradle
Expand Up @@ -34,6 +34,7 @@ dependencies {
implementation libraries.espresso_web
implementation libraries.espresso_intents
implementation libraries.espresso_contrib
implementation libraries.viewpager2
}

apply plugin: 'com.github.dcendents.android-maven'
Expand Down
@@ -0,0 +1,31 @@
@file:Suppress("unused")

package com.agoda.kakao.common.matchers

import android.view.View
import androidx.test.espresso.matcher.BoundedMatcher
import androidx.viewpager2.widget.ViewPager2
import org.hamcrest.Description

/**
* Matches ViewPager2 with count of children
*
* @param size of children count in ViewPager2
*/
class ViewPager2AdapterSizeMatcher(private val size: Int) : BoundedMatcher<View, ViewPager2>(ViewPager2::class.java) {

private var itemCount: Int = 0

override fun matchesSafely(view: ViewPager2) = run {
itemCount = view.adapter?.itemCount ?: 0
itemCount == size
}

override fun describeTo(description: Description) {
description
.appendText("ViewPager2 with ")
.appendValue(size)
.appendText(" item(s), but got with ")
.appendValue(itemCount)
}
}
@@ -0,0 +1,16 @@
@file:Suppress("unused")

package com.agoda.kakao.pager2

import android.view.View
import org.hamcrest.Matcher

/**
* Empty implementation of KViewPagerItem
*
* Use this if you want to perform/assert on the root view of view holder
*
* @param parent Matcher of the root view of view holder
* @see KViewPagerItem
*/
class KEmptyViewPagerItem(parent: Matcher<View>) : KViewPagerItem<KEmptyViewPagerItem>(parent)
132 changes: 132 additions & 0 deletions kakao/src/main/kotlin/com/agoda/kakao/pager2/KViewPager2.kt
@@ -0,0 +1,132 @@
@file:Suppress("unused")

package com.agoda.kakao.pager2

import android.view.View
import androidx.test.espresso.DataInteraction
import androidx.test.espresso.Root
import androidx.test.espresso.matcher.RootMatchers
import com.agoda.kakao.common.KakaoDslMarker
import com.agoda.kakao.common.actions.SwipeableActions
import com.agoda.kakao.common.assertions.BaseAssertions
import com.agoda.kakao.common.builders.ViewBuilder
import com.agoda.kakao.delegate.ViewInteractionDelegate
import org.hamcrest.Matcher
import kotlin.reflect.KClass

/**
* View with SwipeableActions and ViewPager2Assertions
*
* @see SwipeableActions
*/
@KakaoDslMarker
class KViewPager2 : ViewPager2Actions, ViewPager2AdapterAssertions, SwipeableActions, BaseAssertions {
val matcher: Matcher<View>
val itemTypes: Map<KClass<out KViewPagerItem<*>>, KViewPagerItemType<KViewPagerItem<*>>>

override val view: ViewInteractionDelegate
override var root: Matcher<Root> = RootMatchers.DEFAULT

/**
* Constructs view class with view interaction from given ViewBuilder
*
* @param builder ViewBuilder which will result in view's interaction
* @param itemTypeBuilder Lambda with receiver where you pass your item providers
*
* @see ViewBuilder
*/
constructor(builder: ViewBuilder.() -> Unit, itemTypeBuilder: KViewPagerItemTypeBuilder.() -> Unit) {
val vb = ViewBuilder().apply(builder)
matcher = vb.getViewMatcher()
view = vb.getViewInteractionDelegate()
itemTypes = KViewPagerItemTypeBuilder().apply(itemTypeBuilder).itemTypes
}

/**
* Constructs view class with parent and view interaction from given ViewBuilder
*
* @param parent Matcher that will be used as parent in isDescendantOfA() matcher
* @param builder ViewBuilder which will result in view's interaction
* @param itemTypeBuilder Lambda with receiver where you pass your item providers
*
* @see ViewBuilder
*/
constructor(
parent: Matcher<View>, builder: ViewBuilder.() -> Unit,
itemTypeBuilder: KViewPagerItemTypeBuilder.() -> Unit
) : this({
isDescendantOfA { withMatcher(parent) }
builder(this)
}, itemTypeBuilder)

/**
* Constructs view class with parent and view interaction from given ViewBuilder
*
* @param parent DataInteraction that will be used as parent to ViewBuilder
* @param builder ViewBuilder which will result in view's interaction
* @param itemTypeBuilder Lambda with receiver where you pass your item providers
*
* @see ViewBuilder
*/
@Suppress("UNCHECKED_CAST")
constructor(
parent: DataInteraction, builder: ViewBuilder.() -> Unit,
itemTypeBuilder: KViewPagerItemTypeBuilder.() -> Unit
) {
val makeTargetMatcher = DataInteraction::class.java.getDeclaredMethod("makeTargetMatcher")
val parentMatcher = makeTargetMatcher.invoke(parent)

val vb = ViewBuilder().apply {
isDescendantOfA { withMatcher(parentMatcher as Matcher<View>) }
builder(this)
}

matcher = vb.getViewMatcher()
view = vb.getViewInteractionDelegate()
itemTypes = KViewPagerItemTypeBuilder().apply(itemTypeBuilder).itemTypes
}

/**
* Performs given actions/assertion on child at given position
*
* @param T Type of item at given position. Must be registered via constructor.
* @param position Position of item in adapter
* @param function Tail lambda which receiver will be matched item with given type T
*/
inline fun <reified T : KViewPagerItem<*>> childAt(position: Int, function: T.() -> Unit) {
val provideItem = itemTypes.getOrElse(T::class) {
throw IllegalStateException("${T::class.java.simpleName} did not register to KViewPager2")
}.provideItem

try {
scrollTo(position)
} catch (error: Throwable) {
}

function(provideItem(matcher) as T).also { inRoot { withMatcher(this@KViewPager2.root) } }
}

/**
* Operator that allows usage of DSL style
*
* @param function Tail lambda with receiver which is your view
*/
operator fun invoke(function: KViewPager2.() -> Unit) {
function(this)
}

/**
* Infix function for invoking lambda on your view
*
* Sometimes instance of view is a result of a function or constructor.
* In this specific case you can't call invoke() since it will be considered as
* tail lambda of your fun/constructor. In such cases please use this function.
*
* @param function Tail lambda with receiver which is your view
* @return This object
*/
infix fun perform(function: KViewPager2.() -> Unit): KViewPager2 {
function(this)
return this
}
}
58 changes: 58 additions & 0 deletions kakao/src/main/kotlin/com/agoda/kakao/pager2/KViewPagerItem.kt
@@ -0,0 +1,58 @@
@file:Suppress("unused")

package com.agoda.kakao.pager2

import android.view.View
import androidx.test.espresso.Espresso
import androidx.test.espresso.ViewAction
import androidx.test.espresso.ViewAssertion
import androidx.test.espresso.ViewInteraction
import androidx.test.espresso.matcher.RootMatchers
import com.agoda.kakao.common.KakaoDslMarker
import com.agoda.kakao.common.actions.BaseActions
import com.agoda.kakao.common.assertions.BaseAssertions
import com.agoda.kakao.delegate.ViewInteractionDelegate
import com.agoda.kakao.intercept.Interceptable
import org.hamcrest.Matcher

/**
* Base class for KViewPager2 adapter items
*
* Please extend this class to provide custom view pager 2 view item types
*
* @param T type of your item. Used to enable invoke() and perform() on descendants
* @param matcher Matcher of root view of adapter item. Can be used as parent for all views inside item.
*
* @see KViewPagerItemTypeBuilder
*/
@Suppress("UNCHECKED_CAST")
@KakaoDslMarker
open class KViewPagerItem<out T>(matcher: Matcher<View>) : BaseActions, BaseAssertions,
Interceptable<ViewInteraction, ViewAssertion, ViewAction> {
override val view = ViewInteractionDelegate(Espresso.onView(matcher))
override var root = RootMatchers.DEFAULT

/**
* Operator that allows usage of DSL style
*
* @param function Tail lambda with receiver which is your view
*/
operator fun invoke(function: T.() -> Unit) {
function(this as T)
}

/**
* Infix function for invoking lambda on your view
*
* Sometimes instance of view is a result of a function or constructor.
* In this specific case you can't call invoke() since it will be considered as
* tail lambda of your fun/constructor. In such cases please use this function.
*
* @param function Tail lambda with receiver which is your view
* @return This object
*/
infix fun perform(function: T.() -> Unit): T {
function(this as T)
return this
}
}
13 changes: 13 additions & 0 deletions kakao/src/main/kotlin/com/agoda/kakao/pager2/KViewPagerItemType.kt
@@ -0,0 +1,13 @@
@file:Suppress("unused")

package com.agoda.kakao.pager2

import android.view.View
import org.hamcrest.Matcher

/**
* For internal use. Don't use manually.
*
* Holds type and corresponding provider function
*/
class KViewPagerItemType<out T : KViewPagerItem<*>>(val provideItem: (Matcher<View>) -> T)
@@ -0,0 +1,40 @@
@file:Suppress("unused")

package com.agoda.kakao.pager2

import android.view.View
import com.agoda.kakao.common.KakaoDslMarker
import org.hamcrest.Matcher
import kotlin.reflect.KClass

/**
* Class that maps types to providing functions
*
* To be able to support different item types in KViewPager2, this class
* adds support for mapping item type classes to functions that provide them.
* KEmptyViewPagerItem is added by default.
*
* @see itemType
* @see KEmptyViewPagerItem
*/
@KakaoDslMarker
class KViewPagerItemTypeBuilder {
val itemTypes = mutableMapOf<KClass<out KViewPagerItem<*>>, KViewPagerItemType<KViewPagerItem<*>>>()

init {
itemTypes[KViewPagerItem::class] = KViewPagerItemType { matcher -> KEmptyViewPagerItem(matcher) }
itemTypes[KEmptyViewPagerItem::class] = KViewPagerItemType { matcher -> KEmptyViewPagerItem(matcher) }
}

/**
* Adds entry that helps KViewPager2 to automatically build child views
*
* To make it work, you need to pass here function (lambda, constructor), that takes matcher and returns
* instance of your item type. In this case, matcher actually matches root view of your adapter item.
*
* @param provideItem Function that takes matcher of item's root view and returns instance of item view
*/
inline fun <reified T : KViewPagerItem<*>> itemType(noinline provideItem: (Matcher<View>) -> T) {
itemTypes[T::class] = KViewPagerItemType(provideItem)
}
}

0 comments on commit 04cab97

Please sign in to comment.