Skip to content

Commit

Permalink
Enhance Image and ActionableButton Widget to add ability to copy text (
Browse files Browse the repository at this point in the history
…#3040)

* Enhance Actionable Button to add ability to copy text

* Remove unnecessary icon

* Run spotlessApply

* Remove context as param from Composable function

* Refactor Image Widget to enable on click

* Remove unnecessary ActionableButton previews

* Add text property

* Run spotlessApply

* Fix failing tests and add test to confirm toast is shown

* Run spotlessApply

* Add text color

* Update button as per design requirements

* Add condition when clicking an image

* Run spotlessApply on Image.kt

* Add docs for Copy data on actionable button

* Add tests for ConfigurationRegistry.writeFile()

---------

Co-authored-by: Peter Lubell-Doughtie <[email protected]>
  • Loading branch information
SebaMutuku and pld authored Feb 22, 2024
1 parent 04110ad commit 0b03f5b
Show file tree
Hide file tree
Showing 23 changed files with 393 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ constructor(
} // is focus.identifier a necessary check
.groupBy { section ->
section.focus.reference.substringBefore(
ConfigurationRegistry.TYPE_REFERENCE_DELIMITER,
TYPE_REFERENCE_DELIMITER,
missingDelimiterValue = "",
)
}
Expand Down Expand Up @@ -586,7 +586,7 @@ constructor(
?: "${openSrpApplication?.getFhirServerHost().toString()?.trimEnd { it == '/' }}/${this.referenceValue()}"
}

private fun writeToFile(resource: Resource): File {
fun writeToFile(resource: Resource): File {
val fileName =
if (resource is MetadataResource && resource.name != null) {
resource.name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ data class NavigationMenuConfig(
data class ImageConfig(
val type: String = ICON_TYPE_LOCAL,
val reference: String? = null,
val color: String? = null,
@Contextual var decodedBitmap: Bitmap? = null,
) : Parcelable, java.io.Serializable {
fun interpolate(computedValuesMap: Map<String, Any>): ImageConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.compose.ui.graphics.Shape
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.Serializable
import org.smartregister.fhircore.engine.configuration.navigation.ImageConfig
import org.smartregister.fhircore.engine.domain.model.ActionConfig
import org.smartregister.fhircore.engine.domain.model.ViewType
import org.smartregister.fhircore.engine.util.extension.interpolate

Expand All @@ -40,9 +41,12 @@ data class ImageProperties(
override val clickable: String = "false",
override val visible: String = "true",
val tint: String? = null,
val text: String? = null,
val imageConfig: ImageConfig? = null,
val size: Int? = null,
val shape: ImageShape? = null,
val textColor: String? = null,
val actions: List<ActionConfig> = emptyList(),
) : ViewProperties(), Parcelable {
override fun interpolate(computedValuesMap: Map<String, Any>): ViewProperties {
return this.copy(
Expand All @@ -53,6 +57,7 @@ data class ImageProperties(
),
tint = this.tint?.interpolate(computedValuesMap),
backgroundColor = this.backgroundColor?.interpolate(computedValuesMap),
text = this.text?.interpolate(computedValuesMap),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,7 @@ enum class ApplicationWorkflow {

/** A workflow that launches user insight screen */
LAUNCH_INSIGHT_SCREEN,

/** A workflow that copies text to keyboard */
COPY_TEXT,
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ import dagger.hilt.android.testing.HiltAndroidTest
import dagger.hilt.android.testing.HiltTestApplication
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import java.io.File
import java.net.URL
import javax.inject.Inject
import kotlinx.coroutines.test.runTest
Expand All @@ -50,9 +52,11 @@ import org.hl7.fhir.r4.model.Reference
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
import org.junit.Assert
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.smartregister.fhircore.engine.OpenSrpApplication
import org.smartregister.fhircore.engine.app.AppConfigService
import org.smartregister.fhircore.engine.app.fakes.Faker
Expand All @@ -68,6 +72,7 @@ import org.smartregister.fhircore.engine.rule.CoroutineTestRule
import org.smartregister.fhircore.engine.util.DispatcherProvider
import org.smartregister.fhircore.engine.util.SharedPreferenceKey
import org.smartregister.fhircore.engine.util.SharedPreferencesHelper
import org.smartregister.fhircore.engine.util.extension.encodeResourceToString
import org.smartregister.fhircore.engine.util.extension.getPayload
import org.smartregister.fhircore.engine.util.extension.second

Expand All @@ -86,6 +91,8 @@ class ConfigurationRegistryTest : RobolectricTest() {
private val fhirResourceService = mockk<FhirResourceService>()
private lateinit var fhirResourceDataSource: FhirResourceDataSource
private lateinit var configRegistry: ConfigurationRegistry
private lateinit var mockedContext: Context
private lateinit var mockedJsonParser: IParser

@Before
@kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -110,6 +117,8 @@ class ConfigurationRegistryTest : RobolectricTest() {
}
},
)
mockedContext = mockk()
mockedJsonParser = mockk()
configRegistry.setNonProxy(false)
Assert.assertNotNull(configRegistry)
}
Expand All @@ -126,26 +135,26 @@ class ConfigurationRegistryTest : RobolectricTest() {
configRegistry.configsJsonMap["strings"] = "name.title=Mr.\n" + "gender.male=Male"
val resource = configRegistry.retrieveResourceBundleConfiguration("strings_en")
Assert.assertNotNull(resource)
Assert.assertEquals("Mr.", resource?.getString("name.title"))
Assert.assertEquals("Male", resource?.getString("gender.male"))
assertEquals("Mr.", resource?.getString("name.title"))
assertEquals("Male", resource?.getString("gender.male"))
}

@Test
fun testRetrieveResourceBundleConfigurationReturnsCorrectBundle() {
configRegistry.configsJsonMap["stringsSw"] = "name.title=Bwana.\n" + "gender.male=Kijana"
val resource = configRegistry.retrieveResourceBundleConfiguration("strings_sw")
Assert.assertNotNull(resource)
Assert.assertEquals("Bwana.", resource?.getString("name.title"))
Assert.assertEquals("Kijana", resource?.getString("gender.male"))
assertEquals("Bwana.", resource?.getString("name.title"))
assertEquals("Kijana", resource?.getString("gender.male"))
}

@Test
fun testRetrieveResourceBundleConfigurationWithLocaleVariantReturnsCorrectBundle() {
configRegistry.configsJsonMap["stringsSw"] = "name.title=Bwana.\n" + "gender.male=Kijana"
val resource = configRegistry.retrieveResourceBundleConfiguration("strings_sw_KE")
Assert.assertNotNull(resource)
Assert.assertEquals("Bwana.", resource?.getString("name.title"))
Assert.assertEquals("Kijana", resource?.getString("gender.male"))
assertEquals("Bwana.", resource?.getString("name.title"))
assertEquals("Kijana", resource?.getString("gender.male"))
}

@Test
Expand All @@ -154,7 +163,7 @@ class ConfigurationRegistryTest : RobolectricTest() {
configRegistry.configsJsonMap[ConfigType.Application.name] = "{\"appId\": \"${appId}\"}"
val appConfig =
configRegistry.retrieveConfiguration<ApplicationConfiguration>(ConfigType.Application)
Assert.assertEquals(appId, appConfig.appId)
assertEquals(appId, appConfig.appId)
}

@Test
Expand All @@ -166,8 +175,8 @@ class ConfigurationRegistryTest : RobolectricTest() {
"{\"appId\": \"${appId}\", \"id\": \"${id}\", \"fhirResource\": {\"baseResource\": { \"resource\": \"Patient\"}}}"
val registerConfig =
configRegistry.retrieveConfiguration<RegisterConfiguration>(ConfigType.Register)
Assert.assertEquals(appId, registerConfig.appId)
Assert.assertEquals(id, registerConfig.id)
assertEquals(appId, registerConfig.appId)
assertEquals(id, registerConfig.id)
}

@Test
Expand All @@ -181,8 +190,8 @@ class ConfigurationRegistryTest : RobolectricTest() {
val registerConfig =
configRegistry.retrieveConfiguration<RegisterConfiguration>(ConfigType.Register, configId)
Assert.assertTrue(configRegistry.configCacheMap.containsKey(configId))
Assert.assertEquals(appId, registerConfig.appId)
Assert.assertEquals(id, registerConfig.id)
assertEquals(appId, registerConfig.appId)
assertEquals(id, registerConfig.id)
}

@Test
Expand All @@ -202,8 +211,8 @@ class ConfigurationRegistryTest : RobolectricTest() {
mapOf(appId to paramAppId, id to paramId),
)
Assert.assertTrue(configRegistry.configCacheMap.containsKey(configId))
Assert.assertEquals(paramAppId, registerConfig.appId)
Assert.assertEquals(paramId, registerConfig.id)
assertEquals(paramAppId, registerConfig.appId)
assertEquals(paramId, registerConfig.id)
}

@Test
Expand Down Expand Up @@ -282,8 +291,8 @@ class ConfigurationRegistryTest : RobolectricTest() {
val requestPathArgumentSlot = mutableListOf<Resource>()

coVerify(exactly = 1) { fhirEngine.create(capture(requestPathArgumentSlot)) }
Assert.assertEquals("composition-id-1", requestPathArgumentSlot.first().id)
Assert.assertEquals(ResourceType.Composition, requestPathArgumentSlot.first().resourceType)
assertEquals("composition-id-1", requestPathArgumentSlot.first().id)
assertEquals(ResourceType.Composition, requestPathArgumentSlot.first().resourceType)
}

@Test
Expand Down Expand Up @@ -562,7 +571,7 @@ class ConfigurationRegistryTest : RobolectricTest() {
)

Assert.assertNotNull(applicationConfiguration)
Assert.assertEquals("thisApp", applicationConfiguration.appId)
assertEquals("thisApp", applicationConfiguration.appId)
Assert.assertNotNull(ConfigType.Application.name, applicationConfiguration.configType)
// Config cache map now contains application config
Assert.assertTrue(configRegistry.configCacheMap.containsKey(ConfigType.Application.name))
Expand All @@ -573,7 +582,7 @@ class ConfigurationRegistryTest : RobolectricTest() {
)
Assert.assertTrue(configRegistry.configCacheMap.containsKey(ConfigType.Application.name))
Assert.assertNotNull(anotherApplicationConfig)
Assert.assertEquals("thisApp", anotherApplicationConfig.appId)
assertEquals("thisApp", anotherApplicationConfig.appId)
Assert.assertNotNull(ConfigType.Application.name, anotherApplicationConfig.configType)
}

Expand Down Expand Up @@ -620,7 +629,7 @@ class ConfigurationRegistryTest : RobolectricTest() {
)

Assert.assertNotNull(applicationConfiguration)
Assert.assertEquals("thisApp", applicationConfiguration.appId)
assertEquals("thisApp", applicationConfiguration.appId)
Assert.assertNotNull(ConfigType.Application.name, applicationConfiguration.configType)
// Config cache map now contains application config

Expand All @@ -643,7 +652,7 @@ class ConfigurationRegistryTest : RobolectricTest() {
)
}

Assert.assertEquals(21, compositionSections.size)
assertEquals(21, compositionSections.size)

val composition =
Composition().apply {
Expand All @@ -666,12 +675,12 @@ class ConfigurationRegistryTest : RobolectricTest() {
fhirResourceDataSource.post(capture(urlArgumentSlot), capture(requestPathArgumentSlot))
}

Assert.assertEquals(2, requestPathArgumentSlot.size)
Assert.assertEquals(
assertEquals(2, requestPathArgumentSlot.size)
assertEquals(
"{\"resourceType\":\"Bundle\",\"type\":\"batch\",\"entry\":[{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-1\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-2\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-3\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-4\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-5\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-6\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-7\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-8\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-9\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-10\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-11\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-12\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-13\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-14\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-15\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-16\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-17\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-18\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-19\"}},{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-20\"}}]}",
requestPathArgumentSlot.first().getPayload(),
)
Assert.assertEquals(
assertEquals(
"{\"resourceType\":\"Bundle\",\"type\":\"batch\",\"entry\":[{\"request\":{\"method\":\"GET\",\"url\":\"StructureMap/id-21\"}}]}",
requestPathArgumentSlot.last().getPayload(),
)
Expand Down Expand Up @@ -723,16 +732,16 @@ class ConfigurationRegistryTest : RobolectricTest() {
fhirEngine.create(capture(requestPathArgumentSlot), isLocalOnly = true)
}

Assert.assertEquals(3, requestPathArgumentSlot.size)
assertEquals(3, requestPathArgumentSlot.size)

Assert.assertEquals("Group/1000001", requestPathArgumentSlot.first().id)
Assert.assertEquals(ResourceType.Group, requestPathArgumentSlot.first().resourceType)
assertEquals("Group/1000001", requestPathArgumentSlot.first().id)
assertEquals(ResourceType.Group, requestPathArgumentSlot.first().resourceType)

Assert.assertEquals("Group/2000001", requestPathArgumentSlot.second().id)
Assert.assertEquals(ResourceType.Group, requestPathArgumentSlot.second().resourceType)
assertEquals("Group/2000001", requestPathArgumentSlot.second().id)
assertEquals(ResourceType.Group, requestPathArgumentSlot.second().resourceType)

Assert.assertEquals("composition-id-1", requestPathArgumentSlot.last().id)
Assert.assertEquals(ResourceType.Composition, requestPathArgumentSlot.last().resourceType)
assertEquals("composition-id-1", requestPathArgumentSlot.last().id)
assertEquals(ResourceType.Composition, requestPathArgumentSlot.last().resourceType)
}

@Test
Expand Down Expand Up @@ -793,19 +802,19 @@ class ConfigurationRegistryTest : RobolectricTest() {
fhirEngine.create(capture(requestPathArgumentSlot), isLocalOnly = true)
}

Assert.assertEquals(4, requestPathArgumentSlot.size)
assertEquals(4, requestPathArgumentSlot.size)

Assert.assertEquals("Bundle/the-commodities-bundle-id", requestPathArgumentSlot.first().id)
Assert.assertEquals(ResourceType.Bundle, requestPathArgumentSlot.first().resourceType)
assertEquals("Bundle/the-commodities-bundle-id", requestPathArgumentSlot.first().id)
assertEquals(ResourceType.Bundle, requestPathArgumentSlot.first().resourceType)

Assert.assertEquals("Group/1000001", requestPathArgumentSlot.second().id)
Assert.assertEquals(ResourceType.Group, requestPathArgumentSlot.second().resourceType)
assertEquals("Group/1000001", requestPathArgumentSlot.second().id)
assertEquals(ResourceType.Group, requestPathArgumentSlot.second().resourceType)

Assert.assertEquals("Group/2000001", requestPathArgumentSlot[2].id)
Assert.assertEquals(ResourceType.Group, requestPathArgumentSlot[2].resourceType)
assertEquals("Group/2000001", requestPathArgumentSlot[2].id)
assertEquals(ResourceType.Group, requestPathArgumentSlot[2].resourceType)

Assert.assertEquals("composition-id-1", requestPathArgumentSlot.last().id)
Assert.assertEquals(ResourceType.Composition, requestPathArgumentSlot.last().resourceType)
assertEquals("composition-id-1", requestPathArgumentSlot.last().id)
assertEquals(ResourceType.Composition, requestPathArgumentSlot.last().resourceType)
}

@Test
Expand All @@ -827,8 +836,22 @@ class ConfigurationRegistryTest : RobolectricTest() {
listResourceTypeToken,
)

Assert.assertEquals(2, savedSyncResourceTypes.size)
Assert.assertEquals(ResourceType.Task, savedSyncResourceTypes.first())
Assert.assertEquals(ResourceType.Patient, savedSyncResourceTypes.last())
assertEquals(2, savedSyncResourceTypes.size)
assertEquals(ResourceType.Task, savedSyncResourceTypes.first())
assertEquals(ResourceType.Patient, savedSyncResourceTypes.last())
}

@Test
fun writeToFileWithMetadataResourceWithNameShouldCreateFileWithResourceName() {
val resource = Faker.buildPatient().apply { id = "1661662881" }
val expectedFileName = "1661662881.json"
every { mockedContext.filesDir } returns File(ArgumentMatchers.anyString())
every { mockedJsonParser.encodeResourceToString(any()) } returns
resource.encodeResourceToString()
val expectedEncodedResource = mockedJsonParser.encodeResourceToString(resource)

val resultFile = configRegistry.writeToFile(resource)
assertEquals(expectedFileName, resultFile.name)
assertEquals(expectedEncodedResource, resultFile.readText())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.unit.dp
import androidx.navigation.testing.TestNavHostController
import androidx.test.core.app.ApplicationProvider
import org.hl7.fhir.r4.model.ResourceType
import org.junit.Rule
import org.junit.Test
Expand All @@ -43,7 +42,6 @@ import org.smartregister.fhircore.quest.ui.shared.components.ActionableButton

class ActionableButtonTest {
@get:Rule val composeRule = createComposeRule()
private val navController = TestNavHostController(ApplicationProvider.getApplicationContext())

@Test
fun testActionableButtonRendersAncClickWorksCorrectlyWithStatusDue() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,12 +350,19 @@ class ViewGeneratorTest {
fun testImageIsRenderedFromLocalAsset() {
composeRule.setContent {
GenerateView(
properties = ImageProperties(imageConfig = ImageConfig(ICON_TYPE_LOCAL, "ic_walk")),
properties =
ImageProperties(
imageConfig = ImageConfig(ICON_TYPE_LOCAL, "ic_walk", color = "#FFF000"),
text = "Copy text",
),
resourceData = resourceData,
navController = TestNavHostController(LocalContext.current),
)
}
composeRule.onNodeWithTag(SIDE_MENU_ITEM_LOCAL_ICON_TEST_TAG).assertExists().assertIsDisplayed()
composeRule
.onNodeWithTag(SIDE_MENU_ITEM_LOCAL_ICON_TEST_TAG, useUnmergedTree = true)
.assertExists()
.assertIsDisplayed()
}

@Test
Expand All @@ -375,7 +382,7 @@ class ViewGeneratorTest {
)
}
composeRule
.onNodeWithTag(SIDE_MENU_ITEM_REMOTE_ICON_TEST_TAG)
.onNodeWithTag(SIDE_MENU_ITEM_REMOTE_ICON_TEST_TAG, useUnmergedTree = true)
.assertExists()
.assertIsDisplayed()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ constructor(
val workManager: WorkManager,
val fhirCarePlanGenerator: FhirCarePlanGenerator,
) : ViewModel() {

val appMainUiState: MutableState<AppMainUiState> =
mutableStateOf(
appMainUiStateOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ private fun SideMenuItem(
paddingEnd = 10,
imageProperties = ImageProperties(imageConfig = imageConfig, size = 32),
tint = MenuItemColor,
navController = rememberNavController(),
)
SideMenuItemText(title = title, textColor = Color.White)
}
Expand Down
Loading

0 comments on commit 0b03f5b

Please sign in to comment.