Skip to content

Commit

Permalink
Merge pull request #34 from Lambda-School-Labs/ui_tests
Browse files Browse the repository at this point in the history
UI Integration Tests
  • Loading branch information
TylerBerrett committed Feb 7, 2020
2 parents 6975c6d + 5aafb7b commit 7819295
Show file tree
Hide file tree
Showing 7 changed files with 516 additions and 5 deletions.
16 changes: 11 additions & 5 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,17 @@ dependencies {


// Testing
testImplementation 'junit:junit:4.12'
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'org.mockito:mockito-core:3.0.0'


androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0'
androidTestImplementation 'androidx.test:rules:1.3.0-alpha03'

testImplementation 'junit:junit:4.13'
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'org.mockito:mockito-core:3.0.0'
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'androidx.test.ext:truth:1.2.0'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.lambda_labs.community_calendar.util

import android.view.View
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
import com.google.android.material.chip.Chip
import org.hamcrest.Matcher


/**
* Click the close icon for the Chip that this action is being performed on
*/
class ClickChipCloseIcon : ViewAction {

override fun getConstraints(): Matcher<View> {
return isAssignableFrom(Chip::class.java)
}

override fun getDescription(): String {
return "Clicked close icon on chip"
}

override fun perform(uiController: UiController, view: View) {
val chip: Chip = view as Chip//we matched
chip.performCloseIconClick()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.lambda_labs.community_calendar.util

import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.DatePicker
import androidx.test.espresso.matcher.BoundedMatcher
import androidx.test.platform.app.InstrumentationRegistry
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
import java.text.SimpleDateFormat
import java.util.*


object Extra {

fun getChildAtPosition(parentMatcher: Matcher<View>, position: Int): Matcher<View> {
return object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("Child at position $position in parent ")
parentMatcher.describeTo(description)
}

public override fun matchesSafely(view: View): Boolean {
val parent = view.parent
return (parent is ViewGroup) && parentMatcher.matches(parent)
&& view == parent.getChildAt(position)
}
}
}

fun matchesDate(year: Int, month: Int, day: Int): Matcher<View?> {
return object : BoundedMatcher<View?, DatePicker>(DatePicker::class.java) {
override fun describeTo(description: Description) {
description.appendText("matches date:")
}

override fun matchesSafely(dp: DatePicker): Boolean {
return (year == dp.year) && (month == dp.month) && (day == dp.dayOfMonth)
}
}
}

fun get_resource_id_from_string(idString: String): Int {
val targetContext: Context = InstrumentationRegistry.getInstrumentation().context
val packageName: String = targetContext.packageName
return targetContext.resources.getIdentifier(idString, "id", packageName)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.lambda_labs.community_calendar.util

import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.Drawable
import android.view.View
import android.widget.ImageView
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher

/**
* Allow drawables to be checked in Espresso tests
* @see Gist https://gist.github.com/RyanBurnsworth/9bf15ebd29c321b4e5517b98f5142b99
*/
class ImageViewMatcher {
companion object {
fun withDrawable(resourceId: Int): Matcher<View> = DrawableMatcher(resourceId)

fun noDrawable(): Matcher<View> = DrawableMatcher(-1)
}

open class DrawableMatcher(private val resourceId: Int) : TypeSafeMatcher<View>() {
private var expectedId = 0

init {
expectedId = resourceId
}

override fun describeTo(description: Description?) {
description?.appendText("with drawable from resource id: ")
description?.appendValue(expectedId)
}

override fun matchesSafely(item: View?): Boolean {
if (!(item != null && item is ImageView))
return false

val imageView = item as ImageView
if (expectedId < 0)
return imageView.drawable == null

val resources = item.getContext().resources
val expectedDrawable = resources.getDrawable(expectedId) ?: return false
val bitmap = getBitmap(imageView.drawable)
val otherBitmap = getBitmap(expectedDrawable)
return bitmap.sameAs(otherBitmap)
}

private fun getBitmap(drawable: Drawable): Bitmap {
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package com.lambda_labs.community_calendar.view

import android.view.View
import android.widget.DatePicker
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.contrib.PickerActions
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.lambda_labs.community_calendar.R
import com.lambda_labs.community_calendar.util.ClickChipCloseIcon
import com.lambda_labs.community_calendar.util.Extra.getChildAtPosition
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.*
import kotlin.random.Random


@RunWith(AndroidJUnit4::class)
@LargeTest
class FilterFragmentTest {

@Rule // Rule for JUnit
@JvmField // For Kotlin compatibility
var activityScenarioRule: ActivityScenarioRule<MainActivity> =
ActivityScenarioRule(MainActivity::class.java) // To wrap up the activity

companion object {
private const val APPLIED_FILTER_COUNT_TEXT = "Filters (1)"
}

@Before
fun navigate_to_the_filter_fragment() {
Thread.sleep(5000)
onView(withId(R.id.search_bar))
.perform(click())
Thread.sleep(2000)

onView(withId(R.id.btn_filters))
.perform(click())
Thread.sleep(2000)
}

@Test
fun select_one_chip_and_apply() {
val chipGroupSuggestedMatcher: Matcher<View> =
withId(R.id.chip_group_fragment_filter_suggested)
val chipStartCount = 10
val chipIndexToShift: Int = Random.nextInt(0, chipStartCount)
onView(chipGroupSuggestedMatcher)
.check(matches(isDisplayed()))
.check(matches(hasChildCount(chipStartCount)))
Thread.sleep(1000)

val chipMatcher: Matcher<View> =
getChildAtPosition(chipGroupSuggestedMatcher, chipIndexToShift)
val clickChipCloseIcon: ViewAction = ClickChipCloseIcon()
onView(chipMatcher)
.perform(clickChipCloseIcon)
onView(chipGroupSuggestedMatcher)
.check(matches(hasChildCount(chipStartCount - 1)))
onView(withId(R.id.chip_group_fragment_filter_added))
.check(matches(isDisplayed()))
.check(matches(hasChildCount(1)))
Thread.sleep(1000)

onView(withId(R.id.button_fragment_filter_apply))
.perform(click())
Thread.sleep(2000)

onView(withId(R.id.filter_count))
.check(matches(withText(APPLIED_FILTER_COUNT_TEXT)))
Thread.sleep(1000)
}

@Test
fun select_a_location_and_apply() {
onView(withId(R.id.spinner_fragment_filter_location))
.perform(click())

val className = "android.widget.PopupWindow\$PopupBackgroundView"
onData(Matchers.anything())
.inAdapterView(getChildAtPosition(withClassName(Matchers.`is`(className)), 0))
.atPosition(1)
.perform(click())
Thread.sleep(1000)

onView(withId(R.id.button_fragment_filter_apply))
.perform(click())
Thread.sleep(2000)

onView(withId(R.id.filter_count))
.check(matches(withText(APPLIED_FILTER_COUNT_TEXT)))
Thread.sleep(1000)
}

@Test
fun type_in_a_zip_code_and_apply() {
onView(withId(R.id.edit_text_fragment_filter_zip_code))
.perform(typeText("98207"))
Thread.sleep(1000)

onView(isRoot())
.perform(closeSoftKeyboard())
Thread.sleep(1000)

onView(withId(R.id.button_fragment_filter_apply))
.perform(click())
Thread.sleep(2000)

onView(withId(R.id.filter_count))
.check(matches(withText(APPLIED_FILTER_COUNT_TEXT)))
Thread.sleep(1000)
}

@Test
fun pick_a_date_from_the_picker_and_apply() {
onView(withId(R.id.image_view_fragment_filter_date))
.perform(click())
Thread.sleep(1000)

val randomCal: Calendar = Calendar.getInstance()
randomCal.isLenient = true
randomCal.set(Calendar.YEAR, Random.nextInt(1900, 2022))
randomCal.set(Calendar.MONTH, Random.nextInt(0, 11))
randomCal.set(Calendar.DAY_OF_MONTH, Random.nextInt(1, 31))

val datePickerMatcher: Matcher<View> =
withClassName(Matchers.equalTo(DatePicker::class.java.name))
onView(datePickerMatcher)
.perform(
PickerActions.setDate(
randomCal.get(Calendar.YEAR),
randomCal.get(Calendar.MONTH),
randomCal.get(Calendar.DAY_OF_MONTH)
)
)

onView(withId(android.R.id.button1))
.perform(click())
Thread.sleep(1000)

onView(withId(R.id.button_fragment_filter_apply))
.perform(click())
Thread.sleep(2000)

onView(withId(R.id.filter_count))
.check(matches(withText(APPLIED_FILTER_COUNT_TEXT)))
Thread.sleep(1000)
}

//TODO: Test needed for testing the 'Search for Tags' search bar
}

0 comments on commit 7819295

Please sign in to comment.