Skip to content

Commit 26eb430

Browse files
feat: junit test
1 parent 528dd3f commit 26eb430

File tree

2 files changed

+380
-4
lines changed

2 files changed

+380
-4
lines changed

core/src/main/java/org/openedx/core/presentation/dialog/downloaddialog/DownloadDialogManager.kt

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,14 @@ class DownloadDialogManager(
238238
}
239239
}
240240
val size = blocks.sumOf { it.getFileSize() }
241-
if (size > 0) DownloadDialogItem(
242-
title = subSectionBlock.displayName,
243-
size = size
244-
) else null
241+
if (size > 0) {
242+
DownloadDialogItem(
243+
title = subSectionBlock.displayName,
244+
size = size
245+
)
246+
} else {
247+
null
248+
}
245249
}
246250

247251
uiState.emit(
Lines changed: 372 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
1+
package org.openedx.downloads
2+
3+
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
4+
import androidx.fragment.app.FragmentManager
5+
import io.mockk.coEvery
6+
import io.mockk.coVerify
7+
import io.mockk.every
8+
import io.mockk.mockk
9+
import io.mockk.verify
10+
import kotlinx.coroutines.Dispatchers
11+
import kotlinx.coroutines.async
12+
import kotlinx.coroutines.flow.first
13+
import kotlinx.coroutines.flow.flow
14+
import kotlinx.coroutines.flow.flowOf
15+
import kotlinx.coroutines.test.StandardTestDispatcher
16+
import kotlinx.coroutines.test.advanceUntilIdle
17+
import kotlinx.coroutines.test.resetMain
18+
import kotlinx.coroutines.test.runTest
19+
import kotlinx.coroutines.test.setMain
20+
import org.junit.After
21+
import org.junit.Assert.assertEquals
22+
import org.junit.Assert.assertFalse
23+
import org.junit.Before
24+
import org.junit.Rule
25+
import org.junit.Test
26+
import org.junit.rules.TestRule
27+
import org.openedx.core.BlockType
28+
import org.openedx.core.R
29+
import org.openedx.core.config.Config
30+
import org.openedx.core.data.storage.CorePreferences
31+
import org.openedx.core.domain.model.AssignmentProgress
32+
import org.openedx.core.domain.model.Block
33+
import org.openedx.core.domain.model.BlockCounts
34+
import org.openedx.core.domain.model.CourseStructure
35+
import org.openedx.core.domain.model.CoursewareAccess
36+
import org.openedx.core.domain.model.DownloadCoursePreview
37+
import org.openedx.core.module.DownloadWorkerController
38+
import org.openedx.core.module.db.DownloadDao
39+
import org.openedx.core.module.db.DownloadModel
40+
import org.openedx.core.module.db.DownloadModelEntity
41+
import org.openedx.core.module.db.DownloadedState
42+
import org.openedx.core.module.db.FileType
43+
import org.openedx.core.module.download.DownloadHelper
44+
import org.openedx.core.presentation.CoreAnalytics
45+
import org.openedx.core.presentation.DownloadsAnalytics
46+
import org.openedx.core.presentation.dialog.downloaddialog.DownloadDialogManager
47+
import org.openedx.core.system.connection.NetworkConnection
48+
import org.openedx.downloads.domain.interactor.DownloadInteractor
49+
import org.openedx.downloads.presentation.DownloadsRouter
50+
import org.openedx.downloads.presentation.download.DownloadsViewModel
51+
import org.openedx.foundation.presentation.UIMessage
52+
import org.openedx.foundation.system.ResourceManager
53+
import org.openedx.foundation.utils.FileUtil
54+
import java.net.UnknownHostException
55+
import java.util.Date
56+
57+
class DownloadsViewModelTest {
58+
59+
@get:Rule
60+
val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
61+
62+
private val dispatcher = StandardTestDispatcher()
63+
64+
// Mocks for all dependencies
65+
private val downloadsRouter = mockk<DownloadsRouter>(relaxed = true)
66+
private val networkConnection = mockk<NetworkConnection>(relaxed = true)
67+
private val interactor = mockk<DownloadInteractor>(relaxed = true)
68+
private val downloadDialogManager = mockk<DownloadDialogManager>(relaxed = true)
69+
private val resourceManager = mockk<ResourceManager>(relaxed = true)
70+
private val fileUtil = mockk<FileUtil>(relaxed = true)
71+
private val config = mockk<Config>(relaxed = true)
72+
private val analytics = mockk<DownloadsAnalytics>(relaxed = true)
73+
private val preferencesManager = mockk<CorePreferences>(relaxed = true)
74+
private val coreAnalytics = mockk<CoreAnalytics>(relaxed = true)
75+
private val downloadDao = mockk<DownloadDao>(relaxed = true)
76+
private val workerController = mockk<DownloadWorkerController>(relaxed = true)
77+
private val downloadHelper = mockk<DownloadHelper>(relaxed = true)
78+
79+
private val noInternet = "No connection"
80+
private val unknownError = "Unknown error"
81+
82+
private val downloadCoursePreview =
83+
DownloadCoursePreview(id = "", name = "", image = "", totalSize = 0)
84+
private val assignmentProgress = AssignmentProgress(
85+
assignmentType = "Homework",
86+
numPointsEarned = 1f,
87+
numPointsPossible = 3f
88+
)
89+
private val blocks = listOf(
90+
Block(
91+
id = "id",
92+
blockId = "blockId",
93+
lmsWebUrl = "lmsWebUrl",
94+
legacyWebUrl = "legacyWebUrl",
95+
studentViewUrl = "studentViewUrl",
96+
type = BlockType.CHAPTER,
97+
displayName = "Block",
98+
graded = false,
99+
studentViewData = null,
100+
studentViewMultiDevice = false,
101+
blockCounts = BlockCounts(0),
102+
descendants = listOf("1", "id1"),
103+
descendantsType = BlockType.HTML,
104+
completion = 0.0,
105+
assignmentProgress = assignmentProgress,
106+
due = Date(),
107+
offlineDownload = null,
108+
),
109+
Block(
110+
id = "id1",
111+
blockId = "blockId",
112+
lmsWebUrl = "lmsWebUrl",
113+
legacyWebUrl = "legacyWebUrl",
114+
studentViewUrl = "studentViewUrl",
115+
type = BlockType.HTML,
116+
displayName = "Block",
117+
graded = false,
118+
studentViewData = null,
119+
studentViewMultiDevice = false,
120+
blockCounts = BlockCounts(0),
121+
descendants = listOf("id2"),
122+
descendantsType = BlockType.HTML,
123+
completion = 0.0,
124+
assignmentProgress = assignmentProgress,
125+
due = Date(),
126+
offlineDownload = null,
127+
),
128+
Block(
129+
id = "id2",
130+
blockId = "blockId",
131+
lmsWebUrl = "lmsWebUrl",
132+
legacyWebUrl = "legacyWebUrl",
133+
studentViewUrl = "studentViewUrl",
134+
type = BlockType.HTML,
135+
displayName = "Block",
136+
graded = false,
137+
studentViewData = null,
138+
studentViewMultiDevice = false,
139+
blockCounts = BlockCounts(0),
140+
descendants = emptyList(),
141+
descendantsType = BlockType.HTML,
142+
completion = 0.0,
143+
assignmentProgress = assignmentProgress,
144+
due = Date(),
145+
offlineDownload = null,
146+
)
147+
)
148+
149+
private val downloadModel = DownloadModel(
150+
"id",
151+
"title",
152+
"",
153+
0,
154+
"",
155+
"url",
156+
FileType.VIDEO,
157+
DownloadedState.NOT_DOWNLOADED,
158+
null
159+
)
160+
161+
private val courseStructure = CourseStructure(
162+
root = "",
163+
blockData = blocks,
164+
id = "id",
165+
name = "Course name",
166+
number = "",
167+
org = "Org",
168+
start = Date(),
169+
startDisplay = "",
170+
startType = "",
171+
end = Date(),
172+
coursewareAccess = CoursewareAccess(
173+
true,
174+
"",
175+
"",
176+
"",
177+
"",
178+
""
179+
),
180+
media = null,
181+
certificate = null,
182+
isSelfPaced = false,
183+
progress = null
184+
)
185+
186+
@Before
187+
fun setUp() {
188+
Dispatchers.setMain(dispatcher)
189+
every { config.getApiHostURL() } returns "http://localhost:8000"
190+
every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
191+
every { resourceManager.getString(R.string.core_error_unknown_error) } returns unknownError
192+
every { networkConnection.isOnline() } returns true
193+
194+
coEvery { interactor.getDownloadCoursesPreview(any()) } returns flow {
195+
emit(listOf(downloadCoursePreview))
196+
}
197+
coEvery { interactor.getCourseStructureFromCache("course1") } returns courseStructure
198+
coEvery { interactor.getCourseStructure("course1") } returns courseStructure
199+
coEvery { interactor.getDownloadModels() } returns flowOf(emptyList())
200+
coEvery { interactor.getAllDownloadModels() } returns emptyList()
201+
coEvery { downloadDao.getAllDataFlow() } returns flowOf(
202+
listOf(
203+
DownloadModelEntity.createFrom(
204+
downloadModel
205+
)
206+
)
207+
)
208+
}
209+
210+
@After
211+
fun tearDown() {
212+
Dispatchers.resetMain()
213+
}
214+
215+
@Test
216+
fun `onSettingsClick should navigate to settings`() = runTest {
217+
val viewModel = DownloadsViewModel(
218+
downloadsRouter,
219+
networkConnection,
220+
interactor,
221+
downloadDialogManager,
222+
resourceManager,
223+
fileUtil,
224+
config,
225+
analytics,
226+
preferencesManager,
227+
coreAnalytics,
228+
downloadDao,
229+
workerController,
230+
downloadHelper
231+
)
232+
advanceUntilIdle()
233+
234+
val fragmentManager = mockk<FragmentManager>(relaxed = true)
235+
viewModel.onSettingsClick(fragmentManager)
236+
verify(exactly = 1) { downloadsRouter.navigateToSettings(fragmentManager) }
237+
}
238+
239+
@Test
240+
fun `downloadCourse should update courseDownloadState and show download dialog`() = runTest {
241+
val viewModel = DownloadsViewModel(
242+
downloadsRouter,
243+
networkConnection,
244+
interactor,
245+
downloadDialogManager,
246+
resourceManager,
247+
fileUtil,
248+
config,
249+
analytics,
250+
preferencesManager,
251+
coreAnalytics,
252+
downloadDao,
253+
workerController,
254+
downloadHelper
255+
)
256+
advanceUntilIdle()
257+
258+
val fragmentManager = mockk<FragmentManager>(relaxed = true)
259+
viewModel.downloadCourse(fragmentManager, "course1")
260+
advanceUntilIdle()
261+
262+
verify(exactly = 1) { analytics.logEvent(any(), any()) }
263+
264+
coVerify(exactly = 1) {
265+
downloadDialogManager.showPopup(
266+
any(), any(), any(), any(), any(), any(), any(), any(), any()
267+
)
268+
}
269+
}
270+
271+
@Test
272+
fun `cancelDownloading should update courseDownloadState to NOT_DOWNLOADED and cancel download job`() =
273+
runTest {
274+
val viewModel = DownloadsViewModel(
275+
downloadsRouter,
276+
networkConnection,
277+
interactor,
278+
downloadDialogManager,
279+
resourceManager,
280+
fileUtil,
281+
config,
282+
analytics,
283+
preferencesManager,
284+
coreAnalytics,
285+
downloadDao,
286+
workerController,
287+
downloadHelper
288+
)
289+
advanceUntilIdle()
290+
291+
val fragmentManager = mockk<FragmentManager>(relaxed = true)
292+
viewModel.downloadCourse(fragmentManager, "course1")
293+
advanceUntilIdle()
294+
295+
viewModel.cancelDownloading("course1")
296+
advanceUntilIdle()
297+
298+
assertEquals(
299+
DownloadedState.NOT_DOWNLOADED,
300+
viewModel.uiState.value.courseDownloadState["course1"]
301+
)
302+
303+
coVerify { interactor.getAllDownloadModels() }
304+
}
305+
306+
@Test
307+
fun `removeDownloads should show remove popup with correct parameters`() = runTest {
308+
coEvery { interactor.getDownloadModels() } returns flowOf(listOf(downloadModel))
309+
310+
val viewModel = DownloadsViewModel(
311+
downloadsRouter,
312+
networkConnection,
313+
interactor,
314+
downloadDialogManager,
315+
resourceManager,
316+
fileUtil,
317+
config,
318+
analytics,
319+
preferencesManager,
320+
coreAnalytics,
321+
downloadDao,
322+
workerController,
323+
downloadHelper
324+
)
325+
advanceUntilIdle()
326+
327+
val fragmentManager = mockk<FragmentManager>(relaxed = true)
328+
viewModel.removeDownloads(fragmentManager, "course1")
329+
advanceUntilIdle()
330+
331+
coVerify {
332+
downloadDialogManager.showRemoveDownloadModelPopup(
333+
any(),
334+
any(),
335+
any()
336+
)
337+
}
338+
339+
verify(exactly = 1) { analytics.logEvent(any(), any()) }
340+
}
341+
342+
@Test
343+
fun `refreshData no internet error should emit snack bar message`() = runTest {
344+
every { networkConnection.isOnline() } returns true
345+
coEvery { interactor.getDownloadCoursesPreview(any()) } returns flow { throw UnknownHostException() }
346+
347+
val viewModel = DownloadsViewModel(
348+
downloadsRouter,
349+
networkConnection,
350+
interactor,
351+
downloadDialogManager,
352+
resourceManager,
353+
fileUtil,
354+
config,
355+
analytics,
356+
preferencesManager,
357+
coreAnalytics,
358+
downloadDao,
359+
workerController,
360+
downloadHelper
361+
)
362+
val deferred = async { viewModel.uiMessage.first() }
363+
advanceUntilIdle()
364+
365+
viewModel.refreshData()
366+
advanceUntilIdle()
367+
368+
assertEquals(noInternet, (deferred.await() as? UIMessage.SnackBarMessage)?.message)
369+
// Also verify that the refreshing flag is cleared.
370+
assertFalse(viewModel.uiState.value.isRefreshing)
371+
}
372+
}

0 commit comments

Comments
 (0)