mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-03-18 04:08:44 +03:00
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
18 changed files with 753 additions and 55 deletions
1
changelog.d/5234.bugfix
Normal file
1
changelog.d/5234.bugfix
Normal file
|
@ -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.EmojiCompatWrapper
|
||||
import im.vector.app.EmojiSpanify
|
||||
import im.vector.app.config.analyticsConfig
|
||||
import im.vector.app.core.dispatchers.CoroutineDispatchers
|
||||
import im.vector.app.core.error.DefaultErrorFormatter
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
import im.vector.app.core.time.Clock
|
||||
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.VectorAnalytics
|
||||
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 kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import org.matrix.android.sdk.api.Matrix
|
||||
import org.matrix.android.sdk.api.MatrixConfiguration
|
||||
|
@ -159,4 +162,16 @@ object VectorStaticModule {
|
|||
fun providesCoroutineDispatchers(): CoroutineDispatchers {
|
||||
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
|
||||
|
||||
import android.content.Context
|
||||
import com.posthog.android.Options
|
||||
import com.posthog.android.PostHog
|
||||
import com.posthog.android.Properties
|
||||
import im.vector.app.BuildConfig
|
||||
import im.vector.app.config.analyticsConfig
|
||||
import im.vector.app.core.di.NamedGlobalScope
|
||||
import im.vector.app.features.analytics.AnalyticsConfig
|
||||
import im.vector.app.features.analytics.VectorAnalytics
|
||||
import im.vector.app.features.analytics.itf.VectorAnalyticsEvent
|
||||
import im.vector.app.features.analytics.itf.VectorAnalyticsScreen
|
||||
import im.vector.app.features.analytics.log.analyticsTag
|
||||
import im.vector.app.features.analytics.plan.UserProperties
|
||||
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.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
@ -41,15 +40,30 @@ private val IGNORED_OPTIONS: Options? = null
|
|||
|
||||
@Singleton
|
||||
class DefaultVectorAnalytics @Inject constructor(
|
||||
private val context: Context,
|
||||
private val analyticsStore: AnalyticsStore
|
||||
postHogFactory: PostHogFactory,
|
||||
analyticsConfig: AnalyticsConfig,
|
||||
private val analyticsStore: AnalyticsStore,
|
||||
private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory,
|
||||
@NamedGlobalScope private val globalScope: CoroutineScope
|
||||
) : 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
|
||||
private var userConsent: Boolean? = null
|
||||
private var analyticsId: String? = null
|
||||
|
||||
override fun init() {
|
||||
observeUserConsent()
|
||||
observeAnalyticsId()
|
||||
}
|
||||
|
||||
override fun getUserConsent(): Flow<Boolean> {
|
||||
return analyticsStore.userConsentFlow
|
||||
}
|
||||
|
@ -82,13 +96,6 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
setAnalyticsId("")
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
observeUserConsent()
|
||||
observeAnalyticsId()
|
||||
createAnalyticsClient()
|
||||
}
|
||||
|
||||
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||
private fun observeAnalyticsId() {
|
||||
getAnalyticsId()
|
||||
.onEach { id ->
|
||||
|
@ -96,21 +103,20 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
analyticsId = id
|
||||
identifyPostHog()
|
||||
}
|
||||
.launchIn(GlobalScope)
|
||||
.launchIn(globalScope)
|
||||
}
|
||||
|
||||
private fun identifyPostHog() {
|
||||
private suspend fun identifyPostHog() {
|
||||
val id = analyticsId ?: return
|
||||
if (id.isEmpty()) {
|
||||
Timber.tag(analyticsTag.value).d("reset")
|
||||
posthog?.reset()
|
||||
} else {
|
||||
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() {
|
||||
getUserConsent()
|
||||
.onEach { consent ->
|
||||
|
@ -118,49 +124,13 @@ class DefaultVectorAnalytics @Inject constructor(
|
|||
userConsent = consent
|
||||
optOutPostHog()
|
||||
}
|
||||
.launchIn(GlobalScope)
|
||||
.launchIn(globalScope)
|
||||
}
|
||||
|
||||
private fun optOutPostHog() {
|
||||
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) {
|
||||
Timber.tag(analyticsTag.value).d("capture($event)")
|
||||
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
|
||||
}
|
||||
}
|
81
vector/src/test/java/im/vector/app/test/fakes/FakePostHog.kt
Normal file
81
vector/src/test/java/im/vector/app/test/fakes/FakePostHog.kt
Normal file
|
@ -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
|
||||
|
||||
import im.vector.app.core.extensions.vectorStore
|
||||
import im.vector.app.features.session.VectorSessionStore
|
||||
import im.vector.app.test.testCoroutineDispatchers
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
||||
class FakeSession(
|
||||
|
@ -25,7 +29,19 @@ class FakeSession(
|
|||
val fakeSharedSecretStorageService: FakeSharedSecretStorageService = FakeSharedSecretStorageService()
|
||||
) : Session by mockk(relaxed = true) {
|
||||
|
||||
init {
|
||||
mockkStatic("im.vector.app.core.extensions.SessionKt")
|
||||
}
|
||||
|
||||
override fun cryptoService() = fakeCryptoService
|
||||
override val sharedSecretStorageService = fakeSharedSecretStorageService
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
33
vector/src/test/java/im/vector/app/test/fixtures/AnalyticsConfigFixture.kt
vendored
Normal file
33
vector/src/test/java/im/vector/app/test/fixtures/AnalyticsConfigFixture.kt
vendored
Normal file
|
@ -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
|
||||
}
|
||||
}
|
26
vector/src/test/java/im/vector/app/test/fixtures/UserPropertiesFixture.kt
vendored
Normal file
26
vector/src/test/java/im/vector/app/test/fixtures/UserPropertiesFixture.kt
vendored
Normal file
|
@ -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
|
||||
)
|
36
vector/src/test/java/im/vector/app/test/fixtures/VectorAnalyticsFixture.kt
vendored
Normal file
36
vector/src/test/java/im/vector/app/test/fixtures/VectorAnalyticsFixture.kt
vendored
Normal file
|
@ -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…
Add table
Reference in a new issue