anytype-kotlin-wild/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationStatusValueTest.kt
Evgenii Kozlov 36fe52e5ad
DROID-607 Relations & types and options | Refactoring | Everything-is-an-object refactoring (#2720)
* DROID-439 App | Relations refactoring, use SearchObjects for object types (#2592)
* DROID-446 Objects | Enhancement | Store relation links and process its updates (#2597)
* DROID-456 Tech | Remove deprecated API for creating object types (#2601)
* DROID-455 Object types | Enhancement Get object type list from ObjectSearch on Global search screen (#2602)
* DROID-459 Sets | Refactoring | Use relations links instead of relations for building search params to get data view data (#2603)
* DROID-458 Objects | Refactoring | Integrate new api for creating objets (#2604)
* DROID-464 Relations | Refactoring | New API for creating a relation from scratch - for data view or for an object (#2605)
* DROID-467 Relations | Refactoring | New API for creating options - for tags and statuses (#2607)
* DROID-470 Relations | Refactoring | New API for removing any relation from an object or from a data view (#2608)
* DROID-460 Tech | Object relations list (#2610)
* DROID-486 Sets | Refactoring | Displaying and controlling visibility of data view relations (#2611)
* DROID-500 Relations | Refactoring | Extend API of ObjectStore to be able to get a relation by its id (#2619)
* DROID-459 Sets | Fix | Get relations from store by id and not by key (#2618)
* DROID-489 Tech | Relations as object, add relationKey to relation connected screens (#2622)
* DROID-490 Sets | Refactoring | Displaying relation values for current object in bottom sheet cells (#2629)
* DROID-505 Relations | Refactoring | New interface for relations store (#2633)
* DROID-509 Relations | Refactoring | Bind new relations store with subscription container (#2635)
* DROID-507 Tech | MW , migration + relation links (#2636)
* DROID-507 Tech | MW , migration + relation links, fixes (#2637)
* DROID-517 Sets | Fix | Provide correct keys for data view search-and-subscribe query (#2641)
* DROID-409 Relations | Refactoring | Use relation key instead of id when creating new relation (#2642)
* DROID-521 Sets & Objects | Refactoring | Add relation to a data view or to an object from existing relations (#2644)
* DROID-522 Relations | Refactoring | Add objects to relations with object format (#2645)
* DROID-523 Object types | Refactoring | Implement global store for object types (#2646)
* DROID-527 Object types | Refactoring | Integrate global store for object types (#2647)
* DROID-531 Relations | Refactoring | Parse tag and status relations values (#2649)
* DROID-535 Tech | Integrate new MW lib with migration fixes (#2653)
* DROID-535 Tech | MW integration fixes (#2660)
* DROID-559 Relations | Refactoring | Parse tag and status values in editor (#2662)
* DROID-560 Relations | Refactoring | Integrate new lib with fixes (#2663)
* DROID-561 Relations | Refactoring | Parsing tag and status values in dv (#2665)
* DROID-562 Dashboard | Refactoring | Use store of object types as object type provider for favorites tab on dashboard (#2667)
* DROID-567 Relations | Refactoring | Suggest available options to populate a relation (#2671)
* DROID-604 Relations | Refactoring | Use details from Object.CreateRelation.Response to populate relation store (#2705)
* DROID-603 Relations | Refactoring | Creating relation options + Deleting relation from object (#2706)
* DROID-619 Relations | Refactoring | Migrate data view sorts and filters to the new relation-as-object paradigm (#2711)
* DROID-622 Relations | Tech | Update MW to 0.24.0-rc1 (#2714)
* DROID-598 Sets | Refactoring | Provide relation format for date filters (#2715)
* DROID-625 Protocol | Enhancement | Integrate v0.24.0-rc2 (#2718)
2022-11-24 18:11:19 +03:00

620 lines
20 KiB
Kotlin

package com.anytypeio.anytype.features.relations
import android.os.Bundle
import androidx.core.os.bundleOf
import androidx.fragment.app.testing.FragmentScenario
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import com.anytypeio.anytype.R
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_models.Block
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Key
import com.anytypeio.anytype.core_models.Payload
import com.anytypeio.anytype.core_models.Relation
import com.anytypeio.anytype.core_models.StubRelationOptionObject
import com.anytypeio.anytype.core_models.ThemeColor
import com.anytypeio.anytype.core_ui.extensions.dark
import com.anytypeio.anytype.domain.`object`.UpdateDetail
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.Gateway
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.objects.DefaultObjectStore
import com.anytypeio.anytype.domain.objects.DefaultStoreOfRelations
import com.anytypeio.anytype.domain.objects.ObjectStore
import com.anytypeio.anytype.domain.objects.StoreOfRelations
import com.anytypeio.anytype.domain.objects.options.GetOptions
import com.anytypeio.anytype.domain.relations.CreateRelationOption
import com.anytypeio.anytype.presentation.relations.ObjectSetConfig
import com.anytypeio.anytype.presentation.relations.add.AddOptionsRelationDVViewModel
import com.anytypeio.anytype.presentation.relations.add.AddOptionsRelationProvider
import com.anytypeio.anytype.presentation.relations.providers.DataViewObjectRelationProvider
import com.anytypeio.anytype.presentation.relations.providers.DataViewObjectValueProvider
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
import com.anytypeio.anytype.presentation.sets.ObjectSet
import com.anytypeio.anytype.presentation.sets.ObjectSetDatabase
import com.anytypeio.anytype.presentation.util.Dispatcher
import com.anytypeio.anytype.test_utils.MockDataFactory
import com.anytypeio.anytype.test_utils.utils.checkHasText
import com.anytypeio.anytype.test_utils.utils.checkHasTextColor
import com.anytypeio.anytype.test_utils.utils.checkIsNotDisplayed
import com.anytypeio.anytype.test_utils.utils.checkIsRecyclerSize
import com.anytypeio.anytype.test_utils.utils.matchView
import com.anytypeio.anytype.test_utils.utils.onItemView
import com.anytypeio.anytype.test_utils.utils.performClick
import com.anytypeio.anytype.test_utils.utils.rVMatcher
import com.anytypeio.anytype.test_utils.utils.resources
import com.anytypeio.anytype.test_utils.utils.type
import com.anytypeio.anytype.ui.relations.add.BaseAddOptionsRelationFragment
import com.anytypeio.anytype.utils.CoroutinesTestRule
import com.bartoszlipinski.disableanimationsrule.DisableAnimationsRule
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.stub
import org.mockito.kotlin.times
import org.mockito.kotlin.verifyBlocking
@RunWith(AndroidJUnit4::class)
@LargeTest
class AddRelationStatusValueTest {
@Mock
lateinit var repo: BlockRepository
@Mock
lateinit var gateway: Gateway
@Mock
lateinit var dispatcher: Dispatcher<Payload>
@Mock
lateinit var analytics: Analytics
@Mock
lateinit var objectDetailProvider: ObjectDetailProvider
private lateinit var createRelationOption: CreateRelationOption
private lateinit var updateDetail: UpdateDetail
private lateinit var getOptions: GetOptions
private lateinit var urlBuilder: UrlBuilder
@get:Rule
val animationsRule = DisableAnimationsRule()
@get:Rule
val coroutineTestRule = CoroutinesTestRule()
private val ctx = MockDataFactory.randomUuid()
private val state = MutableStateFlow(ObjectSet.init())
private val store : ObjectStore = DefaultObjectStore()
private val storeOfRelations: StoreOfRelations = DefaultStoreOfRelations()
private val db = ObjectSetDatabase(store = store)
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
createRelationOption = CreateRelationOption(repo)
getOptions = GetOptions(repo)
updateDetail = UpdateDetail(repo)
urlBuilder = UrlBuilder(gateway)
TestRelationOptionValueDVAddFragment.testVmFactory = AddOptionsRelationDVViewModel.Factory(
relations = DataViewObjectRelationProvider(
objectSetState = state,
storeOfRelations = storeOfRelations
),
values = DataViewObjectValueProvider(
db = db
),
dispatcher = dispatcher,
optionsProvider = AddOptionsRelationProvider(),
setObjectDetail = updateDetail,
analytics = analytics,
createRelationOption = createRelationOption,
detailsProvider = objectDetailProvider,
getOptions = getOptions
)
}
@Test
fun shouldStartCreatingDataViewOptionWhenTypeAndButtonClicked() {
// SETUP
val relation = Relation(
key = MockDataFactory.randomUuid(),
defaultValue = null,
isHidden = false,
isReadOnly = false,
isMulti = true,
name = MockDataFactory.randomString(),
source = Relation.Source.values().random(),
format = Relation.Format.STATUS,
selections = emptyList()
)
val obj = MockDataFactory.randomUuid()
val record: Map<String, Any?> = mapOf(
ObjectSetConfig.ID_KEY to obj,
relation.key to emptyList<Id>()
)
val viewer = Block.Content.DataView.Viewer(
id = MockDataFactory.randomUuid(),
name = MockDataFactory.randomString(),
filters = emptyList(),
sorts = emptyList(),
viewerRelations = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID
)
val dv = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields.empty(),
content = Block.Content.DataView(
relations = listOf(relation),
viewers = listOf(viewer),
sources = listOf(
MockDataFactory.randomUuid()
)
)
)
state.value = ObjectSet(blocks = listOf(dv))
stubCreateRelationOption()
// TESTING
launchFragment(
bundleOf(
BaseAddOptionsRelationFragment.CTX_KEY to ctx,
BaseAddOptionsRelationFragment.RELATION_KEY to relation.key,
BaseAddOptionsRelationFragment.DATAVIEW_KEY to dv.id,
BaseAddOptionsRelationFragment.VIEWER_KEY to viewer.id,
BaseAddOptionsRelationFragment.TARGET_KEY to obj
)
)
// Creating name for a new option
R.id.filterInputField.type("In progress")
Thread.sleep(1000)
val btn = R.id.recycler.rVMatcher().onItemView(0, R.id.tvCreateOptionValue)
btn.checkHasText("Create option \"In progress\"")
// Pressing button, in order to trigger request.
btn.performClick()
// Verifying that the request is made.
verifyCreateRelationOptionCalled()
}
@Test
fun addButtonShouldNotBeVisible() {
// SETUP
val relation = Relation(
key = MockDataFactory.randomUuid(),
defaultValue = null,
isHidden = false,
isReadOnly = false,
isMulti = true,
name = MockDataFactory.randomString(),
source = Relation.Source.values().random(),
format = Relation.Format.STATUS,
selections = emptyList()
)
val obj = MockDataFactory.randomUuid()
val record: Map<String, Any?> = mapOf(
ObjectSetConfig.ID_KEY to obj,
relation.key to emptyList<Id>()
)
val viewer = Block.Content.DataView.Viewer(
id = MockDataFactory.randomUuid(),
name = MockDataFactory.randomString(),
filters = emptyList(),
sorts = emptyList(),
viewerRelations = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID
)
val dv = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields.empty(),
content = Block.Content.DataView(
relations = listOf(relation),
viewers = listOf(viewer),
sources = listOf(
MockDataFactory.randomUuid()
)
)
)
state.value = ObjectSet(
blocks = listOf(dv),
// viewerDb = mapOf(
// viewer.id to ObjectSet.ViewerData(
// records = listOf(record),
// total = 1
// )
// )
)
stubCreateRelationOption()
// TESTING
launchFragment(
bundleOf(
BaseAddOptionsRelationFragment.CTX_KEY to ctx,
BaseAddOptionsRelationFragment.RELATION_KEY to relation.key,
BaseAddOptionsRelationFragment.DATAVIEW_KEY to dv.id,
BaseAddOptionsRelationFragment.VIEWER_KEY to viewer.id,
BaseAddOptionsRelationFragment.TARGET_KEY to obj
)
)
R.id.btnAdd.matchView().checkIsNotDisplayed()
}
@Test
fun shouldRenderOnlyStatusesWhichRelationValueDoesNotContain() {
// SETUP
val option1Color = ThemeColor.values().random()
val option2Color = ThemeColor.values().random()
val option1 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "In progress",
color = option1Color.code
)
val option2 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "Done",
color = option2Color.code
)
val option3 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "Development",
color = ""
)
val relationKey = MockDataFactory.randomUuid()
val target = MockDataFactory.randomUuid()
val record: Map<String, Any?> = mapOf(
ObjectSetConfig.ID_KEY to target,
relationKey to listOf(option2.id, option3.id)
)
val viewer = Block.Content.DataView.Viewer(
id = MockDataFactory.randomUuid(),
name = MockDataFactory.randomString(),
filters = emptyList(),
sorts = emptyList(),
viewerRelations = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID
)
val relation = Relation(
key = relationKey,
defaultValue = null,
isHidden = false,
isReadOnly = false,
isMulti = true,
name = "Roles",
source = Relation.Source.values().random(),
format = Relation.Format.STATUS,
selections = listOf(option1, option2, option3)
)
val dv = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields.empty(),
content = Block.Content.DataView(
relations = listOf(relation),
viewers = listOf(viewer),
sources = listOf(
MockDataFactory.randomUuid()
)
)
)
state.value = ObjectSet(
blocks = listOf(dv),
// viewerDb = mapOf(
// viewer.id to ObjectSet.ViewerData(
// records = listOf(record),
// total = 1
// )
// )
)
// TESTING
launchFragment(
bundleOf(
BaseAddOptionsRelationFragment.CTX_KEY to ctx,
BaseAddOptionsRelationFragment.RELATION_KEY to relation.key,
BaseAddOptionsRelationFragment.DATAVIEW_KEY to dv.id,
BaseAddOptionsRelationFragment.VIEWER_KEY to viewer.id,
BaseAddOptionsRelationFragment.TARGET_KEY to target
)
)
R.id.recycler.rVMatcher().apply {
checkIsRecyclerSize(1)
onItemView(0, R.id.tvStatusName).checkHasText(option1.text)
onItemView(0, R.id.tvStatusName).checkHasTextColor(resources.dark(option1Color))
}
}
@Test
fun matchedStatusesShouldBeInTheListWhenTypingToCreateNewOption() {
// SETUP
val option1 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "In Testing",
color = ThemeColor.values().random().code
)
val option2 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "Done",
color = ThemeColor.values().random().code
)
val option3 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "In Development",
color = ThemeColor.values().random().code
)
val relationKey = MockDataFactory.randomUuid()
val target = MockDataFactory.randomUuid()
val record: Map<String, Any?> = mapOf(
ObjectSetConfig.ID_KEY to target,
relationKey to emptyList<String>()
)
val viewer = Block.Content.DataView.Viewer(
id = MockDataFactory.randomUuid(),
name = MockDataFactory.randomString(),
filters = emptyList(),
sorts = emptyList(),
viewerRelations = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID
)
val relation = Relation(
key = relationKey,
defaultValue = null,
isHidden = false,
isReadOnly = false,
isMulti = true,
name = "Roles",
source = Relation.Source.values().random(),
format = Relation.Format.STATUS,
selections = listOf(option1, option2, option3)
)
val dv = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields.empty(),
content = Block.Content.DataView(
relations = listOf(relation),
viewers = listOf(viewer),
sources = listOf(
MockDataFactory.randomUuid()
)
)
)
state.value = ObjectSet(
blocks = listOf(dv),
// viewerDb = mapOf(
// viewer.id to ObjectSet.ViewerData(
// records = listOf(record),
// total = 1
// )
// )
)
// TESTING
launchFragment(
bundleOf(
BaseAddOptionsRelationFragment.CTX_KEY to ctx,
BaseAddOptionsRelationFragment.RELATION_KEY to relation.key,
BaseAddOptionsRelationFragment.DATAVIEW_KEY to dv.id,
BaseAddOptionsRelationFragment.VIEWER_KEY to viewer.id,
BaseAddOptionsRelationFragment.TARGET_KEY to target
)
)
// Typing name for a new option
val textToType = "In"
R.id.filterInputField.type(textToType)
// Checking that not only create-option view button, but also tags are visible
R.id.recycler.rVMatcher().apply {
onItemView(0, R.id.tvCreateOptionValue).checkHasText("Create option \"$textToType\"")
onItemView(1, R.id.tvStatusName).checkHasText(option1.text)
onItemView(2, R.id.tvStatusName).checkHasText(option3.text)
checkIsRecyclerSize(3)
}
}
@Test
fun shouldRequestAddingStatusToDataViewRecordWhenStatusClicked() {
// SETUP
val option1 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "In progress",
color = ThemeColor.values().random().code
)
val option2 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "Testing",
color = ThemeColor.values().random().code
)
val option3 = Relation.Option(
id = MockDataFactory.randomUuid(),
text = "Development",
color = ThemeColor.values().random().code
)
val relationKey = MockDataFactory.randomUuid()
val target = MockDataFactory.randomUuid()
val record: Map<String, Any?> = mapOf(
ObjectSetConfig.ID_KEY to target,
relationKey to emptyList<String>()
)
val viewer = Block.Content.DataView.Viewer(
id = MockDataFactory.randomUuid(),
name = MockDataFactory.randomString(),
filters = emptyList(),
sorts = emptyList(),
viewerRelations = emptyList(),
type = Block.Content.DataView.Viewer.Type.GRID
)
val relation = Relation(
key = relationKey,
defaultValue = null,
isHidden = false,
isReadOnly = false,
isMulti = true,
name = "Roles",
source = Relation.Source.values().random(),
format = Relation.Format.STATUS,
selections = listOf(option1, option2, option3)
)
val dv = Block(
id = MockDataFactory.randomUuid(),
children = emptyList(),
fields = Block.Fields.empty(),
content = Block.Content.DataView(
relations = listOf(relation),
viewers = listOf(viewer),
sources = listOf(
MockDataFactory.randomUuid()
)
)
)
state.value = ObjectSet(
blocks = listOf(dv),
// viewerDb = mapOf(
// viewer.id to ObjectSet.ViewerData(
// records = listOf(record),
// total = 1
// )
// )
)
// TESTING
launchFragment(
bundleOf(
BaseAddOptionsRelationFragment.CTX_KEY to ctx,
BaseAddOptionsRelationFragment.RELATION_KEY to relation.key,
BaseAddOptionsRelationFragment.DATAVIEW_KEY to dv.id,
BaseAddOptionsRelationFragment.VIEWER_KEY to viewer.id,
BaseAddOptionsRelationFragment.TARGET_KEY to target
)
)
// Selecting the first two tags
R.id.recycler.rVMatcher().onItemView(1, R.id.tvStatusName).performClick()
// Veryfying UI
verifyBlocking(repo, times(1)) {
updateDetail(
ctx = target,
key = relationKey,
value = listOf(option2.id)
)
}
}
private fun launchFragment(args: Bundle): FragmentScenario<TestRelationOptionValueDVAddFragment> {
return launchFragmentInContainer(
fragmentArgs = args,
themeResId = R.style.AppTheme
)
}
private fun verifyCreateRelationOptionCalled() {
verifyBlocking(repo, times(1)) {
createRelationOption(
relation = any(),
color = any(),
name = any()
)
}
}
private fun stubCreateRelationOption(
relation: Key = MockDataFactory.randomUuid(),
name: String = MockDataFactory.randomString(),
id: Id = MockDataFactory.randomUuid(),
color: String = ThemeColor.values().random().code
) {
repo.stub {
onBlocking {
createRelationOption(
relation = relation,
color = color,
name = name
)
} doReturn StubRelationOptionObject(
id = id,
text = name,
color = color
)
}
}
}