Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: E2E #167

Merged
merged 19 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c8977d4
test: Adding e2e test with Hilt injections, started to mock the profi…
yassine04e Nov 11, 2024
7f6cba7
test: Finishing the e2e test (testing the creation of a travel) with …
yassine04e Nov 12, 2024
7dd4b48
Merge branch 'main' into test/end-to-end-test-final-version
yassine04e Nov 12, 2024
802cd61
fix: correcting the alignement of the google sign in button and the s…
yassine04e Nov 12, 2024
ce017dd
fix: we are now supporting java 17, so replacing a mock of sealed cla…
yassine04e Nov 12, 2024
fce4187
test: Adding tests for the sign in with Password and email UI and add…
yassine04e Nov 12, 2024
e8261f7
fix: restoring the notification view model on the main activity and c…
yassine04e Nov 12, 2024
259e51a
test: Adding test for the mock repositories to increase coverage
yassine04e Nov 12, 2024
b973f5f
feat: Making the sign in screen with password prettier, with palcehol…
yassine04e Nov 13, 2024
ccd72a6
Merge branch 'main' into test/end-to-end-test-final-version
yassine04e Nov 13, 2024
21a8a31
test: adding test for the login logic with password and email and add…
yassine04e Nov 13, 2024
83b81d2
Merge branch 'test/end-to-end-test-final-version' of github.com:Trave…
yassine04e Nov 13, 2024
a5bfef7
fix: correcting the duration of the e2e test from 30 seconds to 40 se…
yassine04e Nov 13, 2024
65ce5df
test: Adding test to verify logic of login button
yassine04e Nov 13, 2024
eb26cc1
fix: Correcting the test logInWithPasswordCallsProfileVM because it c…
yassine04e Nov 13, 2024
2f1ff21
fix: Blocking sign in with email and password button when logging wit…
yassine04e Nov 13, 2024
52e929e
Merge branch 'main' into test/end-to-end-test-final-version
yassine04e Nov 14, 2024
9e1320c
fix: Blocking the other methods of connection when the login or sign …
yassine04e Nov 14, 2024
7207e08
Merge branch 'test/end-to-end-test-final-version' of github.com:Trave…
yassine04e Nov 14, 2024
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
31 changes: 20 additions & 11 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ plugins {
alias(libs.plugins.sonar)
id("jacoco")
alias(libs.plugins.gms)
id("kotlin-kapt")
id("com.google.dagger.hilt.android")
}

