diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 86dd9eb36d..0f5760eb36 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -18,7 +18,7 @@ jobs: amplitude_secret_debug: ${{ secrets.ANYTYPE_AMPLITUDE_DEBUG_SECRET }} run: ./middleware2.sh $token_secret $user_secret $amplitude_secret $amplitude_secret_debug - name: Compile android test sources - run: ./gradlew compileDebugAndroidTestSources + run: ./gradlew compileExperimentalDebugAndroidTestSources - name: Run unit tests run: ./gradlew testDebugUnitTest -Dpre-dex=false - name: Android test report diff --git a/.gitignore b/.gitignore index d307a9c579..8dfbf32039 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ /github.properties /apikeys.properties .DS_Store -/build +**/build /libs /captures .externalNativeBuild diff --git a/app/build.gradle b/app/build.gradle index 03df0005b7..3b93f738b1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -221,7 +221,7 @@ dependencies { testImplementation unitTestDependencies.mockitoKotlin //Acceptance tests dependencies - androidTestImplementation project(':test-utils') + androidTestImplementation project(':test:android-utils') androidTestImplementation acceptanceTesting.mockitoAndroid androidTestImplementation unitTestDependencies.mockitoKotlin androidTestImplementation acceptanceTesting.espressoCore diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationStatusValueTest.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationStatusValueTest.kt index 2bc825ac18..2cd5b30e28 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationStatusValueTest.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationStatusValueTest.kt @@ -117,7 +117,6 @@ class AddRelationStatusValueTest { addStatusToDataViewRecord = addStatusToDataViewRecord, urlBuilder = urlBuilder, dispatcher = dispatcher, - analytics = analytics ) } diff --git a/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationTagValueTest.kt b/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationTagValueTest.kt index 6c72131bad..94324505c6 100644 --- a/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationTagValueTest.kt +++ b/app/src/androidTest/java/com/anytypeio/anytype/features/relations/AddRelationTagValueTest.kt @@ -116,7 +116,6 @@ class AddRelationTagValueTest { addStatusToDataViewRecord = addStatusToDataViewRecord, urlBuilder = urlBuilder, dispatcher = dispatcher, - analytics = analytics ) } diff --git a/app/src/main/java/com/anytypeio/anytype/di/feature/AddObjectRelationValueDI.kt b/app/src/main/java/com/anytypeio/anytype/di/feature/AddObjectRelationValueDI.kt index a55d2e32e3..e3a3b2105b 100644 --- a/app/src/main/java/com/anytypeio/anytype/di/feature/AddObjectRelationValueDI.kt +++ b/app/src/main/java/com/anytypeio/anytype/di/feature/AddObjectRelationValueDI.kt @@ -52,7 +52,6 @@ object AddObjectRelationValueModule { addTagToDataViewRecord: AddTagToDataViewRecord, addStatusToDataViewRecord: AddStatusToDataViewRecord, urlBuilder: UrlBuilder, - analytics: Analytics ): RelationOptionValueDVAddViewModel.Factory = RelationOptionValueDVAddViewModel.Factory( relations = relations, values = values, @@ -63,7 +62,6 @@ object AddObjectRelationValueModule { addDataViewRelationOption = addDataViewRelationOption, addTagToDataViewRecord = addTagToDataViewRecord, addStatusToDataViewRecord = addStatusToDataViewRecord, - analytics = analytics ) @JvmStatic diff --git a/core-models/.gitignore b/core-models/.gitignore deleted file mode 100644 index 42afabfd2a..0000000000 --- a/core-models/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/core-models/build.gradle b/core-models/build.gradle index e64f08167d..29d6192dd6 100644 --- a/core-models/build.gradle +++ b/core-models/build.gradle @@ -1,13 +1,11 @@ -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' +plugins { + id "kotlin" + id "org.jetbrains.dokka" +} dependencies { + implementation mainApplication.kotlin - def applicationDependencies = rootProject.ext.mainApplication - def unitTestDependencies = rootProject.ext.unitTesting - - implementation applicationDependencies.kotlin - - testImplementation unitTestDependencies.kotlinTest - testImplementation unitTestDependencies.mockitoKotlin + testImplementation unitTesting.kotlinTest + testImplementation unitTesting.mockitoKotlin } \ No newline at end of file diff --git a/core-ui/build.gradle b/core-ui/build.gradle index e6e8d1ff5d..420f5ad0b7 100644 --- a/core-ui/build.gradle +++ b/core-ui/build.gradle @@ -76,7 +76,7 @@ dependencies { debugImplementation applicationDependencies.composeTooling testImplementation acceptanceTesting.fragmentTesting - testImplementation project(':test-utils') + testImplementation project(':test:android-utils') testImplementation acceptanceTesting.espressoCore testImplementation unitTestDependencies.junit testImplementation unitTestDependencies.kotlinTest diff --git a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/FragmentExtensions.kt b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/FragmentExtensions.kt index 53873c22dc..8e5cc42fab 100644 --- a/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/FragmentExtensions.kt +++ b/core-utils/src/main/java/com/anytypeio/anytype/core_utils/ext/FragmentExtensions.kt @@ -18,8 +18,7 @@ inline fun Fragment.argOrNull(key: String): T? { fun Fragment.argString(key: String): String { val value = requireArguments().getString(key) - checkNotNull(value) { "Value missing for $key" } - return value + return checkNotNull(value) { "Value missing for $key" } } fun Fragment.argStringOrNull(key: String): String? { @@ -36,8 +35,7 @@ fun Fragment.argLong(key: String): Long { fun Fragment.argList(key: String): ArrayList { val value = requireArguments().getParcelableArrayList(key) - checkNotNull(value) - return value + return checkNotNull(value) } fun CoroutineScope.subscribe(flow: Flow, body: suspend (T) -> Unit): Job = diff --git a/presentation/build.gradle b/presentation/build.gradle index 4910d52d52..8573c1a87d 100644 --- a/presentation/build.gradle +++ b/presentation/build.gradle @@ -55,4 +55,5 @@ dependencies { testImplementation unitTestDependencies.robolectricLatest testImplementation unitTestDependencies.timberJUnit testImplementation unitTestDependencies.turbine + testImplementation project(":test:core-models-stub") } diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModel.kt index 3c54e32410..942363db5a 100644 --- a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModel.kt +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModel.kt @@ -1,37 +1,25 @@ package com.anytypeio.anytype.presentation.relations -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import com.anytypeio.anytype.analytics.base.Analytics import com.anytypeio.anytype.core_models.Id import com.anytypeio.anytype.core_models.ObjectWrapper -import com.anytypeio.anytype.core_models.Payload import com.anytypeio.anytype.core_models.Relation import com.anytypeio.anytype.core_utils.ext.typeOf import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider -import com.anytypeio.anytype.domain.`object`.UpdateDetail -import com.anytypeio.anytype.domain.dataview.interactor.AddDataViewRelationOption -import com.anytypeio.anytype.domain.dataview.interactor.AddStatusToDataViewRecord -import com.anytypeio.anytype.domain.dataview.interactor.AddTagToDataViewRecord import com.anytypeio.anytype.domain.misc.UrlBuilder -import com.anytypeio.anytype.domain.relations.AddObjectRelationOption import com.anytypeio.anytype.presentation.common.BaseViewModel -import com.anytypeio.anytype.presentation.editor.editor.ThemeColor -import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent import com.anytypeio.anytype.presentation.objects.ObjectIcon import com.anytypeio.anytype.presentation.objects.getProperName import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel.RelationValueView -import com.anytypeio.anytype.presentation.util.Dispatcher import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch -import timber.log.Timber abstract class AddObjectRelationValueViewModel( @@ -40,7 +28,6 @@ abstract class AddObjectRelationValueViewModel( protected val relations: ObjectRelationProvider, protected val types: ObjectTypesProvider, protected val urlBuilder: UrlBuilder, - analytics: Analytics ) : BaseViewModel() { private val jobs = mutableListOf() @@ -60,34 +47,49 @@ abstract class AddObjectRelationValueViewModel( init { viewModelScope.launch { views.combine(query) { all, query -> - if (query.isEmpty()) - all - else - mutableListOf().apply { - add(RelationValueView.Create(query)) - addAll( - all.filter { view -> - when(view) { - is RelationValueView.Status -> { - view.name.contains(query, true) - } - is RelationValueView.Tag -> { - view.name.contains(query, true) - } - else -> true - } - } - ) - } + println("views=$all, query=$query") + filterRelationsBy(query, all) }.collect { ui.value = it } } } + private fun filterRelationsBy( + query: String, + all: List + ): List { + return if (query.isEmpty()) + all + else { + val result = mutableListOf() + val filteredRelationViews = all.filter { view -> + when (view) { + is RelationValueView.Status -> { + view.name.contains(query, true) + } + is RelationValueView.Tag -> { + view.name.contains(query, true) + } + else -> true + } + } + val searchedExistentTag = + filteredRelationViews.filterIsInstance() + .any { view -> view.name == query } + + if (!searchedExistentTag) { + result.add(RelationValueView.Create(query)) + } + result.addAll(filteredRelationViews) + result + } + } + fun onStart(target: Id, relationId: Id) { val s1 = relations.subscribe(relationId) val s2 = values.subscribe(target) jobs += viewModelScope.launch { s1.combine(s2) { relation, record -> + println("relation=$relation, record=$record") buildViews(relation, record, relationId).also { if (relation.format == Relation.Format.STATUS) { isAddButtonVisible.value = false @@ -216,9 +218,12 @@ abstract class AddObjectRelationValueViewModel( } views.value = result + println("views.value set $result") } - fun onFilterInputChanged(input: String) { query.value = input } + fun onFilterInputChanged(input: String) { + query.value = input + } fun onTagClicked(tag: RelationValueView.Tag) { views.value = views.value.map { view -> @@ -233,372 +238,9 @@ abstract class AddObjectRelationValueViewModel( view } }.also { result -> - counter.value = result.count { it is RelationValueView.Selectable && it.isSelected == true } + counter.value = + result.count { it is RelationValueView.Selectable && it.isSelected == true } } } } -class RelationOptionValueDVAddViewModel( - details: ObjectDetailProvider, - types: ObjectTypesProvider, - urlBuilder: UrlBuilder, - values: ObjectValueProvider, - relations: ObjectRelationProvider, - private val addDataViewRelationOption: AddDataViewRelationOption, - private val addTagToDataViewRecord: AddTagToDataViewRecord, - private val addStatusToDataViewRecord: AddStatusToDataViewRecord, - private val dispatcher: Dispatcher, - private val analytics: Analytics -) : AddObjectRelationValueViewModel( - details = details, - values = values, - types = types, - urlBuilder = urlBuilder, - relations = relations, - analytics = analytics -) { - - fun onCreateDataViewRelationOptionClicked( - ctx: Id, - dataview: Id, - viewer: Id, - relation: Id, - target: Id, - name: String - ) { - viewModelScope.launch { - addDataViewRelationOption( - AddDataViewRelationOption.Params( - ctx = ctx, - relation = relation, - dataview = dataview, - record = target, - name = name, - color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title - ) - ).proceed( - success = { (payload, option) -> - dispatcher.send(payload) - if (option != null) { - when (relations.get(relation).format) { - Relation.Format.TAG -> { - proceedWithAddingTagToDataViewRecord( - ctx = ctx, - dataview = dataview, - viewer = viewer, - relation = relation, - target = target, - tags = listOf(option) - ) - } - Relation.Format.STATUS -> { - proceedWithAddingStatusToDataViewRecord( - ctx = ctx, - dataview = dataview, - viewer = viewer, - relation = relation, - obj = target, - status = option - ) - } - else -> Timber.e("Trying to create option for wrong relation.") - } - } - }, - failure = { Timber.e(it, "Error while creating a new option") } - ) - } - } - - fun onAddObjectSetStatusClicked( - ctx: Id, - dataview: Id, - viewer: Id, - relation: Id, - obj: Id, - status: RelationValueView.Status - ) { - proceedWithAddingStatusToDataViewRecord(ctx, dataview, viewer, relation, obj, status.id) - } - - private fun proceedWithAddingStatusToDataViewRecord( - ctx: Id, - dataview: Id, - viewer: Id, - relation: Id, - obj: Id, - status: Id - ) { - viewModelScope.launch { - addStatusToDataViewRecord( - AddStatusToDataViewRecord.Params( - ctx = ctx, - dataview = dataview, - viewer = viewer, - relation = relation, - obj = obj, - status = status, - record = values.get(target = obj) - ) - ).process( - failure = { Timber.e(it, "Error while adding tag") }, - success = { isParentDismissed.value = true } - ) - } - } - - fun onAddSelectedValuesToDataViewClicked( - ctx: Id, - dataview: Id, - target: Id, - relation: Id, - viewer: Id - ) { - val tags = views.value.mapNotNull { view -> - if (view is RelationValueView.Tag && view.isSelected == true) - view.id - else - null - } - proceedWithAddingTagToDataViewRecord( - target = target, - ctx = ctx, - dataview = dataview, - relation = relation, - viewer = viewer, - tags = tags - ) - } - - private fun proceedWithAddingTagToDataViewRecord( - target: Id, - ctx: Id, - dataview: Id, - relation: Id, - viewer: Id, - tags: List - ) { - viewModelScope.launch { - val record = values.get(target = target) - addTagToDataViewRecord( - AddTagToDataViewRecord.Params( - ctx = ctx, - tags = tags, - record = record, - dataview = dataview, - relation = relation, - viewer = viewer, - target = target - ) - ).process( - failure = { Timber.e(it, "Error while adding tag") }, - success = { isDismissed.value = true } - ) - } - } - - class Factory( - private val values: ObjectValueProvider, - private val details: ObjectDetailProvider, - private val relations: ObjectRelationProvider, - private val types: ObjectTypesProvider, - private val addDataViewRelationOption: AddDataViewRelationOption, - private val addTagToDataViewRecord: AddTagToDataViewRecord, - private val addStatusToDataViewRecord: AddStatusToDataViewRecord, - private val urlBuilder: UrlBuilder, - private val dispatcher: Dispatcher, - private val analytics: Analytics - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return RelationOptionValueDVAddViewModel( - details = details, - values = values, - relations = relations, - types = types, - urlBuilder = urlBuilder, - addDataViewRelationOption = addDataViewRelationOption, - addTagToDataViewRecord = addTagToDataViewRecord, - addStatusToDataViewRecord = addStatusToDataViewRecord, - dispatcher = dispatcher, - analytics = analytics - ) as T - } - } -} - -class RelationOptionValueAddViewModel( - details: ObjectDetailProvider, - types: ObjectTypesProvider, - urlBuilder: UrlBuilder, - values: ObjectValueProvider, - relations: ObjectRelationProvider, - private val addObjectRelationOption: AddObjectRelationOption, - private val updateDetail: UpdateDetail, - private val dispatcher: Dispatcher, - private val analytics: Analytics -) : AddObjectRelationValueViewModel( - details = details, - values = values, - types = types, - urlBuilder = urlBuilder, - relations = relations, - analytics = analytics -) { - - fun onAddObjectStatusClicked( - ctx: Id, - relation: Id, - status: RelationValueView.Status - ) = proceedWithAddingStatusToObject( - ctx = ctx, - relation = relation, - status = status.id - ) - - private fun proceedWithAddingStatusToObject( - ctx: Id, - relation: Id, - status: Id - ) { - viewModelScope.launch { - updateDetail( - UpdateDetail.Params( - ctx = ctx, - key = relation, - value = listOf(status) - ) - ).process( - failure = { Timber.e(it, "Error while adding tag") }, - success = { dispatcher.send(it).also { - sendAnalyticsRelationValueEvent(analytics) - isParentDismissed.value = true - } - } - ) - } - } - - fun onAddSelectedValuesToObjectClicked( - ctx: Id, - obj: Id, - relation: Id - ) { - val tags = views.value.mapNotNull { view -> - if (view is RelationValueView.Tag && view.isSelected == true) - view.id - else - null - } - proceedWithAddingTagToObject( - obj = obj, - ctx = ctx, - relation = relation, - tags = tags - ) - } - - private fun proceedWithAddingTagToObject( - obj: Id, - ctx: Id, - relation: Id, - tags: List - ) { - viewModelScope.launch { - val obj = values.get(target = obj) - val result = mutableListOf() - val value = obj[relation] - if (value is List<*>) { - result.addAll(value.typeOf()) - } else if (value is Id) { - result.add(value) - } - result.addAll(tags) - updateDetail( - UpdateDetail.Params( - ctx = ctx, - key = relation, - value = result - ) - ).process( - failure = { Timber.e(it, "Error while adding tag") }, - success = { dispatcher.send(it).also { - sendAnalyticsRelationValueEvent(analytics) - isDismissed.value = true - } } - ) - } - } - - fun onCreateObjectRelationOptionClicked( - ctx: Id, - relation: Id, - name: String, - obj: Id - ) { - viewModelScope.launch { - addObjectRelationOption( - AddObjectRelationOption.Params( - ctx = ctx, - relation = relation, - name = name, - color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title - ) - ).proceed( - success = { (payload, option) -> - dispatcher.send(payload) - if (option != null) { - when (val format = relations.get(relation).format) { - Relation.Format.TAG -> { - proceedWithAddingTagToObject( - ctx = ctx, - relation = relation, - obj = obj, - tags = listOf(option) - ) - } - Relation.Format.STATUS -> { - proceedWithAddingStatusToObject( - ctx = ctx, - relation = relation, - status = option - ) - } - else -> { - Timber.e("Trying to create an option for relation format: $format") - } - } - } - }, - failure = { Timber.e(it, "Error while creating a new option for object") } - ) - } - } - - class Factory( - private val values: ObjectValueProvider, - private val details: ObjectDetailProvider, - private val relations: ObjectRelationProvider, - private val types: ObjectTypesProvider, - private val addObjectRelationOption: AddObjectRelationOption, - private val updateDetail: UpdateDetail, - private val urlBuilder: UrlBuilder, - private val dispatcher: Dispatcher, - private val analytics: Analytics - ) : ViewModelProvider.Factory { - @Suppress("UNCHECKED_CAST") - override fun create(modelClass: Class): T { - return RelationOptionValueAddViewModel( - details = details, - values = values, - relations = relations, - types = types, - urlBuilder = urlBuilder, - addObjectRelationOption = addObjectRelationOption, - updateDetail = updateDetail, - dispatcher = dispatcher, - analytics = analytics - ) as T - } - } -} \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationOptionValueAddViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationOptionValueAddViewModel.kt new file mode 100644 index 0000000000..0eac375f56 --- /dev/null +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationOptionValueAddViewModel.kt @@ -0,0 +1,201 @@ +package com.anytypeio.anytype.presentation.relations + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.anytypeio.anytype.analytics.base.Analytics +import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.Payload +import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_utils.ext.typeOf +import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider +import com.anytypeio.anytype.domain.`object`.UpdateDetail +import com.anytypeio.anytype.domain.misc.UrlBuilder +import com.anytypeio.anytype.domain.relations.AddObjectRelationOption +import com.anytypeio.anytype.presentation.editor.editor.ThemeColor +import com.anytypeio.anytype.presentation.extension.sendAnalyticsRelationValueEvent +import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider +import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider +import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider +import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel +import com.anytypeio.anytype.presentation.util.Dispatcher +import kotlinx.coroutines.launch +import timber.log.Timber + +class RelationOptionValueAddViewModel( + details: ObjectDetailProvider, + types: ObjectTypesProvider, + urlBuilder: UrlBuilder, + values: ObjectValueProvider, + relations: ObjectRelationProvider, + private val addObjectRelationOption: AddObjectRelationOption, + private val updateDetail: UpdateDetail, + private val dispatcher: Dispatcher, + private val analytics: Analytics +) : AddObjectRelationValueViewModel( + details = details, + values = values, + types = types, + urlBuilder = urlBuilder, + relations = relations, +) { + + fun onAddObjectStatusClicked( + ctx: Id, + relation: Id, + status: RelationValueBaseViewModel.RelationValueView.Status + ) = proceedWithAddingStatusToObject( + ctx = ctx, + relation = relation, + status = status.id + ) + + private fun proceedWithAddingStatusToObject( + ctx: Id, + relation: Id, + status: Id + ) { + viewModelScope.launch { + updateDetail( + UpdateDetail.Params( + ctx = ctx, + key = relation, + value = listOf(status) + ) + ).process( + failure = { Timber.e(it, "Error while adding tag") }, + success = { + dispatcher.send(it).also { + sendAnalyticsRelationValueEvent(analytics) + isParentDismissed.value = true + } + } + ) + } + } + + fun onAddSelectedValuesToObjectClicked( + ctx: Id, + obj: Id, + relation: Id + ) { + val tags = views.value.mapNotNull { view -> + if (view is RelationValueBaseViewModel.RelationValueView.Tag && view.isSelected == true) + view.id + else + null + } + proceedWithAddingTagToObject( + obj = obj, + ctx = ctx, + relation = relation, + tags = tags + ) + } + + private fun proceedWithAddingTagToObject( + obj: Id, + ctx: Id, + relation: Id, + tags: List + ) { + viewModelScope.launch { + val obj = values.get(target = obj) + val result = mutableListOf() + val value = obj[relation] + if (value is List<*>) { + result.addAll(value.typeOf()) + } else if (value is Id) { + result.add(value) + } + result.addAll(tags) + updateDetail( + UpdateDetail.Params( + ctx = ctx, + key = relation, + value = result + ) + ).process( + failure = { Timber.e(it, "Error while adding tag") }, + success = { + dispatcher.send(it).also { + sendAnalyticsRelationValueEvent(analytics) + isDismissed.value = true + } + } + ) + } + } + + fun onCreateObjectRelationOptionClicked( + ctx: Id, + relation: Id, + name: String, + obj: Id + ) { + viewModelScope.launch { + addObjectRelationOption( + AddObjectRelationOption.Params( + ctx = ctx, + relation = relation, + name = name, + color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title + ) + ).proceed( + success = { (payload, option) -> + dispatcher.send(payload) + if (option != null) { + when (val format = relations.get(relation).format) { + Relation.Format.TAG -> { + proceedWithAddingTagToObject( + ctx = ctx, + relation = relation, + obj = obj, + tags = listOf(option) + ) + } + Relation.Format.STATUS -> { + proceedWithAddingStatusToObject( + ctx = ctx, + relation = relation, + status = option + ) + } + else -> { + Timber.e("Trying to create an option for relation format: $format") + } + } + } + }, + failure = { Timber.e(it, "Error while creating a new option for object") } + ) + } + } + + class Factory( + private val values: ObjectValueProvider, + private val details: ObjectDetailProvider, + private val relations: ObjectRelationProvider, + private val types: ObjectTypesProvider, + private val addObjectRelationOption: AddObjectRelationOption, + private val updateDetail: UpdateDetail, + private val urlBuilder: UrlBuilder, + private val dispatcher: Dispatcher, + private val analytics: Analytics + ) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return RelationOptionValueAddViewModel( + details = details, + values = values, + relations = relations, + types = types, + urlBuilder = urlBuilder, + addObjectRelationOption = addObjectRelationOption, + updateDetail = updateDetail, + dispatcher = dispatcher, + analytics = analytics + ) as T + } + } +} \ No newline at end of file diff --git a/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationOptionValueDVAddViewModel.kt b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationOptionValueDVAddViewModel.kt new file mode 100644 index 0000000000..64c7d40af3 --- /dev/null +++ b/presentation/src/main/java/com/anytypeio/anytype/presentation/relations/RelationOptionValueDVAddViewModel.kt @@ -0,0 +1,206 @@ +package com.anytypeio.anytype.presentation.relations + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.Payload +import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider +import com.anytypeio.anytype.domain.dataview.interactor.AddDataViewRelationOption +import com.anytypeio.anytype.domain.dataview.interactor.AddStatusToDataViewRecord +import com.anytypeio.anytype.domain.dataview.interactor.AddTagToDataViewRecord +import com.anytypeio.anytype.domain.misc.UrlBuilder +import com.anytypeio.anytype.presentation.editor.editor.ThemeColor +import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider +import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider +import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider +import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel +import com.anytypeio.anytype.presentation.util.Dispatcher +import kotlinx.coroutines.launch +import timber.log.Timber + +class RelationOptionValueDVAddViewModel( + details: ObjectDetailProvider, + types: ObjectTypesProvider, + urlBuilder: UrlBuilder, + values: ObjectValueProvider, + relations: ObjectRelationProvider, + private val addDataViewRelationOption: AddDataViewRelationOption, + private val addTagToDataViewRecord: AddTagToDataViewRecord, + private val addStatusToDataViewRecord: AddStatusToDataViewRecord, + private val dispatcher: Dispatcher, +) : AddObjectRelationValueViewModel( + details = details, + values = values, + types = types, + urlBuilder = urlBuilder, + relations = relations, +) { + + fun onCreateDataViewRelationOptionClicked( + ctx: Id, + dataview: Id, + viewer: Id, + relation: Id, + target: Id, + name: String + ) { + viewModelScope.launch { + addDataViewRelationOption( + AddDataViewRelationOption.Params( + ctx = ctx, + relation = relation, + dataview = dataview, + record = target, + name = name, + color = ThemeColor.values().filter { it != ThemeColor.DEFAULT }.random().title + ) + ).proceed( + success = { (payload, option) -> + dispatcher.send(payload) + if (option != null) { + when (relations.get(relation).format) { + Relation.Format.TAG -> { + proceedWithAddingTagToDataViewRecord( + ctx = ctx, + dataview = dataview, + viewer = viewer, + relation = relation, + target = target, + tags = listOf(option) + ) + } + Relation.Format.STATUS -> { + proceedWithAddingStatusToDataViewRecord( + ctx = ctx, + dataview = dataview, + viewer = viewer, + relation = relation, + obj = target, + status = option + ) + } + else -> Timber.e("Trying to create option for wrong relation.") + } + } + }, + failure = { Timber.e(it, "Error while creating a new option") } + ) + } + } + + fun onAddObjectSetStatusClicked( + ctx: Id, + dataview: Id, + viewer: Id, + relation: Id, + obj: Id, + status: RelationValueBaseViewModel.RelationValueView.Status + ) { + proceedWithAddingStatusToDataViewRecord(ctx, dataview, viewer, relation, obj, status.id) + } + + private fun proceedWithAddingStatusToDataViewRecord( + ctx: Id, + dataview: Id, + viewer: Id, + relation: Id, + obj: Id, + status: Id + ) { + viewModelScope.launch { + addStatusToDataViewRecord( + AddStatusToDataViewRecord.Params( + ctx = ctx, + dataview = dataview, + viewer = viewer, + relation = relation, + obj = obj, + status = status, + record = values.get(target = obj) + ) + ).process( + failure = { Timber.e(it, "Error while adding tag") }, + success = { isParentDismissed.value = true } + ) + } + } + + fun onAddSelectedValuesToDataViewClicked( + ctx: Id, + dataview: Id, + target: Id, + relation: Id, + viewer: Id + ) { + val tags = views.value.mapNotNull { view -> + if (view is RelationValueBaseViewModel.RelationValueView.Tag && view.isSelected == true) + view.id + else + null + } + proceedWithAddingTagToDataViewRecord( + target = target, + ctx = ctx, + dataview = dataview, + relation = relation, + viewer = viewer, + tags = tags + ) + } + + private fun proceedWithAddingTagToDataViewRecord( + target: Id, + ctx: Id, + dataview: Id, + relation: Id, + viewer: Id, + tags: List + ) { + viewModelScope.launch { + val record = values.get(target = target) + addTagToDataViewRecord( + AddTagToDataViewRecord.Params( + ctx = ctx, + tags = tags, + record = record, + dataview = dataview, + relation = relation, + viewer = viewer, + target = target + ) + ).process( + failure = { Timber.e(it, "Error while adding tag") }, + success = { isDismissed.value = true } + ) + } + } + + class Factory( + private val values: ObjectValueProvider, + private val details: ObjectDetailProvider, + private val relations: ObjectRelationProvider, + private val types: ObjectTypesProvider, + private val addDataViewRelationOption: AddDataViewRelationOption, + private val addTagToDataViewRecord: AddTagToDataViewRecord, + private val addStatusToDataViewRecord: AddStatusToDataViewRecord, + private val urlBuilder: UrlBuilder, + private val dispatcher: Dispatcher, + ) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return RelationOptionValueDVAddViewModel( + details = details, + values = values, + relations = relations, + types = types, + urlBuilder = urlBuilder, + addDataViewRelationOption = addDataViewRelationOption, + addTagToDataViewRecord = addTagToDataViewRecord, + addStatusToDataViewRecord = addStatusToDataViewRecord, + dispatcher = dispatcher, + ) as T + } + } +} \ No newline at end of file diff --git a/presentation/src/test/java/FakeGateWay.kt b/presentation/src/test/java/FakeGateWay.kt new file mode 100644 index 0000000000..93421b27e8 --- /dev/null +++ b/presentation/src/test/java/FakeGateWay.kt @@ -0,0 +1,5 @@ +import com.anytypeio.anytype.domain.config.Gateway + +object FakeGateWay : Gateway { + override fun obtain(): String = "anytype.io" +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/extension/DashboardViewExtensionKtTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/extension/DashboardViewExtensionKtTest.kt index 49b5dd3104..7425dc7d43 100644 --- a/presentation/src/test/java/com/anytypeio/anytype/presentation/extension/DashboardViewExtensionKtTest.kt +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/extension/DashboardViewExtensionKtTest.kt @@ -1,5 +1,6 @@ package com.anytypeio.anytype.presentation.extension +import FakeGateWay import MockDataFactory import com.anytypeio.anytype.core_models.Block import com.anytypeio.anytype.domain.config.Gateway @@ -46,14 +47,7 @@ class DashboardViewExtensionKtTest { ) ) - - val testGate = object : Gateway { - override fun obtain(): String { - return "anytype.io" - } - } - - val builder = UrlBuilder(gateway = testGate) + val builder = UrlBuilder(FakeGateWay) val result = views.updateDetails( target = views[1].target, diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModelTest.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModelTest.kt new file mode 100644 index 0000000000..ce52b194c2 --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/AddObjectRelationValueViewModelTest.kt @@ -0,0 +1,152 @@ +package com.anytypeio.anytype.presentation.relations + +import FakeGateWay +import app.cash.turbine.test +import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_models.StubRelation +import com.anytypeio.anytype.core_models.StubRelationOption +import com.anytypeio.anytype.domain.misc.UrlBuilder +import com.anytypeio.anytype.presentation.relations.providers.FakeObjectDetailsProvider +import com.anytypeio.anytype.presentation.relations.providers.FakeObjectRelationProvider +import com.anytypeio.anytype.presentation.relations.providers.FakeObjectTypesProvider +import com.anytypeio.anytype.presentation.relations.providers.FakeObjectValueProvider +import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel.RelationValueView +import com.anytypeio.anytype.presentation.util.CoroutinesTestRule +import com.anytypeio.anytype.test_utils.MockDataFactory +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test +import kotlin.test.BeforeTest +import kotlin.test.assertEquals + +@ExperimentalCoroutinesApi +class AddObjectRelationValueViewModelTest { + + @get:Rule + internal val coroutineTestRule = CoroutinesTestRule() + + private val option = StubRelationOption() + private val relation = StubRelation( + format = Relation.Format.TAG, + selections = listOf(option) + ) + + private val relationsProvider = FakeObjectRelationProvider + + @Test + fun `query is empty - there is only tag view`() { + val viewModel = createViewModel() + relationsProvider.relation = relation + + runTest { + viewModel.ui.test { + viewModel.onStart("", "") + // first item is emmit in constructor + val actual = listOf(awaitItem(), awaitItem())[1] + assertEquals( + expected = listOf( + RelationValueView.Tag( + id = option.id, + name = option.text, + color = option.color, + isSelected = false + ) + ), + actual = actual + ) + } + } + } + + @Test + fun `query doesn't contain tag - there is only create view`() { + val viewModel = createViewModel() + relationsProvider.relation = relation + + runTest { + viewModel.ui.test { + viewModel.onStart("", "") + val query = MockDataFactory.randomString() + option.text + viewModel.onFilterInputChanged(query) + // first item is emmit in constructor, second - at onStart + val actual = listOf(awaitItem(), awaitItem(), awaitItem())[2] + assertEquals( + expected = listOf( + RelationValueView.Create(query), + ), + actual = actual + ) + } + } + } + + @Test + fun `query contains tag parts but doesn't equal - there are create and tag views`() { + val viewModel = createViewModel() + relationsProvider.relation = relation + + runTest { + viewModel.ui.test { + viewModel.onStart("", "") + + val query = option.text.substring(1) + viewModel.onFilterInputChanged(query) + // first item is emmit in constructor, second - at onStart + val actual = listOf(awaitItem(), awaitItem(), awaitItem())[2] + assertEquals( + expected = listOf( + RelationValueView.Create(query), + RelationValueView.Tag( + id = option.id, + name = option.text, + color = option.color, + isSelected = false + ) + ), + actual = actual + ) + } + } + } + + @Test + fun `query equals tag - there is only tag view`() { + val viewModel = createViewModel() + relationsProvider.relation = relation + + runTest { + viewModel.ui.test { + viewModel.onStart("", "") + // first item is emmit in constructor, second - at onStart + val actual = listOf(awaitItem(), awaitItem())[1] + assertEquals( + expected = listOf( + RelationValueView.Tag( + id = option.id, + name = option.text, + color = option.color, + isSelected = false + ) + ), + actual = actual + ) + + val query = option.text + viewModel.onFilterInputChanged(query) + + // because `ui` has distinct under the hood + expectNoEvents() + } + } + } + + private fun createViewModel(): AddObjectRelationValueViewModel = object : + AddObjectRelationValueViewModel( + FakeObjectValueProvider, + FakeObjectDetailsProvider, + relationsProvider, + FakeObjectTypesProvider, + UrlBuilder(FakeGateWay) + ) {} +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectDetailsProvider.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectDetailsProvider.kt new file mode 100644 index 0000000000..70d2f1066d --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectDetailsProvider.kt @@ -0,0 +1,11 @@ +package com.anytypeio.anytype.presentation.relations.providers + +import com.anytypeio.anytype.core_models.Block +import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider + +internal object FakeObjectDetailsProvider : ObjectDetailProvider { + override fun provide(): Map { + return emptyMap() + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectRelationProvider.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectRelationProvider.kt new file mode 100644 index 0000000000..c05db5b91e --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectRelationProvider.kt @@ -0,0 +1,21 @@ +package com.anytypeio.anytype.presentation.relations.providers + +import com.anytypeio.anytype.core_models.Id +import com.anytypeio.anytype.core_models.Relation +import com.anytypeio.anytype.core_models.StubRelation +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + +internal object FakeObjectRelationProvider : ObjectRelationProvider { + internal var relation: Relation = StubRelation() + + override fun get(relation: Id): Relation { + return this.relation + } + + override fun subscribe(relationId: Id): Flow { + return flow { + emit(relation) + } + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectTypesProvider.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectTypesProvider.kt new file mode 100644 index 0000000000..f985989cf2 --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectTypesProvider.kt @@ -0,0 +1,14 @@ +package com.anytypeio.anytype.presentation.relations.providers + +import com.anytypeio.anytype.core_models.ObjectType +import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider + +internal object FakeObjectTypesProvider : ObjectTypesProvider { + internal var list = emptyList() + + override fun get(): List = list + + override fun set(objectTypes: List) { + this.list = objectTypes + } +} \ No newline at end of file diff --git a/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectValueProvider.kt b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectValueProvider.kt new file mode 100644 index 0000000000..108ef17702 --- /dev/null +++ b/presentation/src/test/java/com/anytypeio/anytype/presentation/relations/providers/FakeObjectValueProvider.kt @@ -0,0 +1,13 @@ +package com.anytypeio.anytype.presentation.relations.providers + +import com.anytypeio.anytype.core_models.Id +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow + +internal object FakeObjectValueProvider : ObjectValueProvider { + + override fun get(target: Id): Map = emptyMap() + + override fun subscribe(target: Id): Flow> = + flow { emit(emptyMap()) } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index d50d3bb2cd..4313b44b8e 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,5 +15,8 @@ include ':app', ':analytics', ':protocol', ':core-models', - ':test-utils' -include ':ui-settings' + ':test:android-utils', + ':test:utils', + ':test:core-models-stub' + +include ':ui-settings' \ No newline at end of file diff --git a/test-utils/.gitignore b/test-utils/.gitignore deleted file mode 100644 index 796b96d1c4..0000000000 --- a/test-utils/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/test-utils/build.gradle b/test-utils/build.gradle deleted file mode 100644 index 4b501446d3..0000000000 --- a/test-utils/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - def config = rootProject.extensions.getByName("ext") - - compileSdkVersion config["compile_sdk"] - - defaultConfig { - minSdkVersion config["min_sdk"] - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 - } - - kotlinOptions { - jvmTarget = JavaVersion.VERSION_11 - } -} - -dependencies { - def applicationDependencies = rootProject.ext.mainApplication - def acceptanceTesting = rootProject.ext.acceptanceTesting - - implementation applicationDependencies.appcompat - implementation applicationDependencies.kotlin - implementation applicationDependencies.coroutinesAndroid - implementation applicationDependencies.androidxCore - implementation acceptanceTesting.espressoCore - - implementation applicationDependencies.design - implementation applicationDependencies.recyclerView -} \ No newline at end of file diff --git a/test/android-utils/build.gradle b/test/android-utils/build.gradle new file mode 100644 index 0000000000..58e5113a12 --- /dev/null +++ b/test/android-utils/build.gradle @@ -0,0 +1,30 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion compile_sdk + + defaultConfig { + minSdkVersion min_sdk + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11 + } +} + +dependencies { + implementation mainApplication.appcompat + implementation mainApplication.kotlin + implementation mainApplication.coroutinesAndroid + implementation mainApplication.androidxCore + implementation acceptanceTesting.espressoCore + + implementation mainApplication.design + implementation mainApplication.recyclerView +} \ No newline at end of file diff --git a/test-utils/src/main/AndroidManifest.xml b/test/android-utils/src/main/AndroidManifest.xml similarity index 100% rename from test-utils/src/main/AndroidManifest.xml rename to test/android-utils/src/main/AndroidManifest.xml diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/TestFragment.kt b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/TestFragment.kt similarity index 100% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/TestFragment.kt rename to test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/TestFragment.kt diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/EspressoExt.kt b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/EspressoExt.kt similarity index 95% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/EspressoExt.kt rename to test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/EspressoExt.kt index a6ad93cada..a9918b7dcc 100644 --- a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/EspressoExt.kt +++ b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/EspressoExt.kt @@ -75,11 +75,12 @@ fun ViewInteraction.checkHasChildViewCount(count: Int) : ViewInteraction { return check(matches(WithChildViewCount(count))) } -fun Int.rVMatcher(): RecyclerViewMatcher = RecyclerViewMatcher(this) +fun Int.rVMatcher(): RecyclerViewMatcher = + RecyclerViewMatcher(this) fun Int.checkRecyclerItemCount(expected: Int) = matchView().check(RecyclerViewItemCountAssertion(expected)) -fun RecyclerViewMatcher.onItemView(pos: Int,target: Int): ViewInteraction { +fun RecyclerViewMatcher.onItemView(pos: Int, target: Int): ViewInteraction { return onView(atPositionOnView(pos, target)) } diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewItemCountAssertion.kt b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewItemCountAssertion.kt similarity index 100% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewItemCountAssertion.kt rename to test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewItemCountAssertion.kt diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewMatcher.java b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewMatcher.java similarity index 100% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewMatcher.java rename to test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/RecyclerViewMatcher.java diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/TestUtils.java b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/TestUtils.java similarity index 100% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/TestUtils.java rename to test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/TestUtils.java diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/espresso/ViewMatchers.kt b/test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/espresso/ViewMatchers.kt similarity index 100% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/espresso/ViewMatchers.kt rename to test/android-utils/src/main/java/com/anytypeio/anytype/test_utils/utils/espresso/ViewMatchers.kt diff --git a/test-utils/src/main/res/layout/blank.xml b/test/android-utils/src/main/res/layout/blank.xml similarity index 100% rename from test-utils/src/main/res/layout/blank.xml rename to test/android-utils/src/main/res/layout/blank.xml diff --git a/test/core-models-stub/build.gradle b/test/core-models-stub/build.gradle new file mode 100644 index 0000000000..1cfd368afd --- /dev/null +++ b/test/core-models-stub/build.gradle @@ -0,0 +1,8 @@ +plugins { + id "kotlin" +} + +dependencies { + api project(':test:utils') + api project(":core-models") +} \ No newline at end of file diff --git a/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Relation.kt b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Relation.kt new file mode 100644 index 0000000000..270259d55d --- /dev/null +++ b/test/core-models-stub/src/main/java/com/anytypeio/anytype/core_models/Relation.kt @@ -0,0 +1,37 @@ +package com.anytypeio.anytype.core_models + +import com.anytypeio.anytype.test_utils.MockDataFactory + +fun StubRelation( + key: String = MockDataFactory.randomString(), + name: String = MockDataFactory.randomString(), + format: Relation.Format = Relation.Format.SHORT_TEXT, + source: Relation.Source = Relation.Source.LOCAL, + isHidden: Boolean = false, + isReadOnly: Boolean = false, + isMulti: Boolean = false, + selections: List = emptyList(), + objectTypes: List = emptyList(), + defaultValue: Any? = null +): Relation = Relation( + key, + name, + format, + source, + isHidden, + isReadOnly, + isMulti, + selections, + objectTypes, + defaultValue +) + +fun StubRelationOption( + id: String = MockDataFactory.randomUuid(), + text: String = MockDataFactory.randomString(), + color: String = MockDataFactory.randomString(), + scope: Relation.OptionScope = Relation.OptionScope.LOCAL +): Relation.Option = + Relation.Option( + id, text, color, scope + ) \ No newline at end of file diff --git a/test/utils/build.gradle b/test/utils/build.gradle new file mode 100644 index 0000000000..f7d2feb37d --- /dev/null +++ b/test/utils/build.gradle @@ -0,0 +1,7 @@ +plugins { + id "kotlin" +} + +dependencies { + implementation mainApplication.kotlin +} \ No newline at end of file diff --git a/test-utils/src/main/java/com/anytypeio/anytype/test_utils/MockDataFactory.kt b/test/utils/src/main/java/com/anytypeio/anytype/test_utils/MockDataFactory.kt similarity index 100% rename from test-utils/src/main/java/com/anytypeio/anytype/test_utils/MockDataFactory.kt rename to test/utils/src/main/java/com/anytypeio/anytype/test_utils/MockDataFactory.kt