@@ -2,10 +2,13 @@ package com.gravatar.app.homeUi.presentation.home.share
22
33import app.cash.turbine.test
44import com.gravatar.app.testUtils.CoroutineTestRule
5+ import com.gravatar.app.usercomponent.domain.model.PrivateContactInfo
56import com.gravatar.app.usercomponent.domain.model.UserSharePreferences
67import com.gravatar.app.usercomponent.domain.repository.UserRepository
78import com.gravatar.app.usercomponent.domain.usecase.GetAvatarUrl
9+ import com.gravatar.app.usercomponent.domain.usecase.GetPrivateContactInfo
810import com.gravatar.app.usercomponent.domain.usecase.GetUserSharePreferences
11+ import com.gravatar.app.usercomponent.domain.usecase.UpdatePrivateContactInfo
912import com.gravatar.app.usercomponent.domain.usecase.UpdateUserSharePreferences
1013import com.gravatar.restapi.models.Profile
1114import com.gravatar.restapi.models.ProfileContactInfo
@@ -14,6 +17,7 @@ import io.mockk.mockk
1417import kotlinx.coroutines.ExperimentalCoroutinesApi
1518import kotlinx.coroutines.flow.MutableSharedFlow
1619import kotlinx.coroutines.test.StandardTestDispatcher
20+ import kotlinx.coroutines.test.advanceTimeBy
1721import kotlinx.coroutines.test.advanceUntilIdle
1822import kotlinx.coroutines.test.runTest
1923import org.junit.Assert.assertEquals
@@ -43,18 +47,34 @@ class ShareViewModelTest {
4347 userSharePreferencesFlow.emit(userSharePreferences)
4448 }
4549 }
50+ private val getPrivateContactInfo: GetPrivateContactInfo = object : GetPrivateContactInfo {
51+ override fun invoke () = privateContactInfoFlow
52+ }
53+ private val updatePrivateContactInfo = object : UpdatePrivateContactInfo {
54+ override suspend fun invoke (privateContactInfo : PrivateContactInfo ) {
55+ privateContactInfoFlow.emit(privateContactInfo)
56+ }
57+ }
4658 private val userRepository = mockk<UserRepository >()
4759
4860 private lateinit var viewModel: ShareViewModel
4961
5062 private val avatarUrlFlow: MutableSharedFlow <URL ?> = MutableSharedFlow ()
5163 private val profileFlow: MutableSharedFlow <Profile ?> = MutableSharedFlow ()
5264 private val userSharePreferencesFlow: MutableSharedFlow <UserSharePreferences > = MutableSharedFlow ()
65+ private val privateContactInfoFlow: MutableSharedFlow <PrivateContactInfo > = MutableSharedFlow ()
5366
5467 @Before
5568 fun setup () {
5669 every { userRepository.getProfile() } returns profileFlow
57- viewModel = ShareViewModel (userRepository, getAvatarUrl, getUserSharePreferences, updateUserSharePreferences)
70+ viewModel = ShareViewModel (
71+ userRepository,
72+ getAvatarUrl,
73+ getUserSharePreferences,
74+ updateUserSharePreferences,
75+ getPrivateContactInfo,
76+ updatePrivateContactInfo
77+ )
5878 }
5979
6080 @Test
@@ -321,6 +341,25 @@ class ShareViewModelTest {
321341 }
322342 }
323343
344+ @Test
345+ fun `when private contact info is emitted then uiState is updated with private contact info` () = runTest {
346+ // Given
347+ val testPrivateContactInfo = PrivateContactInfo (
348+ privateEmail = " test@example.com" ,
349+ privatePhone = " 123-456-7890"
350+ )
351+
352+ // When
353+ privateContactInfoFlow.emit(testPrivateContactInfo)
354+ advanceUntilIdle()
355+
356+ // Then
357+ viewModel.uiState.test {
358+ val state = awaitItem()
359+ assertEquals(testPrivateContactInfo, state.privateContactInfo)
360+ }
361+ }
362+
324363 @Test
325364 fun `when OnPrivateInformationClicked event is triggered then isPrivateInformationDialogVisible is set to true` () = runTest {
326365 // When
@@ -352,6 +391,109 @@ class ShareViewModelTest {
352391 }
353392 }
354393
394+ @Test
395+ fun `when OnEmailValueChanged event is triggered then updatePrivateContactInfo is called` () = runTest {
396+ // Given
397+ val newEmailValue = " test@example.com"
398+
399+ privateContactInfoFlow.test {
400+ // When
401+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (newEmailValue))
402+
403+ // Then
404+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
405+ privateEmail = newEmailValue
406+ )
407+ assertEquals(expectedPrivateContactInfo, awaitItem())
408+ }
409+ }
410+
411+ @Test
412+ fun `when OnPhoneValueChanged event is triggered then updatePrivateContactInfo is called` () = runTest {
413+ // Given
414+ val newPhoneValue = " 123-456-7890"
415+
416+ privateContactInfoFlow.test {
417+ // When
418+ viewModel.onEvent(ShareEvent .OnPhoneValueChanged (newPhoneValue))
419+
420+ // Then
421+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
422+ privatePhone = newPhoneValue
423+ )
424+ assertEquals(expectedPrivateContactInfo, awaitItem())
425+ }
426+ }
427+
428+ @Test
429+ fun `when email value is changed then updatePrivateContactInfo is not called immediately` () = runTest {
430+ // Given
431+ val newEmailValue = " test@example.com"
432+
433+ privateContactInfoFlow.test {
434+ // When
435+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (newEmailValue))
436+
437+ // Advance time by just under the debounce delay
438+ advanceTimeBy(499 ) // Just under the 500ms debounce delay
439+
440+ // Then - no emissions yet because of debounce
441+ expectNoEvents()
442+ cancel()
443+ }
444+ }
445+
446+ @Test
447+ fun `when email value is changed then updatePrivateContactInfo is called after debounce delay` () = runTest {
448+ // Given
449+ val newEmailValue = " test@example.com"
450+
451+ privateContactInfoFlow.test {
452+ // When
453+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (newEmailValue))
454+
455+ // Advance time past the debounce delay
456+ advanceTimeBy(501 ) // Just past the 500ms debounce delay
457+
458+ // Then
459+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
460+ privateEmail = newEmailValue
461+ )
462+ assertEquals(expectedPrivateContactInfo, expectMostRecentItem())
463+ cancel()
464+ }
465+ }
466+
467+ @Test
468+ fun `when multiple rapid email value changes occur then only the last one triggers updatePrivateContactInfo` () = runTest {
469+ // Given
470+ val firstEmailValue = " first@example.com"
471+ val secondEmailValue = " second@example.com"
472+ val thirdEmailValue = " third@example.com"
473+
474+ privateContactInfoFlow.test {
475+ // When - rapid changes
476+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (firstEmailValue))
477+ advanceTimeBy(100 ) // Not enough time for debounce
478+
479+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (secondEmailValue))
480+ advanceTimeBy(100 ) // Not enough time for debounce
481+
482+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (thirdEmailValue))
483+
484+ // Advance time past the debounce delay
485+ advanceTimeBy(501 ) // Just past the 500ms debounce delay
486+
487+ // Then - only the last value should be emitted
488+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
489+ privateEmail = thirdEmailValue
490+ )
491+ assertEquals(expectedPrivateContactInfo, expectMostRecentItem())
492+
493+ cancel()
494+ }
495+ }
496+
355497 private fun createTestProfile () = Profile {
356498 hash = " test-hash"
357499 displayName = " Test User"
0 commit comments