android {
Expand Down Expand Up @@ -38,7 +40,7 @@ android {
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner = "com.github.se.travelpouch.HiltTestRunner"
vectorDrawables {
useSupportLibrary = true
}
Expand Down Expand Up @@ -99,12 +101,12 @@ android {
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

kotlinOptions {
jvmTarget = "11"
jvmTarget = "17"
}

packaging {
Expand Down Expand Up @@ -186,14 +188,16 @@ dependencies {
globalTestImplementation(libs.androidx.junit)
globalTestImplementation(libs.androidx.espresso.core)

//todo: wait for a listener to finish libraries
implementation(libs.guava)
//Hilt
implementation(libs.hilt.android)
kapt(libs.hilt.android.compiler)
kapt(libs.androidx.hilt.compiler)
implementation(libs.androidx.hilt.navigation.compose)

// To use CallbackToFutureAdapter
implementation(libs.androidx.concurrent.futures)

// Kotlin
implementation(libs.kotlinx.coroutines.guava)
testImplementation(libs.hilt.android.testing)
kaptTest(libs.hilt.android.compiler)
androidTestImplementation(libs.hilt.android.testing)
kaptAndroidTest(libs.hilt.android.compiler)


// Google Service and Maps
Expand Down Expand Up @@ -276,6 +280,11 @@ dependencies {
androidTestImplementation(libs.mockito.kotlin)
}

// Allow references to generated code
kapt {
correctErrorTypes = true
}

tasks.withType<Test> {
// Configure Jacoco for each tests
configure<JacocoTaskExtension> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.se.travelpouch

import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
import dagger.hilt.android.testing.HiltTestApplication

class HiltTestRunner : AndroidJUnitRunner() {
override fun newApplication(
cl: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(cl, HiltTestApplication::class.java.name, context)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.github.se.travelpouch.di

import com.github.se.travelpouch.model.authentication.AuthenticationService
import com.github.se.travelpouch.model.authentication.MockFirebaseAuthenticationService
import com.github.se.travelpouch.model.profile.ProfileRepository
import com.github.se.travelpouch.model.profile.ProfileRepositoryMock
import com.github.se.travelpouch.model.travels.TravelRepository
import com.github.se.travelpouch.model.travels.TravelRepositoryMock
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@InstallIn(SingletonComponent::class)
@Module
object TestModule {

@Provides
@Singleton
fun provideFirebaseAuth(): AuthenticationService {
return MockFirebaseAuthenticationService()
}

@Provides
@Singleton
fun providesProfileRepository(): ProfileRepository {
return ProfileRepositoryMock()
}

@Provides
@Singleton
fun providesTravelRepository(): TravelRepository {
return TravelRepositoryMock()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.github.se.travelpouch.endtoend

import androidx.compose.ui.test.assert
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.isDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import com.github.se.travelpouch.MainActivity
import com.github.se.travelpouch.di.AppModule
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.UninstallModules
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test

@OptIn(ExperimentalCoroutinesApi::class)
@HiltAndroidTest
@UninstallModules(AppModule::class)
class EndToEndTest {

@get:Rule(order = 0) val hiltRule = HiltAndroidRule(this)

@get:Rule(order = 1) val composeTestRule = createAndroidComposeRule<MainActivity>()

@Before
fun setUp() {
hiltRule.inject()
}

@Test
fun verifyUserFlowForTravelCreation() =
runTest(timeout = 40.seconds) {

// assert that login screen is displayed
composeTestRule.onNodeWithTag("appLogo").assertIsDisplayed()
composeTestRule.onNodeWithTag("welcomText").assertIsDisplayed()
yassine04e marked this conversation as resolved.
Show resolved Hide resolved
composeTestRule.onNodeWithText("Sign in with email and password").assertIsDisplayed()

// go to sign in screen with email and password and log in
composeTestRule.onNodeWithText("Sign in with email and password").performClick()

composeTestRule.onNodeWithTag("emailField").assertIsDisplayed()
composeTestRule.onNodeWithTag("passwordField").assertIsDisplayed()
composeTestRule.onNodeWithText("Sign in").assertIsDisplayed()

composeTestRule.onNodeWithTag("emailField").performTextInput("[email protected]")
composeTestRule.onNodeWithTag("passwordField").performTextInput("travelpouchtest1password")
composeTestRule.onNodeWithText("Sign in").performClick()

// wait until we are in the travel list screen
composeTestRule.waitUntil {
composeTestRule.onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true).isDisplayed()
}

// test that no travels are displayed because we have a new account
composeTestRule
.onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true)
.assertIsDisplayed()
composeTestRule.onNodeWithTag("createTravelFab").assertIsDisplayed()
composeTestRule.onNodeWithTag("createTravelFab").performClick()

// wait until we are in the screen to add a travel
composeTestRule.waitUntil {
composeTestRule.onNodeWithTag("travelTitle", useUnmergedTree = true).isDisplayed()
}

// test that everything is displayed
composeTestRule.onNodeWithTag("travelTitle").assertIsDisplayed()
composeTestRule.onNodeWithTag("travelTitle").assertTextEquals("Create a new travel")
composeTestRule.onNodeWithTag("inputTravelTitle").assertIsDisplayed()
composeTestRule.onNodeWithTag("inputTravelDescription").assertIsDisplayed()
composeTestRule.onNodeWithTag("inputTravelLocation").assertIsDisplayed()
composeTestRule.onNodeWithTag("inputTravelStartDate").assertIsDisplayed()
composeTestRule.onNodeWithTag("inputTravelEndDate").assertIsDisplayed()
composeTestRule.onNodeWithTag("travelSaveButton").assertIsDisplayed()
composeTestRule.onNodeWithTag("travelSaveButton").assertTextEquals("Save")

// input fields to create a travel
composeTestRule.onNodeWithTag("inputTravelTitle").performTextInput("e2e travel 1")
composeTestRule
.onNodeWithTag("inputTravelDescription")
.performTextInput("test travel description")
composeTestRule.onNodeWithTag("inputTravelLocation").performTextInput("L")

// wait to have La paz displayed
composeTestRule.waitUntil {
composeTestRule.onNodeWithText("La Paz, Bolivia").isDisplayed()
}

composeTestRule.onNodeWithText("La Paz, Bolivia").performClick()
composeTestRule.onNodeWithTag("inputTravelStartDate").performTextInput("10/11/2024")
composeTestRule.onNodeWithTag("inputTravelEndDate").performTextInput("20/11/2024")

// save the travel and go back to the list of travels
composeTestRule.onNodeWithTag("travelSaveButton").performClick()

// verify that the previous buttons are still here
composeTestRule
.onNodeWithTag("TravelListScreen", useUnmergedTree = true)
.assertIsDisplayed()
composeTestRule.onNodeWithTag("createTravelFab").assertIsDisplayed()

// verify that the empty travel prompt does not exist since we saved a travel
composeTestRule
.onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true)
.assertDoesNotExist()

composeTestRule.onNodeWithText("e2e travel 1").assertIsDisplayed()
composeTestRule.onNodeWithText("e2e travel 1").assert(hasText("e2e travel 1"))
composeTestRule.onNodeWithText("e2e travel 1").assert(hasText("test travel description"))
composeTestRule.onNodeWithText("e2e travel 1").assert(hasText("La Paz, Bolivia"))
composeTestRule.onNodeWithText("e2e travel 1").assert(hasText("10/11/2024"))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.github.se.travelpouch.model.profile.ProfileModelView
import com.github.se.travelpouch.model.profile.ProfileRepository
Expand Down Expand Up @@ -66,4 +67,13 @@ class SignInViewTest {
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag("loadingSpinner").assertIsDisplayed()
}

@Test
fun signInWithWmailAndPasswordIsDisplayed() {
composeTestRule.setContent {
SignInScreen(navigationActions = mockNavigationActions, profileModelView, travelViewModel)
}

composeTestRule.onNodeWithText("Sign in with email and password").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package com.github.se.travelpouch.ui.authentication

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import com.github.se.travelpouch.model.authentication.AuthenticationService
import com.github.se.travelpouch.model.authentication.MockFirebaseAuthenticationService
import com.github.se.travelpouch.model.profile.ProfileModelView
import com.github.se.travelpouch.model.profile.ProfileRepository
import com.github.se.travelpouch.model.travels.ListTravelViewModel
import com.github.se.travelpouch.model.travels.TravelRepository
import com.github.se.travelpouch.ui.navigation.NavigationActions
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.verify

class TestSignInEmailPasswordUI {
val mockNavigationActions = mock(NavigationActions::class.java)
val travelRepository = mock(TravelRepository::class.java)
val profileRepository = mock(ProfileRepository::class.java)

val travelViewModel = ListTravelViewModel(travelRepository)
val profileModelView = ProfileModelView(profileRepository)

val authenticationService: AuthenticationService = mock()
val authenticationServiceMock = MockFirebaseAuthenticationService()

@get:Rule val composeTestRule = createComposeRule()

@Test
fun verifiesTopBarAppDisplayed() {
composeTestRule.setContent {
SignInWithPassword(
mockNavigationActions, profileModelView, travelViewModel, authenticationService)
}

composeTestRule.onNodeWithTag("PasswordTitle").assertIsDisplayed()
composeTestRule.onNodeWithTag("PasswordTitle").assertTextEquals("Signing in with password")

composeTestRule.onNodeWithTag("goBackButton").assertIsDisplayed()
composeTestRule.onNodeWithTag("goBackButton").performClick()
verify(mockNavigationActions).navigateTo(anyOrNull())
}

@Test
fun signingInWithPasswordCallsCreateUser() =
runTest(timeout = 20.seconds) {
composeTestRule.setContent {
SignInWithPassword(
mockNavigationActions, profileModelView, travelViewModel, authenticationService)
}

composeTestRule.onNodeWithTag("emailField").assertIsDisplayed()
composeTestRule.onNodeWithTag("passwordField").assertIsDisplayed()
composeTestRule.onNodeWithText("Sign in").assertIsDisplayed()

composeTestRule.onNodeWithTag("emailField").performTextInput("[email protected]")
composeTestRule.onNodeWithTag("passwordField").performTextInput("travelpouchtest1password")
composeTestRule.onNodeWithText("Sign in").performClick()

verify(authenticationService).createUser(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}

@Test
fun logInWithPasswordCallsLogin() =
runTest(timeout = 20.seconds) {
composeTestRule.setContent {
SignInWithPassword(
mockNavigationActions, profileModelView, travelViewModel, authenticationService)
}

composeTestRule.onNodeWithTag("emailField").assertIsDisplayed()
composeTestRule.onNodeWithTag("passwordField").assertIsDisplayed()
composeTestRule.onNodeWithText("Log in").assertIsDisplayed()

composeTestRule.onNodeWithTag("emailField").performTextInput("[email protected]")
composeTestRule.onNodeWithTag("passwordField").performTextInput("travelpouchtest1password")
composeTestRule.onNodeWithText("Log in").performClick()

verify(authenticationService).login(anyOrNull(), anyOrNull(), anyOrNull(), anyOrNull())
}

@Test
fun logInWithPasswordCallsProfileVM() =
runTest(timeout = 20.seconds) {
composeTestRule.setContent {
SignInWithPassword(
mockNavigationActions, profileModelView, travelViewModel, authenticationServiceMock)
}

composeTestRule.onNodeWithTag("emailField").assertIsDisplayed()
composeTestRule.onNodeWithTag("passwordField").assertIsDisplayed()
composeTestRule.onNodeWithText("Log in").assertIsDisplayed()

composeTestRule.onNodeWithTag("emailField").performTextInput("[email protected]")
composeTestRule.onNodeWithTag("passwordField").performTextInput("travelpouchtest1password")
composeTestRule.onNodeWithText("Log in").performClick()

verify(profileRepository).initAfterLogin(anyOrNull())
verify(mockNavigationActions).navigateTo(anyOrNull())
}
}
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">

<application
android:name=".TravelPouchApp"
Unsaved2 marked this conversation as resolved.
Show resolved Hide resolved
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand Down
Loading