Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b43c6f2 authored by Ricki Hirner's avatar Ricki Hirner
Browse files

Merge branch 'dev-3.x_EspressoExperimental-ose' into 'dev-3.x-ose'

Dev 3.x espresso experimental ose

See merge request bitfireAT/davx5-ose!59
parents 6f398701 a5c51bc0
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -164,6 +164,17 @@ dependencies {
    androidTestImplementation 'androidx.test:rules:1.3.0'
    androidTestImplementation 'junit:junit:4.13.2'
    androidTestImplementation "com.squareup.okhttp3:mockwebserver:${versions.okhttp}"
    androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.31"
    androidTestImplementation 'androidx.test:core:1.4.0-alpha05'
    androidTestImplementation 'androidx.test:core-ktx:1.4.0-alpha05'

    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0'
    androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
    androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.2.1"
    androidTestImplementation 'androidx.test:runner:1.4.0-alpha05'

    testImplementation 'junit:junit:4.13.2'
    testImplementation "com.squareup.okhttp3:mockwebserver:${versions.okhttp}"
+215 −0
Original line number Diff line number Diff line
package at.bitfire.davdroid.ui


import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import androidx.test.espresso.Espresso
import androidx.test.espresso.Espresso.*
import androidx.test.espresso.PerformException
import androidx.test.espresso.UiController
import androidx.test.espresso.ViewAction
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.*
import androidx.test.espresso.assertion.ViewAssertions.*
import androidx.test.espresso.contrib.DrawerActions
import androidx.test.espresso.contrib.DrawerMatchers.isClosed
import androidx.test.espresso.matcher.ViewMatchers.*
import androidx.test.espresso.util.HumanReadables
import androidx.test.espresso.util.TreeIterables
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import at.bitfire.davdroid.R
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.TypeSafeMatcher
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.concurrent.TimeoutException


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

    @get:Rule var activityScenarioRule = activityScenarioRule<AccountsActivity>()


    private val username = "test"
    private val password = "test"
    private val baseUrl = "https://davtest.dev001.net/radicale/htpasswd/"


    @Test
    fun accountsActivityTest() {


        onView(withId(R.id.takeControl)).check(matches(withText(R.string.intro_slogan2)))   // intro_welcome is the first fragment, check first if the String Resource "intro_slogan2" is shown.
        onView(withId(R.id.next)).perform(click())                                          // change the fragment by clicking on the FAB
        onView(withId(R.id.next)).perform(click())
        onView(withId(R.id.next)).perform(click())
        onView(withId(R.id.done)).perform(click())
        onView(withText(R.string.account_list_empty)).check(matches(isDisplayed()))

        onView(withId(R.id.fab)).perform(click())

        // open first the option for Login with Base URL and then enter the test-data and confirm
        onView(withText(R.string.login_type_url)).perform(click())
        onView(withId(R.id.loginUrlBaseUrlEdittext)).perform(typeText(baseUrl), ViewActions.closeSoftKeyboard())
        onView(withId(R.id.loginUrlUsernameEdittext)).perform(typeText(username), ViewActions.closeSoftKeyboard())
        onView(withId(R.id.loginUrlPasswordEdittext)).perform(typeText(password), ViewActions.closeSoftKeyboard())
        onView(withId(R.id.login)).perform(click())

        // The detect configuration screen (detect_configuration.xml) is not asserted here, it's just skipped.
        // login_account_details.xml is the next expected fragment, check if the expected headline appears and then click on the "Create Account" button
        onView(isRoot()).perform(waitForView(R.id.accountName, 30000))

        onView(withText(R.string.login_account_name_info)).check(matches(isDisplayed()))
        onView(withId(R.id.create_account)).perform(click())

        Thread.sleep(1000)

        // check if the "test" calendar appeared
        onView(isRoot()).perform(waitForView(R.id.title, 5000))
        //onView(withId(R.id.title)).perform(waitForText("Test Addressbook", 30000))
        onView(withText("Test Addressbook")).check(matches(withText("Test Addressbook")))

        // Go back to the overview with all Accounts
        Espresso.pressBack()
        //Thread.sleep(2000)

        // check if the account exists and click on it
        onView(isRoot()).perform(waitForView((R.id.account_name), 5000))
        onView(withText("test")).check(matches(withText("test")))
        onView(withText("test")).perform(click())


        // open the overflowMenu to delete the account
        val overflowMenuButton = onView(
                Matchers.allOf(withContentDescription("More options"),
                        childAtPosition(
                                childAtPosition(
                                        withId(R.id.toolbar),
                                        2),
                                1),
                        isDisplayed()))
        overflowMenuButton.perform(click())


        // click on the delete button
        onView(withText(R.string.account_delete)).perform(click())
        onView(withText(R.string.account_delete_confirmation_title)).check(matches(isDisplayed()))
        // confirm deletion by clicking on YES
        onView(withId(android.R.id.button1)).perform(click())

        // doublecheck to make sure that the account doesn't exist anymore. The welcome text is displayed
        onView(withText(R.string.account_list_empty)).check(matches(withText(R.string.account_list_empty)))

    }


    @Test
    fun menuDrawerTest() {

        onView(withId(R.id.takeControl)).check(matches(withText(R.string.intro_slogan2)))
        onView(withId(R.id.next)).perform(click())                                          // change the fragment by clicking on the FAB
        onView(withId(R.id.next)).perform(click())
        onView(withId(R.id.next)).perform(click())
        onView(withId(R.id.done)).perform(click())
        onView(withText(R.string.account_list_empty)).check(matches(isDisplayed()))

        // TESTING ABOUT DIALOG
        // Open Drawer to click on navigation.
        onView(withId(R.id.drawer_layout))
                .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
                .perform(DrawerActions.open()); // Open Drawer
        // check if about can be opened
        onView(withText(R.string.navigation_drawer_about)).perform(click())
        onView(withText(R.string.about_copyright)).check(matches(isDisplayed()))
        Espresso.pressBack()

        // TESTING SETTINGS DIALOG
        // Open Drawer to click on navigation.
        onView(withId(R.id.drawer_layout))
                .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
                .perform(DrawerActions.open()); // Open Drawer
        // check if about can be opened
        onView(withText(R.string.navigation_drawer_settings)).perform(click())
        onView(withText(R.string.app_settings_show_debug_info)).check(matches(isDisplayed()))
        Espresso.pressBack()

        // TESTING WEBSITE MENU ENTRY
        // Open Drawer to click on navigation.
        onView(withId(R.id.drawer_layout))
                .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed.
                .perform(DrawerActions.open()); // Open Drawer
        // check if Website can be opened
        onView(withText(R.string.navigation_drawer_website)).perform(click())


    }

        /**
     * This ViewAction tells espresso to wait till a certain view is found in the view hierarchy.
     * Source: https://www.repeato.app/espresso-wait-for-view/
     * @param viewId The id of the view to wait for.
     * @param timeout The maximum time which espresso will wait for the view to show up (in milliseconds)
     */
    private fun waitForView(viewId: Int, timeout: Long): ViewAction {
        return object : ViewAction {
            override fun getConstraints(): Matcher<View> {
                return isRoot()
            }

            override fun getDescription(): String {
                return "wait for a specific view with id $viewId; during $timeout millis."
            }

            override fun perform(uiController: UiController, rootView: View) {
                uiController.loopMainThreadUntilIdle()
                val startTime = System.currentTimeMillis()
                val endTime = startTime + timeout
                val viewMatcher = withId(viewId)

                do {
                    // Iterate through all views on the screen and see if the view we are looking for is there already
                    for (child in TreeIterables.breadthFirstViewTraversal(rootView)) {
                        // found view with required ID
                        if (viewMatcher.matches(child)) {
                            return
                        }
                    }
                    // Loops the main thread for a specified period of time.
                    // Control may not return immediately, instead it'll return after the provided delay has passed and the queue is in an idle state again.
                    uiController.loopMainThreadForAtLeast(100)
                } while (System.currentTimeMillis() < endTime) // in case of a timeout we throw an exception -&gt; test fails
                throw PerformException.Builder()
                        .withCause(TimeoutException())
                        .withActionDescription(this.description)
                        .withViewDescription(HumanReadables.describe(rootView))
                        .build()
            }
        }
    }

    private fun childAtPosition(
            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)
            }
        }
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -339,7 +339,7 @@ class DavResourceFinder(
     * @return principal URL, or null if none found
     */
    @Throws(IOException::class, HttpException::class, DavException::class)
    private fun discoverPrincipalUrl(domain: String, service: Service): HttpUrl? {
    fun discoverPrincipalUrl(domain: String, service: Service): HttpUrl? {
        val scheme: String
        val fqdn: String
        var port = 443
+3 −0
Original line number Diff line number Diff line
@@ -136,6 +136,7 @@
                        app:errorEnabled="true">
                        <!--suppress AndroidUnknownAttribute -->
                        <com.google.android.material.textfield.TextInputEditText
                            android:id="@+id/loginUrlBaseUrlEdittext"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:afterTextChanged="@{model::clearUrlError}"
@@ -152,6 +153,7 @@
                        app:errorEnabled="true">
                        <!--suppress AndroidUnknownAttribute -->
                        <com.google.android.material.textfield.TextInputEditText
                            android:id="@+id/loginUrlUsernameEdittext"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:afterTextChanged="@{model::clearUsernameError}"
@@ -171,6 +173,7 @@
                        app:errorEnabled="true">
                        <!--suppress AndroidUnknownAttribute -->
                        <com.google.android.material.textfield.TextInputEditText
                            android:id="@+id/loginUrlPasswordEdittext"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:afterTextChanged="@{model::clearPasswordError}"