Add Login UI Test covering happy path

MAILANDR-5
This commit is contained in:
Marino Meneghel
2021-11-17 12:08:34 +01:00
parent dcda322f85
commit d028d326a6
13 changed files with 132 additions and 55 deletions
+3 -3
View File
@@ -28,9 +28,6 @@ gradlew.bat
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
@@ -51,3 +48,6 @@ keystore/*
# Private properties
private.properties
# Test users credentials
**/users.json
+4
View File
@@ -27,6 +27,10 @@ In order to someone as a tester for such builds, their email address needs to be
Crashes and errors that happen in `release` (non debuggable) builds are reported to Sentry in an anonymised form.
The CI sets up the integration with Sentry by providing in the build environment a `private.properties` file that contains the secrets needed. This can as well be performed locally by creating a `private.properties` file (which will be ignored by git) and filling it with the needed secrets (eg. `SentryDSN`)
### UI Tests
UI tests are executed on firebase through the CI. Firebase test lab can be triggered also locally with `bundle exec fastlane uiTests` or tests can be run in a local emulator through android studio.
The `app/src/uiTest/assets/users.json` file will be needed for UI tests to work, its value can be found on the CI
## Code style
This project's code style and formatting is checked by detekt. The rule set is [ktlint's default one](https://github.com/pinterest/ktlint)
+1
View File
@@ -114,6 +114,7 @@ android {
getByName("main").java.srcDirs("src/main/kotlin")
getByName("test").java.srcDirs("src/test/kotlin")
getByName("androidTest").java.srcDirs("src/androidTest/kotlin", "src/uiTest/kotlin")
getByName("androidTest").assets.srcDirs("src/uiTest/assets")
}
}
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2021 Proton Technologies AG
* This file is part of Proton Technologies AG and ProtonCore.
*
* ProtonCore is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ProtonCore is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
*/
package ch.protonmail.android.uitests
import ch.protonmail.android.MainActivity
import ch.protonmail.android.di.AppDatabaseModule
import kotlinx.coroutines.runBlocking
import me.proton.core.test.android.instrumented.ProtonTest
import me.proton.core.test.android.plugins.data.User.Users
import org.junit.After
import timber.log.Timber
open class BaseTest(
private val clearAppDatabaseOnTearDown: Boolean = true,
defaultTimeout: Long = 20_000L
) : ProtonTest(MainActivity::class.java, defaultTimeout) {
@After
override fun tearDown() {
super.tearDown()
if (clearAppDatabaseOnTearDown) {
runBlocking {
appDatabase.accountDao().deleteAll()
}
}
Timber.d("Finishing Testing: Clearing all database tables")
}
companion object {
val users = Users("users.json")
val appDatabase = AppDatabaseModule.provideAppDatabase(getTargetContext())
}
}
@@ -1,3 +1,3 @@
package ch.protonmail.android.uitests.testsHelper.annotations
package ch.protonmail.android.uitests.annotations
interface SmokeTest
@@ -1,8 +0,0 @@
package ch.protonmail.android.uitests.testsHelper.annotations
/**
* Use this annotation to annotate test cases with TestRail test case id.
*/
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class TestId(val id: String)
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2021 Proton Technologies AG
* This file is part of Proton Technologies AG and ProtonMail.
*
* ProtonCore is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ProtonCore is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with ProtonMail. If not, see <https://www.gnu.org/licenses/>.
*/
package ch.protonmail.android.uitests.login
import ch.protonmail.android.R
import me.proton.core.test.android.robots.CoreRobot
import me.proton.core.test.android.robots.CoreVerify
class InboxRobot : CoreRobot() {
class Verify : CoreVerify() {
fun mailboxScreenDisplayed() {
// Currently, mailbox screen only contains the account switcher
view.withId(R.id.account_name_textview).checkDisplayed()
}
}
inline fun verify(block: Verify.() -> Unit) = Verify().apply(block)
}
@@ -1,27 +0,0 @@
package ch.protonmail.android.uitests.login
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
/**
* [LoginRobot] class contains actions and verifications for login functionality.
*/
class LoginRobot {
fun launchApp() = this
/**
* Contains all the validations that can be performed by [LoginRobot].
*/
class Verify {
fun appIsLaunchedCorrectly(): LoginRobot {
onView(withText("Hello World!")).check(matches(isDisplayed()))
return LoginRobot()
}
}
inline fun verify(block: Verify.() -> Unit) = Verify().apply(block)
}
@@ -1,29 +1,46 @@
package ch.protonmail.android.uitests.tests.login
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import ch.protonmail.android.MainActivity
import ch.protonmail.android.uitests.login.LoginRobot
import ch.protonmail.android.uitests.testsHelper.annotations.SmokeTest
import org.junit.Rule
import ch.protonmail.android.uitests.BaseTest
import ch.protonmail.android.uitests.annotations.SmokeTest
import ch.protonmail.android.uitests.login.InboxRobot
import me.proton.core.test.android.robots.auth.AddAccountRobot
import me.proton.core.test.android.robots.auth.login.LoginRobot
import me.proton.core.test.android.robots.auth.login.MailboxPasswordRobot
import org.junit.Before
import org.junit.Test
import org.junit.experimental.categories.Category
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class LoginTests {
@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)
class LoginTests : BaseTest() {
private val addAccountRobot = AddAccountRobot()
private val loginRobot = LoginRobot()
@Category(SmokeTest::class)
@Test
fun openEmptyApp() {
loginRobot
.launchApp()
.verify { appIsLaunchedCorrectly() }
@Before
fun signIn() {
addAccountRobot
.signIn()
.verify { loginElementsDisplayed() }
}
@Test
@Category(SmokeTest::class)
fun loginUserHappyPath() {
val user = users.getUser { it.name == "pro" }
loginRobot
.loginUser<InboxRobot>(user)
.verify { mailboxScreenDisplayed() }
}
@Test
@Category(SmokeTest::class)
fun loginUserWithSecondaryPasswordHappyPath() {
val user = users.getUser(usernameAndOnePass = false) { it.name == "twopasswords" }
loginRobot
.loginUser<MailboxPasswordRobot>(user)
.unlockMailbox<InboxRobot>(user)
.verify { mailboxScreenDisplayed() }
}
}
@@ -1,7 +1,7 @@
package ch.protonmail.android.uitests.tests.suites
import ch.protonmail.android.uitests.tests.login.LoginTests
import ch.protonmail.android.uitests.testsHelper.annotations.SmokeTest
import ch.protonmail.android.uitests.annotations.SmokeTest
import org.junit.experimental.categories.Categories
import org.junit.runner.RunWith
import org.junit.runners.Suite
+1
View File
@@ -112,6 +112,7 @@ object Dependencies {
add(AndroidX.Test.runner)
add(AndroidX.Test.rules)
add(AndroidX.Test.espresso)
add(Core.testAndroidInstrumented)
add(Mockk.mockkAndroid)
}
}
+4
View File
@@ -116,6 +116,10 @@ object Core {
val utilKotlin = coreArtifact("util-kotlin", Versions.Core.utilKotlin)
val testKotlin = coreArtifact("test-kotlin", Versions.Core.testKotlin)
val testAndroid = coreArtifact("test-android", Versions.Core.testAndroid)
val testAndroidInstrumented = coreArtifact(
"test-android-instrumented",
Versions.Core.testAndroidInstrumented
)
}
object Dagger {
+1
View File
@@ -50,6 +50,7 @@ object Versions {
const val utilKotlin = "1.15.2"
const val testKotlin = "1.15"
const val testAndroid = "1.15.1"
const val testAndroidInstrumented = "1.15.5"
}
object Dagger {