package com.contentsquare.rn import androidx.core.util.Consumer import androidx.test.ext.junit.runners.AndroidJUnit4 import com.contentsquare.android.Contentsquare import com.contentsquare.rn.csq.CSQTransitionWrapper import com.contentsquare.rn.csq.LegacyTransitionWrapper import com.contentsquare.rn.eventEmitter.CSEventEmitterModuleImpl import com.contentsquare.rn.utils.ReactNativeUiThreadUtil import com.contentsquare.rn.utils.ReactNativeViewFinder import com.contentsquare.rn.webview.WebViewInjector import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import io.mockk.Runs import io.mockk.clearAllMocks import io.mockk.clearMocks import io.mockk.every import io.mockk.just import io.mockk.mockk import io.mockk.mockkStatic import io.mockk.runs import io.mockk.slot import io.mockk.spyk import io.mockk.verify import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.robolectric.annotation.Config @RunWith(AndroidJUnit4::class) @Config(manifest = Config.NONE) class ContentsquareModuleTest { // mocks private val mockWebViewTag = 999 private val mockReactApplicationContext = mockk() private val mockWebViewInjector = mockk() private val mockCSEventEmitter = mockk() private val mockReactNativeUiThreadUtil = mockk() private val mockReactNativeViewFinder = mockk() private lateinit var module: ContentsquareModule private lateinit var csqWrapperSpy: CSQTransitionWrapper private lateinit var legacyWrapperSpy: LegacyTransitionWrapper @Before fun setUp() { // Setup ReactNativeUiThreadUtil mocking before creating any objects that use it val slot = slot() every { mockReactNativeUiThreadUtil.runOnUiThread(capture(slot)) } answers { slot.captured.run() Unit } module = ContentsquareModule( mockReactApplicationContext, mockWebViewInjector, mockCSEventEmitter, mockReactNativeUiThreadUtil, mockReactNativeViewFinder ) mockkStatic(Contentsquare::class) mockkStatic(CSQTransitionWrapper::class) mockkStatic(LegacyTransitionWrapper::class) setupTransitionWrapperSpies() } private fun setupTransitionWrapperSpies() { // Setup ReactNativeUiThreadUtil mocking before creating CSQTransitionWrapper val slot = slot() every { mockReactNativeUiThreadUtil.runOnUiThread(capture(slot)) } answers { slot.captured.run() Unit } // Create spy objects for the actual TransitionWrapper implementations csqWrapperSpy = spyk( CSQTransitionWrapper( mockReactApplicationContext, mockWebViewInjector, mockCSEventEmitter, mockReactNativeUiThreadUtil, mockReactNativeViewFinder ) ) legacyWrapperSpy = spyk( LegacyTransitionWrapper( mockReactApplicationContext, mockWebViewInjector, mockCSEventEmitter, mockReactNativeUiThreadUtil ) ) // Create the module with our spy objects module = ContentsquareModule( mockReactApplicationContext, mockWebViewInjector, mockCSEventEmitter, mockReactNativeUiThreadUtil, mockReactNativeViewFinder ) // Use reflection to replace the private implementations with our spies val implField = ContentsquareModule::class.java.getDeclaredField("implementation") implField.isAccessible = true implField.set(module, legacyWrapperSpy) val csqField = ContentsquareModule::class.java.getDeclaredField("csqTransitionWrapper") csqField.isAccessible = true csqField.set(module, csqWrapperSpy) // Configure the spies every { csqWrapperSpy.start() } just Runs every { legacyWrapperSpy.start() } just Runs every { csqWrapperSpy.configureProductAnalytics(any(), any()) } just Runs every { csqWrapperSpy.optIn() } just Runs every { legacyWrapperSpy.optIn() } just Runs every { csqWrapperSpy.optOut() } just Runs every { legacyWrapperSpy.optOut() } just Runs every { csqWrapperSpy.identify(any()) } just Runs every { legacyWrapperSpy.identify(any()) } just Runs every { csqWrapperSpy.resetIdentity() } just Runs every { legacyWrapperSpy.resetIdentity() } just Runs every { csqWrapperSpy.stop() } just Runs every { legacyWrapperSpy.stop() } just Runs every { csqWrapperSpy.pauseTracking() } just Runs every { legacyWrapperSpy.pauseTracking() } just Runs every { csqWrapperSpy.resumeTracking() } just Runs every { legacyWrapperSpy.resumeTracking() } just Runs every { csqWrapperSpy.addDynamicVar(any(), any()) } just Runs every { legacyWrapperSpy.addDynamicVar(any(), any()) } just Runs every { csqWrapperSpy.addDynamicVar(any(), any()) } just Runs every { legacyWrapperSpy.addDynamicVar(any(), any()) } just Runs every { csqWrapperSpy.sendUserIdentifier(any()) } just Runs every { legacyWrapperSpy.sendUserIdentifier(any()) } just Runs every { csqWrapperSpy.trackScreenView(any(), any(), any()) } just Runs every { legacyWrapperSpy.trackScreenView(any(), any(), any()) } just Runs every { csqWrapperSpy.trackTransaction(any(), any(), any()) } just Runs every { legacyWrapperSpy.trackTransaction(any(), any(), any()) } just Runs every { csqWrapperSpy.trackTransaction(any(), any(), any()) } just Runs every { legacyWrapperSpy.trackTransaction(any(), any(), any()) } just Runs every { csqWrapperSpy.trackEvent(any(), any(), any()) } just Runs every { legacyWrapperSpy.trackEvent(any(), any(), any()) } just Runs every { csqWrapperSpy.setDefaultMasking(any()) } just Runs every { legacyWrapperSpy.setDefaultMasking(any()) } just Runs every { csqWrapperSpy.addEventProperties(any()) } just Runs every { legacyWrapperSpy.addEventProperties(any()) } just Runs every { csqWrapperSpy.removeEventProperty(any()) } just Runs every { legacyWrapperSpy.removeEventProperty(any()) } just Runs every { csqWrapperSpy.clearEventProperties() } just Runs every { legacyWrapperSpy.clearEventProperties() } just Runs every { csqWrapperSpy.addUserProperties(any()) } just Runs every { legacyWrapperSpy.addUserProperties(any()) } just Runs every { csqWrapperSpy.setUrlMaskingPatterns(any()) } just Runs every { legacyWrapperSpy.setUrlMaskingPatterns(any()) } just Runs every { csqWrapperSpy.triggerNativeCrash() } just Runs every { legacyWrapperSpy.triggerNativeCrash() } just Runs every { csqWrapperSpy.setOnMetadataChange() } just Runs every { legacyWrapperSpy.setOnMetadataChange() } just Runs } @After fun tearDown() { clearAllMocks() } @Test fun `Given a SDK, When inject, Then should inject JS interface`() { // Clear previous calls from setup clearMocks(mockReactNativeUiThreadUtil, answers = false) val runnableSlot = slot() every { mockReactNativeUiThreadUtil.runOnUiThread(capture(runnableSlot)) } answers { runnableSlot.captured.run() } every { mockWebViewInjector.injectWebView(any(), any()) } just Runs // when module.injectWebView(mockWebViewTag) verify(exactly = 1) { mockReactNativeUiThreadUtil.runOnUiThread(withArg { assertTrue(it is Runnable) // then verify { mockWebViewInjector.injectWebView( eq(mockReactApplicationContext), eq(mockWebViewTag) ) } }) } } @Test fun `Given a SDK, When remove, Then should remove JS interface`() { // Clear previous calls from setup clearMocks(mockReactNativeUiThreadUtil, answers = false) val runnableSlot = slot() every { mockReactNativeUiThreadUtil.runOnUiThread(capture(runnableSlot)) } answers { runnableSlot.captured.run() } every { mockWebViewInjector.removeWebViewInjection(any(), any()) } just Runs // given // when module.removeWebViewInjection(mockWebViewTag) verify(exactly = 1) { mockReactNativeUiThreadUtil.runOnUiThread(withArg { assertTrue(it is Runnable) // then verify { mockWebViewInjector.removeWebViewInjection( eq(mockReactApplicationContext), eq(mockWebViewTag) ) } }) } } @Test fun `Given a SDK, when setting session replay link change, then it should register the callback`() { // Clear previous calls from setup clearMocks(mockReactNativeUiThreadUtil, answers = false) every { Contentsquare.onSessionReplayLinkChange(any()) } just runs val runnableSlot = slot() every { mockReactNativeUiThreadUtil.runOnUiThread(capture(runnableSlot)) } answers { runnableSlot.captured.run() } module.setOnSessionReplayLinkChange() verify(exactly = 1) { mockReactNativeUiThreadUtil.runOnUiThread(withArg { assertTrue(it is Runnable) verify(exactly = 1) { Contentsquare.onSessionReplayLinkChange(any()) } }) } } @Test fun `Given a SDK, when session replay link change, then it should emit event`() { // Clear previous calls from setup clearMocks(mockReactNativeUiThreadUtil, answers = false) val testUrl = "testurl.com" val captureCallback = slot>() val captureData = slot() val runnableSlot = slot() every { mockReactNativeUiThreadUtil.runOnUiThread(capture(runnableSlot)) } answers { runnableSlot.captured.run() } every { Contentsquare.onSessionReplayLinkChange(capture(captureCallback)) } answers { captureCallback.captured.accept(testUrl) } every { mockCSEventEmitter.sendSessionReplayLink(any()) } just runs module.setOnSessionReplayLinkChange() verify(exactly = 1) { mockCSEventEmitter.sendSessionReplayLink(capture(captureData)) } verify(exactly = 1) { mockReactNativeUiThreadUtil.runOnUiThread(withArg { assertTrue(it is Runnable) verify(exactly = 1) { mockCSEventEmitter.sendSessionReplayLink(capture(captureData)) } }) } captureData.captured.let { url -> assertEquals(testUrl, url) } } @Test fun `Given isCSQEnabled is true, when start is called, then CSQTransitionWrapper is used`() { // Run the test module.start(true) // Verify verify(exactly = 1) { csqWrapperSpy.start() } verify(exactly = 0) { legacyWrapperSpy.start() } } @Test fun `Given isCSQEnabled is false, when start is called, then LegacyTransitionWrapper is used`() { // Run the test module.start(false) // Verify verify(exactly = 0) { csqWrapperSpy.start() } verify(exactly = 1) { legacyWrapperSpy.start() } } @Test fun `Given configureProductAnalytics is called, then it should switch to CSQTransitionWrapper and call configureProductAnalytics`() { val envId = "test-env-id" val mockProductAnalyticsOptions = mockk() every { csqWrapperSpy.configureProductAnalytics(any(), any()) } just Runs module.configureProductAnalytics(envId, mockProductAnalyticsOptions) // Assert verify(exactly = 1) { csqWrapperSpy.configureProductAnalytics(eq(envId), eq(mockProductAnalyticsOptions)) } } @Test fun `Given isCSQEnabled is true, when optIn is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.optIn() // then verify(exactly = 1) { csqWrapperSpy.optIn() } } @Test fun `Given isCSQEnabled is false, when optIn is called, then LegacyTransitionWrapper is used`() { // Run the test module.optIn() // Verify verify(exactly = 0) { csqWrapperSpy.optIn() } verify(exactly = 1) { legacyWrapperSpy.optIn() } } @Test fun `Given isCSQEnabled is true, when optOut is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.optOut() // then verify(exactly = 1) { csqWrapperSpy.optOut() } } @Test fun `Given isCSQEnabled is false, when optOut is called, then LegacyTransitionWrapper is used`() { // Run the test module.optOut() // Verify verify(exactly = 0) { csqWrapperSpy.optOut() } verify(exactly = 1) { legacyWrapperSpy.optOut() } } @Test fun `Given isCSQEnabled is true, when stop is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.stop() // then verify(exactly = 1) { csqWrapperSpy.stop() } } @Test fun `Given isCSQEnabled is false, when stop is called, then LegacyTransitionWrapper is used`() { // when module.stop() // then verify(exactly = 0) { csqWrapperSpy.stop() } verify(exactly = 1) { legacyWrapperSpy.stop() } } @Test fun `Given isCSQEnabled is true, when pauseTracking is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.pauseTracking() // then verify(exactly = 1) { csqWrapperSpy.pauseTracking() } } @Test fun `Given isCSQEnabled is false, when pauseTracking is called, then LegacyTransitionWrapper is used`() { // when module.pauseTracking() // then verify(exactly = 0) { csqWrapperSpy.pauseTracking() } verify(exactly = 1) { legacyWrapperSpy.pauseTracking() } } @Test fun `Given isCSQEnabled is true, when resumeTracking is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.resumeTracking() // then verify(exactly = 1) { csqWrapperSpy.resumeTracking() } } @Test fun `Given isCSQEnabled is false, when resumeTracking is called, then LegacyTransitionWrapper is used`() { // when module.resumeTracking() // then verify(exactly = 0) { csqWrapperSpy.resumeTracking() } verify(exactly = 1) { legacyWrapperSpy.resumeTracking() } } @Test fun `Given isCSQEnabled is true, when addDynamicNumberVar is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testKey = "testKey" val testValue = 123.45 // when module.addDynamicNumberVar(testKey, testValue) // then verify(exactly = 1) { csqWrapperSpy.addDynamicVar(testKey, testValue) } } @Test fun `Given isCSQEnabled is false, when addDynamicNumberVar is called, then LegacyTransitionWrapper is used`() { // given val testKey = "testKey" val testValue = 123.45 // when module.addDynamicNumberVar(testKey, testValue) // then verify(exactly = 0) { csqWrapperSpy.addDynamicVar(testKey, testValue) } verify(exactly = 1) { legacyWrapperSpy.addDynamicVar(testKey, testValue) } } @Test fun `Given isCSQEnabled is true, when addDynamicStringVar is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testKey = "testKey" val testValue = "testValue" // when module.addDynamicStringVar(testKey, testValue) // then verify(exactly = 1) { csqWrapperSpy.addDynamicVar(testKey, testValue) } } @Test fun `Given isCSQEnabled is false, when addDynamicStringVar is called, then LegacyTransitionWrapper is used`() { // given val testKey = "testKey" val testValue = "testValue" // when module.addDynamicStringVar(testKey, testValue) // then verify(exactly = 0) { csqWrapperSpy.addDynamicVar(testKey, testValue) } verify(exactly = 1) { legacyWrapperSpy.addDynamicVar(testKey, testValue) } } @Test fun `Given isCSQEnabled is true, when identify is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testIdentity = "test-user-123" // when module.identify(testIdentity) // then verify(exactly = 1) { csqWrapperSpy.identify(testIdentity) } verify(exactly = 0) { legacyWrapperSpy.identify(testIdentity) } } @Test fun `Given isCSQEnabled is false, when identify is called, then LegacyTransitionWrapper is used`() { // given val testIdentity = "test-user-123" // when module.identify(testIdentity) // then verify(exactly = 0) { csqWrapperSpy.identify(testIdentity) } verify(exactly = 1) { legacyWrapperSpy.identify(testIdentity) } } @Test fun `Given isCSQEnabled is true, when resetIdentity is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.resetIdentity() // then verify(exactly = 1) { csqWrapperSpy.resetIdentity() } verify(exactly = 0) { legacyWrapperSpy.resetIdentity() } } @Test fun `Given isCSQEnabled is false, when resetIdentity is called, then LegacyTransitionWrapper is used`() { // when module.resetIdentity() // then verify(exactly = 0) { csqWrapperSpy.resetIdentity() } verify(exactly = 1) { legacyWrapperSpy.resetIdentity() } } @Test fun `Given isCSQEnabled is true, when sendUserIdentifier is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testUserIdentifier = "test-user-123" // when module.sendUserIdentifier(testUserIdentifier) // then verify(exactly = 1) { csqWrapperSpy.sendUserIdentifier(testUserIdentifier) } verify(exactly = 0) { legacyWrapperSpy.sendUserIdentifier(testUserIdentifier) } } @Test fun `Given isCSQEnabled is false, when sendUserIdentifier is called, then LegacyTransitionWrapper is used`() { // given val testUserIdentifier = "test-user-123" // when module.sendUserIdentifier(testUserIdentifier) // then verify(exactly = 0) { csqWrapperSpy.sendUserIdentifier(testUserIdentifier) } verify(exactly = 1) { legacyWrapperSpy.sendUserIdentifier(testUserIdentifier) } } @Test fun `Given isCSQEnabled is true, when trackScreenView is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testScreenName = "TestScreen" val mockCustomVars = mockk(relaxed = true) val mockSourceInfo = mockk(relaxed = true) // when module.trackScreenView(testScreenName, mockCustomVars, mockSourceInfo) // then verify(exactly = 1) { csqWrapperSpy.trackScreenView(testScreenName, mockCustomVars, mockSourceInfo) } verify(exactly = 0) { legacyWrapperSpy.trackScreenView(testScreenName, mockCustomVars, mockSourceInfo) } } @Test fun `Given isCSQEnabled is false, when trackScreenView is called, then LegacyTransitionWrapper is used`() { // given val testScreenName = "TestScreen" val mockCustomVars = mockk(relaxed = true) val mockSourceInfo = mockk(relaxed = true) // when module.trackScreenView(testScreenName, mockCustomVars, mockSourceInfo) // then verify(exactly = 0) { csqWrapperSpy.trackScreenView(testScreenName, mockCustomVars, mockSourceInfo) } verify(exactly = 1) { legacyWrapperSpy.trackScreenView(testScreenName, mockCustomVars, mockSourceInfo) } } @Test fun `Given isCSQEnabled is true, when trackTransaction with integer currency is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testId = "test-transaction-id" val testValue = 50.5f val testCurrency = 978 // EUR // when module.trackTransaction(testId, testValue, testCurrency) // then verify(exactly = 1) { csqWrapperSpy.trackTransaction(testId, testValue, testCurrency) } verify(exactly = 0) { legacyWrapperSpy.trackTransaction(testId, testValue, testCurrency) } } @Test fun `Given isCSQEnabled is false, when trackTransaction with integer currency is called, then LegacyTransitionWrapper is used`() { // given val testId = "test-transaction-id" val testValue = 50.5f val testCurrency = 978 // EUR // when module.trackTransaction(testId, testValue, testCurrency) // then verify(exactly = 0) { csqWrapperSpy.trackTransaction(testId, testValue, testCurrency) } verify(exactly = 1) { legacyWrapperSpy.trackTransaction(testId, testValue, testCurrency) } } @Test fun `Given isCSQEnabled is true, when trackTransaction with string currency is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testId = "test-transaction-id" val testValue = 25.99f val testCurrency = "EUR" // when module.trackTransactionWithStringCurrency(testId, testValue, testCurrency) // then verify(exactly = 1) { csqWrapperSpy.trackTransaction(testId, testValue, testCurrency) } verify(exactly = 0) { legacyWrapperSpy.trackTransaction(testId, testValue, testCurrency) } } @Test fun `Given isCSQEnabled is false, when trackTransaction with string currency is called, then LegacyTransitionWrapper is used`() { // given val testId = "test-transaction-id" val testValue = 25.99f val testCurrency = "EUR" // when module.trackTransactionWithStringCurrency(testId, testValue, testCurrency) // then verify(exactly = 0) { csqWrapperSpy.trackTransaction(testId, testValue, testCurrency) } verify(exactly = 1) { legacyWrapperSpy.trackTransaction(testId, testValue, testCurrency) } } @Test fun `Given isCSQEnabled is true, when trackEvent is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testEvent = "button_click" val mockProperties = mockk(relaxed = true) // when module.trackEvent(testEvent, mockProperties, null) // then verify(exactly = 1) { csqWrapperSpy.trackEvent(testEvent, mockProperties, null) } verify(exactly = 0) { legacyWrapperSpy.trackEvent(testEvent, mockProperties, null) } } @Test fun `Given isCSQEnabled is false, when trackEvent is called, then LegacyTransitionWrapper is used`() { // given val testEvent = "button_click" val mockProperties = mockk(relaxed = true) // when module.trackEvent(testEvent, mockProperties, null) // then verify(exactly = 0) { csqWrapperSpy.trackEvent(testEvent, mockProperties, null) } verify(exactly = 1) { legacyWrapperSpy.trackEvent(testEvent, mockProperties, null) } } @Test fun `Given isCSQEnabled is true, when trackEvent is called with null properties, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val testEvent = "page_view" // when module.trackEvent(testEvent, null, null) // then verify(exactly = 1) { csqWrapperSpy.trackEvent(testEvent, null, null) } verify(exactly = 0) { legacyWrapperSpy.trackEvent(testEvent, null, null) } } @Test fun `Given isCSQEnabled is false, when trackEvent is called with null properties, then LegacyTransitionWrapper is used`() { // given val testEvent = "page_view" // when module.trackEvent(testEvent, null, null) // then verify(exactly = 0) { csqWrapperSpy.trackEvent(testEvent, null, null) } verify(exactly = 1) { legacyWrapperSpy.trackEvent(testEvent, null, null) } } @Test fun `Given isCSQEnabled is true, when setDefaultMasking is called, then CSQTransitionWrapper is used`() { // given val isMasked = true // given - start with CSQ enabled to switch implementation module.start(true) // when module.setDefaultMasking(isMasked) // then verify(exactly = 1) { csqWrapperSpy.setDefaultMasking(isMasked) } verify(exactly = 0) { legacyWrapperSpy.setDefaultMasking(isMasked) } } @Test fun `Given isCSQEnabled is false, when setDefaultMasking is called, then LegacyTransitionWrapper is used`() { // given val isMasked = true // when module.setDefaultMasking(isMasked) // then verify(exactly = 0) { csqWrapperSpy.setDefaultMasking(isMasked) } verify(exactly = 1) { legacyWrapperSpy.setDefaultMasking(isMasked) } } @Test fun `Given isCSQEnabled is true, when addEventProperties is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val mockProperties = mockk(relaxed = true) // when module.addEventProperties(mockProperties) // then verify(exactly = 1) { csqWrapperSpy.addEventProperties(mockProperties) } verify(exactly = 0) { legacyWrapperSpy.addEventProperties(mockProperties) } } @Test fun `Given isCSQEnabled is false, when addEventProperties is called, then LegacyTransitionWrapper is used`() { // given val mockProperties = mockk(relaxed = true) // when module.addEventProperties(mockProperties) // then verify(exactly = 0) { csqWrapperSpy.addEventProperties(mockProperties) } verify(exactly = 1) { legacyWrapperSpy.addEventProperties(mockProperties) } } @Test fun `Given isCSQEnabled is true, when removeEventProperty is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val propertyName = "test_property" // when module.removeEventProperty(propertyName) // then verify(exactly = 1) { csqWrapperSpy.removeEventProperty(propertyName) } verify(exactly = 0) { legacyWrapperSpy.removeEventProperty(propertyName) } } @Test fun `Given isCSQEnabled is false, when removeEventProperty is called, then LegacyTransitionWrapper is used`() { // given val propertyName = "test_property" // when module.removeEventProperty(propertyName) // then verify(exactly = 0) { csqWrapperSpy.removeEventProperty(propertyName) } verify(exactly = 1) { legacyWrapperSpy.removeEventProperty(propertyName) } } @Test fun `Given isCSQEnabled is true, when clearEventProperties is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.clearEventProperties() // then verify(exactly = 1) { csqWrapperSpy.clearEventProperties() } verify(exactly = 0) { legacyWrapperSpy.clearEventProperties() } } @Test fun `Given isCSQEnabled is false, when clearEventProperties is called, then LegacyTransitionWrapper is used`() { // when module.clearEventProperties() // then verify(exactly = 0) { csqWrapperSpy.clearEventProperties() } verify(exactly = 1) { legacyWrapperSpy.clearEventProperties() } } @Test fun `Given isCSQEnabled is true, when addUserProperties is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val mockProperties = mockk(relaxed = true) // when module.addUserProperties(mockProperties) // then verify(exactly = 1) { csqWrapperSpy.addUserProperties(mockProperties) } verify(exactly = 0) { legacyWrapperSpy.addUserProperties(mockProperties) } } @Test fun `Given isCSQEnabled is false, when addUserProperties is called, then LegacyTransitionWrapper is used`() { // given val mockProperties = mockk(relaxed = true) // when module.addUserProperties(mockProperties) // then verify(exactly = 0) { csqWrapperSpy.addUserProperties(mockProperties) } verify(exactly = 1) { legacyWrapperSpy.addUserProperties(mockProperties) } } @Test fun `Given isCSQEnabled is true, when setUrlMaskingPatterns is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) val mockPatterns = mockk(relaxed = true) // when module.setUrlMaskingPatterns(mockPatterns) // then verify(exactly = 1) { csqWrapperSpy.setUrlMaskingPatterns(mockPatterns) } verify(exactly = 0) { legacyWrapperSpy.setUrlMaskingPatterns(mockPatterns) } } @Test fun `Given isCSQEnabled is false, when setUrlMaskingPatterns is called, then LegacyTransitionWrapper is used`() { // given val mockPatterns = mockk(relaxed = true) // when module.setUrlMaskingPatterns(mockPatterns) // then verify(exactly = 0) { csqWrapperSpy.setUrlMaskingPatterns(mockPatterns) } verify(exactly = 1) { legacyWrapperSpy.setUrlMaskingPatterns(mockPatterns) } } @Test fun `Given isCSQEnabled is true, when triggerNativeCrash is called, then CSQTransitionWrapper is used`() { // given - start with CSQ enabled to switch implementation module.start(true) // when module.triggerNativeCrash() // then verify(exactly = 1) { csqWrapperSpy.triggerNativeCrash() } verify(exactly = 0) { legacyWrapperSpy.triggerNativeCrash() } } @Test fun `Given isCSQEnabled is false, when triggerNativeCrash is called, then LegacyTransitionWrapper is used`() { // when module.triggerNativeCrash() // then verify(exactly = 0) { csqWrapperSpy.triggerNativeCrash() } verify(exactly = 1) { legacyWrapperSpy.triggerNativeCrash() } } @Test fun `Given isCSQEnabled is true, when setOnMetadataChange is called, then CSQTransitionWrapper should set up metadata listener`() { // given - start with CSQ enabled to switch implementation module.start(true) // Configure the spy to have the setOnMetadataChange method every { csqWrapperSpy.setOnMetadataChange() } just Runs // when csqWrapperSpy.setOnMetadataChange() // then verify(exactly = 1) { csqWrapperSpy.setOnMetadataChange() } verify(exactly = 0) { legacyWrapperSpy.setOnMetadataChange() } } @Test fun `Given isCSQEnabled is false, when setOnMetadataChange is called, then LegacyTransitionWrapper should handle metadata change`() { // Configure the spy to have the setOnMetadataChange method every { legacyWrapperSpy.setOnMetadataChange() } just Runs // when legacyWrapperSpy.setOnMetadataChange() // then verify(exactly = 0) { csqWrapperSpy.setOnMetadataChange() } verify(exactly = 1) { legacyWrapperSpy.setOnMetadataChange() } } }