From 58eac0ffa09ac67cb240882bbdfa182b248f755d Mon Sep 17 00:00:00 2001 From: Konstantin Ivanov <54908981+konstantiniiv@users.noreply.github.com> Date: Wed, 13 Sep 2023 18:50:04 +0200 Subject: [PATCH] DROID-1715 Set | Enhancment | View default object type and templates (#373) --- .../widgets/ObjectTypeTemplatesWidget.kt | 2 + .../presentation/sets/ObjectSetExtension.kt | 107 ++- .../presentation/sets/ObjectSetViewModel.kt | 97 +- .../presentation/templates/TemplateView.kt | 5 + .../collections/ObjectCreateTest.kt | 28 + .../presentation/sets/ObjectSetReducerTest.kt | 33 +- .../sets/main/ObjectSetViewModelTestSetup.kt | 17 + .../sets/main/SetByRelationTest.kt | 56 -- .../CollectionViewerTypeAndTemplateTest.kt | 652 +++++++++++++ .../SetByRelationViewerTypeAndTemplateTest.kt | 902 ++++++++++++++++++ .../SetByTypeViewerTypeAndTemplateTest.kt | 639 +++++++++++++ .../anytypeio/anytype/core_models/DataView.kt | 6 +- .../anytypeio/anytype/core_models/Object.kt | 6 +- 13 files changed, 2390 insertions(+), 160 deletions(-) create mode 100644 presentation/src/test/java/com/anytypeio/anytype/presentation/templates/CollectionViewerTypeAndTemplateTest.kt create mode 100644 presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByRelationViewerTypeAndTemplateTest.kt create mode 100644 presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByTypeViewerTypeAndTemplateTest.kt diff --git a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectTypeTemplatesWidget.kt b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectTypeTemplatesWidget.kt index 9484b661c7..680d2af890 100644 --- a/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectTypeTemplatesWidget.kt +++ b/core-ui/src/main/java/com/anytypeio/anytype/core_ui/widgets/ObjectTypeTemplatesWidget.kt @@ -83,6 +83,7 @@ import com.anytypeio.anytype.presentation.editor.cover.CoverColor import com.anytypeio.anytype.presentation.editor.cover.CoverGradient import com.anytypeio.anytype.presentation.templates.TemplateMenuClick import com.anytypeio.anytype.presentation.templates.TemplateView +import com.anytypeio.anytype.presentation.templates.TemplateView.Companion.DEFAULT_TEMPLATE_ID_BLANK import com.anytypeio.anytype.presentation.widgets.TemplatesWidgetUiState import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope @@ -813,6 +814,7 @@ enum class DragStates { fun ComposablePreview() { val items = listOf( TemplateView.Blank( + id = DEFAULT_TEMPLATE_ID_BLANK, typeId = "page", typeName = "Page", layout = ObjectType.Layout.BASIC.code diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt index 48f1a67e79..58181bb55e 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetExtension.kt @@ -23,7 +23,6 @@ import com.anytypeio.anytype.core_utils.ext.addAfterIndexInLine import com.anytypeio.anytype.core_utils.ext.mapInPlace import com.anytypeio.anytype.core_utils.ext.moveAfterIndexInLine import com.anytypeio.anytype.core_utils.ext.moveOnTop -import com.anytypeio.anytype.domain.launch.GetDefaultPageType import com.anytypeio.anytype.domain.misc.UrlBuilder import com.anytypeio.anytype.domain.objects.StoreOfObjectTypes import com.anytypeio.anytype.domain.objects.StoreOfRelations @@ -48,6 +47,8 @@ import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_ import com.anytypeio.anytype.presentation.sets.state.ObjectState.Companion.VIEW_TYPE_UNSUPPORTED import com.anytypeio.anytype.presentation.sets.viewer.ViewerView import com.anytypeio.anytype.presentation.templates.TemplateView +import com.anytypeio.anytype.presentation.templates.TemplateView.Companion.DEFAULT_TEMPLATE_ID_BLANK +import timber.log.Timber fun ObjectState.DataView.featuredRelations( ctx: Id, @@ -398,16 +399,6 @@ fun ObjectState.DataView.Set.isSetByRelation(setOfValue: List): Boolean { return objectDetails.type == ObjectTypeIds.RELATION } -suspend fun StoreOfObjectTypes.isTemplatesAllowedForDefaultType(getDefaultPageType: GetDefaultPageType): Boolean { - try { - val defaultObjectType = getDefaultPageType.run(Unit).type ?: return false - val defaultObjType = get(defaultObjectType) ?: return false - return defaultObjType.isTemplatesAllowed() - } catch (e: Exception) { - return false - } -} - private fun ObjectState.DataView.isValidObject(objectId: Id): Boolean { val objectDetails = details[objectId] ?: return false val objectWrapper = ObjectWrapper.Basic(objectDetails.map) @@ -436,11 +427,10 @@ fun Viewer.isEmpty(): Boolean = } fun ObjectWrapper.Basic.toTemplateView( - typeId: Id, urlBuilder: UrlBuilder, coverImageHashProvider: CoverImageHashProvider, - objectTypeDefaultTemplate: Id? = null, - viewerDefaultTemplate: Id? = null + viewerDefObjType: ObjectWrapper.Type, + viewerDefTemplateId: Id?, ): TemplateView.Template { val coverContainer = if (coverType != CoverType.NONE) { BasicObjectCoverWrapper(this) @@ -448,32 +438,28 @@ fun ObjectWrapper.Basic.toTemplateView( } else { null } - val isDefault = if (viewerDefaultTemplate != null) { - viewerDefaultTemplate == id - } else { - false - } return TemplateView.Template( id = id, name = name.orEmpty(), - typeId = typeId, + typeId = viewerDefObjType.id, emoji = if (!iconEmoji.isNullOrBlank()) iconEmoji else null, image = if (!iconImage.isNullOrBlank()) urlBuilder.thumbnail(iconImage!!) else null, layout = layout ?: ObjectType.Layout.BASIC, coverColor = coverContainer?.coverColor, coverImage = coverContainer?.coverImage, coverGradient = coverContainer?.coverGradient, - isDefault = isDefault + isDefault = viewerDefTemplateId == id ) } fun ObjectWrapper.Type.toTemplateViewBlank( - viewerDefaultTemplate: Id? = null + viewerDefaultTemplate: Id? ): TemplateView.Blank { return TemplateView.Blank( + id = DEFAULT_TEMPLATE_ID_BLANK, typeId = id, layout = recommendedLayout?.code ?: ObjectType.Layout.BASIC.code, - isDefault = viewerDefaultTemplate.isNullOrEmpty() + isDefault = viewerDefaultTemplate.isNullOrEmpty() || viewerDefaultTemplate == DEFAULT_TEMPLATE_ID_BLANK ) } @@ -536,26 +522,6 @@ suspend fun List.isActiveWithTemplates(storeOfObjectTypes: StoreOfOb return viewerDefaultObjectType.isTemplatesAllowed() } -fun ObjectState.DataView.Collection.getDefaultObjectTypeForCollection(viewer: DVViewer?): Id { - return viewer?.defaultObjectType ?: VIEW_DEFAULT_OBJECT_TYPE -} - -fun ObjectState.DataView.Set.getDefaultObjectTypeForSet(ctx: Id, viewer: DVViewer?): Id? { - val setOfValue = getSetOfValue(ctx) - return if (isSetByRelation(setOfValue = setOfValue)) { - viewer?.defaultObjectType ?: VIEW_DEFAULT_OBJECT_TYPE - } else { - setOfValue.firstOrNull() - } -} - -fun ObjectState.DataView.getDefaultObjectType(ctx: Id, viewer: DVViewer?): Id? { - return when (this) { - is ObjectState.DataView.Collection -> getDefaultObjectTypeForCollection(viewer) - is ObjectState.DataView.Set -> getDefaultObjectTypeForSet(ctx, viewer) - } -} - suspend fun DVViewer.getProperTemplateId( storeOfObjectTypes: StoreOfObjectTypes ): Id? { @@ -569,4 +535,57 @@ suspend fun DVViewer.getProperTemplateId( } else { null } -} \ No newline at end of file +} + +suspend fun ObjectState.DataView.getActiveViewTypeAndTemplate( + ctx: Id, + activeView: DVViewer?, + storeOfObjectTypes: StoreOfObjectTypes +): Pair { + if (activeView == null) return Pair(null, null) + when (this) { + is ObjectState.DataView.Collection -> { + return resolveTypeAndActiveViewTemplate(activeView, storeOfObjectTypes) + } + is ObjectState.DataView.Set -> { + val setOfValue = getSetOfValue(ctx) + return if (isSetByRelation(setOfValue = setOfValue)) { + resolveTypeAndActiveViewTemplate(activeView, storeOfObjectTypes) + } else { + val setOf = setOfValue.firstOrNull() + if (setOf.isNullOrBlank()) { + Timber.d("Set by type setOf param is null or empty, not possible to get Type and Template") + Pair(null, null) + } else { + val defaultSetObjectType = ObjectWrapper.Type(details[setOf]?.map.orEmpty()) + if (activeView.defaultTemplate.isNullOrEmpty()) { + val defaultTemplateId = defaultSetObjectType.defaultTemplateId + Pair(defaultSetObjectType, defaultTemplateId) + } else { + Pair(defaultSetObjectType, activeView.defaultTemplate) + } + } + } + } + } +} + +private suspend fun resolveTypeAndActiveViewTemplate( + activeView: DVViewer, + storeOfObjectTypes: StoreOfObjectTypes +): Pair { + val activeViewDefaultObjectType = activeView.defaultObjectType + val defaultSetObjectTypId = if (!activeViewDefaultObjectType.isNullOrBlank()) { + activeViewDefaultObjectType + } else { + VIEW_DEFAULT_OBJECT_TYPE + } + val defaultSetObjectType = storeOfObjectTypes.get(defaultSetObjectTypId) + return if (activeView.defaultTemplate.isNullOrEmpty()) { + val defaultTemplateId = defaultSetObjectType?.defaultTemplateId + Pair(defaultSetObjectType, defaultTemplateId) + } else { + Pair(defaultSetObjectType, activeView.defaultTemplate) + } +} + diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt index f72c78c6b3..41e8e3dfe7 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/sets/ObjectSetViewModel.kt @@ -505,14 +505,20 @@ class ObjectSetViewModel( when { viewer == null -> DataViewViewState.Collection.NoView viewer.isEmpty() -> { - val hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) + val (defType, _) = objectState.getActiveViewTypeAndTemplate( + context, dvViewer, storeOfObjectTypes + ) + val hasTemplates = defType?.isTemplatesAllowed() ?: false DataViewViewState.Collection.NoItems( title = viewer.title, hasTemplates = hasTemplates ) } else -> { - val hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) + val (defType, _) = objectState.getActiveViewTypeAndTemplate( + context, dvViewer, storeOfObjectTypes + ) + val hasTemplates = defType?.isTemplatesAllowed() ?: false DataViewViewState.Collection.Default( viewer = viewer, hasTemplates = hasTemplates @@ -561,15 +567,21 @@ class ObjectSetViewModel( query.isEmpty() || setOfValue.isEmpty() -> DataViewViewState.Set.NoQuery render == null -> DataViewViewState.Set.NoView render.isEmpty() -> { + val (defType, _) = objectState.getActiveViewTypeAndTemplate( + context, viewer, storeOfObjectTypes + ) DataViewViewState.Set.NoItems( title = render.title, - hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) + hasTemplates = defType?.isTemplatesAllowed() ?: false ) } else -> { + val (defType, _) = objectState.getActiveViewTypeAndTemplate( + context, viewer, storeOfObjectTypes + ) DataViewViewState.Set.Default( viewer = render, - hasTemplates = _dvViews.value.isActiveWithTemplates(storeOfObjectTypes) + hasTemplates = defType?.isTemplatesAllowed() ?: false ) } } @@ -895,7 +907,7 @@ class ObjectSetViewModel( } } - private suspend fun proceedWithCreatingSetObject(currentState: ObjectState.DataView.Set, templateId: Id?) { + private suspend fun proceedWithCreatingSetObject(currentState: ObjectState.DataView.Set, templateChosenBy: Id?) { if (isRestrictionPresent(DataViewRestriction.CREATE_OBJECT)) { toast(NOT_ALLOWED) } else { @@ -908,7 +920,8 @@ class ObjectSetViewModel( return } - val defaultObjectType = currentState.getDefaultObjectTypeForSet(ctx = context, viewer = viewer) + val (defaultObjectType, defaultTemplate) + = currentState.getActiveViewTypeAndTemplate(context, viewer, storeOfObjectTypes) val sourceId = setObject.setOf.singleOrNull() if (defaultObjectType == null) { @@ -925,23 +938,30 @@ class ObjectSetViewModel( ) ) } else { + val validTemplateId = getValidTemplateId( + templateChosenBy = templateChosenBy, + viewDefaultTemplate = defaultTemplate + ) proceedWithCreatingDataViewObject( CreateDataViewObject.Params.SetByType( - type = defaultObjectType, + type = defaultObjectType.id, filters = viewer.filters, - template = templateId + template = validTemplateId ) ) } } ObjectTypeIds.RELATION -> { - val createObjectTemplateId = templateId?: viewer.getProperTemplateId(storeOfObjectTypes) + val validTemplateId = getValidTemplateId( + templateChosenBy = templateChosenBy, + viewDefaultTemplate = defaultTemplate + ) proceedWithCreatingDataViewObject( CreateDataViewObject.Params.SetByRelation( filters = viewer.filters, relations = setObject.setOf, - template = createObjectTemplateId, - type = defaultObjectType + template = validTemplateId, + type = defaultObjectType.id ) ) } @@ -953,18 +973,32 @@ class ObjectSetViewModel( } } + private fun getValidTemplateId(templateChosenBy: Id?, viewDefaultTemplate: Id?): Id? { + return when (templateChosenBy) { + null -> if (viewDefaultTemplate != TemplateView.DEFAULT_TEMPLATE_ID_BLANK) viewDefaultTemplate else null + TemplateView.DEFAULT_TEMPLATE_ID_BLANK -> null + else -> templateChosenBy + } + } + fun onSelectQueryButtonClicked() { dispatch(ObjectSetCommand.Modal.OpenEmptyDataViewSelectQueryScreen) } - private suspend fun proceedWithAddingObjectToCollection(templateId: Id? = null) { + private suspend fun proceedWithAddingObjectToCollection(templateChosenBy: Id? = null) { val state = stateReducer.state.value.dataViewState() ?: return val viewer = state.viewerById(session.currentViewerId.value) ?: return - val createObjectTemplateId = templateId ?: viewer.getProperTemplateId(storeOfObjectTypes) + val (defaultObjectType, defaultTemplate) + = state.getActiveViewTypeAndTemplate(context, viewer, storeOfObjectTypes) + + val validTemplateId = getValidTemplateId( + templateChosenBy = templateChosenBy, + viewDefaultTemplate = defaultTemplate + ) val createObjectParams = CreateDataViewObject.Params.Collection( - templateId = createObjectTemplateId, - type = viewer.defaultObjectType + templateId = validTemplateId, + type = defaultObjectType?.id ) proceedWithCreatingDataViewObject(createObjectParams) { result -> val params = AddObjectToCollection.Params( @@ -1493,26 +1527,22 @@ class ObjectSetViewModel( currentViewId ) }.flatMapLatest { (state, currentViewId) -> - val viewer = state.dataViewState()?.viewerById(currentViewId) - val viewerDefObjType = fetchViewerDefaultObjectType(state, viewer) - if (viewer != null && viewerDefObjType?.isTemplatesAllowed() == true) { - fetchAndProcessTemplates(viewerDefObjType, viewer) + val viewer = state.dataViewState()?.viewerById(currentViewId) ?: return@flatMapLatest emptyFlow() + val (type, template) = state.getActiveViewTypeAndTemplate(context, viewer, storeOfObjectTypes) + if (type == null) return@flatMapLatest emptyFlow() + if (type.isTemplatesAllowed()) { + fetchAndProcessTemplates(type, template) } else { - Timber.d("Templates are not allowed for type:[${viewerDefObjType?.id}]") + Timber.d("Templates are not allowed for type:[${type.id}]") emptyFlow() } }.onEach { _templateViews.value = it }.collect() } } - private suspend fun fetchViewerDefaultObjectType(state: ObjectState.DataView, viewer: DVViewer?): ObjectWrapper.Type? { - val viewerDefaultObjectTypeId = state.getDefaultObjectType(ctx = context, viewer = viewer) ?: return null - return storeOfObjectTypes.get(viewerDefaultObjectTypeId) - } - private suspend fun fetchAndProcessTemplates( viewerDefObjType: ObjectWrapper.Type, - viewer: DVViewer + viewerDefTemplateId: Id?, ): Flow> { Timber.d("Fetching templates for type ${viewerDefObjType.id}") @@ -1523,27 +1553,26 @@ class ObjectSetViewModel( emptyFlow>() } .map { results -> - processTemplates(results, viewerDefObjType, viewer) + processTemplates(results, viewerDefObjType, viewerDefTemplateId) } } private fun processTemplates( results: List, viewerDefObjType: ObjectWrapper.Type, - viewer: DVViewer + viewerDefTemplateId: Id? ): List { val blankTemplate = listOf( viewerDefObjType.toTemplateViewBlank( - viewerDefaultTemplate = viewer.defaultTemplate + viewerDefaultTemplate = viewerDefTemplateId ) ) return blankTemplate + results.map { objTemplate -> objTemplate.toTemplateView( - typeId = viewerDefObjType.id, urlBuilder = urlBuilder, coverImageHashProvider = coverImageHashProvider, - objectTypeDefaultTemplate = viewerDefObjType.defaultTemplateId, - viewerDefaultTemplate = viewer.defaultTemplate + viewerDefObjType = viewerDefObjType, + viewerDefTemplateId = viewerDefTemplateId, ) }.sortedByDescending { it.isDefault } + listOf(TemplateView.New(viewerDefObjType.id)) } @@ -1867,10 +1896,10 @@ class ObjectSetViewModel( viewModelScope.launch { when (state) { is ObjectState.DataView.Collection -> { - proceedWithAddingObjectToCollection(templateId = templateId) + proceedWithAddingObjectToCollection(templateChosenBy = templateId) } is ObjectState.DataView.Set -> { - proceedWithCreatingSetObject(currentState = state, templateId = templateId) + proceedWithCreatingSetObject(currentState = state, templateChosenBy = templateId) } } } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateView.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateView.kt index c1ac3d35a8..c652377fe0 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateView.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/templates/TemplateView.kt @@ -10,6 +10,7 @@ sealed class TemplateView { abstract val isDefault: Boolean data class Blank( + val id: Id, val typeId: Id, val typeName: String = "", val layout: Int, @@ -41,6 +42,10 @@ sealed class TemplateView { return image != null || emoji != null } } + + companion object { + const val DEFAULT_TEMPLATE_ID_BLANK = "blank" + } } sealed class TemplateMenuClick { diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt index a50fdbb011..c95ca92a01 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/collections/ObjectCreateTest.kt @@ -1,14 +1,18 @@ package com.anytypeio.anytype.presentation.collections import app.cash.turbine.testIn +import com.anytypeio.anytype.core_models.ObjectType import com.anytypeio.anytype.core_models.ObjectTypeIds import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_models.Relations import com.anytypeio.anytype.core_models.StubRelationObject import com.anytypeio.anytype.domain.base.Resultat import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject import com.anytypeio.anytype.presentation.sets.ObjectSetCommand import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup +import com.anytypeio.anytype.presentation.sets.state.ObjectState +import com.anytypeio.anytype.test_utils.MockDataFactory import kotlin.test.assertIs import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceUntilIdle @@ -18,6 +22,7 @@ import org.junit.After import org.junit.Before import org.junit.Test import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.times import org.mockito.kotlin.verifyBlocking @@ -171,6 +176,17 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { isReadOnlyValue = false, format = Relation.Format.LONG_TEXT ) + val pageTypeId = ObjectState.VIEW_DEFAULT_OBJECT_TYPE + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to null + ) + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } // SETUP stubWorkspaceManager(mockObjectSet.workspaceId) @@ -258,6 +274,18 @@ class ObjectCreateTest : ObjectSetViewModelTestSetup() { details = objectCollection.details ) + val pageTypeId = ObjectState.VIEW_DEFAULT_OBJECT_TYPE + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to null + ) + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + val newObjectId = "objNew-${RandomString.make()}" val result = CreateDataViewObject.Result( objectId = newObjectId, diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt index 61fd7f45ed..fb9ef7b790 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/ObjectSetReducerTest.kt @@ -99,8 +99,7 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Sort.Type.DESC ) ), - filters = listOf(), - defaultObjectType = ObjectTypeIds.PAGE + filters = listOf() ) val dataView = Block( @@ -228,8 +227,7 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Sort.Type.ASC ) ), - filters = listOf(), - defaultObjectType = ObjectTypeIds.PAGE + filters = listOf() ) val viewerList = Block.Content.DataView.Viewer( @@ -276,8 +274,7 @@ class ObjectSetReducerTest { ) ), filters = listOf(), - viewerRelations = viewerGrid.viewerRelations, - defaultObjectType = ObjectTypeIds.PAGE + viewerRelations = viewerGrid.viewerRelations ) ) @@ -299,8 +296,7 @@ class ObjectSetReducerTest { type = viewerGrid.type, viewerRelations = viewerGrid.viewerRelations, sorts = viewerGrid.sorts, - filters = viewerGrid.filters, - defaultObjectType = ObjectTypeIds.PAGE + filters = viewerGrid.filters ), Block.Content.DataView.Viewer( id = viewerList.id, @@ -313,8 +309,7 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Sort.Type.DESC ) ), - filters = listOf(), - defaultObjectType = ObjectTypeIds.PAGE + filters = listOf() ) ) ), @@ -439,8 +434,7 @@ class ObjectSetReducerTest { type = viewer1.type, viewerRelations = viewer1.viewerRelations, sorts = expectedSorts, - filters = viewer1.filters, - defaultObjectType = ObjectTypeIds.PAGE + filters = viewer1.filters ), Block.Content.DataView.Viewer( id = viewer2.id, @@ -448,8 +442,7 @@ class ObjectSetReducerTest { type = Block.Content.DataView.Viewer.Type.GRID, viewerRelations = viewer2.viewerRelations, sorts = listOf(sort1), - filters = listOf(), - defaultObjectType = ObjectTypeIds.PAGE + filters = listOf() ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -561,8 +554,7 @@ class ObjectSetReducerTest { type = viewer1.type, viewerRelations = viewer1.viewerRelations, sorts = viewer1.sorts, - filters = expectedFilters, - defaultObjectType = ObjectTypeIds.PAGE + filters = expectedFilters ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -669,8 +661,7 @@ class ObjectSetReducerTest { type = viewer1.type, viewerRelations = expectedRelations, sorts = viewer1.sorts, - filters = viewer1.filters, - defaultObjectType = ObjectTypeIds.PAGE + filters = viewer1.filters ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -769,8 +760,7 @@ class ObjectSetReducerTest { name = newViewerName, cardSize = newCardSize, coverFit = newCoverFit, - coverRelationKey = relationKey1, - defaultObjectType = ObjectTypeIds.PAGE + coverRelationKey = relationKey1 ) ), targetObjectId = (dataView.content as Block.Content.DataView).targetObjectId @@ -848,8 +838,7 @@ class ObjectSetReducerTest { sorts = viewer.sorts, filters = viewer.filters, name = viewer.name, - coverRelationKey = viewer.coverRelationKey, - defaultObjectType = ObjectTypeIds.PAGE + coverRelationKey = viewer.coverRelationKey ) ), relationLinks = listOf(relationLink1, relationLink3), diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt index ac9bb6228f..097a96cd6f 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/ObjectSetViewModelTestSetup.kt @@ -15,6 +15,7 @@ import com.anytypeio.anytype.core_models.restrictions.DataViewRestrictions import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers import com.anytypeio.anytype.domain.base.Either import com.anytypeio.anytype.domain.base.Result +import com.anytypeio.anytype.domain.base.Resultat import com.anytypeio.anytype.domain.block.interactor.UpdateText import com.anytypeio.anytype.domain.block.repo.BlockRepository import com.anytypeio.anytype.domain.collections.AddObjectToCollection @@ -390,4 +391,20 @@ open class ObjectSetViewModelTestSetup { onBlocking { subscribe(type) }.thenReturn(flowOf(templates)) } } + + fun stubCreateDataViewObject( + objectId: Id = MockDataFactory.randomString(), + objectType: Id = MockDataFactory.randomString() + ) { + createDataViewObject.stub { + onBlocking { async(any()) }.thenReturn( + Resultat.success( + CreateDataViewObject.Result( + objectId = objectId, + objectType = objectType + ) + ) + ) + } + } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt index bc24cef877..067a005934 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/sets/main/SetByRelationTest.kt @@ -91,60 +91,4 @@ class SetByRelationTest : ObjectSetViewModelTestSetup() { } } } - - @Test - fun `should create new object with default object type if given set is aggregated by relations`() = runTest { - // SETUP - stubWorkspaceManager(mockObjectSet.workspaceId) - stubInterceptEvents() - stubInterceptThreadStatus() - stubStoreOfRelations(mockObjectSet) - stubOpenObject( - doc = listOf(mockObjectSet.header, mockObjectSet.title, mockObjectSet.dataView), - details = mockObjectSet.detailsSetByRelation - ) - stubSubscriptionResults( - subscription = mockObjectSet.subscriptionId, - workspace = mockObjectSet.workspaceId, - storeOfRelations = storeOfRelations, - keys = mockObjectSet.dvKeys, - sources = listOf(mockObjectSet.relationObject3.id), - objects = listOf(mockObjectSet.obj1, mockObjectSet.obj2), - dvFilters = mockObjectSet.filters - ) - doReturn(Unit).`when`(createDataViewObject).async( - CreateDataViewObject.Params.SetByRelation( - relations = listOf(mockObjectSet.relationObject3.id), - filters = mockObjectSet.filters, - template = null, - type = ObjectTypeIds.PAGE - ) - ) - - // TESTING - viewModel.onStart(ctx = root) - - // ASSERT DATA VIEW STATE - viewModel.currentViewer.test { - val first = awaitItem() - assertIs(first) - - val second = awaitItem() - assertIs(second) - - viewModel.proceedWithDataViewObjectCreate() - - advanceUntilIdle() - verifyBlocking(createDataViewObject, times(1)) { - async( - CreateDataViewObject.Params.SetByRelation( - relations = listOf(mockObjectSet.relationObject3.id), - filters = mockObjectSet.filters, - template = null, - type = ObjectTypeIds.PAGE - ) - ) - } - } - } } \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/CollectionViewerTypeAndTemplateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/CollectionViewerTypeAndTemplateTest.kt new file mode 100644 index 0000000000..c4e597f8c7 --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/CollectionViewerTypeAndTemplateTest.kt @@ -0,0 +1,652 @@ +package com.anytypeio.anytype.presentation.templates + +import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.core_models.ObjectTypeIds +import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.StubDataView +import com.anytypeio.anytype.core_models.StubDataViewView +import com.anytypeio.anytype.core_models.StubDataViewViewRelation +import com.anytypeio.anytype.core_models.StubObject +import com.anytypeio.anytype.core_models.StubRelationLink +import com.anytypeio.anytype.core_models.StubRelationObject +import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject +import com.anytypeio.anytype.presentation.sets.DataViewViewState +import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel +import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup +import com.anytypeio.anytype.presentation.sets.state.ObjectState +import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription +import com.anytypeio.anytype.test_utils.MockDataFactory +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import net.bytebuddy.utility.RandomString +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.times +import org.mockito.kotlin.verifyBlocking + +@OptIn(ExperimentalCoroutinesApi::class) +class CollectionViewerTypeAndTemplateTest: ObjectSetViewModelTestSetup() { + + private lateinit var viewModel: ObjectSetViewModel + private lateinit var closable: AutoCloseable + + val pageTypeId = ObjectState.VIEW_DEFAULT_OBJECT_TYPE + val pageTemplate1 = StubObject( + id = "pagetemplate1-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = pageTypeId + ) + + val pageTemplate2 = StubObject( + id = "pagetemplate2-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = pageTypeId + ) + + val customType1Id = "customType1-${RandomString.make()}" + + val template1 = StubObject( + id = "template1-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = customType1Id + ) + + val template2 = StubObject( + id = "template2-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = customType1Id + ) + + val collectionObjDetails = mapOf( + Relations.ID to root, + Relations.LAYOUT to ObjectType.Layout.COLLECTION.code.toDouble(), + ) + + @Before + fun setup() { + closable = MockitoAnnotations.openMocks(this) + viewModel = givenViewModel() + } + + @After + fun after() { + rule.advanceTime() + closable.close() + } + + /** + * Collection + * View default type : empty + * View default template : empty + * PAGE type default template : empty + */ + @Test + fun `set by relation, view type and template are empty, page template empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView( + views = listOf(viewer), + relationLinks = listOf(relationLink1), + isCollection = true + ) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to null + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = collectionObjDetails), + pageTypeId to Block.Fields(map = pageTypeMap) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = pageTypeId, templates = listOf(pageTemplate1, pageTemplate2) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(), + collection = root + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Collection.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.Collection( + type = pageTypeId, + templateId = null + ) + async(params) + } + } + + /** + * Collection + * View default type : empty + * View default template : empty + * PAGE type default template : blank + */ + @Test + fun `set by relation, view type and template are empty, page template blank`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView( + views = listOf(viewer), + relationLinks = listOf(relationLink1), + isCollection = true + ) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to TemplateView.DEFAULT_TEMPLATE_ID_BLANK + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = collectionObjDetails), + pageTypeId to Block.Fields(map = pageTypeMap) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = pageTypeId, templates = listOf(pageTemplate1, pageTemplate2) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(), + collection = root + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Collection.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.Collection( + type = pageTypeId, + templateId = null + ) + async(params) + } + } + + /** + * Collection + * View default type : empty + * View default template : empty + * PAGE type default template : Custom + */ + @Test + fun `set by relation, view type and template are empty, page template custom`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView( + views = listOf(viewer), + relationLinks = listOf(relationLink1), + isCollection = true + ) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate2.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = collectionObjDetails), + pageTypeId to Block.Fields(map = pageTypeMap) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = pageTypeId, templates = listOf(pageTemplate1, pageTemplate2) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(), + collection = root + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Collection.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.Collection( + type = pageTypeId, + templateId = pageTemplate2.id + ) + async(params) + } + } + + /** + * Collection + * View default type : Type1 + * View default template : empty + * CUSTOM type default template : Template1 + */ + @Test + fun `set by relation, view type custom and template is empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = null + ) + val dv = StubDataView( + views = listOf(viewer), + relationLinks = listOf(relationLink1), + isCollection = true + ) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = collectionObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(), + collection = root + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Collection.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.Collection( + type = customType1Id, + templateId = template1.id + ) + async(params) + } + } + + /** + * Collection + * View default type : Type1 + * View default template : blank + * CUSTOM type default template : Template1 + */ + @Test + fun `set by relation, view type is custom and template is blank`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = TemplateView.DEFAULT_TEMPLATE_ID_BLANK + ) + val dv = StubDataView( + views = listOf(viewer), + relationLinks = listOf(relationLink1), + isCollection = true + ) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = collectionObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(), + collection = root + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Collection.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.Collection( + type = customType1Id, + templateId = null + ) + async(params) + } + } + + /** + * Collection + * View default type : Type1 + * View default template : Template2 + * CUSTOM type default template : Template1 + */ + @Test + fun `set by relation, view type is custom and template is not empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = template2.id + ) + val dv = StubDataView( + views = listOf(viewer), + relationLinks = listOf(relationLink1), + isCollection = true + ) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = collectionObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + objects = listOf(), + collection = root + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Collection.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.Collection( + type = customType1Id, + templateId = template2.id + ) + async(params) + } + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByRelationViewerTypeAndTemplateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByRelationViewerTypeAndTemplateTest.kt new file mode 100644 index 0000000000..68fdd14f6c --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByRelationViewerTypeAndTemplateTest.kt @@ -0,0 +1,902 @@ +package com.anytypeio.anytype.presentation.templates + +import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.core_models.ObjectTypeIds +import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.StubDataView +import com.anytypeio.anytype.core_models.StubDataViewView +import com.anytypeio.anytype.core_models.StubDataViewViewRelation +import com.anytypeio.anytype.core_models.StubObject +import com.anytypeio.anytype.core_models.StubRelationLink +import com.anytypeio.anytype.core_models.StubRelationObject +import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject +import com.anytypeio.anytype.presentation.sets.DataViewViewState +import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel +import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup +import com.anytypeio.anytype.presentation.sets.state.ObjectState +import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription +import com.anytypeio.anytype.presentation.templates.TemplateView.Companion.DEFAULT_TEMPLATE_ID_BLANK +import com.anytypeio.anytype.test_utils.MockDataFactory +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import net.bytebuddy.utility.RandomString +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.times +import org.mockito.kotlin.verifyBlocking + +@OptIn(ExperimentalCoroutinesApi::class) +class SetByRelationViewerTypeAndTemplateTest : ObjectSetViewModelTestSetup() { + + private lateinit var viewModel: ObjectSetViewModel + private lateinit var closable: AutoCloseable + + private val setByRelationId = "setByRelationId-${RandomString.make()}" + private val setByRelationKey = "setByRelationKey-${RandomString.make()}" + val setByRelationMap = mapOf( + Relations.ID to setByRelationId, + Relations.TYPE to ObjectTypeIds.RELATION, + Relations.RELATION_KEY to setByRelationKey + ) + + val pageTypeId = ObjectState.VIEW_DEFAULT_OBJECT_TYPE + val pageTemplate1 = StubObject( + id = "pagetemplate1-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = pageTypeId + ) + + val pageTemplate2 = StubObject( + id = "pagetemplate2-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = pageTypeId + ) + + val customType1Id = "customType1-${RandomString.make()}" + + val template1 = StubObject( + id = "template1-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = customType1Id + ) + + val template2 = StubObject( + id = "template2-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = customType1Id + ) + + val setObjDetails = mapOf( + Relations.ID to root, + Relations.LAYOUT to ObjectType.Layout.SET.code.toDouble(), + Relations.SET_OF to listOf(setByRelationId), + ) + + @Before + fun setup() { + closable = MockitoAnnotations.openMocks(this) + viewModel = givenViewModel() + } + + @After + fun after() { + rule.advanceTime() + closable.close() + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : PAGE + * View default template : empty + * PAGE type default template : empty + */ + @Test + fun `set by relation, view type and template are empty, page template empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to null + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = pageTypeId, templates = listOf(pageTemplate1, pageTemplate2) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = pageTypeId, + template = null, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : PAGE + * View default template : empty + * PAGE type default template : Blank + */ + @Test + fun `set by relation, view type and template are empty, page template blank`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to DEFAULT_TEMPLATE_ID_BLANK + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = pageTypeId, templates = listOf(pageTemplate1, pageTemplate2) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = pageTypeId, + template = null, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : PAGE + * View default template : empty + * PAGE type default template : Custom + */ + @Test + fun `set by relation, view type and template are empty, page template custom`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate2.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = pageTypeId, templates = listOf(pageTemplate1, pageTemplate2) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = pageTypeId, + template = pageTemplate2.id, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : CUSTOM + * View default template : empty + * CUSTOM type default template : template1 + */ + @Test + fun `set by relation, view type is custom and template is empty, custom type template is not empty`() = + runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate1.id + ) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = customType1Id, + template = template1.id, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : CUSTOM + * View default template : empty + * CUSTOM type default template : empty + */ + @Test + fun `set by relation, view type is custom and template is empty, custom type template is empty`() = + runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate1.id + ) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to null + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = customType1Id, + template = null, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : CUSTOM + * View default template : empty + * CUSTOM type default template : blank + */ + @Test + fun `set by relation, view type is custom and template is empty, custom type template is blank`() = + runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate1.id + ) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to DEFAULT_TEMPLATE_ID_BLANK + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = customType1Id, + template = null, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : CUSTOM + * View default template : Blank + * CUSTOM type default template : Template1 + */ + @Test + fun `set by relation, view type is custom and template is blank, custom type template is not empty`() = + runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = DEFAULT_TEMPLATE_ID_BLANK + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate1.id + ) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = customType1Id, + template = null, + relations = listOf(setByRelationId), + ) + async(params) + } + } + + /** + * Set by relation, + * SetOf: Relation1 + * View default type : CUSTOM + * View default template : Template2 + * CUSTOM type default template : Template1 + */ + @Test + fun `set by relation, view type is custom and template is not empty, custom type template is different`() = + runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType1Id, + defaultTemplateId = template2.id + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val pageTypeMap = mapOf( + Relations.ID to pageTypeId, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.BASIC.code.toDouble(), + Relations.NAME to MockDataFactory.randomString(), + Relations.DEFAULT_TEMPLATE_ID to pageTemplate1.id + ) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + setByRelationId to Block.Fields(map = setByRelationMap), + pageTypeId to Block.Fields(map = pageTypeMap), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + with(storeOfObjectTypes) { + set(pageTypeId, pageTypeMap) + set(customType1Id, customType1Map) + } + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), details = details + ) + + stubTemplatesContainer( + type = customType1Id, templates = listOf(template2, template1) + ) + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(setByRelationId), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByRelation( + filters = listOf(), + type = customType1Id, + template = template2.id, + relations = listOf(setByRelationId), + ) + async(params) + } + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByTypeViewerTypeAndTemplateTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByTypeViewerTypeAndTemplateTest.kt new file mode 100644 index 0000000000..d1ad40f81a --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/templates/SetByTypeViewerTypeAndTemplateTest.kt @@ -0,0 +1,639 @@ +package com.anytypeio.anytype.presentation.templates + +import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.core_models.ObjectTypeIds +import com.anytypeio.anytype.core_models.Relations +import com.anytypeio.anytype.core_models.StubDataView +import com.anytypeio.anytype.core_models.StubDataViewView +import com.anytypeio.anytype.core_models.StubDataViewViewRelation +import com.anytypeio.anytype.core_models.StubObject +import com.anytypeio.anytype.core_models.StubRelationLink +import com.anytypeio.anytype.core_models.StubRelationObject +import com.anytypeio.anytype.domain.dataview.interactor.CreateDataViewObject +import com.anytypeio.anytype.presentation.sets.DataViewViewState +import com.anytypeio.anytype.presentation.sets.ObjectSetViewModel +import com.anytypeio.anytype.presentation.sets.main.ObjectSetViewModelTestSetup +import com.anytypeio.anytype.presentation.sets.subscription.DefaultDataViewSubscription +import com.anytypeio.anytype.presentation.templates.TemplateView.Companion.DEFAULT_TEMPLATE_ID_BLANK +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import net.bytebuddy.utility.RandomString +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.times +import org.mockito.kotlin.verifyBlocking + +@OptIn(ExperimentalCoroutinesApi::class) +class SetByTypeViewerTypeAndTemplateTest : ObjectSetViewModelTestSetup() { + + private lateinit var viewModel: ObjectSetViewModel + private lateinit var closable: AutoCloseable + + private val customType1Id = "customType1-${RandomString.make()}" + + private val template1 = StubObject( + id = "template1-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = customType1Id + ) + + private val template2 = StubObject( + id = "template2-${RandomString.make()}", + objectType = ObjectTypeIds.TEMPLATE, + targetObjectType = customType1Id + ) + + private val setObjDetails = mapOf( + Relations.ID to root, + Relations.LAYOUT to ObjectType.Layout.SET.code.toDouble(), + Relations.SET_OF to listOf(customType1Id) + ) + + @Before + fun setup() { + closable = MockitoAnnotations.openMocks(this) + viewModel = givenViewModel() + } + + @After + fun after() { + rule.advanceTime() + closable.close() + } + + /** + * Set by type, + * SetOf: Type1 + * Type1 default template : empty + * View default type : empty + * View default template : empty + */ + @Test + fun `set by type, type default template is empty, views settings are empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to null + ) + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), + details = details + ) + + stubTemplatesContainer( + type = customType1Id, + templates = listOf(template1, template2) + ) + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(customType1Id), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByType( + filters = listOf(), + type = customType1Id, + template = null, + ) + async(params) + } + } + + /** + * Set by type, + * SetOf: Type1 + * Type1 default template : Blank + * View default type : empty + * View default template : empty + */ + @Test + fun `set by type, type default template is blank, views settings are empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to DEFAULT_TEMPLATE_ID_BLANK + ) + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), + details = details + ) + + stubTemplatesContainer( + type = customType1Id, + templates = listOf(template1, template2) + ) + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(customType1Id), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT BLANK IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByType( + filters = listOf(), + type = customType1Id, + template = null, + ) + async(params) + } + } + + /** + * Set by type, + * SetOf: Type1 + * Type1 default template : Template2 + * View default type : empty + * View default template : empty + */ + @Test + fun `set by type, type default template is not blank, views settings are empty`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = null + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template2.id + ) + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), + details = details + ) + + stubTemplatesContainer( + type = customType1Id, + templates = listOf(template1, template2) + ) + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(customType1Id), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT CUSTOM TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByType( + filters = listOf(), + type = customType1Id, + template = template2.id, + ) + async(params) + } + } + + /** + * Set by type, + * SetO f: Type1 + * Type1 default template : Template2 + * View default type : Type2 + * View default template : Template1 + * In that case we should ignore VIEW default type but use VIEW default template + * But this case should not happen in real life, because it's not possible to set to VIEW a different type other then setOf + */ + @Test + fun `set by type, type default template is not blank, views settings has different type and template`() = + runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template2.id + ) + val customType2Id = "customType2-${RandomString.make()}" + val customType2Map = mapOf( + Relations.ID to customType2Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to RandomString.make(), + Relations.DEFAULT_TEMPLATE_ID to template1.id + ) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = customType2Id, + defaultTemplateId = template1.id + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + customType1Id to Block.Fields(map = customType1Map), + customType2Id to Block.Fields(map = customType2Map) + ) + ) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), + details = details + ) + + stubTemplatesContainer( + type = customType1Id, + templates = listOf(template1, template2) + ) + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(customType1Id), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT CUSTOM TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByType( + filters = listOf(), + type = customType1Id, + template = template1.id, + ) + async(params) + } + } + + /** + * Set by type, + * SetOf: Type1 + * Type1 default template : Template2 + * View default type : empty + * View default template : Template1 + */ + @Test + fun `set by type, type default template is not blank, view template is different`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = template1.id + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template2.id + ) + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), + details = details + ) + + stubTemplatesContainer( + type = customType1Id, + templates = listOf(template1, template2) + ) + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(customType1Id), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT CUSTOM TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Template) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByType( + filters = listOf(), + type = customType1Id, + template = template1.id, + ) + async(params) + } + } + + /** + * Set by type, + * SetOf: Type1 + * Type1 default template : Template2 + * View default type : empty + * View default template : Blank + */ + @Test + fun `set by type, type default template is not blank, view template is blank`() = runTest { + val workspaceId = RandomString.make() + val subscriptionId = DefaultDataViewSubscription.getSubscriptionId(root) + val relationObject1 = StubRelationObject() + val dvViewerRelation1 = + StubDataViewViewRelation(key = relationObject1.key, isVisible = true) + val relationLink1 = StubRelationLink(relationObject1.key) + val viewer = StubDataViewView( + viewerRelations = listOf(dvViewerRelation1), + defaultObjectType = null, + defaultTemplateId = DEFAULT_TEMPLATE_ID_BLANK + ) + val dv = StubDataView(views = listOf(viewer), relationLinks = listOf(relationLink1)) + val dvKeys = listOf(relationObject1.key) + val customType1Map = mapOf( + Relations.ID to customType1Id, + Relations.TYPE to ObjectTypeIds.OBJECT_TYPE, + Relations.RECOMMENDED_LAYOUT to ObjectType.Layout.TODO.code.toDouble(), + Relations.NAME to "name-$customType1Id", + Relations.DEFAULT_TEMPLATE_ID to template2.id + ) + val details = Block.Details( + details = mapOf( + root to Block.Fields(map = setObjDetails), + customType1Id to Block.Fields(map = customType1Map) + ) + ) + + stubWorkspaceManager(workspaceId) + stubInterceptEvents() + stubInterceptThreadStatus() + stubOpenObject( + doc = listOf(dv), + details = details + ) + + stubTemplatesContainer( + type = customType1Id, + templates = listOf(template1, template2) + ) + with(storeOfObjectTypes) { + set(customType1Id, customType1Map) + } + + stubSubscriptionResults( + subscription = subscriptionId, + workspace = workspaceId, + storeOfRelations = storeOfRelations, + keys = dvKeys, + sources = listOf(customType1Id), + objects = listOf() + ) + stubCreateDataViewObject() + + //TESTING + viewModel.onStart(ctx = root) + + advanceUntilIdle() + + val result = viewModel.templatesWidgetState.value.items.filter { it.isDefault } + + // ASSERT CUSTOM TEMPLATE IS DEFAULT + assertEquals(1, result.size) + assertTrue(result[0] is TemplateView.Blank) + + val uiState = viewModel.currentViewer.value as DataViewViewState.Set.NoItems + + // ASSERT NEW + BUTTON + assertTrue(uiState.hasTemplates) + + viewModel.proceedWithDataViewObjectCreate() + + advanceUntilIdle() + + // ASSERT OBJECT CREATION ON NEW BUTTON CLICK + verifyBlocking(createDataViewObject, times(1)) { + val params = CreateDataViewObject.Params.SetByType( + filters = listOf(), + type = customType1Id, + template = null + ) + async(params) + } + } +} \ No newline at end of file diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt index c052b4fb8c..6da73954df 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/DataView.kt @@ -33,7 +33,8 @@ fun StubDataViewView( hideIcon: Boolean = false, coverFit: Boolean = false, coverRelationKey: String? = null, - defaultObjectType: Id = ObjectTypeIds.PAGE + defaultObjectType: Id? = null, + defaultTemplateId: Id? = null ): DVViewer = DVViewer( id = id, filters = filters, @@ -45,7 +46,8 @@ fun StubDataViewView( hideIcon = hideIcon, coverFit = coverFit, coverRelationKey = coverRelationKey, - defaultObjectType = defaultObjectType + defaultObjectType = defaultObjectType, + defaultTemplate = defaultTemplateId ) fun StubDataViewViewRelation( diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt index ba6e31a2bc..69afc0098e 100644 --- a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Object.kt @@ -16,7 +16,8 @@ fun StubObject( iconEmoji: String? = null, isReadOnly: Boolean? = null, isHidden: Boolean? = null, - links: List = emptyList() + links: List = emptyList(), + targetObjectType: Id? = null ): ObjectWrapper.Basic = ObjectWrapper.Basic( map = mapOf( Relations.ID to id, @@ -30,7 +31,8 @@ fun StubObject( Relations.ICON_EMOJI to iconEmoji, Relations.IS_READ_ONLY to isReadOnly, Relations.IS_HIDDEN to isHidden, - Relations.LINKS to links + Relations.LINKS to links, + Relations.TARGET_OBJECT_TYPE to targetObjectType ) )