2022-09-02 10:09:02 +01:00
//
2024-09-06 16:34:30 +03:00
// C o p y r i g h t 2 0 2 2 - 2 0 2 4 N e w V e c t o r L t d .
2022-09-02 10:09:02 +01:00
//
2025-01-06 11:27:37 +01:00
// S P D X - L i c e n s e - I d e n t i f i e r : A G P L - 3 . 0 - o n l y O R L i c e n s e R e f - E l e m e n t - C o m m e r c i a l
// P l e a s e s e e L I C E N S E f i l e s i n t h e r e p o s i t o r y r o o t f o r f u l l d e t a i l s .
2022-09-02 10:09:02 +01:00
//
import AnalyticsEvents
@ testable import ElementX
2024-05-02 17:21:09 +02:00
import PostHog
2022-09-02 10:09:02 +01:00
import XCTest
class AnalyticsTests : XCTestCase {
2023-07-27 12:38:48 +01:00
private var appSettings : AppSettings !
2023-04-18 09:33:32 +02:00
private var analyticsClient : AnalyticsClientMock !
2024-05-02 17:21:09 +02:00
private var posthogMock : PHGPostHogMock !
2022-12-16 10:02:22 +02:00
override func setUp ( ) {
2024-03-21 14:01:23 +02:00
AppSettings . resetAllSettings ( )
2023-07-27 12:38:48 +01:00
appSettings = AppSettings ( )
2023-04-25 17:12:56 +01:00
2023-04-18 09:33:32 +02:00
analyticsClient = AnalyticsClientMock ( )
analyticsClient . isRunning = false
2023-06-23 09:56:22 +03:00
ServiceLocator . shared . register ( analytics : AnalyticsService ( client : analyticsClient ,
2024-05-30 14:10:51 +03:00
appSettings : appSettings ) )
2024-05-02 17:21:09 +02:00
posthogMock = PHGPostHogMock ( )
posthogMock . configureMockBehavior ( )
2022-12-16 10:02:22 +02:00
}
2023-10-16 17:28:06 +02:00
override func tearDown ( ) {
2024-03-21 14:01:23 +02:00
AppSettings . resetAllSettings ( )
2023-10-16 17:28:06 +02:00
}
2022-09-02 10:09:02 +01:00
func testAnalyticsPromptNewUser ( ) {
// G i v e n a f r e s h i n s t a l l o f t h e a p p ( w i t h o u t P o s t H o g a n a l y t i c s h a v i n g b e e n s e t ) .
// W h e n t h e u s e r i s p r o m p t e d f o r a n a l y t i c s .
2023-04-18 09:33:32 +02:00
let showPrompt = ServiceLocator . shared . analytics . shouldShowAnalyticsPrompt
2022-09-02 10:09:02 +01:00
// T h e n t h e p r o m p t s h o u l d b e s h o w n .
XCTAssertTrue ( showPrompt , " A prompt should be shown for a new user. " )
}
func testAnalyticsPromptUserDeclinedPostHog ( ) {
// G i v e n a n e x i s t i n g i n s t a l l o f t h e a p p w h e r e t h e u s e r p r e v i o u s l y d e c l i n e d P o s t H o g
2023-04-25 17:12:56 +01:00
appSettings . analyticsConsentState = . optedOut
2022-09-02 10:09:02 +01:00
// W h e n t h e u s e r i s p r o m p t e d f o r a n a l y t i c s
2023-04-18 09:33:32 +02:00
let showPrompt = ServiceLocator . shared . analytics . shouldShowAnalyticsPrompt
2022-09-02 10:09:02 +01:00
// T h e n n o p r o m p t s h o u l d b e s h o w n .
XCTAssertFalse ( showPrompt , " A prompt should not be shown any more. " )
}
func testAnalyticsPromptUserAcceptedPostHog ( ) {
// G i v e n a n e x i s t i n g i n s t a l l o f t h e a p p w h e r e t h e u s e r p r e v i o u s l y a c c e p t e d P o s t H o g
2023-04-25 17:12:56 +01:00
appSettings . analyticsConsentState = . optedIn
2022-09-02 10:09:02 +01:00
// W h e n t h e u s e r i s p r o m p t e d f o r a n a l y t i c s
2023-04-18 09:33:32 +02:00
let showPrompt = ServiceLocator . shared . analytics . shouldShowAnalyticsPrompt
2022-09-02 10:09:02 +01:00
// T h e n n o p r o m p t s h o u l d b e s h o w n .
XCTAssertFalse ( showPrompt , " A prompt should not be shown any more. " )
}
2023-04-18 09:33:32 +02:00
func testAnalyticsPromptNotDisplayed ( ) {
2024-05-30 14:10:51 +03:00
// G i v e n a f r e s h i n s t a l l o f t h e a p p A n a l y t i c s s h o u l d b e d i s a b l e d
2023-07-27 12:38:48 +01:00
XCTAssertEqual ( appSettings . analyticsConsentState , . unknown )
2023-04-21 17:05:39 +02:00
XCTAssertFalse ( ServiceLocator . shared . analytics . isEnabled )
2023-06-23 09:56:22 +03:00
XCTAssertFalse ( analyticsClient . startAnalyticsConfigurationCalled )
2023-04-18 09:33:32 +02:00
}
2024-05-02 17:21:09 +02:00
2023-04-18 09:33:32 +02:00
func testAnalyticsOptOut ( ) {
// G i v e n a f r e s h i n s t a l l o f t h e a p p ( w i t h o u t P o s t H o g a n a l y t i c s h a v i n g b e e n s e t ) .
// W h e n a n a l y t i c s i s o p t - o u t
ServiceLocator . shared . analytics . optOut ( )
// T h e n a n a l y t i c s s h o u l d b e d i s a b l e d
2023-04-25 17:12:56 +01:00
XCTAssertEqual ( appSettings . analyticsConsentState , . optedOut )
2023-04-21 17:05:39 +02:00
XCTAssertFalse ( ServiceLocator . shared . analytics . isEnabled )
2023-04-18 09:33:32 +02:00
XCTAssertFalse ( analyticsClient . isRunning )
2024-05-30 14:10:51 +03:00
// A n a l y t i c s c l i e n t s h o u l d h a v e b e e n s t o p p e d
2023-04-18 09:33:32 +02:00
XCTAssertTrue ( analyticsClient . stopCalled )
}
2024-05-02 17:21:09 +02:00
2023-04-18 09:33:32 +02:00
func testAnalyticsOptIn ( ) {
// G i v e n a f r e s h i n s t a l l o f t h e a p p ( w i t h o u t P o s t H o g a n a l y t i c s h a v i n g b e e n s e t ) .
// W h e n a n a l y t i c s i s o p t - i n
ServiceLocator . shared . analytics . optIn ( )
// T h e a n a l y t i c s s h o u l d b e e n a b l e d
2023-04-25 17:12:56 +01:00
XCTAssertEqual ( appSettings . analyticsConsentState , . optedIn )
2023-04-21 17:05:39 +02:00
XCTAssertTrue ( ServiceLocator . shared . analytics . isEnabled )
2024-05-30 14:10:51 +03:00
// A n a l y t i c s c l i e n t s h o u l d h a v e b e e n s t a r t e d
2023-06-23 09:56:22 +03:00
XCTAssertTrue ( analyticsClient . startAnalyticsConfigurationCalled )
2023-04-18 09:33:32 +02:00
}
2024-05-02 17:21:09 +02:00
2023-04-18 09:33:32 +02:00
func testAnalyticsStartIfNotEnabled ( ) {
// G i v e n a n e x i s t i n g i n s t a l l o f t h e a p p w h e r e t h e u s e r p r e v i o u s l y d e c l i n e d t h e t r a c k i n g
2023-04-25 17:12:56 +01:00
appSettings . analyticsConsentState = . optedOut
2023-04-18 09:33:32 +02:00
// A n a l y t i c s s h o u l d n o t s t a r t
2023-04-21 17:05:39 +02:00
XCTAssertFalse ( ServiceLocator . shared . analytics . isEnabled )
2023-04-18 09:33:32 +02:00
ServiceLocator . shared . analytics . startIfEnabled ( )
2023-06-23 09:56:22 +03:00
XCTAssertFalse ( analyticsClient . startAnalyticsConfigurationCalled )
2023-04-18 09:33:32 +02:00
}
func testAnalyticsStartIfEnabled ( ) {
2024-05-30 14:10:51 +03:00
// G i v e n a n e x i s t i n g i n s t a l l o f t h e a p p w h e r e t h e u s e r p r e v i o u s l y a c c e p t e d t h e t r a c k i n g
2023-04-25 17:12:56 +01:00
appSettings . analyticsConsentState = . optedIn
2023-04-18 09:33:32 +02:00
// A n a l y t i c s s h o u l d s t a r t
2023-04-21 17:05:39 +02:00
XCTAssertTrue ( ServiceLocator . shared . analytics . isEnabled )
2023-04-18 09:33:32 +02:00
ServiceLocator . shared . analytics . startIfEnabled ( )
2023-06-23 09:56:22 +03:00
XCTAssertTrue ( analyticsClient . startAnalyticsConfigurationCalled )
2023-04-18 09:33:32 +02:00
}
2022-09-02 10:09:02 +01:00
func testAddingUserProperties ( ) {
// G i v e n a c l i e n t w i t h n o u s e r p r o p e r t i e s s e t
let client = PostHogAnalyticsClient ( )
XCTAssertNil ( client . pendingUserProperties , " No user properties should have been set yet. " )
// W h e n u p d a t i n g t h e u s e r p r o p e r t i e s
2023-12-19 12:12:16 +01:00
client . updateUserProperties ( AnalyticsEvent . UserProperties ( allChatsActiveFilter : nil ,
ftueUseCaseSelection : . PersonalMessaging ,
2022-09-02 10:09:02 +01:00
numFavouriteRooms : 4 ,
2024-05-07 16:31:12 +02:00
numSpaces : 5 , recoveryState : . Disabled , verificationState : . Verified ) )
2022-09-02 10:09:02 +01:00
// T h e n t h e p r o p e r t i e s s h o u l d b e c a c h e d
XCTAssertNotNil ( client . pendingUserProperties , " The user properties should be cached. " )
XCTAssertEqual ( client . pendingUserProperties ? . ftueUseCaseSelection , . PersonalMessaging , " The use case selection should match. " )
XCTAssertEqual ( client . pendingUserProperties ? . numFavouriteRooms , 4 , " The number of favorite rooms should match. " )
XCTAssertEqual ( client . pendingUserProperties ? . numSpaces , 5 , " The number of spaces should match. " )
2024-05-07 16:31:12 +02:00
XCTAssertEqual ( client . pendingUserProperties ? . verificationState , AnalyticsEvent . UserProperties . VerificationState . Verified , " The verification state should match. " )
XCTAssertEqual ( client . pendingUserProperties ? . recoveryState , AnalyticsEvent . UserProperties . RecoveryState . Disabled , " The recovery state should match. " )
2022-09-02 10:09:02 +01:00
}
func testMergingUserProperties ( ) {
// G i v e n a c l i e n t w i t h a c a c h e d u s e c a s e u s e r p r o p e r t i e s
let client = PostHogAnalyticsClient ( )
2023-12-19 12:12:16 +01:00
client . updateUserProperties ( AnalyticsEvent . UserProperties ( allChatsActiveFilter : nil , ftueUseCaseSelection : . PersonalMessaging ,
2022-09-02 10:09:02 +01:00
numFavouriteRooms : nil ,
2024-05-07 16:31:12 +02:00
numSpaces : nil , recoveryState : nil , verificationState : nil ) )
2022-09-02 10:09:02 +01:00
XCTAssertNotNil ( client . pendingUserProperties , " The user properties should be cached. " )
XCTAssertEqual ( client . pendingUserProperties ? . ftueUseCaseSelection , . PersonalMessaging , " The use case selection should match. " )
XCTAssertNil ( client . pendingUserProperties ? . numFavouriteRooms , " The number of favorite rooms should not be set. " )
XCTAssertNil ( client . pendingUserProperties ? . numSpaces , " The number of spaces should not be set. " )
// W h e n u p d a t i n g t h e n u m b e r o f s p a c e d
2023-12-19 12:12:16 +01:00
client . updateUserProperties ( AnalyticsEvent . UserProperties ( allChatsActiveFilter : nil , ftueUseCaseSelection : nil ,
2022-09-02 10:09:02 +01:00
numFavouriteRooms : 4 ,
2024-05-07 16:31:12 +02:00
numSpaces : 5 , recoveryState : nil , verificationState : nil ) )
2022-09-02 10:09:02 +01:00
// T h e n t h e n e w p r o p e r t i e s s h o u l d b e u p d a t e d a n d t h e e x i s t i n g p r o p e r t i e s s h o u l d r e m a i n u n c h a n g e d
XCTAssertNotNil ( client . pendingUserProperties , " The user properties should be cached. " )
XCTAssertEqual ( client . pendingUserProperties ? . ftueUseCaseSelection , . PersonalMessaging , " The use case selection shouldn't have changed. " )
XCTAssertEqual ( client . pendingUserProperties ? . numFavouriteRooms , 4 , " The number of favorite rooms should have been updated. " )
XCTAssertEqual ( client . pendingUserProperties ? . numSpaces , 5 , " The number of spaces should have been updated. " )
}
func testSendingUserProperties ( ) {
// G i v e n a c l i e n t w i t h u s e r p r o p e r t i e s s e t
2024-05-07 12:08:56 +02:00
let client = PostHogAnalyticsClient ( posthogFactory : MockPostHogFactory ( mock : posthogMock ) )
client . start ( analyticsConfiguration : appSettings . analyticsConfiguration )
2023-12-19 12:12:16 +01:00
client . updateUserProperties ( AnalyticsEvent . UserProperties ( allChatsActiveFilter : nil , ftueUseCaseSelection : . PersonalMessaging ,
2022-09-02 10:09:02 +01:00
numFavouriteRooms : nil ,
2024-05-07 16:31:12 +02:00
numSpaces : nil , recoveryState : nil , verificationState : nil ) )
2022-09-02 10:09:02 +01:00
XCTAssertNotNil ( client . pendingUserProperties , " The user properties should be cached. " )
XCTAssertEqual ( client . pendingUserProperties ? . ftueUseCaseSelection , . PersonalMessaging , " The use case selection should match. " )
// W h e n s e n d i n g a n e v e n t ( t e s t s r u n u n d e r D e b u g c o n f i g u r a t i o n s o t h i s i s s e n t t o t h e d e v e l o p m e n t i n s t a n c e )
2024-05-07 12:08:56 +02:00
let someEvent = AnalyticsEvent . Error ( context : nil ,
cryptoModule : . Rust ,
cryptoSDK : . Rust ,
domain : . E2EE ,
eventLocalAgeMillis : nil ,
isFederated : nil ,
isMatrixDotOrg : nil ,
name : . OlmKeysNotSentError ,
timeToDecryptMillis : nil ,
userTrustsOwnIdentity : nil ,
wasVisibleToUser : nil )
client . capture ( someEvent )
let capturedEvent = posthogMock . capturePropertiesUserPropertiesReceivedArguments
// T h e u s e r p r o p e r t i e s s h o u l d h a v e b e e n a d d e d
XCTAssertEqual ( capturedEvent ? . userProperties ? [ " ftueUseCaseSelection " ] as ? String , AnalyticsEvent . UserProperties . FtueUseCaseSelection . PersonalMessaging . rawValue )
2022-09-02 10:09:02 +01:00
// T h e n t h e p r o p e r t i e s s h o u l d b e c l e a r e d
XCTAssertNil ( client . pendingUserProperties , " The user properties should be cleared. " )
}
2023-04-21 17:05:39 +02:00
func testResetConsentState ( ) {
// G i v e n a n e x i s t i n g i n s t a l l o f t h e a p p w h e r e t h e u s e r p r e v i o u s l y a c c p e t e d t h e t r a c k i n g
2023-04-25 17:12:56 +01:00
appSettings . analyticsConsentState = . optedIn
2023-04-21 17:05:39 +02:00
XCTAssertFalse ( ServiceLocator . shared . analytics . shouldShowAnalyticsPrompt )
2024-05-02 17:21:09 +02:00
2023-04-21 17:05:39 +02:00
// W h e n f o r g e t t i n g a n a l y t i c s c o n s e n t s
ServiceLocator . shared . analytics . resetConsentState ( )
// T h e n t h e a n a l y t i c s p r o m p t s h o u l d b e p r e s e n t e d a g a i n
2023-04-25 17:12:56 +01:00
XCTAssertEqual ( appSettings . analyticsConsentState , . unknown )
2023-04-21 17:05:39 +02:00
XCTAssertTrue ( ServiceLocator . shared . analytics . shouldShowAnalyticsPrompt )
}
2024-05-02 17:21:09 +02:00
func testSendingAndUpdatingSuperProperties ( ) {
// G i v e n a c l i e n t w i t h u s e r p r o p e r t i e s s e t
let client = PostHogAnalyticsClient ( posthogFactory : MockPostHogFactory ( mock : posthogMock ) )
client . start ( analyticsConfiguration : appSettings . analyticsConfiguration )
client . updateSuperProperties (
2024-05-30 14:10:51 +03:00
AnalyticsEvent . SuperProperties ( appPlatform : . EXI ,
2024-05-02 17:21:09 +02:00
cryptoSDK : . Rust ,
cryptoSDKVersion : " 000 " )
)
// W h e n s e n d i n g a n e v e n t ( t e s t s r u n u n d e r D e b u g c o n f i g u r a t i o n s o t h i s i s s e n t t o t h e d e v e l o p m e n t i n s t a n c e )
client . screen ( AnalyticsEvent . MobileScreen ( durationMs : nil , screenName : . Home ) )
let screenEvent = posthogMock . screenPropertiesReceivedArguments
XCTAssertEqual ( screenEvent ? . screenTitle , AnalyticsEvent . MobileScreen . ScreenName . Home . rawValue )
// A l l t h e s u p e r p r o p e r t i e s s h o u l d h a v e b e e n a d d e d
XCTAssertEqual ( screenEvent ? . properties ? [ " cryptoSDK " ] as ? String , AnalyticsEvent . SuperProperties . CryptoSDK . Rust . rawValue )
2024-05-30 14:10:51 +03:00
XCTAssertEqual ( screenEvent ? . properties ? [ " appPlatform " ] as ? String , " EXI " )
2024-05-02 17:21:09 +02:00
XCTAssertEqual ( screenEvent ? . properties ? [ " cryptoSDKVersion " ] as ? String , " 000 " )
// I t s h o u l d b e t h e s a m e f o r a n y e v e n t
let someEvent = AnalyticsEvent . Error ( context : nil ,
cryptoModule : . Rust ,
cryptoSDK : . Rust ,
domain : . E2EE ,
eventLocalAgeMillis : nil ,
isFederated : nil ,
isMatrixDotOrg : nil ,
name : . OlmKeysNotSentError ,
timeToDecryptMillis : nil ,
userTrustsOwnIdentity : nil ,
wasVisibleToUser : nil )
client . capture ( someEvent )
2024-05-07 12:08:56 +02:00
let capturedEvent = posthogMock . capturePropertiesUserPropertiesReceivedArguments
2024-05-02 17:21:09 +02:00
// A l l t h e s u p e r p r o p e r t i e s s h o u l d h a v e b e e n a d d e d
XCTAssertEqual ( capturedEvent ? . properties ? [ " cryptoSDK " ] as ? String , AnalyticsEvent . SuperProperties . CryptoSDK . Rust . rawValue )
2024-05-30 14:10:51 +03:00
XCTAssertEqual ( capturedEvent ? . properties ? [ " appPlatform " ] as ? String , " EXI " )
2024-05-02 17:21:09 +02:00
XCTAssertEqual ( capturedEvent ? . properties ? [ " cryptoSDKVersion " ] as ? String , " 000 " )
// U p d a t i n g s h o u l d k e e p t h e p r e v i o u s l y s e t p r o p e r t i e s
client . updateSuperProperties (
2024-05-30 14:10:51 +03:00
AnalyticsEvent . SuperProperties ( appPlatform : . EXI ,
cryptoSDK : . Rust ,
2024-05-02 17:21:09 +02:00
cryptoSDKVersion : " 001 " )
)
client . capture ( someEvent )
2024-05-07 12:08:56 +02:00
let capturedEvent2 = posthogMock . capturePropertiesUserPropertiesReceivedArguments
2024-05-02 17:21:09 +02:00
// A l l t h e s u p e r p r o p e r t i e s s h o u l d h a v e b e e n a d d e d , w i t h t h e o n e u d p a t e d
XCTAssertEqual ( capturedEvent2 ? . properties ? [ " cryptoSDK " ] as ? String , AnalyticsEvent . SuperProperties . CryptoSDK . Rust . rawValue )
2024-05-30 14:10:51 +03:00
XCTAssertEqual ( capturedEvent2 ? . properties ? [ " appPlatform " ] as ? String , " EXI " )
2024-05-02 17:21:09 +02:00
XCTAssertEqual ( capturedEvent2 ? . properties ? [ " cryptoSDKVersion " ] as ? String , " 001 " )
}
2024-05-06 17:07:05 +02:00
func testShouldNotReportIfNotStarted ( ) {
// G i v e n a c l i e n t w i t h u s e r p r o p e r t i e s s e t
let client = PostHogAnalyticsClient ( posthogFactory : MockPostHogFactory ( mock : posthogMock ) )
// N o c a l l t o s t a r t
client . screen ( AnalyticsEvent . MobileScreen ( durationMs : nil , screenName : . Home ) )
XCTAssertEqual ( posthogMock . screenPropertiesCalled , false )
// I t s h o u l d b e t h e s a m e f o r a n y e v e n t
let someEvent = AnalyticsEvent . Error ( context : nil ,
cryptoModule : . Rust ,
cryptoSDK : . Rust ,
domain : . E2EE ,
eventLocalAgeMillis : nil ,
isFederated : nil ,
isMatrixDotOrg : nil ,
name : . OlmKeysNotSentError ,
timeToDecryptMillis : nil ,
userTrustsOwnIdentity : nil ,
wasVisibleToUser : nil )
client . capture ( someEvent )
2024-05-07 12:08:56 +02:00
XCTAssertEqual ( posthogMock . capturePropertiesUserPropertiesCalled , false )
2024-05-06 17:07:05 +02:00
// s t a r t n o w
client . start ( analyticsConfiguration : appSettings . analyticsConfiguration )
XCTAssertEqual ( posthogMock . optInCalled , true )
client . capture ( someEvent )
2024-05-07 12:08:56 +02:00
XCTAssertEqual ( posthogMock . capturePropertiesUserPropertiesCalled , true )
2024-05-06 17:07:05 +02:00
}
2022-09-02 10:09:02 +01:00
}