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

Update release 3.0.0.1 #3693

Merged
merged 14 commits into from
Jun 18, 2024
Merged
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
fix: [ANDROAPP-6194] Search outside the program (#3664)
* fix: [ANDROAPP-6194] Send fetched list as parameter to avoid duplicated on search

Signed-off-by: andresmr <[email protected]>

* fix: [ANDROAPP-6194] Send fetched list as parameter to avoid duplicated on search

Signed-off-by: andresmr <[email protected]>

* fix: [ANDROAPP-6194] Add mockedWebServer response to mock get tracked entity instances

Signed-off-by: andresmr <[email protected]>

---------

Signed-off-by: andresmr <[email protected]>
andresmr authored Jun 6, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit ae2439939ff89f158f1c16ed77745eb06f3f2167
Original file line number Diff line number Diff line change
@@ -15,4 +15,11 @@ class MockWebServerRobot(private val dhis2MockServer: Dhis2MockServer) {
fun addResponse(method: String, path: String, sdkResource: String, responseCode: Int = 200) {
dhis2MockServer.addResponse(method, path, sdkResource, responseCode)
}

companion object {
const val API_OLD_TRACKED_ENTITY_PATH = "/api/trackedEntityInstances/query?.*"
const val API_OLD_TRACKED_ENTITY_RESPONSE =
"mocks/teilist/old_tracked_entity_empty_response.json"

}
}
Original file line number Diff line number Diff line change
@@ -7,11 +7,14 @@ import androidx.compose.ui.text.intl.Locale
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import org.dhis2.R
import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_OLD_TRACKED_ENTITY_PATH
import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_OLD_TRACKED_ENTITY_RESPONSE
import org.dhis2.usescases.BaseTest
import org.dhis2.usescases.flow.teiFlow.entity.DateRegistrationUIModel
import org.dhis2.usescases.flow.teiFlow.entity.RegisterTEIUIModel
import org.dhis2.usescases.flow.teiFlow.teiFlowRobot
import org.dhis2.usescases.searchTrackEntity.SearchTEActivity
import org.hisp.dhis.android.core.mockwebserver.ResponseController
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -28,8 +31,19 @@ class SearchFlowTest : BaseTest() {
private val dateRegistration = createFirstSpecificDate()
private val dateEnrollment = createEnrollmentDate()

override fun setUp() {
super.setUp()
setupMockServer()
}

@Test
fun shouldCreateTEIAndFilterByEnrollment() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

setDatePicker()
val registerTEIDetails = createRegisterTEI()
val enrollmentStatus = context.getString(R.string.filters_title_enrollment_status)
Original file line number Diff line number Diff line change
@@ -4,12 +4,15 @@ import android.content.Intent
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_OLD_TRACKED_ENTITY_PATH
import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_OLD_TRACKED_ENTITY_RESPONSE
import org.dhis2.usescases.BaseTest
import org.dhis2.usescases.flow.teiFlow.entity.DateRegistrationUIModel
import org.dhis2.usescases.flow.teiFlow.entity.EnrollmentListUIModel
import org.dhis2.usescases.flow.teiFlow.entity.RegisterTEIUIModel
import org.dhis2.usescases.searchTrackEntity.SearchTEActivity
import org.dhis2.usescases.teiDashboard.TeiDashboardMobileActivity
import org.hisp.dhis.android.core.mockwebserver.ResponseController
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -33,8 +36,19 @@ class TeiFlowTest : BaseTest() {
private val dateEnrollment = createEnrollmentDate()
private val currentDate = getCurrentDate()

override fun setUp() {
super.setUp()
setupMockServer()
}

@Test
fun shouldEnrollToSameProgramAfterClosingIt() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val totalEventsPerEnrollment = 3
val enrollmentListDetails = createEnrollmentList()
val registerTeiDetails = createRegisterTEI()
@@ -105,6 +119,5 @@ class TeiFlowTest : BaseTest() {

const val DATE_FORMAT = "dd/M/yyyy"
const val DATE_PICKER_FORMAT = ", d MMMM"

}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package org.dhis2.usescases.searchte

import androidx.compose.ui.test.hasText
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.text.capitalize
import androidx.compose.ui.text.intl.Locale
@@ -19,6 +17,8 @@ import dispatch.android.espresso.IdlingDispatcherProviderRule
import org.dhis2.R
import org.dhis2.bindings.app
import org.dhis2.common.idlingresources.MapIdlingResource
import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_OLD_TRACKED_ENTITY_PATH
import org.dhis2.common.mockwebserver.MockWebServerRobot.Companion.API_OLD_TRACKED_ENTITY_RESPONSE
import org.dhis2.commons.date.DateUtils.SIMPLE_DATE_FORMAT
import org.dhis2.lazyActivityScenarioRule
import org.dhis2.ui.dialogs.bottomsheet.SECONDARY_BUTTON_TAG
@@ -32,6 +32,7 @@ import org.dhis2.usescases.searchte.entity.DisplayListFieldsUIModel
import org.dhis2.usescases.searchte.robot.filterRobot
import org.dhis2.usescases.searchte.robot.searchTeiRobot
import org.dhis2.usescases.teidashboard.robot.teiDashboardRobot
import org.hisp.dhis.android.core.mockwebserver.ResponseController
import org.junit.After
import org.junit.Ignore
import org.junit.Rule
@@ -61,8 +62,19 @@ class SearchTETest : BaseTest() {
@get:Rule
val composeTestRule = createComposeRule()

override fun setUp() {
super.setUp()
setupMockServer()
}

@Test
fun shouldSuccessfullySearchByName() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val firstName = "Tim"
val lastName = "Johnson"

@@ -82,6 +94,12 @@ class SearchTETest : BaseTest() {

@Test
fun shouldShowErrorWhenCanNotFindSearchResult() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val firstName = "asdssds"

prepareTestProgramRulesProgrammeIntentAndLaunchActivity(rule)
@@ -97,6 +115,12 @@ class SearchTETest : BaseTest() {

@Test
fun shouldSuccessfullySearchUsingMoreThanOneField() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val firstName = "Anna"
val lastName = "Jones"

@@ -133,6 +157,12 @@ class SearchTETest : BaseTest() {

@Test
fun shouldCheckDisplayInList() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val displayInListData = createDisplayListFields()

prepareTestAdultWomanProgrammeIntentAndLaunchActivity(rule)
@@ -286,6 +316,12 @@ class SearchTETest : BaseTest() {

@Test
fun shouldSuccessfullyFilterBySync() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val teiName = "Frank"
val teiLastName = "Fjordsen"
val syncFilter = context.getString(R.string.action_sync)
@@ -321,6 +357,12 @@ class SearchTETest : BaseTest() {

@Test
fun shouldSuccessfullySearchAndFilter() {
mockWebServerRobot.addResponse(
ResponseController.GET,
API_OLD_TRACKED_ENTITY_PATH,
API_OLD_TRACKED_ENTITY_RESPONSE,
)

val name = "Anna"
val lastName = "Jones"
val enrollmentStatus = context.getString(R.string.filters_title_enrollment_status)
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"headers": [
{
"name": "instance",
"column": "Instance",
"type": "java.lang.String",
"hidden": false,
"meta": false
},
{
"name": "created",
"column": "Created",
"type": "java.lang.String",
"hidden": false,
"meta": false
},
{
"name": "lastupdated",
"column": "Last updated",
"type": "java.lang.String",
"hidden": false,
"meta": false
},
{
"name": "ou",
"column": "Organisation unit",
"type": "java.lang.String",
"hidden": false,
"meta": false
},
{
"name": "ouname",
"column": "Organisation unit name",
"type": "java.lang.String",
"hidden": false,
"meta": false
},
{
"name": "te",
"column": "Tracked entity type",
"type": "java.lang.String",
"hidden": false,
"meta": false
},
{
"name": "inactive",
"column": "Inactive",
"type": "java.lang.String",
"hidden": false,
"meta": false
}
],
"metaData": {
"names": {
"nEenWmSyUEp": "Person"
}
},
"width": 9,
"height": 0,
"rows": [
]
}
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@
import org.dhis2.commons.data.EventViewModel;
import org.dhis2.commons.data.SearchTeiModel;
import org.dhis2.commons.data.tuples.Pair;
import org.dhis2.commons.filters.FilterManager;
import org.dhis2.commons.filters.sorting.SortingItem;
import org.dhis2.data.search.SearchParametersModel;
import org.hisp.dhis.android.core.arch.call.D2Progress;
@@ -17,14 +18,16 @@
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem;
import org.jetbrains.annotations.NotNull;

import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import io.reactivex.Flowable;
import io.reactivex.Observable;
import kotlin.Deprecated;

@Deprecated(message = "Use SearchRepositoryKt instead")
public interface SearchRepository {

Observable<List<Program>> programsWithRegistration(String programTypeId);
@@ -82,4 +85,10 @@ public interface SearchRepository {
List<String> trackedEntityTypeFields();

boolean filtersApplyOnGlobalSearch();

@NotNull HashSet<String> getFetchedTeiUIDs();

SearchParametersModel getSavedSearchParameters();

FilterManager getSavedFilters();
}
Original file line number Diff line number Diff line change
@@ -24,8 +24,8 @@
import org.dhis2.commons.filters.sorting.SortingItem;
import org.dhis2.commons.network.NetworkUtils;
import org.dhis2.commons.reporting.CrashReportController;
import org.dhis2.commons.resources.MetadataIconProvider;
import org.dhis2.commons.resources.DhisPeriodUtils;
import org.dhis2.commons.resources.MetadataIconProvider;
import org.dhis2.commons.resources.ResourceManager;
import org.dhis2.data.dhislogic.DhisEnrollmentUtils;
import org.dhis2.data.forms.dataentry.SearchTEIRepository;
@@ -43,7 +43,6 @@
import org.dhis2.utils.ValueUtils;
import org.hisp.dhis.android.core.D2;
import org.hisp.dhis.android.core.arch.call.D2Progress;
import org.hisp.dhis.android.core.arch.helpers.Result;
import org.hisp.dhis.android.core.arch.helpers.UidsHelper;
import org.hisp.dhis.android.core.arch.repositories.scope.RepositoryScope;
import org.hisp.dhis.android.core.common.FeatureType;
@@ -57,7 +56,6 @@
import org.hisp.dhis.android.core.event.Event;
import org.hisp.dhis.android.core.event.EventCollectionRepository;
import org.hisp.dhis.android.core.event.EventStatus;
import org.hisp.dhis.android.core.maintenance.D2Error;
import org.hisp.dhis.android.core.organisationunit.OrganisationUnit;
import org.hisp.dhis.android.core.program.Program;
import org.hisp.dhis.android.core.program.ProgramStage;
@@ -79,7 +77,6 @@
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem;
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItemAttribute;
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItemHelper;
import org.hisp.dhis.mobile.ui.designsystem.theme.SurfaceColor;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
@@ -606,6 +603,21 @@ public boolean filtersApplyOnGlobalSearch() {
!FilterManager.getInstance().getStateFilters().isEmpty();
}

@Override
public @NotNull HashSet<String> getFetchedTeiUIDs() {
return fetchedTeiUids;
}

@Override
public SearchParametersModel getSavedSearchParameters() {
return savedSearchParameters;
}

@Override
public FilterManager getSavedFilters() {
return savedFilters;
}

@Override
public Observable<TrackedEntityType> getTrackedEntityType(String trackedEntityUid) {
return d2.trackedEntityModule().trackedEntityTypes().uid(trackedEntityUid).get().toObservable();
@@ -697,17 +709,6 @@ public TeiDownloadResult download(String teiUid, @Nullable String enrollmentUid,
return teiDownloader.download(teiUid, enrollmentUid, reason);
}

public SearchTeiModel transformResult(Result<TrackedEntitySearchItem, D2Error> result, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem) {
try {
return transform(result.getOrThrow(), selectedProgram, offlineOnly, sortingItem);
} catch (Exception e) {
SearchTeiModel errorModel = new SearchTeiModel();
errorModel.onlineErrorMessage = resources.parseD2Error(e);
errorModel.onlineErrorCode = ((D2Error) e).errorCode();
return errorModel;
}
}

@Override
public SearchTeiModel transform(TrackedEntitySearchItem searchItem, @Nullable Program selectedProgram, boolean offlineOnly, SortingItem sortingItem) {
if (!fetchedTeiUids.contains(searchItem.uid())) {
Original file line number Diff line number Diff line change
@@ -30,14 +30,8 @@ class SearchRepositoryImplKt(
private val metadataIconProvider: MetadataIconProvider,
) : SearchRepositoryKt {

private lateinit var savedSearchParamenters: SearchParametersModel

private lateinit var savedFilters: FilterManager

private lateinit var trackedEntityInstanceQuery: TrackedEntitySearchCollectionRepository

private val fetchedTeiUids = HashSet<String>()

override fun searchTrackedEntities(
searchParametersModel: SearchParametersModel,
isOnline: Boolean,
@@ -51,26 +45,23 @@ class SearchRepositoryImplKt(
isOnline: Boolean,
): TrackedEntitySearchCollectionRepository {
var allowCache = false
savedSearchParamenters = searchParametersModel.copy()
savedFilters = FilterManager.getInstance().copy()

if (searchParametersModel != savedSearchParamenters || !FilterManager.getInstance()
.sameFilters(savedFilters)
if (searchParametersModel != searchRepositoryJava.savedSearchParameters || !FilterManager.getInstance()
.sameFilters(searchRepositoryJava.savedFilters)
) {
trackedEntityInstanceQuery =
searchRepositoryJava.getFilteredRepository(searchParametersModel)
} else {
trackedEntityInstanceQuery =
searchRepositoryJava.getFilteredRepository(searchParametersModel)
searchRepositoryJava.getFilteredRepository(searchParametersModel)
allowCache = true
}

if (fetchedTeiUids.isNotEmpty() && searchParametersModel.selectedProgram == null) {
if (searchRepositoryJava.fetchedTeiUIDs.isNotEmpty() && searchParametersModel.selectedProgram == null) {
trackedEntityInstanceQuery =
trackedEntityInstanceQuery.excludeUids().`in`(fetchedTeiUids.toList())
trackedEntityInstanceQuery.excludeUids().`in`(searchRepositoryJava.fetchedTeiUIDs.toList())
}

val pagerFlow = if (isOnline && FilterManager.getInstance().stateFilters.isNotEmpty()) {
val pagerFlow = if (isOnline && FilterManager.getInstance().stateFilters.isEmpty()) {
trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineFirst()
} else {
trackedEntityInstanceQuery.allowOnlineCache().eq(allowCache).offlineOnly()
@@ -131,7 +122,8 @@ class SearchRepositoryImplKt(
options.associate {
it.uid() to metadataIconProvider(
it.style(),
program?.style()?.color()?.toColor() ?: SurfaceColor.Primary,
program?.style()?.color()?.toColor()
?: SurfaceColor.Primary,
)
}

@@ -177,7 +169,12 @@ class SearchRepositoryImplKt(
.blockingGet()

val metadataIconMap =
options.associate { it.uid() to metadataIconProvider(it.style(), SurfaceColor.Primary) }
options.associate {
it.uid() to metadataIconProvider(
it.style(),
SurfaceColor.Primary,
)
}

OptionSetConfiguration.OptionConfigData(
options = options,
Original file line number Diff line number Diff line change
@@ -373,7 +373,7 @@ class SearchTEIViewModel(

suspend fun fetchGlobalResults() = withContext(dispatchers.io()) {
val searchParametersModel = SearchParametersModel(
selectedProgram = searchRepository.getProgram(initialProgramUid),
selectedProgram = null,
queryData = queryData,
)
val getPagingData = searchRepositoryKt.searchTrackedEntities(
@@ -927,6 +927,7 @@ class SearchTEIViewModel(
ValueType.ORGANISATION_UNIT, ValueType.MULTI_TEXT -> {
map[item.uid] = (item.displayName ?: "")
}

ValueType.DATE, ValueType.AGE -> {
item.value?.let {
if (it.isNotEmpty()) {
@@ -941,6 +942,7 @@ class SearchTEIViewModel(
}
}
}

ValueType.DATETIME -> {
item.value?.let {
if (it.isNotEmpty()) {
@@ -955,19 +957,23 @@ class SearchTEIViewModel(
}
}
}

ValueType.BOOLEAN -> {
map[item.uid] = "${item.label}: ${item.value}"
}

ValueType.TRUE_ONLY -> {
item.value?.let {
if (it == "true") {
map[item.uid] = item.label
}
}
}

ValueType.PERCENTAGE -> {
map[item.uid] = "${item.value}%"
}

else -> {
map[item.uid] = (item.value ?: "")
}
389 changes: 0 additions & 389 deletions app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEUi.kt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.dhis2.usescases.searchTrackEntity

import androidx.paging.PagingData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.setMain
import org.dhis2.commons.resources.MetadataIconProvider
import org.dhis2.commons.viewmodel.DispatcherProvider
import org.dhis2.data.search.SearchParametersModel
import org.dhis2.form.ui.FieldViewModelFactory
import org.hisp.dhis.android.core.D2
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchCollectionRepository
import org.hisp.dhis.android.core.trackedentity.search.TrackedEntitySearchItem
import org.junit.After
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito
import org.mockito.Mockito.verify
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@OptIn(ExperimentalCoroutinesApi::class)
class SearchRepositoryTest {
private val searchRepositoryJava: SearchRepository = mock()

private val d2: D2 = mock(defaultAnswer = Mockito.RETURNS_DEEP_STUBS)

private val dispatcher: DispatcherProvider = mock()

private val fieldViewModelFactory: FieldViewModelFactory = mock()

private val metadataIconProvider: MetadataIconProvider = mock()

private lateinit var searchRepositoryImplKt: SearchRepositoryImplKt

private val testDispatcher = UnconfinedTestDispatcher()

@Before
fun setup() {
Dispatchers.setMain(testDispatcher)
searchRepositoryImplKt = SearchRepositoryImplKt(
searchRepositoryJava,
d2,
dispatcher,
fieldViewModelFactory,
metadataIconProvider,
)
}

@After
fun tearDown() {
Dispatchers.resetMain()
}

@Test
fun `test searchTrackedEntities returns PagingData flow`() = runTest {
val searchParametersModel: SearchParametersModel = SearchParametersModel(
selectedProgram = null,
queryData = null,
)
val trackedEntitySearchCollectionRepository: TrackedEntitySearchCollectionRepository =
mock()
val pagingDataFlow = flowOf(PagingData.empty<TrackedEntitySearchItem>())

whenever(searchRepositoryJava.getFilteredRepository(searchParametersModel)) doReturn trackedEntitySearchCollectionRepository
whenever(trackedEntitySearchCollectionRepository.allowOnlineCache()) doReturn mock()
whenever(trackedEntitySearchCollectionRepository.allowOnlineCache().eq(false)) doReturn mock()
whenever(trackedEntitySearchCollectionRepository.allowOnlineCache().eq(false).offlineFirst()) doReturn mock()
whenever(trackedEntitySearchCollectionRepository.allowOnlineCache().eq(false).offlineOnly()) doReturn mock()
whenever(trackedEntitySearchCollectionRepository.allowOnlineCache().eq(false).offlineOnly().getPagingData(10)) doReturn mock()
whenever(trackedEntitySearchCollectionRepository.allowOnlineCache().eq(false).offlineFirst().getPagingData(10)) doReturn mock()
whenever(trackedEntitySearchCollectionRepository.getPagingData(10)) doReturn pagingDataFlow

val result =
searchRepositoryImplKt.searchTrackedEntities(searchParametersModel, isOnline = true)

result.collect { pagingData ->
assertTrue(pagingData is PagingData<TrackedEntitySearchItem>)
}

verify(searchRepositoryJava).getFilteredRepository(searchParametersModel)
// verify(trackedEntitySearchCollectionRepository).getPagingData(10)
}
}