Make all UI tests pass and add more tests (#2700)

This commit is contained in:
Vivek Maskara 2019-03-24 03:08:06 +05:30 committed by Adam Jones
parent 239f74942f
commit f7e6b20cab
8 changed files with 214 additions and 60 deletions

View file

@ -74,6 +74,7 @@ dependencies {
androidTestImplementation 'androidx.annotation:annotation:1.0.2'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
androidTestImplementation 'org.mockito:mockito-core:2.10.0'
androidTestUtil 'androidx.test:orchestrator:1.1.1'
// Debugging
implementation 'com.facebook.stetho:stetho:1.5.0'
@ -109,6 +110,12 @@ android {
minSdkVersion 19
targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
vectorDrawables.useSupportLibrary = true
}

View file

@ -0,0 +1,38 @@
package fr.free.nrw.commons
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.contrib.DrawerActions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.MediumTest
import androidx.test.runner.AndroidJUnit4
import fr.free.nrw.commons.achievements.AchievementsActivity
import fr.free.nrw.commons.auth.LoginActivity
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@MediumTest
@RunWith(AndroidJUnit4::class)
class AchievementsActivityTest {
@get:Rule
var activityRule = IntentsTestRule(LoginActivity::class.java)
@Before
fun setup() {
UITestHelper.skipWelcome()
UITestHelper.loginUser()
}
@Test
fun testAchievements() {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
onView(withId(R.id.user_icon)).perform(click())
Intents.intended(hasComponent(AchievementsActivity::class.java.name))
}
}

View file

@ -1,36 +1,57 @@
package fr.free.nrw.commons
import android.app.Activity
import android.app.Instrumentation.ActivityResult
import android.content.Intent
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.Intents.intending
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.intent.matcher.IntentMatchers.isInternal
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.LargeTest
import androidx.test.filters.MediumTest
import androidx.test.rule.ActivityTestRule
import androidx.test.runner.AndroidJUnit4
import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.auth.SignupActivity
import fr.free.nrw.commons.contributions.MainActivity
import org.hamcrest.CoreMatchers
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@LargeTest
@MediumTest
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@get:Rule
var activity: ActivityTestRule<*> = ActivityTestRule(LoginActivity::class.java)
var activityRule = ActivityTestRule(LoginActivity::class.java)
@Before
fun setup() {
try {
Intents.init()
} catch (ex: IllegalStateException) {
}
UITestHelper.skipWelcome()
intending(not(isInternal())).respondWith(ActivityResult(Activity.RESULT_OK, null))
}
@Test
fun isSignUpButtonWorks() {
// Clicks the SignUp Button
Intents.init()
Espresso.onView(withId(R.id.signupButton))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
.perform(click())
intended(hasComponent(SignupActivity::class.java.name))
Intents.release()
fun testLogin() {
UITestHelper.loginUser()
UITestHelper.sleep(10000)
Intents.intended(hasComponent(MainActivity::class.java.name))
}
@Test
fun testForgotPassword() {
Espresso.onView(ViewMatchers.withId(R.id.forgotPassword))
.perform(ViewActions.click())
Intents.intended(CoreMatchers.allOf(IntentMatchers.hasAction(Intent.ACTION_VIEW), IntentMatchers.hasData(BuildConfig.FORGOT_PASSWORD_URL)));
}
}

View file

@ -60,6 +60,7 @@ class NavigationBaseActivityTest {
private fun openNavigationDrawerAndNavigateTo(menuItemId: Int) {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
UITestHelper.sleep(500)
onView(withId(R.id.navigation_view)).perform(NavigationViewActions.navigateTo(menuItemId))
}
}

View file

@ -123,8 +123,8 @@ class SettingsActivityTest {
@Test
fun useAuthorNameTogglesOn() {
// Turn on "Use external storage" preference if currently off
if (!defaultKvStore.getBoolean("useAuthorName", true)) {
// Turn on "Use author name" preference if currently off
if (!defaultKvStore.getBoolean("useAuthorName", false)) {
Espresso.onData(PreferenceMatchers.withKey("useAuthorName"))
.inAdapterView(withId(android.R.id.list))
.perform(click())

View file

@ -0,0 +1,46 @@
package fr.free.nrw.commons
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.filters.MediumTest
import androidx.test.rule.ActivityTestRule
import androidx.test.runner.AndroidJUnit4
import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.auth.SignupActivity
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@MediumTest
@RunWith(AndroidJUnit4::class)
class SignupTest {
@get:Rule
var activityRule: ActivityTestRule<*> = ActivityTestRule(LoginActivity::class.java)
@Before
fun setup() {
UITestHelper.skipWelcome()
}
@Test
fun testSignupButton() {
try {
Intents.init()
} catch (ex: IllegalStateException) {
}
Espresso.onView(withId(R.id.signupButton))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
.perform(click())
intended(hasComponent(SignupActivity::class.java.name))
Intents.release()
}
}

View file

@ -0,0 +1,61 @@
package fr.free.nrw.commons
import androidx.test.espresso.Espresso.closeSoftKeyboard
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.NoMatchingViewException
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.matcher.ViewMatchers
import fr.free.nrw.commons.utils.StringUtils
import timber.log.Timber
class UITestHelper {
companion object {
fun skipWelcome() {
try {
//Skip tutorial
onView(ViewMatchers.withId(R.id.finishTutorialButton))
.perform(ViewActions.click())
} catch (ignored: NoMatchingViewException) {
}
}
fun loginUser() {
try {
//Perform Login
onView(ViewMatchers.withId(R.id.loginUsername))
.perform(ViewActions.clearText(), ViewActions.typeText(getTestUsername()))
onView(ViewMatchers.withId(R.id.loginPassword))
.perform(ViewActions.clearText(), ViewActions.typeText(getTestUserPassword()))
closeSoftKeyboard()
onView(ViewMatchers.withId(R.id.loginButton))
.perform(ViewActions.click())
sleep(5000)
} catch (ignored: NoMatchingViewException) {
}
}
fun sleep(timeInMillis: Long) {
try {
Timber.d("Sleeping for %d", timeInMillis)
Thread.sleep(timeInMillis)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
private fun getTestUsername(): String {
val username = BuildConfig.TEST_USERNAME
if (StringUtils.isNullOrWhiteSpace(username) || username == "null") {
throw NotImplementedError("Configure your beta account's username")
} else return username
}
private fun getTestUserPassword(): String {
val password = BuildConfig.TEST_PASSWORD
if (StringUtils.isNullOrWhiteSpace(password) || password == "null") {
throw NotImplementedError("Configure your beta account's password")
} else return password
}
}
}

View file

@ -10,13 +10,14 @@ import android.os.Environment
import android.view.View
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.NoMatchingViewException
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.Intents.intended
import androidx.test.espresso.intent.Intents.intending
import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction
import androidx.test.espresso.intent.matcher.IntentMatchers.hasType
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.filters.LargeTest
import androidx.test.rule.ActivityTestRule
@ -25,6 +26,7 @@ import androidx.test.runner.AndroidJUnit4
import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.utils.ConfigUtils
import org.hamcrest.core.AllOf.allOf
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ -41,10 +43,10 @@ import java.util.*
class UploadTest {
@get:Rule
var permissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.ACCESS_FINE_LOCATION)
Manifest.permission.ACCESS_FINE_LOCATION)!!
@get:Rule
var activityRule: ActivityTestRule<*> = IntentsTestRule(LoginActivity::class.java)
var activityRule = ActivityTestRule(LoginActivity::class.java)
private val randomBitmap: Bitmap
get() {
@ -56,9 +58,21 @@ class UploadTest {
@Before
fun setup() {
try {
Intents.init()
} catch (ex: IllegalStateException) {
}
UITestHelper.skipWelcome()
UITestHelper.loginUser()
saveToInternalStorage()
}
@After
fun teardown() {
Intents.release()
}
private fun saveToInternalStorage() {
val bitmapImage = randomBitmap
@ -86,30 +100,12 @@ class UploadTest {
}
}
private fun getToMainActivity() {
try {
//Skip tutorial
onView(withId(R.id.finishTutorialButton))
.perform(click())
//Perform Login
onView(withId(R.id.loginUsername))
.perform(clearText(), typeText(BuildConfig.TEST_USERNAME))
onView(withId(R.id.loginPassword))
.perform(clearText(), typeText(BuildConfig.TEST_PASSWORD))
onView(withId(R.id.loginButton))
.perform(click())
} catch (ignored: NoMatchingViewException) {}
}
@Test
fun uploadTest() {
if (!ConfigUtils.isBetaFlavour()) {
throw Error("This test should only be run in Beta!")
}
getToMainActivity()
// Uri to return by our mock gallery selector
// Requires file 'image.jpg' to be placed at root of file structure
val imageUri = Uri.parse("file://mnt/sdcard/image.jpg")
@ -151,20 +147,12 @@ class UploadTest {
onView(withId(R.id.bottom_card_next))
.perform(click())
try {
Thread.sleep(500)
} catch (e: InterruptedException) {
e.printStackTrace()
}
UITestHelper.sleep(1000)
onView(withId(R.id.category_search))
.perform(replaceText("Uploaded with Mobile/Android Tests"))
try {
Thread.sleep(3000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
UITestHelper.sleep(3000)
onView(withParent(withId(R.id.categories)))
.perform(click())
@ -172,20 +160,12 @@ class UploadTest {
onView(withId(R.id.category_next))
.perform(click())
try {
Thread.sleep(500)
} catch (e: InterruptedException) {
e.printStackTrace()
}
UITestHelper.sleep(500)
onView(withId(R.id.submit))
.perform(click())
try {
Thread.sleep(10000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
UITestHelper.sleep(10000)
val fileUrl = "https://commons.wikimedia.beta.wmflabs.org/wiki/File:" +
commonsFileName.replace(' ', '_') + ".jpg"