@@ -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,128 @@ 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+
363+ @Test
364+ fun `when OnEmailValueChanged event is triggered then updatePrivateContactInfo is called` () = runTest {
365+ // Given
366+ val newEmailValue = " test@example.com"
367+
368+ privateContactInfoFlow.test {
369+ // When
370+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (newEmailValue))
371+
372+ // Then
373+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
374+ privateEmail = newEmailValue
375+ )
376+ assertEquals(expectedPrivateContactInfo, awaitItem())
377+ }
378+ }
379+
380+ @Test
381+ fun `when OnPhoneValueChanged event is triggered then updatePrivateContactInfo is called` () = runTest {
382+ // Given
383+ val newPhoneValue = " 123-456-7890"
384+
385+ privateContactInfoFlow.test {
386+ // When
387+ viewModel.onEvent(ShareEvent .OnPhoneValueChanged (newPhoneValue))
388+
389+ // Then
390+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
391+ privatePhone = newPhoneValue
392+ )
393+ assertEquals(expectedPrivateContactInfo, awaitItem())
394+ }
395+ }
396+
397+ @Test
398+ fun `when email value is changed then updatePrivateContactInfo is not called immediately` () = runTest {
399+ // Given
400+ val newEmailValue = " test@example.com"
401+
402+ privateContactInfoFlow.test {
403+ // When
404+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (newEmailValue))
405+
406+ // Advance time by just under the debounce delay
407+ advanceTimeBy(499 ) // Just under the 500ms debounce delay
408+
409+ // Then - no emissions yet because of debounce
410+ expectNoEvents()
411+ cancel()
412+ }
413+ }
414+
415+ @Test
416+ fun `when email value is changed then updatePrivateContactInfo is called after debounce delay` () = runTest {
417+ // Given
418+ val newEmailValue = " test@example.com"
419+
420+ privateContactInfoFlow.test {
421+ // When
422+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (newEmailValue))
423+
424+ // Advance time past the debounce delay
425+ advanceTimeBy(501 ) // Just past the 500ms debounce delay
426+
427+ // Then
428+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
429+ privateEmail = newEmailValue
430+ )
431+ assertEquals(expectedPrivateContactInfo, expectMostRecentItem())
432+ cancel()
433+ }
434+ }
435+
436+ @Test
437+ fun `when multiple rapid email value changes occur then only the last one triggers updatePrivateContactInfo` () = runTest {
438+ // Given
439+ val firstEmailValue = " first@example.com"
440+ val secondEmailValue = " second@example.com"
441+ val thirdEmailValue = " third@example.com"
442+
443+ privateContactInfoFlow.test {
444+ // When - rapid changes
445+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (firstEmailValue))
446+ advanceTimeBy(100 ) // Not enough time for debounce
447+
448+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (secondEmailValue))
449+ advanceTimeBy(100 ) // Not enough time for debounce
450+
451+ viewModel.onEvent(ShareEvent .OnEmailValueChanged (thirdEmailValue))
452+
453+ // Advance time past the debounce delay
454+ advanceTimeBy(501 ) // Just past the 500ms debounce delay
455+
456+ // Then - only the last value should be emitted
457+ val expectedPrivateContactInfo = PrivateContactInfo .Default .copy(
458+ privateEmail = thirdEmailValue
459+ )
460+ assertEquals(expectedPrivateContactInfo, expectMostRecentItem())
461+
462+ cancel()
463+ }
464+ }
465+
324466 private fun createTestProfile () = Profile {
325467 hash = " test-hash"
326468 displayName = " Test User"
0 commit comments