Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

US1955755: add refactored tests under new card config integration tes… #230

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions demo-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ android {
}

testOptions {
animationsDisabled = true
unitTests {
includeAndroidResources = true
returnDefaultValues = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class CardFragmentTestUtils(activityRule: ActivityTestRule<MainActivity>) : Abst
return this
}

fun isInErrorState(pan: String? = null, cvc: String? = null, expiryDate: String? = null): CardFragmentTestUtils {
fun areFieldsEnabledAndAssertCardDetails(pan: String? = null, cvc: String? = null, expiryDate: String? = null): CardFragmentTestUtils {
progressBarNotVisible()
enabledStateIs(pan = true, cvc = true, expiryDate = true, paymentsCvcSwitch = true, submitButton = true)
cardDetailsAre(pan, cvc, expiryDate)
Expand Down Expand Up @@ -202,12 +202,12 @@ class CardFragmentTestUtils(activityRule: ActivityTestRule<MainActivity>) : Abst

fun hasNoBrand(): CardFragmentTestUtils {
val resourceEntryName = activity().resources.getResourceEntryName(R.drawable.card_unknown_logo)
wait { assertEquals(resourceEntryName, brandLogo().getTag(R.integer.card_tag)) }
wait { assertEquals(resourceEntryName, brandLogo().tag) }
return this
}

fun hasBrand(cardBrand: CardBrand): CardFragmentTestUtils {
wait(maxWaitTimeInMillis = 20000) { assertEquals(cardBrand.cardBrandName, brandLogo().getTag(R.integer.card_tag)) }
wait(maxWaitTimeInMillis = 20000) { assertEquals(cardBrand.cardBrandName, brandLogo().tag) }
return this
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package com.worldpay.access.checkout.sample.card.standard.testutil

import android.view.View
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withTagValue
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.worldpay.access.checkout.sample.MainActivity
import com.worldpay.access.checkout.sample.R
import com.worldpay.access.checkout.sample.testutil.NewAbstractFragmentTestUtils
import com.worldpay.access.checkout.sample.testutil.UITestUtils.uiObjectWithId
import com.worldpay.access.checkout.ui.AccessCheckoutEditText
import org.hamcrest.Matcher
import org.hamcrest.Matchers

class NewCardFragmentTestUtils(activityRule: ActivityScenarioRule<MainActivity>) : NewAbstractFragmentTestUtils(activityRule) {

enum class Input {
PAN, CVC, EXPIRY_DATE
}

fun isInInitialState() = apply {
progressBarNotVisible()
enabledStateIs(pan = true, cvc = true, expiryDate = true, submitButton = false)
cardDetailsAre(pan = "", cvc = "", expiryDate = "")
hasNoBrand()
paymentsCvcSwitchIs(checked = false)
}

fun setPaymentsCvcSwitchState(checked: Boolean) = apply {
waitForAssertion { onView(withId(R.id.card_flow_payments_cvc_switch)).check(matches(isDisplayed())) }

if (checked != withId(R.id.card_flow_payments_cvc_switch).matches(isChecked()))
uiObjectWithId(R.id.card_flow_payments_cvc_switch).click()
}

fun paymentsCvcSwitchIs(checked: Boolean) = apply {
val checkedViewMatcher = if (checked) isChecked() else isNotChecked()
waitForAssertion { onView(withId(R.id.card_flow_payments_cvc_switch)).check(matches(isDisplayed())) }
waitForAssertion { onView(withId(R.id.card_flow_payments_cvc_switch)).check(matches(checkedViewMatcher)) }
}

fun requestIsInProgress() = apply {
progressBarIsVisible()
enabledStateIs(pan = false, cvc = false, expiryDate = false, paymentsCvcSwitch = false, submitButton = false)
}

fun hasResponseDialogWithMessage(response: String) = apply {
dialogHasText(response)
}

fun hasErrorDialogWithMessage(error: String) = apply {
dialogHasText(error)
}

fun closeDialog() = apply {
onView(withId(android.R.id.button1)).perform(click())
}

fun areFieldsEnabledAndAssertCardDetails(pan: String? = null, cvc: String? = null, expiryDate: String? = null) = apply {
progressBarNotVisible()
enabledStateIs(pan = true, cvc = true, expiryDate = true, paymentsCvcSwitch = true, submitButton = true)
cardDetailsAre(pan, cvc, expiryDate)
}

fun enabledStateIs(
pan: Boolean? = null,
cvc: Boolean? = null,
expiryDate: Boolean? = null,
paymentsCvcSwitch: Boolean? = null,
submitButton: Boolean? = null
) = apply {
lateinit var enabledViewMatcher: Matcher<View>
lateinit var viewMatcher: Matcher<View>

val accessCheckoutFieldViewMatcher: AccessCheckoutFieldViewMatcher = when {
pan != null -> AccessCheckoutFieldViewMatcher.PanViewMatcher(pan)
cvc != null -> AccessCheckoutFieldViewMatcher.CvcViewMatcher(cvc)
expiryDate != null -> AccessCheckoutFieldViewMatcher.ExpiryDateViewMatcher(expiryDate)
paymentsCvcSwitch != null -> AccessCheckoutFieldViewMatcher.SwitchViewMatcher(paymentsCvcSwitch)
submitButton != null -> AccessCheckoutFieldViewMatcher.SubmitButtonViewMatcher(submitButton)
else -> throw RuntimeException("field view matcher not recognised")
}

waitForAssertion { onView(accessCheckoutFieldViewMatcher.viewMatcher).check(matches(isDisplayed())) }
waitForAssertion { onView(accessCheckoutFieldViewMatcher.viewMatcher).check(matches(accessCheckoutFieldViewMatcher.enabledViewMatcher)) }

}

fun clickSubmitButton() = apply {
enabledStateIs(submitButton = true)
uiObjectWithId(R.id.card_flow_btn_submit).click()
}

fun focusOn(input: Input) = apply {
when (input) {
Input.PAN -> uiObjectWithId(R.id.card_flow_text_pan).click()
Input.CVC -> uiObjectWithId(R.id.cvc_flow_text_cvc).click()
Input.EXPIRY_DATE -> uiObjectWithId(R.id.card_flow_expiry_date).click()
}
}

fun enterCardDetails(
pan: String? = null,
cvc: String? = null,
expiryDate: String? = null,
assertText: Boolean = false
) = apply {
if (pan != null) enterText(R.id.card_flow_text_pan, pan)
ochalet-wp marked this conversation as resolved.
Show resolved Hide resolved
if (cvc != null) enterText(R.id.card_flow_text_cvc, cvc)
if (expiryDate != null) enterText(R.id.card_flow_expiry_date, expiryDate)

if (assertText) {
cardDetailsAre(pan, cvc, expiryDate)
}
}

fun enterPanCvcExpirydate(pan: String, cvc: String, expiryDate: String) = apply {
assertViewIsVisible(R.id.card_flow_text_pan)
assertViewIsVisible(R.id.card_flow_text_cvc)
assertViewIsVisible(R.id.card_flow_expiry_date)
enterTextOnViewWithId(pan, R.id.card_flow_text_pan)
enterTextOnViewWithId(cvc, R.id.card_flow_text_cvc)
enterTextOnViewWithId(expiryDate, R.id.card_flow_expiry_date)
}

fun clearCardDetails(
pan: Boolean? = null,
cvc: Boolean? = null,
expiryDate: Boolean? = null
) {
if (pan == true) clearText(R.id.card_flow_text_pan)
if (expiryDate == true) clearText(R.id.card_flow_expiry_date)
if (cvc == true) clearText(R.id.card_flow_text_cvc)
}

fun setCursorPositionOnPan(position: Int) = apply {
setCursorPosition(R.id.card_flow_text_pan, position, position)
}

fun cursorPositionIs(position: Int) = apply {
waitForAssertion { onView(withId(R.id.card_flow_text_pan)).check { view, noViewFoundException ->
val isPosition = (view as AccessCheckoutEditText).selectionEnd == position
isPosition
}
}
}

fun cardDetailsAre(pan: String? = null, cvc: String? = null, expiryDate: String? = null) = apply {
if (pan != null) waitForAssertion { onView(withText(pan)).check(matches(isDisplayed())) }
if (pan != null) waitForAssertion { onView(withText(cvc)).check(matches(isDisplayed())) }
if (pan != null) waitForAssertion { onView(withText(expiryDate)).check(matches(isDisplayed())) }
}

fun validationStateIs(pan: Boolean? = null, cvc: Boolean? = null, expiryDate: Boolean? = null) = apply {
if (pan != null) checkValidationState(withParent(withId(R.id.card_flow_text_pan)), pan, "pan")
if (cvc != null) checkValidationState(withParent(withId(R.id.card_flow_text_cvc)), cvc, "cvc")
if (expiryDate != null) checkValidationState(withParent(withId(R.id.card_flow_expiry_date)), expiryDate, "expiry date")
}

fun hasNoBrand() = apply {
var resourceEntryName = ""
activityRule.scenario.onActivity(ActivityScenario.ActivityAction { activity ->
resourceEntryName = activity.resources.getResourceEntryName(R.drawable.card_unknown_logo)
})
waitForAssertion { onView(withId(R.id.card_flow_brand_logo)).check(matches(withTagValue(Matchers.`is`(resourceEntryName)))) }
}

fun hasBrand(cardBrand: CardBrand) = apply {
waitForAssertion(20000) { onView(withId(R.id.card_flow_brand_logo)).check(matches(withTagValue(Matchers.`is`(cardBrand.cardBrandName)))) }
}

sealed class AccessCheckoutFieldViewMatcher(val viewMatcher: Matcher<View>, val isEnabled: Boolean) {
val enabledViewMatcher: Matcher<View> = if (isEnabled) isEnabled() else isNotEnabled()
class PanViewMatcher(isEnabled: Boolean) : AccessCheckoutFieldViewMatcher(withParent(withId(R.id.card_flow_text_pan)), isEnabled)
class CvcViewMatcher(isEnabled: Boolean) : AccessCheckoutFieldViewMatcher(withParent(withId(R.id.card_flow_text_cvc)), isEnabled)
class ExpiryDateViewMatcher(isEnabled: Boolean) : AccessCheckoutFieldViewMatcher(withParent(withId(R.id.card_flow_expiry_date)), isEnabled)
class SwitchViewMatcher(isEnabled: Boolean) : AccessCheckoutFieldViewMatcher(withId(R.id.card_flow_payments_cvc_switch), isEnabled)
class SubmitButtonViewMatcher(isEnabled: Boolean) : AccessCheckoutFieldViewMatcher(withId(R.id.card_flow_btn_submit), isEnabled)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.worldpay.access.checkout.sample.testutil

import android.util.Log
import android.view.View
import android.widget.EditText
import androidx.core.content.res.ResourcesCompat
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import androidx.test.espresso.action.ViewActions.typeText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.hasTextColor
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import com.worldpay.access.checkout.sample.MainActivity
import com.worldpay.access.checkout.sample.R
import org.hamcrest.Matcher
import kotlin.test.assertTrue

abstract class NewAbstractFragmentTestUtils(protected val activityRule: ActivityScenarioRule<MainActivity>) {
private fun progressBar() = UITestUtils.uiObjectWithId(R.id.loading_bar)

protected fun progressBarIsVisible(msToWait: Long = 3000L): Boolean {
return progressBar().waitForExists(msToWait)
}

protected fun progressBarNotVisible() {
waitForAssertion { assertTrue(progressBar().waitUntilGone(3000)) }
}

protected fun checkValidationState(
viewMatcher: Matcher<View>,
isValid: Boolean,
field: String
) = also {
waitForAssertion {
onView(viewMatcher).check(matches(hasTextColor(
when (isValid) {
true -> R.color.SUCCESS
false -> R.color.FAIL
}
)))
}
}

protected fun enterText(id: Int, text: String) {
waitForAssertion { onView(withId(id)).check(matches(isDisplayed())) }
waitForAssertion { onView(withId(id)).check(matches(isEnabled())) }

val editTextUI = UITestUtils.uiObjectWithId(id)
editTextUI.click()

onView(ViewMatchers.withParent(withId(id))).perform(typeText(text))
onView(ViewMatchers.withParent(withId(id))).perform(closeSoftKeyboard())
}

protected fun enterTextOnViewWithId(text: String, id: Int) = also {
onView(withId(id))
.perform(typeText(text))
}

protected fun assertViewIsVisible(id: Int) {
waitForAssertion { onView(withId(id))
.check(matches(isDisplayed())) }
}

protected fun clearText(id: Int) {
waitForAssertion { onView(withId(id)).check(matches(isDisplayed())) }
waitForAssertion { onView(withId(id)).check(matches(isEnabled())) }

val editTextUI = UITestUtils.uiObjectWithId(id)
editTextUI.click()
onView(withId(id)).perform(ViewActions.clearText())
}

protected fun setCursorPosition(
id: Int,
startSelection: Int,
endSelection: Int
) {
activityRule.scenario.onActivity { mainActivity ->
mainActivity.runOnUiThread {
onView(withId(id)).perform(
object : ViewAction {
override fun getConstraints(): Matcher<View> {
return withId(id)
}

override fun getDescription(): String {
return "set selection for view with $id"
}

override fun perform(uiController: UiController?, view: View?) {
if (view is EditText) {
view.setSelection(startSelection, endSelection)
}
}

}
)
}
}
}

protected fun dialogHasText(text: String) {
onView(withText(text))
.inRoot(isDialog())
.check(matches(isDisplayed()))
}

private fun color(colorId: Int) : Int {
var colorInt = 0
activityRule.scenario.onActivity { mainActivity ->
colorInt = ResourcesCompat.getColor(mainActivity.resources, colorId, mainActivity.theme)
}
return colorInt
}

protected inline fun waitForAssertion(maxWaitTimeInMillis: Int = 1000, assertions: () -> Unit) {
val pauseInterval = 100
val maxTimes = maxWaitTimeInMillis / pauseInterval

for (i in 0..maxTimes) {
try {
assertions()
} catch (exception: AssertionError) {
if (i == maxTimes) {
val seconds = maxWaitTimeInMillis / 1000
throw AssertionError(
"Failed assertion after waiting $seconds seconds: ${exception.message}",
exception
)
} else {
Thread.sleep(pauseInterval.toLong())
Log.d(javaClass.simpleName, "Retrying assertion with pause interval: $pauseInterval")
continue
}
}
break
}
}
}
Loading