Skip to content
This repository has been archived by the owner on Jan 11, 2024. It is now read-only.

Added UI/Integration tests for Create/edit, view and list products #194

Open
wants to merge 6 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ android:
- extra-google-m2repository
- extra-android-m2repository

script: "./gradlew build --stacktrace"
script: "./gradlew build --stacktrace"
"./gradlew build connectedAndroidTest --stacktrace"
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package org.apache.fineract.ui.online.products

import android.content.Intent
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.RootMatchers.isDialog
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import org.apache.fineract.R
import org.apache.fineract.couchbase.SynchronizationManager
import org.apache.fineract.data.models.product.InterestBasis
import org.apache.fineract.data.models.product.Product
import org.apache.fineract.ui.adapters.ProductAccountAssignmentsAdapter
import org.apache.fineract.ui.product.ProductAction
import org.apache.fineract.ui.product.createproduct.CreateProductActivity
import org.apache.fineract.utils.Constants
import org.apache.fineract.utils.toDataClass
import org.junit.*
import org.junit.runner.RunWith

/**
* Created by Varun Jain on 21/8/2021.
*/

@RunWith(AndroidJUnit4::class)
class CreateEditProductAndroidTest {

@get:Rule
var activityTestRule =
ActivityTestRule<CreateProductActivity>(CreateProductActivity::class.java, false, false)

lateinit var synchronizationManager: SynchronizationManager

@Before
fun before() {

val intent = Intent().apply {
putExtra(Constants.PRODUCT_ACTION, ProductAction.CREATE)
}

activityTestRule.launchActivity(intent)

synchronizationManager =
SynchronizationManager(InstrumentationRegistry.getInstrumentation().context)
}

@Test
fun checkIfProductCreatedSuccessfully() {

// Filling all the details in Product Details Step fragment
onView(withId(R.id.etIdentifier))
.perform(typeText("test identifier"))
onView(withId(R.id.etName))
.perform(typeText("test Name"))
onView(withId(R.id.etPatternPackage))
.perform(typeText("pattern package"))
onView(withId(R.id.etDescription))
.perform(typeText("product description"))
onView(withId(R.id.etCurrencyCode))
.perform(typeText("Currency code"))
onView(withId(R.id.scrollViewProductDetailsStep))
.perform(swipeUp())
onView(withId(R.id.etParameters))
.perform(typeText("Parameters"))
onView(withId(R.id.etMinorCurrencyUnitDigits))
.perform(typeText("1000"))
onView(withId(R.id.etTemporalUnit))
.perform(typeText("Temporal unit"))
onView(withId(R.id.scrollViewProductDetailsStep))
.perform(swipeUp())
onView(withId(R.id.etTermRangeMax))
.perform(typeText("1000"))
onView(withId(R.id.etBalanceRangeMinimum))
.perform(typeText("10"))
onView(withId(R.id.etMaximumBalanceRange))
.perform(typeText("1000"))
onView(withId(R.id.scrollViewProductDetailsStep))
.perform(swipeUp())
onView(withId(R.id.etMinInterestRange))
.perform(typeText("10"))
onView(withId(R.id.etMaximumInterestRange))
.perform(typeText("1000"))
.perform(closeSoftKeyboard())

// Navigate to the next step
onView(withText("NEXT")).perform(click())

// Create, editing and deleting the Account Assignments
onView(withId(R.id.ibAddAccountAssignment))
.perform(click())
onView(withId(R.id.etDesignator))
.perform(typeText("Desig"))
onView(withId(R.id.etAccountIdentifier))
.perform(typeText("Account Identifier"))
onView(withId(R.id.scrollViewAddAccountAssignmentsStep))
.perform(swipeUp())
onView(withId(R.id.etLedgerIdentifier))
.perform(typeText("Ledger Identifier"))
onView(withId(R.id.btnAddProductAccountAssignment))
.perform(click())

// Edit an Account assignment
onView(withId(R.id.ivEditProductAccountAss))
.perform(click())
onView(withId(R.id.etDesignator))
.perform(typeText("nator"))
onView(withId(R.id.scrollViewAddAccountAssignmentsStep))
.perform(swipeUp())
onView(withId(R.id.btnAddProductAccountAssignment))
.perform(click())

// Delete a Account assignment
onView(withId(R.id.ivDeleteProductAccountAss))
.perform(click())

// Dismiss the Delete dialog
onView(withText("DELETE")).inRoot(isDialog()).check(matches(isDisplayed())).perform(pressBack());

// Navigate to the next step
onView(withText("NEXT")).perform(click())

// Test if the Product Review Step is populated with the correct values
onView(withId(R.id.tvProductIdentifier)).check(matches(withText("test identifier")))
onView(withId(R.id.tvProductName)).check(matches(withText("test Name")))
onView(withId(R.id.tvPatternPackageProduct)).check(matches(withText("pattern package")))
onView(withId(R.id.tvProductDescription)).check(matches(withText("product description")))
onView(withId(R.id.tvProductCurrencyCode)).check(matches(withText("Currency code")))
onView(withId(R.id.tvProductMinorCurrencyUnit)).check(matches(withText("1000")))
onView(withId(R.id.tvParameters)).check(matches(withText("Parameters")))
onView(withId(R.id.tvInterestBasis)).check(matches(withText(InterestBasis.BEGINNING_BALANCE.toString())))
onView(withId(R.id.scrollViewProductReviewStep))
.perform(swipeUp())
onView(withId(R.id.rvProductAccountAssignmentsStepReview))
.perform(
RecyclerViewActions.actionOnItem<ProductAccountAssignmentsAdapter.ViewHolder>(
hasDescendant(withText("Designator")), click()
)
)
onView(withId(R.id.tvProductTemporalUnit)).check(matches(withText("Temporal unit")))

// Complete defining the Product
onView(withText("COMPLETE")).perform(click())

// assert if the product has been created successfully
val mapItem = synchronizationManager.getDocumentForTest(
"test identifier",
InstrumentationRegistry.getInstrumentation().context
)

val product = mapItem.toDataClass<Product>()

Assert.assertEquals(product.name, "test Name")
Assert.assertEquals(product.patternPackage, "pattern package")
}

@After
fun after() {
//delete the created document during the test
synchronizationManager.deleteDocument(
"test identifier"
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.apache.fineract.ui.online.products

import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.contrib.DrawerActions
import androidx.test.espresso.contrib.NavigationViewActions
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import org.apache.fineract.R
import org.apache.fineract.ui.adapters.GroupsAdapter
import org.apache.fineract.ui.online.DashboardActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* Created by Varun Jain on 21/8/2021.
*/

@RunWith(AndroidJUnit4::class)
class ProductListFragmentAndroidTest {

@get:Rule
val activityRule =
ActivityTestRule<DashboardActivity>(DashboardActivity::class.java)

@Test
fun openDrawerOpenProductListClickOnRecyclerViewItem() {

//Open drawer
Espresso.onView(ViewMatchers.withId(R.id.drawer_layout))
.perform(DrawerActions.open())

//From NavigationView navigate to Products list screen
Espresso.onView(ViewMatchers.withId(R.id.nav_view))
.perform(NavigationViewActions.navigateTo(R.id.item_product))

//Click on a recycler View item
Espresso.onView(ViewMatchers.withId(R.id.rvProduct)).perform(
RecyclerViewActions.actionOnItem<GroupsAdapter.ViewHolder>(
ViewMatchers.hasDescendant(ViewMatchers.withText("product identifier")),
ViewActions.click()
)
)

//Assert if the item has been displayed correctly
Espresso.onView(ViewMatchers.withId(R.id.tvIdentifierProduct))
.check(ViewAssertions.matches(ViewMatchers.withText("product identifier")))
}
}
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@

<activity android:name=".ui.online.loanaccounts.loanapplication.loanactivity.LoanApplicationActivity" />

<activity
android:name=".ui.product.createproduct.CreateProductActivity"
android:windowSoftInputMode="adjustResize|stateHidden" />

<activity
android:name=".ui.online.customers.createcustomer.customeractivity.CreateCustomerActivity"
android:windowSoftInputMode="adjustResize|stateHidden" />
Expand Down Expand Up @@ -94,6 +98,8 @@
android:name=".ui.online.groups.creategroup.CreateGroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden" />

<activity android:name=".ui.product.productdetails.ProductDetailsActivity" />

<receiver
android:name=".jobs.JobsReceiver"
android:exported="false">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ package org.apache.fineract.couchbase

enum class DocumentType(val value: String) {
GROUP("Group"),
CUSTOMER("customer")
CUSTOMER("customer"),
PRODUCT("Product")
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package org.apache.fineract.data.models.loan

import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize

/**
* @author Rajan Maurya
* On 12/07/17.
*/

@Parcelize
data class TermRange(
@SerializedName("temporalUnit") var temporalUnit: String?,
@SerializedName("maximum") var maximum: Double?
)
) : Parcelable
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.apache.fineract.data.models.product

import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize

/**
* @author Rajan Maurya
* On 20/07/17.
*/

@Parcelize
data class BalanceRange (
@SerializedName("minimum") var minimum: Double? = null,
@SerializedName("maximum") var maximum: Double? = null
)
) : Parcelable
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.apache.fineract.data.models.product

import kotlinx.android.parcel.Parcelize

/**
* @author Rajan Maurya
* On 20/07/17.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package org.apache.fineract.data.models.product

import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize

/**
* @author Rajan Maurya
* On 20/07/17.
*/

@Parcelize
data class InterestRange (
@SerializedName("minimum") var minimum: Double? = null,
@SerializedName("maximum") var maximum: Double? = null
)
) : Parcelable
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package org.apache.fineract.data.models.product

import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.android.parcel.Parcelize
import kotlinx.android.parcel.RawValue
import org.apache.fineract.couchbase.DocumentType
import org.apache.fineract.data.models.loan.AccountAssignment
import org.apache.fineract.data.models.loan.TermRange
import java.util.ArrayList
Expand All @@ -10,22 +14,24 @@ import java.util.ArrayList
* On 20/07/17.
*/

@Parcelize
data class Product (
@SerializedName("identifier") val identifier: String? = null,
@SerializedName("name") val name: String? = null,
@SerializedName("termRange") val termRange: TermRange? = null,
@SerializedName("balanceRange") val balanceRange: BalanceRange? = null,
@SerializedName("interestRange") val interestRange: InterestRange? = null,
@SerializedName("interestBasis") val interestBasis: InterestBasis? = null,
@SerializedName("patternPackage") val patternPackage: String? = null,
@SerializedName("description") val description: String? = null,
@SerializedName("currencyCode") val currencyCode: String? = null,
@SerializedName("minorCurrencyUnitDigits") val minorCurrencyUnitDigits: Int = 0,
@SerializedName("accountAssignments") val accountAssignments: List<AccountAssignment> =
@SerializedName("identifier") var identifier: String? = null,
@SerializedName("name") var name: String? = null,
@SerializedName("termRange") var termRange: TermRange? = null,
@SerializedName("balanceRange") var balanceRange: BalanceRange? = null,
@SerializedName("interestRange") var interestRange: InterestRange? = null,
@SerializedName("interestBasis") var interestBasis: InterestBasis? = null,
@SerializedName("patternPackage") var patternPackage: String? = null,
@SerializedName("description") var description: String? = null,
@SerializedName("currencyCode") var currencyCode: String? = null,
@SerializedName("minorCurrencyUnitDigits") var minorCurrencyUnitDigits: Int = 0,
@SerializedName("accountAssignments") var accountAssignments: List<AccountAssignment> =
ArrayList(),
@SerializedName("parameters") val parameters: String? = null,
@SerializedName("createdOn") val createdOn: String? = null,
@SerializedName("createdBy") val createdBy: String? = null,
@SerializedName("lastModifiedOn") val lastModifiedOn: String? = null,
@SerializedName("lastModifiedBy") val lastModifiedBy: String? = null
)
@SerializedName("parameters") var parameters: String? = null,
@SerializedName("createdOn") var createdOn: String? = null,
@SerializedName("createdBy") var createdBy: String? = null,
@SerializedName("lastModifiedOn") var lastModifiedOn: String? = null,
@SerializedName("lastModifiedBy") var lastModifiedBy: String? = null,
var documentType: String = DocumentType.PRODUCT.value
) : Parcelable
Loading