Merge pull request #5239 from vector-im/feature/adm/missing-pre-consent-identity-values
Fixing missing identify properties
This commit is contained in:
commit
6784caab9c
|
@ -0,0 +1 @@
|
||||||
|
Analytics: Fixes missing use case identity values from within the onboarding flow
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.core.di
|
||||||
|
|
||||||
|
import javax.inject.Qualifier
|
||||||
|
|
||||||
|
@Qualifier
|
||||||
|
@Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class NamedGlobalScope
|
|
@ -29,11 +29,13 @@ import dagger.hilt.components.SingletonComponent
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.EmojiCompatWrapper
|
import im.vector.app.EmojiCompatWrapper
|
||||||
import im.vector.app.EmojiSpanify
|
import im.vector.app.EmojiSpanify
|
||||||
|
import im.vector.app.config.analyticsConfig
|
||||||
import im.vector.app.core.dispatchers.CoroutineDispatchers
|
import im.vector.app.core.dispatchers.CoroutineDispatchers
|
||||||
import im.vector.app.core.error.DefaultErrorFormatter
|
import im.vector.app.core.error.DefaultErrorFormatter
|
||||||
import im.vector.app.core.error.ErrorFormatter
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
import im.vector.app.core.time.Clock
|
import im.vector.app.core.time.Clock
|
||||||
import im.vector.app.core.time.DefaultClock
|
import im.vector.app.core.time.DefaultClock
|
||||||
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import im.vector.app.features.analytics.AnalyticsTracker
|
import im.vector.app.features.analytics.AnalyticsTracker
|
||||||
import im.vector.app.features.analytics.VectorAnalytics
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
|
import im.vector.app.features.analytics.impl.DefaultVectorAnalytics
|
||||||
|
@ -48,6 +50,7 @@ import im.vector.app.features.ui.SharedPreferencesUiStateRepository
|
||||||
import im.vector.app.features.ui.UiStateRepository
|
import im.vector.app.features.ui.UiStateRepository
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import org.matrix.android.sdk.api.Matrix
|
import org.matrix.android.sdk.api.Matrix
|
||||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||||
|
@ -159,4 +162,16 @@ object VectorStaticModule {
|
||||||
fun providesCoroutineDispatchers(): CoroutineDispatchers {
|
fun providesCoroutineDispatchers(): CoroutineDispatchers {
|
||||||
return CoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.Default)
|
return CoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.Default)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||||
|
@Provides
|
||||||
|
@NamedGlobalScope
|
||||||
|
fun providesGlobalScope(): CoroutineScope {
|
||||||
|
return GlobalScope
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun providesAnalyticsConfig(): AnalyticsConfig {
|
||||||
|
return analyticsConfig
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,19 +16,18 @@
|
||||||
|
|
||||||
package im.vector.app.features.analytics.impl
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.posthog.android.Options
|
import com.posthog.android.Options
|
||||||
import com.posthog.android.PostHog
|
import com.posthog.android.PostHog
|
||||||
import com.posthog.android.Properties
|
import com.posthog.android.Properties
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.core.di.NamedGlobalScope
|
||||||
import im.vector.app.config.analyticsConfig
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
import im.vector.app.features.analytics.VectorAnalytics
|
import im.vector.app.features.analytics.VectorAnalytics
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||||
import im.vector.app.features.analytics.log.analyticsTag
|
import im.vector.app.features.analytics.log.analyticsTag
|
||||||
import im.vector.app.features.analytics.plan.UserProperties
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
import im.vector.app.features.analytics.store.AnalyticsStore
|
import im.vector.app.features.analytics.store.AnalyticsStore
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
@ -41,15 +40,30 @@ private val IGNORED_OPTIONS: Options? = null
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class DefaultVectorAnalytics @Inject constructor(
|
class DefaultVectorAnalytics @Inject constructor(
|
||||||
private val context: Context,
|
postHogFactory: PostHogFactory,
|
||||||
private val analyticsStore: AnalyticsStore
|
analyticsConfig: AnalyticsConfig,
|
||||||
|
private val analyticsStore: AnalyticsStore,
|
||||||
|
private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory,
|
||||||
|
@NamedGlobalScope private val globalScope: CoroutineScope
|
||||||
) : VectorAnalytics {
|
) : VectorAnalytics {
|
||||||
private var posthog: PostHog? = null
|
|
||||||
|
private val posthog: PostHog? = when {
|
||||||
|
analyticsConfig.isEnabled -> postHogFactory.createPosthog()
|
||||||
|
else -> {
|
||||||
|
Timber.tag(analyticsTag.value).w("Analytics is disabled")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Cache for the store values
|
// Cache for the store values
|
||||||
private var userConsent: Boolean? = null
|
private var userConsent: Boolean? = null
|
||||||
private var analyticsId: String? = null
|
private var analyticsId: String? = null
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
|
observeUserConsent()
|
||||||
|
observeAnalyticsId()
|
||||||
|
}
|
||||||
|
|
||||||
override fun getUserConsent(): Flow<Boolean> {
|
override fun getUserConsent(): Flow<Boolean> {
|
||||||
return analyticsStore.userConsentFlow
|
return analyticsStore.userConsentFlow
|
||||||
}
|
}
|
||||||
|
@ -82,13 +96,6 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
setAnalyticsId("")
|
setAnalyticsId("")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun init() {
|
|
||||||
observeUserConsent()
|
|
||||||
observeAnalyticsId()
|
|
||||||
createAnalyticsClient()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
private fun observeAnalyticsId() {
|
private fun observeAnalyticsId() {
|
||||||
getAnalyticsId()
|
getAnalyticsId()
|
||||||
.onEach { id ->
|
.onEach { id ->
|
||||||
|
@ -96,21 +103,20 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
analyticsId = id
|
analyticsId = id
|
||||||
identifyPostHog()
|
identifyPostHog()
|
||||||
}
|
}
|
||||||
.launchIn(GlobalScope)
|
.launchIn(globalScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun identifyPostHog() {
|
private suspend fun identifyPostHog() {
|
||||||
val id = analyticsId ?: return
|
val id = analyticsId ?: return
|
||||||
if (id.isEmpty()) {
|
if (id.isEmpty()) {
|
||||||
Timber.tag(analyticsTag.value).d("reset")
|
Timber.tag(analyticsTag.value).d("reset")
|
||||||
posthog?.reset()
|
posthog?.reset()
|
||||||
} else {
|
} else {
|
||||||
Timber.tag(analyticsTag.value).d("identify")
|
Timber.tag(analyticsTag.value).d("identify")
|
||||||
posthog?.identify(id)
|
posthog?.identify(id, lateInitUserPropertiesFactory.createUserProperties()?.getProperties()?.toPostHogUserProperties(), IGNORED_OPTIONS)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
|
||||||
private fun observeUserConsent() {
|
private fun observeUserConsent() {
|
||||||
getUserConsent()
|
getUserConsent()
|
||||||
.onEach { consent ->
|
.onEach { consent ->
|
||||||
|
@ -118,49 +124,13 @@ class DefaultVectorAnalytics @Inject constructor(
|
||||||
userConsent = consent
|
userConsent = consent
|
||||||
optOutPostHog()
|
optOutPostHog()
|
||||||
}
|
}
|
||||||
.launchIn(GlobalScope)
|
.launchIn(globalScope)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optOutPostHog() {
|
private fun optOutPostHog() {
|
||||||
userConsent?.let { posthog?.optOut(!it) }
|
userConsent?.let { posthog?.optOut(!it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createAnalyticsClient() {
|
|
||||||
Timber.tag(analyticsTag.value).d("createAnalyticsClient()")
|
|
||||||
|
|
||||||
if (analyticsConfig.isEnabled.not()) {
|
|
||||||
Timber.tag(analyticsTag.value).w("Analytics is disabled")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
posthog = PostHog.Builder(context, analyticsConfig.postHogApiKey, analyticsConfig.postHogHost)
|
|
||||||
// Record certain application events automatically! (off/false by default)
|
|
||||||
// .captureApplicationLifecycleEvents()
|
|
||||||
// Record screen views automatically! (off/false by default)
|
|
||||||
// .recordScreenViews()
|
|
||||||
// Capture deep links as part of the screen call. (off by default)
|
|
||||||
// .captureDeepLinks()
|
|
||||||
// Maximum number of events to keep in queue before flushing (default 20)
|
|
||||||
// .flushQueueSize(20)
|
|
||||||
// Max delay before flushing the queue (30 seconds)
|
|
||||||
// .flushInterval(30, TimeUnit.SECONDS)
|
|
||||||
// Enable or disable collection of ANDROID_ID (true)
|
|
||||||
.collectDeviceId(false)
|
|
||||||
.logLevel(getLogLevel())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
optOutPostHog()
|
|
||||||
identifyPostHog()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLogLevel(): PostHog.LogLevel {
|
|
||||||
return if (BuildConfig.DEBUG) {
|
|
||||||
PostHog.LogLevel.DEBUG
|
|
||||||
} else {
|
|
||||||
PostHog.LogLevel.INFO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun capture(event: VectorAnalyticsEvent) {
|
override fun capture(event: VectorAnalyticsEvent) {
|
||||||
Timber.tag(analyticsTag.value).d("capture($event)")
|
Timber.tag(analyticsTag.value).d("capture($event)")
|
||||||
posthog
|
posthog
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import im.vector.app.ActiveSessionDataSource
|
||||||
|
import im.vector.app.core.extensions.vectorStore
|
||||||
|
import im.vector.app.features.analytics.extensions.toTrackingValue
|
||||||
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class LateInitUserPropertiesFactory @Inject constructor(
|
||||||
|
private val activeSessionDataSource: ActiveSessionDataSource,
|
||||||
|
private val context: Context,
|
||||||
|
) {
|
||||||
|
suspend fun createUserProperties(): UserProperties? {
|
||||||
|
val useCase = activeSessionDataSource.currentValue?.orNull()?.vectorStore(context)?.readUseCase()
|
||||||
|
return useCase?.let {
|
||||||
|
UserProperties(ftueUseCaseSelection = it.toTrackingValue())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.posthog.android.PostHog
|
||||||
|
import im.vector.app.BuildConfig
|
||||||
|
import im.vector.app.config.analyticsConfig
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class PostHogFactory @Inject constructor(private val context: Context) {
|
||||||
|
|
||||||
|
fun createPosthog(): PostHog {
|
||||||
|
return PostHog.Builder(context, analyticsConfig.postHogApiKey, analyticsConfig.postHogHost)
|
||||||
|
// Record certain application events automatically! (off/false by default)
|
||||||
|
// .captureApplicationLifecycleEvents()
|
||||||
|
// Record screen views automatically! (off/false by default)
|
||||||
|
// .recordScreenViews()
|
||||||
|
// Capture deep links as part of the screen call. (off by default)
|
||||||
|
// .captureDeepLinks()
|
||||||
|
// Maximum number of events to keep in queue before flushing (default 20)
|
||||||
|
// .flushQueueSize(20)
|
||||||
|
// Max delay before flushing the queue (30 seconds)
|
||||||
|
// .flushInterval(30, TimeUnit.SECONDS)
|
||||||
|
// Enable or disable collection of ANDROID_ID (true)
|
||||||
|
.collectDeviceId(false)
|
||||||
|
.logLevel(getLogLevel())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLogLevel(): PostHog.LogLevel {
|
||||||
|
return if (BuildConfig.DEBUG) {
|
||||||
|
PostHog.LogLevel.DEBUG
|
||||||
|
} else {
|
||||||
|
PostHog.LogLevel.INFO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
|
import com.posthog.android.Properties
|
||||||
|
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||||
|
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||||
|
import im.vector.app.test.fakes.FakeAnalyticsStore
|
||||||
|
import im.vector.app.test.fakes.FakeLateInitUserPropertiesFactory
|
||||||
|
import im.vector.app.test.fakes.FakePostHog
|
||||||
|
import im.vector.app.test.fakes.FakePostHogFactory
|
||||||
|
import im.vector.app.test.fixtures.AnalyticsConfigFixture.anAnalyticsConfig
|
||||||
|
import im.vector.app.test.fixtures.aUserProperties
|
||||||
|
import im.vector.app.test.fixtures.aVectorAnalyticsEvent
|
||||||
|
import im.vector.app.test.fixtures.aVectorAnalyticsScreen
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
private const val AN_ANALYTICS_ID = "analytics-id"
|
||||||
|
private val A_SCREEN_EVENT = aVectorAnalyticsScreen()
|
||||||
|
private val AN_EVENT = aVectorAnalyticsEvent()
|
||||||
|
private val A_LATE_INIT_USER_PROPERTIES = aUserProperties()
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class DefaultVectorAnalyticsTest {
|
||||||
|
|
||||||
|
private val fakePostHog = FakePostHog()
|
||||||
|
private val fakeAnalyticsStore = FakeAnalyticsStore()
|
||||||
|
private val fakeLateInitUserPropertiesFactory = FakeLateInitUserPropertiesFactory()
|
||||||
|
|
||||||
|
private val defaultVectorAnalytics = DefaultVectorAnalytics(
|
||||||
|
postHogFactory = FakePostHogFactory(fakePostHog.instance).instance,
|
||||||
|
analyticsStore = fakeAnalyticsStore.instance,
|
||||||
|
globalScope = CoroutineScope(Dispatchers.Unconfined),
|
||||||
|
analyticsConfig = anAnalyticsConfig(isEnabled = true),
|
||||||
|
lateInitUserPropertiesFactory = fakeLateInitUserPropertiesFactory.instance
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
defaultVectorAnalytics.init()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when setting user consent then updates analytics store`() = runBlockingTest {
|
||||||
|
defaultVectorAnalytics.setUserConsent(true)
|
||||||
|
|
||||||
|
fakeAnalyticsStore.verifyConsentUpdated(updatedValue = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when consenting to analytics then updates posthog opt out to false`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
fakePostHog.verifyOptOutStatus(optedOut = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when revoking consent to analytics then updates posthog opt out to true`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = false)
|
||||||
|
|
||||||
|
fakePostHog.verifyOptOutStatus(optedOut = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when setting the analytics id then updates analytics store`() = runBlockingTest {
|
||||||
|
defaultVectorAnalytics.setAnalyticsId(AN_ANALYTICS_ID)
|
||||||
|
|
||||||
|
fakeAnalyticsStore.verifyAnalyticsIdUpdated(updatedValue = AN_ANALYTICS_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given lateinit user properties when valid analytics id updates then identify with lateinit properties`() = runBlockingTest {
|
||||||
|
fakeLateInitUserPropertiesFactory.givenCreatesProperties(A_LATE_INIT_USER_PROPERTIES)
|
||||||
|
|
||||||
|
fakeAnalyticsStore.givenAnalyticsId(AN_ANALYTICS_ID)
|
||||||
|
|
||||||
|
fakePostHog.verifyIdentifies(AN_ANALYTICS_ID, A_LATE_INIT_USER_PROPERTIES)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `when signing out then resets posthog`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.allowSettingAnalyticsIdToCallBackingFlow()
|
||||||
|
|
||||||
|
defaultVectorAnalytics.onSignOut()
|
||||||
|
|
||||||
|
fakePostHog.verifyReset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given user consent when tracking screen events then submits to posthog`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
||||||
|
|
||||||
|
fakePostHog.verifyScreenTracked(A_SCREEN_EVENT.getName(), A_SCREEN_EVENT.toPostHogProperties())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given user has not consented when tracking screen events then does not track`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = false)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.screen(A_SCREEN_EVENT)
|
||||||
|
|
||||||
|
fakePostHog.verifyNoScreenTracking()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given user consent when tracking events then submits to posthog`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = true)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.capture(AN_EVENT)
|
||||||
|
|
||||||
|
fakePostHog.verifyEventTracked(AN_EVENT.getName(), AN_EVENT.toPostHogProperties())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given user has not consented when tracking events then does not track`() = runBlockingTest {
|
||||||
|
fakeAnalyticsStore.givenUserContent(consent = false)
|
||||||
|
|
||||||
|
defaultVectorAnalytics.capture(AN_EVENT)
|
||||||
|
|
||||||
|
fakePostHog.verifyNoEventTracking()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun VectorAnalyticsScreen.toPostHogProperties(): Properties? {
|
||||||
|
return getProperties()?.let { properties ->
|
||||||
|
Properties().also { it.putAll(properties) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun VectorAnalyticsEvent.toPostHogProperties(): Properties? {
|
||||||
|
return getProperties()?.let { properties ->
|
||||||
|
Properties().also { it.putAll(properties) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.features.analytics.impl
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
|
import im.vector.app.features.onboarding.FtueUseCase
|
||||||
|
import im.vector.app.test.fakes.FakeActiveSessionDataSource
|
||||||
|
import im.vector.app.test.fakes.FakeContext
|
||||||
|
import im.vector.app.test.fakes.FakeSession
|
||||||
|
import im.vector.app.test.fakes.FakeVectorStore
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
class LateInitUserPropertiesFactoryTest {
|
||||||
|
|
||||||
|
private val fakeActiveSessionDataSource = FakeActiveSessionDataSource()
|
||||||
|
private val fakeVectorStore = FakeVectorStore()
|
||||||
|
private val fakeContext = FakeContext()
|
||||||
|
private val fakeSession = FakeSession().also {
|
||||||
|
it.givenVectorStore(fakeVectorStore.instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val lateInitUserProperties = LateInitUserPropertiesFactory(
|
||||||
|
fakeActiveSessionDataSource.instance,
|
||||||
|
fakeContext.instance
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no active session when creating properties then returns null`() = runBlockingTest {
|
||||||
|
val result = lateInitUserProperties.createUserProperties()
|
||||||
|
|
||||||
|
result shouldBeEqualTo null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given no use case set on an active session when creating properties then returns null`() = runBlockingTest {
|
||||||
|
fakeVectorStore.givenUseCase(null)
|
||||||
|
fakeSession.givenVectorStore(fakeVectorStore.instance)
|
||||||
|
fakeActiveSessionDataSource.setActiveSession(fakeSession)
|
||||||
|
|
||||||
|
val result = lateInitUserProperties.createUserProperties()
|
||||||
|
|
||||||
|
result shouldBeEqualTo null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given use case set on an active session when creating properties then includes the use case`() = runBlockingTest {
|
||||||
|
fakeVectorStore.givenUseCase(FtueUseCase.TEAMS)
|
||||||
|
fakeActiveSessionDataSource.setActiveSession(fakeSession)
|
||||||
|
val result = lateInitUserProperties.createUserProperties()
|
||||||
|
|
||||||
|
result shouldBeEqualTo UserProperties(
|
||||||
|
ftueUseCaseSelection = UserProperties.FtueUseCaseSelection.WorkMessaging
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import arrow.core.Option
|
||||||
|
import im.vector.app.ActiveSessionDataSource
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
|
class FakeActiveSessionDataSource {
|
||||||
|
|
||||||
|
val instance = ActiveSessionDataSource()
|
||||||
|
|
||||||
|
fun setActiveSession(session: Session) {
|
||||||
|
instance.post(Option.just(session))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.store.AnalyticsStore
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.coVerify
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
class FakeAnalyticsStore {
|
||||||
|
|
||||||
|
private val _consentFlow = MutableSharedFlow<Boolean>()
|
||||||
|
private val _idFlow = MutableSharedFlow<String>()
|
||||||
|
|
||||||
|
val instance = mockk<AnalyticsStore>(relaxed = true) {
|
||||||
|
every { userConsentFlow } returns _consentFlow
|
||||||
|
every { analyticsIdFlow } returns _idFlow
|
||||||
|
}
|
||||||
|
|
||||||
|
fun allowSettingAnalyticsIdToCallBackingFlow() {
|
||||||
|
coEvery { instance.setAnalyticsId(any()) } answers {
|
||||||
|
runBlocking { _idFlow.emit(firstArg()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyConsentUpdated(updatedValue: Boolean) {
|
||||||
|
coVerify { instance.setUserConsent(updatedValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun givenUserContent(consent: Boolean) {
|
||||||
|
_consentFlow.emit(consent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyAnalyticsIdUpdated(updatedValue: String) {
|
||||||
|
coVerify { instance.setAnalyticsId(updatedValue) }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun givenAnalyticsId(id: String) {
|
||||||
|
_idFlow.emit(id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.impl.LateInitUserPropertiesFactory
|
||||||
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeLateInitUserPropertiesFactory {
|
||||||
|
|
||||||
|
val instance = mockk<LateInitUserPropertiesFactory>()
|
||||||
|
|
||||||
|
fun givenCreatesProperties(userProperties: UserProperties?) {
|
||||||
|
coEvery { instance.createUserProperties() } returns userProperties
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import android.os.Looper
|
||||||
|
import com.posthog.android.PostHog
|
||||||
|
import com.posthog.android.Properties
|
||||||
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.verify
|
||||||
|
|
||||||
|
class FakePostHog {
|
||||||
|
|
||||||
|
init {
|
||||||
|
// workaround to avoid PostHog.HANDLER failing
|
||||||
|
mockkStatic(Looper::class)
|
||||||
|
val looper = mockk<Looper> {
|
||||||
|
every { thread } returns Thread.currentThread()
|
||||||
|
}
|
||||||
|
every { Looper.getMainLooper() } returns looper
|
||||||
|
}
|
||||||
|
|
||||||
|
val instance = mockk<PostHog>(relaxed = true)
|
||||||
|
|
||||||
|
fun verifyOptOutStatus(optedOut: Boolean) {
|
||||||
|
verify { instance.optOut(optedOut) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyIdentifies(analyticsId: String, userProperties: UserProperties?) {
|
||||||
|
verify {
|
||||||
|
val postHogProperties = userProperties?.getProperties()
|
||||||
|
?.let { rawProperties -> Properties().also { it.putAll(rawProperties) } }
|
||||||
|
?.takeIf { it.isNotEmpty() }
|
||||||
|
instance.identify(analyticsId, postHogProperties, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyReset() {
|
||||||
|
verify { instance.reset() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyScreenTracked(name: String, properties: Properties?) {
|
||||||
|
verify { instance.screen(name, properties) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyNoScreenTracking() {
|
||||||
|
verify(exactly = 0) {
|
||||||
|
instance.screen(any())
|
||||||
|
instance.screen(any(), any())
|
||||||
|
instance.screen(any(), any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyEventTracked(name: String, properties: Properties?) {
|
||||||
|
verify { instance.capture(name, properties) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun verifyNoEventTracking() {
|
||||||
|
verify(exactly = 0) {
|
||||||
|
instance.capture(any())
|
||||||
|
instance.capture(any(), any())
|
||||||
|
instance.capture(any(), any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import com.posthog.android.PostHog
|
||||||
|
import im.vector.app.features.analytics.impl.PostHogFactory
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakePostHogFactory(postHog: PostHog) {
|
||||||
|
val instance = mockk<PostHogFactory>().also {
|
||||||
|
every { it.createPosthog() } returns postHog
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,8 +16,12 @@
|
||||||
|
|
||||||
package im.vector.app.test.fakes
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.core.extensions.vectorStore
|
||||||
|
import im.vector.app.features.session.VectorSessionStore
|
||||||
import im.vector.app.test.testCoroutineDispatchers
|
import im.vector.app.test.testCoroutineDispatchers
|
||||||
|
import io.mockk.coEvery
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
import org.matrix.android.sdk.api.session.Session
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
|
||||||
class FakeSession(
|
class FakeSession(
|
||||||
|
@ -25,7 +29,19 @@ class FakeSession(
|
||||||
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService()
|
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService()
|
||||||
) : Session by mockk(relaxed = true) {
|
) : Session by mockk(relaxed = true) {
|
||||||
|
|
||||||
|
init {
|
||||||
|
mockkStatic("im.vector.app.core.extensions.SessionKt")
|
||||||
|
}
|
||||||
|
|
||||||
override fun cryptoService() = fakeCryptoService
|
override fun cryptoService() = fakeCryptoService
|
||||||
override val sharedSecretStorageService = fakeSharedSecretStorageService
|
override val sharedSecretStorageService = fakeSharedSecretStorageService
|
||||||
override val coroutineDispatchers = testCoroutineDispatchers
|
override val coroutineDispatchers = testCoroutineDispatchers
|
||||||
|
|
||||||
|
fun givenVectorStore(vectorSessionStore: VectorSessionStore) {
|
||||||
|
coEvery {
|
||||||
|
this@FakeSession.vectorStore(any())
|
||||||
|
} coAnswers {
|
||||||
|
vectorSessionStore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fakes
|
||||||
|
|
||||||
|
import im.vector.app.features.onboarding.FtueUseCase
|
||||||
|
import im.vector.app.features.session.VectorSessionStore
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.mockk
|
||||||
|
|
||||||
|
class FakeVectorStore {
|
||||||
|
val instance = mockk<VectorSessionStore>()
|
||||||
|
|
||||||
|
fun givenUseCase(useCase: FtueUseCase?) {
|
||||||
|
coEvery {
|
||||||
|
instance.readUseCase()
|
||||||
|
} coAnswers {
|
||||||
|
useCase
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fixtures
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.AnalyticsConfig
|
||||||
|
|
||||||
|
object AnalyticsConfigFixture {
|
||||||
|
fun anAnalyticsConfig(
|
||||||
|
isEnabled: Boolean = false,
|
||||||
|
postHogHost: String = "http://posthog.url",
|
||||||
|
postHogApiKey: String = "api-key",
|
||||||
|
policyLink: String = "http://policy.link"
|
||||||
|
) = object : AnalyticsConfig {
|
||||||
|
override val isEnabled: Boolean = isEnabled
|
||||||
|
override val postHogHost = postHogHost
|
||||||
|
override val postHogApiKey = postHogApiKey
|
||||||
|
override val policyLink = policyLink
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fixtures
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.plan.UserProperties
|
||||||
|
import im.vector.app.features.analytics.plan.UserProperties.FtueUseCaseSelection
|
||||||
|
|
||||||
|
fun aUserProperties(
|
||||||
|
ftueUseCase: FtueUseCaseSelection? = FtueUseCaseSelection.Skip
|
||||||
|
) = UserProperties(
|
||||||
|
ftueUseCaseSelection = ftueUseCase
|
||||||
|
)
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package im.vector.app.test.fixtures
|
||||||
|
|
||||||
|
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||||
|
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||||
|
|
||||||
|
fun aVectorAnalyticsScreen(
|
||||||
|
name: String = "a-screen-name",
|
||||||
|
properties: Map<String, Any>? = null
|
||||||
|
) = object : VectorAnalyticsScreen {
|
||||||
|
override fun getName() = name
|
||||||
|
override fun getProperties() = properties
|
||||||
|
}
|
||||||
|
|
||||||
|
fun aVectorAnalyticsEvent(
|
||||||
|
name: String = "an-event-name",
|
||||||
|
properties: Map<String, Any>? = null
|
||||||
|
) = object : VectorAnalyticsEvent {
|
||||||
|
override fun getName() = name
|
||||||
|
override fun getProperties() = properties
|
||||||
|
}
|
Loading…
Reference in New Issue