NotificationsViewModelTestStatusAction.kt 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright 2023 Tusky Contributors
  3. *
  4. * This file is a part of Tusky.
  5. *
  6. * This program is free software; you can redistribute it and/or modify it under the terms of the
  7. * GNU General Public License as published by the Free Software Foundation; either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
  11. * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
  12. * Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License along with Tusky; if not,
  15. * see <http://www.gnu.org/licenses>.
  16. */
  17. package com.keylesspalace.tusky.components.notifications
  18. import app.cash.turbine.test
  19. import com.google.common.truth.Truth.assertThat
  20. import com.keylesspalace.tusky.FilterTest.Companion.mockStatus
  21. import com.keylesspalace.tusky.viewdata.StatusViewData
  22. import io.reactivex.rxjava3.core.Single
  23. import kotlinx.coroutines.ExperimentalCoroutinesApi
  24. import kotlinx.coroutines.test.runTest
  25. import org.junit.Test
  26. import org.mockito.kotlin.any
  27. import org.mockito.kotlin.argumentCaptor
  28. import org.mockito.kotlin.doReturn
  29. import org.mockito.kotlin.doThrow
  30. import org.mockito.kotlin.stub
  31. import org.mockito.kotlin.verify
  32. /**
  33. * Verify that [StatusAction] are handled correctly on receipt:
  34. *
  35. * - Is the correct [UiSuccess] or [UiError] value emitted?
  36. * - Is the correct [TimelineCases] function called, with the correct arguments?
  37. * This is only tested in the success case; if it passed there it must also
  38. * have passed in the error case.
  39. */
  40. @OptIn(ExperimentalCoroutinesApi::class)
  41. class NotificationsViewModelTestStatusAction : NotificationsViewModelTestBase() {
  42. private val status = mockStatus(pollOptions = listOf("Choice 1", "Choice 2", "Choice 3"))
  43. private val statusViewData = StatusViewData.Concrete(
  44. status = status,
  45. isExpanded = true,
  46. isShowingContent = false,
  47. isCollapsed = false
  48. )
  49. /** Action to bookmark a status */
  50. private val bookmarkAction = StatusAction.Bookmark(true, statusViewData)
  51. /** Action to favourite a status */
  52. private val favouriteAction = StatusAction.Favourite(true, statusViewData)
  53. /** Action to reblog a status */
  54. private val reblogAction = StatusAction.Reblog(true, statusViewData)
  55. /** Action to vote in a poll */
  56. private val voteInPollAction = StatusAction.VoteInPoll(
  57. poll = status.poll!!,
  58. choices = listOf(1, 0, 0),
  59. statusViewData
  60. )
  61. /** Captors for status ID and state arguments */
  62. private val id = argumentCaptor<String>()
  63. private val state = argumentCaptor<Boolean>()
  64. @Test
  65. fun `bookmark succeeds && emits UiSuccess`() = runTest {
  66. // Given
  67. timelineCases.stub { onBlocking { bookmark(any(), any()) } doReturn Single.just(status) }
  68. viewModel.uiSuccess.test {
  69. // When
  70. viewModel.accept(bookmarkAction)
  71. // Then
  72. val item = awaitItem()
  73. assertThat(item).isInstanceOf(StatusActionSuccess.Bookmark::class.java)
  74. assertThat((item as StatusActionSuccess).action).isEqualTo(bookmarkAction)
  75. }
  76. // Then
  77. verify(timelineCases).bookmark(id.capture(), state.capture())
  78. assertThat(id.firstValue).isEqualTo(statusViewData.status.id)
  79. assertThat(state.firstValue).isEqualTo(true)
  80. }
  81. @Test
  82. fun `bookmark fails && emits UiError`() = runTest {
  83. // Given
  84. timelineCases.stub { onBlocking { bookmark(any(), any()) } doThrow httpException }
  85. viewModel.uiError.test {
  86. // When
  87. viewModel.accept(bookmarkAction)
  88. // Then
  89. val item = awaitItem()
  90. assertThat(item).isInstanceOf(UiError.Bookmark::class.java)
  91. assertThat(item.action).isEqualTo(bookmarkAction)
  92. }
  93. }
  94. @Test
  95. fun `favourite succeeds && emits UiSuccess`() = runTest {
  96. // Given
  97. timelineCases.stub {
  98. onBlocking { favourite(any(), any()) } doReturn Single.just(status)
  99. }
  100. viewModel.uiSuccess.test {
  101. // When
  102. viewModel.accept(favouriteAction)
  103. // Then
  104. val item = awaitItem()
  105. assertThat(item).isInstanceOf(StatusActionSuccess.Favourite::class.java)
  106. assertThat((item as StatusActionSuccess).action).isEqualTo(favouriteAction)
  107. }
  108. // Then
  109. verify(timelineCases).favourite(id.capture(), state.capture())
  110. assertThat(id.firstValue).isEqualTo(statusViewData.status.id)
  111. assertThat(state.firstValue).isEqualTo(true)
  112. }
  113. @Test
  114. fun `favourite fails && emits UiError`() = runTest {
  115. // Given
  116. timelineCases.stub { onBlocking { favourite(any(), any()) } doThrow httpException }
  117. viewModel.uiError.test {
  118. // When
  119. viewModel.accept(favouriteAction)
  120. // Then
  121. val item = awaitItem()
  122. assertThat(item).isInstanceOf(UiError.Favourite::class.java)
  123. assertThat(item.action).isEqualTo(favouriteAction)
  124. }
  125. }
  126. @Test
  127. fun `reblog succeeds && emits UiSuccess`() = runTest {
  128. // Given
  129. timelineCases.stub { onBlocking { reblog(any(), any()) } doReturn Single.just(status) }
  130. viewModel.uiSuccess.test {
  131. // When
  132. viewModel.accept(reblogAction)
  133. // Then
  134. val item = awaitItem()
  135. assertThat(item).isInstanceOf(StatusActionSuccess.Reblog::class.java)
  136. assertThat((item as StatusActionSuccess).action).isEqualTo(reblogAction)
  137. }
  138. // Then
  139. verify(timelineCases).reblog(id.capture(), state.capture())
  140. assertThat(id.firstValue).isEqualTo(statusViewData.status.id)
  141. assertThat(state.firstValue).isEqualTo(true)
  142. }
  143. @Test
  144. fun `reblog fails && emits UiError`() = runTest {
  145. // Given
  146. timelineCases.stub { onBlocking { reblog(any(), any()) } doThrow httpException }
  147. viewModel.uiError.test {
  148. // When
  149. viewModel.accept(reblogAction)
  150. // Then
  151. val item = awaitItem()
  152. assertThat(item).isInstanceOf(UiError.Reblog::class.java)
  153. assertThat(item.action).isEqualTo(reblogAction)
  154. }
  155. }
  156. @Test
  157. fun `voteinpoll succeeds && emits UiSuccess`() = runTest {
  158. // Given
  159. timelineCases.stub {
  160. onBlocking { voteInPoll(any(), any(), any()) } doReturn Single.just(status.poll!!)
  161. }
  162. viewModel.uiSuccess.test {
  163. // When
  164. viewModel.accept(voteInPollAction)
  165. // Then
  166. val item = awaitItem()
  167. assertThat(item).isInstanceOf(StatusActionSuccess.VoteInPoll::class.java)
  168. assertThat((item as StatusActionSuccess).action).isEqualTo(voteInPollAction)
  169. }
  170. // Then
  171. val pollId = argumentCaptor<String>()
  172. val choices = argumentCaptor<List<Int>>()
  173. verify(timelineCases).voteInPoll(id.capture(), pollId.capture(), choices.capture())
  174. assertThat(id.firstValue).isEqualTo(statusViewData.status.id)
  175. assertThat(pollId.firstValue).isEqualTo(status.poll!!.id)
  176. assertThat(choices.firstValue).isEqualTo(voteInPollAction.choices)
  177. }
  178. @Test
  179. fun `voteinpoll fails && emits UiError`() = runTest {
  180. // Given
  181. timelineCases.stub { onBlocking { voteInPoll(any(), any(), any()) } doThrow httpException }
  182. viewModel.uiError.test {
  183. // When
  184. viewModel.accept(voteInPollAction)
  185. // Then
  186. val item = awaitItem()
  187. assertThat(item).isInstanceOf(UiError.VoteInPoll::class.java)
  188. assertThat(item.action).isEqualTo(voteInPollAction)
  189. }
  190. }
  191. }