From 95f7d5df66fee54c0bcfa5947803abeebe264c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <β€œayyanchira.akshay@gmail.com”> Date: Sun, 2 Nov 2025 19:12:53 -0800 Subject: [PATCH 1/2] [SDK-16] BCIT EM Introduced new Test like for inapp. 1. Instead of campaign trigger, this test updates the user with `isPremium:true` value. Based on this, a live campaign (assumed to be running endlessly on server) will have this user eligible. 2. Hardcoded PlacementID value is used. 3. EmbeddedTestActivity and its corresponding xml is now updated to be able to manually test the functionality as well. --- .../tests/EmbeddedMessageIntegrationTest.kt | 289 ++++++++++++++++++ .../integration/tests/TestConstants.kt | 3 + .../activities/EmbeddedMessageTestActivity.kt | 170 ++++++++++- .../layout/activity_embedded_message_test.xml | 90 ++++-- 4 files changed, 531 insertions(+), 21 deletions(-) create mode 100644 integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt diff --git a/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt b/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt new file mode 100644 index 000000000..2a8901ee9 --- /dev/null +++ b/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt @@ -0,0 +1,289 @@ +package com.iterable.integration.tests + +import android.content.Intent +import android.util.Log +import androidx.lifecycle.Lifecycle +import androidx.test.core.app.ActivityScenario +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.runner.lifecycle.ActivityLifecycleMonitorRegistry +import androidx.test.runner.lifecycle.Stage +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiSelector +import androidx.test.uiautomator.By +import com.iterable.iterableapi.IterableApi +import com.iterable.iterableapi.IterableEmbeddedMessage +import com.iterable.integration.tests.activities.EmbeddedMessageTestActivity +import com.iterable.iterableapi.ui.embedded.IterableEmbeddedView +import com.iterable.iterableapi.ui.embedded.IterableEmbeddedViewType +import org.awaitility.Awaitility +import org.json.JSONObject +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.util.concurrent.TimeUnit + +@RunWith(AndroidJUnit4::class) +class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { + + companion object { + private const val TAG = "EmbeddedMessageIntegrationTest" + private const val TEST_PLACEMENT_ID = TestConstants.TEST_EMBEDDED_PLACEMENT_ID + } + + private lateinit var uiDevice: UiDevice + private lateinit var mainActivityScenario: ActivityScenario + private lateinit var embeddedActivityScenario: ActivityScenario + + @Before + override fun setUp() { + Log.d(TAG, "πŸ”§ Test setup starting...") + + uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + // Call super.setUp() to initialize SDK with BaseIntegrationTest's config + // This sets test mode flag and initializes SDK with test handlers (including urlHandler) + super.setUp() + + Log.d(TAG, "πŸ”§ Base setup complete, SDK initialized with test handlers") + + // Disable in-app auto display and clear existing messages BEFORE launching app + // This prevents in-app messages from obscuring the embedded message test screen + Log.d(TAG, "πŸ”§ Disabling in-app auto display and clearing existing messages...") + IterableApi.getInstance().inAppManager.setAutoDisplayPaused(true) + Log.d(TAG, "βœ… In-app auto display paused") + + // Clear all existing in-app messages + IterableApi.getInstance().inAppManager.messages.forEach { + Log.d(TAG, "Clearing existing in-app message: ${it.messageId}") + IterableApi.getInstance().inAppManager.removeMessage(it) + } + Log.d(TAG, "βœ… All in-app messages cleared") + + Log.d(TAG, "πŸ”§ MainActivity will skip initialization due to test mode flag") + + // Now launch the app flow with custom handlers already configured + launchAppAndNavigateToEmbeddedTesting() + } + + @After + override fun tearDown() { + super.tearDown() + } + + private fun launchAppAndNavigateToEmbeddedTesting() { + // Step 1: Launch MainActivity (the home page) + Log.d(TAG, "πŸ”§ Step 1: Launching MainActivity...") + val mainIntent = Intent(InstrumentationRegistry.getInstrumentation().targetContext, MainActivity::class.java) + mainActivityScenario = ActivityScenario.launch(mainIntent) + + // Wait for MainActivity to be ready + Awaitility.await() + .atMost(5, TimeUnit.SECONDS) + .pollInterval(500, TimeUnit.MILLISECONDS) + .until { + val state = mainActivityScenario.state + Log.d(TAG, "πŸ”§ MainActivity state: $state") + state == Lifecycle.State.RESUMED + } + + Log.d(TAG, "πŸ”§ MainActivity is ready!") + + // Step 2: Click the "Embedded Messages" button to navigate to EmbeddedMessageTestActivity + Log.d(TAG, "πŸ”§ Step 2: Clicking 'Embedded Messages' button...") + val embeddedButton = uiDevice.findObject(UiSelector().resourceId("com.iterable.integration.tests:id/btnEmbeddedMessages")) + if (embeddedButton.exists()) { + embeddedButton.click() + Log.d(TAG, "πŸ”§ Clicked Embedded Messages button successfully") + } else { + Log.e(TAG, "❌ Embedded Messages button not found!") + Assert.fail("Embedded Messages button not found in MainActivity") + } + + // Step 3: Wait for EmbeddedMessageTestActivity to load + Log.d(TAG, "πŸ”§ Step 3: Waiting for EmbeddedMessageTestActivity to load...") + Thread.sleep(2000) // Give time for navigation + + Log.d(TAG, "πŸ”§ App navigation complete: Now on EmbeddedMessageTestActivity!") + } + + @Test + fun testEmbeddedMessageMVP() { + Log.d(TAG, "πŸš€ Starting MVP embedded message test") + + // Step 1: Ensure user is signed in + Log.d(TAG, "πŸ“§ Step 1: Ensuring user is signed in...") + val userSignedIn = testUtils.ensureUserSignedIn(TestConstants.TEST_USER_EMAIL) + Assert.assertTrue("User should be signed in", userSignedIn) + Log.d(TAG, "βœ… User signed in successfully: ${TestConstants.TEST_USER_EMAIL}") + + // Step 2: Preliminary check - verify view is ready with placement ID + Log.d(TAG, "πŸ” Step 2: Checking view readiness with placement ID...") + var viewReady = false + InstrumentationRegistry.getInstrumentation().runOnMainSync { + val activity = ActivityLifecycleMonitorRegistry.getInstance() + .getActivitiesInStage(Stage.RESUMED) + .firstOrNull() as? EmbeddedMessageTestActivity + + activity?.let { + val fragmentContainer = it.findViewById(R.id.embedded_message_container) + viewReady = fragmentContainer != null + if (viewReady) { + Log.d(TAG, "βœ… View is ready with placementID - $TEST_PLACEMENT_ID") + } + } + } + Assert.assertTrue("FragmentContainerView should exist in EmbeddedMessageTestActivity", viewReady) + + // Step 3: Update user properties to make user eligible + Log.d(TAG, "πŸ“ Step 3: Updating user properties (isPremium = true)...") + val dataFields = JSONObject().apply { + put("isPremium", true) + } + IterableApi.getInstance().updateUser(dataFields) + Log.d(TAG, "βœ… User properties updated") + + // Step 4: Wait 5 seconds for backend to process and make user eligible + Log.d(TAG, "⏳ Step 4: Waiting 5 seconds for backend to process user update...") + Thread.sleep(5000) + + // Step 5: Manually sync embedded messages + Log.d(TAG, "πŸ”„ Step 5: Syncing embedded messages...") + IterableApi.getInstance().embeddedManager.syncMessages() + + // Wait for sync to complete + Thread.sleep(3000) + + // Step 6: Get placement IDs and verify expected placement ID exists + Log.d(TAG, "πŸ” Step 6: Getting placement IDs...") + val placementIds = IterableApi.getInstance().embeddedManager.getPlacementIds() + Log.d(TAG, "πŸ“‹ Found placement IDs: $placementIds") + + Assert.assertTrue( + "Placement ID $TEST_PLACEMENT_ID should exist, but found: $placementIds", + placementIds.contains(TEST_PLACEMENT_ID) + ) + Log.d(TAG, "βœ… Placement ID $TEST_PLACEMENT_ID found") + + // Step 7: Get messages for the placement ID + Log.d(TAG, "πŸ“¨ Step 7: Getting messages for placement ID $TEST_PLACEMENT_ID...") + val messages = IterableApi.getInstance().embeddedManager.getMessages(TEST_PLACEMENT_ID) + Assert.assertNotNull("Messages should not be null", messages) + Assert.assertTrue("Should have at least 1 message for placement $TEST_PLACEMENT_ID", messages!!.isNotEmpty()) + + val message = messages.first() + Log.d(TAG, "βœ… Found message: ${message.metadata.messageId}") + + // Step 8: Display message using IterableEmbeddedView + Log.d(TAG, "🎨 Step 8: Displaying message using IterableEmbeddedView...") + + InstrumentationRegistry.getInstrumentation().runOnMainSync { + val activity = ActivityLifecycleMonitorRegistry.getInstance() + .getActivitiesInStage(Stage.RESUMED) + .firstOrNull() as? EmbeddedMessageTestActivity + + if (activity != null) { + val fragment = IterableEmbeddedView(IterableEmbeddedViewType.BANNER, message, null) + activity.supportFragmentManager.beginTransaction() + .replace(R.id.embedded_message_container, fragment) + .commitNow() + Log.d(TAG, "βœ… Fragment added to FragmentManager") + } else { + Assert.fail("EmbeddedMessageTestActivity not found in RESUMED stage") + } + } + + // Wait for fragment to be displayed + Thread.sleep(2000) + + // Step 9: Verify display - check fragment exists + Log.d(TAG, "βœ… Step 9: Verifying embedded message is displayed...") + var isEmbeddedFragmentDisplayed = false + + InstrumentationRegistry.getInstrumentation().runOnMainSync { + val activity = ActivityLifecycleMonitorRegistry.getInstance() + .getActivitiesInStage(Stage.RESUMED) + .firstOrNull() as? EmbeddedMessageTestActivity + + activity?.let { act -> + val fragmentManager = act.supportFragmentManager + fragmentManager.fragments.forEach { fragment -> + if (fragment is IterableEmbeddedView) { + isEmbeddedFragmentDisplayed = true + Log.d(TAG, "βœ… Found IterableEmbeddedView fragment") + } + } + } + } + + Assert.assertTrue( + "IterableEmbeddedView fragment should be displayed", + isEmbeddedFragmentDisplayed + ) + + Log.d(TAG, "βœ… Embedded message is displayed, now interacting with button...") + + // Step 10: Interact with button - find and click first button + Log.d(TAG, "🎯 Step 10: Clicking button in the embedded message...") + + // Try to find button by resource ID or text + var buttonClicked = false + var attempts = 0 + val maxAttempts = 5 + + while (!buttonClicked && attempts < maxAttempts) { + attempts++ + Log.d(TAG, "Attempt $attempts: Looking for button...") + + // Try to find button by resource ID + val button = uiDevice.findObject(UiSelector().resourceId("com.iterable.iterableapi.ui:id/embedded_message_first_button")) + + if (button.exists()) { + button.click() + buttonClicked = true + Log.d(TAG, "βœ… Clicked embedded message button") + } else { + // Try to find by button text if available + val buttonText = message.elements?.buttons?.firstOrNull()?.title + if (buttonText != null) { + val buttonByText = uiDevice.findObject(By.text(buttonText)) + if (buttonByText != null) { + buttonByText.click() + buttonClicked = true + Log.d(TAG, "βœ… Clicked embedded message button by text: $buttonText") + } + } + } + + if (!buttonClicked) { + Log.d(TAG, "Button not found, waiting 1 second before retry...") + Thread.sleep(1000) + } + } + + if (!buttonClicked) { + Assert.fail("Button not found in the embedded message after $maxAttempts attempts") + } + + // Step 11: Verify URL handler was called + Log.d(TAG, "🎯 Step 11: Verifying URL handler was called after button click...") + + val urlHandlerCalled = waitForUrlHandler(timeoutSeconds = 5) + Assert.assertTrue( + "URL handler should have been called after clicking the button", + urlHandlerCalled + ) + + // Step 12: Verify the correct URL was handled + val handledUrl = getLastHandledUrl() + Log.d(TAG, "🎯 URL handler received: $handledUrl") + + Assert.assertNotNull("Handled URL should not be null", handledUrl) + Log.d(TAG, "βœ… URL handler was called with URL: $handledUrl") + + Log.d(TAG, "βœ…βœ…βœ… Test completed successfully! All steps passed.") + } +} + diff --git a/integration-tests/src/main/java/com/iterable/integration/tests/TestConstants.kt b/integration-tests/src/main/java/com/iterable/integration/tests/TestConstants.kt index 28a58a480..80f2464e0 100644 --- a/integration-tests/src/main/java/com/iterable/integration/tests/TestConstants.kt +++ b/integration-tests/src/main/java/com/iterable/integration/tests/TestConstants.kt @@ -15,6 +15,9 @@ object TestConstants { const val TEST_PUSH_CAMPAIGN_ID = 14332358 const val TEST_EMBEDDED_CAMPAIGN_ID = 14332359 + // Test placement IDs + const val TEST_EMBEDDED_PLACEMENT_ID = 2157L + // Test timeouts const val TIMEOUT_SECONDS = 5L const val POLL_INTERVAL_SECONDS = 1L diff --git a/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt b/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt index 73754d054..639b4496e 100644 --- a/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt +++ b/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt @@ -2,8 +2,18 @@ package com.iterable.integration.tests.activities import android.os.Bundle import android.util.Log +import android.widget.Button +import android.widget.Switch +import android.widget.TextView +import android.widget.Toast +import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity +import com.iterable.iterableapi.IterableApi +import com.iterable.iterableapi.IterableEmbeddedMessage import com.iterable.integration.tests.R +import com.iterable.iterableapi.ui.embedded.IterableEmbeddedView +import com.iterable.iterableapi.ui.embedded.IterableEmbeddedViewType +import org.json.JSONObject class EmbeddedMessageTestActivity : AppCompatActivity() { @@ -11,12 +21,170 @@ class EmbeddedMessageTestActivity : AppCompatActivity() { private const val TAG = "EmbeddedMessageTest" } + private lateinit var statusTextView: TextView + private lateinit var checkIsPremiumButton: Button + private lateinit var isPremiumSwitch: Switch + private lateinit var syncMessagesButton: Button + + // Track current isPremium state locally (since SDK doesn't store it) + private var currentIsPremium = false + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_embedded_message_test) Log.d(TAG, "Embedded Message Test Activity started") - // TODO: Implement embedded message test UI and logic + initializeViews() + setupClickListeners() + updateStatus("Activity initialized - Ready to test embedded messages") + } + + private fun initializeViews() { + statusTextView = findViewById(R.id.status_text) + checkIsPremiumButton = findViewById(R.id.btnCheckIsPremium) + isPremiumSwitch = findViewById(R.id.switchIsPremium) + syncMessagesButton = findViewById(R.id.btnSyncMessages) + + // Initialize switch to false + isPremiumSwitch.isChecked = currentIsPremium + } + + private fun setupClickListeners() { + checkIsPremiumButton.setOnClickListener { + checkIsPremiumStatus() + } + + isPremiumSwitch.setOnCheckedChangeListener { _, isChecked -> + updateUserIsPremium(isChecked) + } + + syncMessagesButton.setOnClickListener { + syncEmbeddedMessages() + } + } + + private fun checkIsPremiumStatus() { + updateStatus("Checking isPremium status...") + Log.d(TAG, "Checking isPremium status") + + // Note: SDK doesn't store user data fields locally, so we show an info dialog + AlertDialog.Builder(this) + .setTitle("isPremium Status") + .setMessage("User data fields are stored on the server, not in the SDK.\n\n" + + "To check isPremium status, you can:\n" + + "1. Check server logs/dashboard\n" + + "2. Call server API to get user profile\n" + + "3. Check logcat for updateUser calls") + .setPositiveButton("OK", null) + .show() + + Toast.makeText(this, "Check logcat or server dashboard for user data fields", Toast.LENGTH_LONG).show() + updateStatus("Status check: See dialog for details") + } + + private fun updateUserIsPremium(isPremium: Boolean) { + currentIsPremium = isPremium + val statusText = if (isPremium) "true" else "false" + + updateStatus("Updating user (isPremium = $statusText)...") + Log.d(TAG, "Updating user with isPremium = $statusText") + + val dataFields = JSONObject().apply { + put("isPremium", isPremium) + } + + // Disable switch during update + isPremiumSwitch.isEnabled = false + + // Note: updateUser doesn't have callbacks in the current SDK API + // But we can track success/failure by monitoring logs or adding listeners + IterableApi.getInstance().updateUser(dataFields) + + // Show toast + Toast.makeText(this, "updateUser called (isPremium = $statusText)\nWait 5 seconds then sync messages to verify", Toast.LENGTH_LONG).show() + Log.d(TAG, "βœ… updateUser called with isPremium = $statusText") + + // Re-enable switch after delay + isPremiumSwitch.postDelayed({ + isPremiumSwitch.isEnabled = true + updateStatus("User update request sent (isPremium = $statusText) - Sync messages to verify") + }, 1000) + } + + private fun syncEmbeddedMessages() { + updateStatus("Syncing embedded messages...") + Log.d(TAG, "Syncing embedded messages") + + IterableApi.getInstance().embeddedManager.syncMessages() + + // Wait a bit for sync to complete, then show status and display messages + Thread { + Thread.sleep(2000) + + runOnUiThread { + val placementIds = IterableApi.getInstance().embeddedManager.getPlacementIds() + val messageCount = placementIds.sumOf { placementId -> + IterableApi.getInstance().embeddedManager.getMessages(placementId)?.size ?: 0 + } + + val statusMessage = if (messageCount > 0) { + "βœ… Sync complete: Found $messageCount message(s) in ${placementIds.size} placement(s)" + } else { + "⚠️ Sync complete: No messages found. Check user eligibility and campaign configuration" + } + + updateStatus(statusMessage) + Toast.makeText(this, statusMessage, Toast.LENGTH_LONG).show() + Log.d(TAG, statusMessage) + + // Display messages if found + if (messageCount > 0) { + displayEmbeddedMessages(placementIds) + } else { + // Clear any existing fragments + clearEmbeddedMessages() + } + } + }.start() + } + + private fun displayEmbeddedMessages(placementIds: List) { + Log.d(TAG, "Displaying embedded messages for ${placementIds.size} placement(s)") + + // For now, display the first message from the first placement + // In a real scenario, you might want to display all messages or have a placement selector + val firstPlacementId = placementIds.firstOrNull() + if (firstPlacementId != null) { + val messages = IterableApi.getInstance().embeddedManager.getMessages(firstPlacementId) + if (messages != null && messages.isNotEmpty()) { + val firstMessage = messages.first() + Log.d(TAG, "Displaying message: ${firstMessage.metadata.messageId} from placement: $firstPlacementId") + + // Create and add fragment + val fragment = IterableEmbeddedView(IterableEmbeddedViewType.BANNER, firstMessage, null) + supportFragmentManager.beginTransaction() + .replace(R.id.embedded_message_container, fragment) + .commitNowAllowingStateLoss() + + updateStatus("βœ… Message displayed: ${firstMessage.metadata.messageId}") + Log.d(TAG, "βœ… Embedded message fragment added") + } + } + } + + private fun clearEmbeddedMessages() { + val fragment = supportFragmentManager.findFragmentById(R.id.embedded_message_container) + if (fragment != null) { + supportFragmentManager.beginTransaction() + .remove(fragment) + .commitNowAllowingStateLoss() + Log.d(TAG, "Cleared embedded message fragment") + } + } + + private fun updateStatus(status: String) { + statusTextView.text = "Status: $status" + Log.d(TAG, "Status: $status") } } \ No newline at end of file diff --git a/integration-tests/src/main/res/layout/activity_embedded_message_test.xml b/integration-tests/src/main/res/layout/activity_embedded_message_test.xml index 29f5d7d72..43d6762e8 100644 --- a/integration-tests/src/main/res/layout/activity_embedded_message_test.xml +++ b/integration-tests/src/main/res/layout/activity_embedded_message_test.xml @@ -1,25 +1,75 @@ - + android:layout_height="match_parent"> - - - + android:orientation="vertical" + android:padding="16dp"> + + + + + + + + + + + + + + + + + - \ No newline at end of file + + \ No newline at end of file From cc6b5569d703539df61e1f845ced99e4d6d79a50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CAkshay?= <β€œayyanchira.akshay@gmail.com”> Date: Sun, 2 Nov 2025 19:37:38 -0800 Subject: [PATCH 2/2] Optimizations added & removed unnecessary logs --- .../tests/EmbeddedMessageIntegrationTest.kt | 63 +++++--------- .../activities/EmbeddedMessageTestActivity.kt | 84 ++++--------------- 2 files changed, 39 insertions(+), 108 deletions(-) diff --git a/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt b/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt index 2a8901ee9..8be15ab26 100644 --- a/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt +++ b/integration-tests/src/androidTest/java/com/iterable/integration/tests/EmbeddedMessageIntegrationTest.kt @@ -35,7 +35,6 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { private lateinit var uiDevice: UiDevice private lateinit var mainActivityScenario: ActivityScenario - private lateinit var embeddedActivityScenario: ActivityScenario @Before override fun setUp() { @@ -111,8 +110,6 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { @Test fun testEmbeddedMessageMVP() { - Log.d(TAG, "πŸš€ Starting MVP embedded message test") - // Step 1: Ensure user is signed in Log.d(TAG, "πŸ“§ Step 1: Ensuring user is signed in...") val userSignedIn = testUtils.ensureUserSignedIn(TestConstants.TEST_USER_EMAIL) @@ -147,14 +144,14 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { // Step 4: Wait 5 seconds for backend to process and make user eligible Log.d(TAG, "⏳ Step 4: Waiting 5 seconds for backend to process user update...") - Thread.sleep(5000) + Thread.sleep(3000) // Step 5: Manually sync embedded messages Log.d(TAG, "πŸ”„ Step 5: Syncing embedded messages...") IterableApi.getInstance().embeddedManager.syncMessages() // Wait for sync to complete - Thread.sleep(3000) + Thread.sleep(2000) // Step 6: Get placement IDs and verify expected placement ID exists Log.d(TAG, "πŸ” Step 6: Getting placement IDs...") @@ -170,7 +167,6 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { // Step 7: Get messages for the placement ID Log.d(TAG, "πŸ“¨ Step 7: Getting messages for placement ID $TEST_PLACEMENT_ID...") val messages = IterableApi.getInstance().embeddedManager.getMessages(TEST_PLACEMENT_ID) - Assert.assertNotNull("Messages should not be null", messages) Assert.assertTrue("Should have at least 1 message for placement $TEST_PLACEMENT_ID", messages!!.isNotEmpty()) val message = messages.first() @@ -196,7 +192,7 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { } // Wait for fragment to be displayed - Thread.sleep(2000) + Thread.sleep(1000) // Step 9: Verify display - check fragment exists Log.d(TAG, "βœ… Step 9: Verifying embedded message is displayed...") @@ -228,49 +224,32 @@ class EmbeddedMessageIntegrationTest : BaseIntegrationTest() { // Step 10: Interact with button - find and click first button Log.d(TAG, "🎯 Step 10: Clicking button in the embedded message...") - // Try to find button by resource ID or text - var buttonClicked = false - var attempts = 0 - val maxAttempts = 5 + // Try to find button by resource ID first + val button = uiDevice.findObject(UiSelector().resourceId("com.iterable.iterableapi.ui:id/embedded_message_first_button")) - while (!buttonClicked && attempts < maxAttempts) { - attempts++ - Log.d(TAG, "Attempt $attempts: Looking for button...") - - // Try to find button by resource ID - val button = uiDevice.findObject(UiSelector().resourceId("com.iterable.iterableapi.ui:id/embedded_message_first_button")) - - if (button.exists()) { - button.click() - buttonClicked = true - Log.d(TAG, "βœ… Clicked embedded message button") - } else { - // Try to find by button text if available - val buttonText = message.elements?.buttons?.firstOrNull()?.title - if (buttonText != null) { - val buttonByText = uiDevice.findObject(By.text(buttonText)) - if (buttonByText != null) { - buttonByText.click() - buttonClicked = true - Log.d(TAG, "βœ… Clicked embedded message button by text: $buttonText") - } + if (button.exists()) { + button.click() + Log.d(TAG, "βœ… Clicked embedded message button") + } else { + // Try to find by button text if available + val buttonText = message.elements?.buttons?.firstOrNull()?.title + if (buttonText != null) { + val buttonByText = uiDevice.findObject(By.text(buttonText)) + if (buttonByText != null) { + buttonByText.click() + Log.d(TAG, "βœ… Clicked embedded message button by text: $buttonText") + } else { + Assert.fail("Button not found in the embedded message (tried resource ID and text: $buttonText)") } + } else { + Assert.fail("Button not found in the embedded message (tried resource ID, but no button text available)") } - - if (!buttonClicked) { - Log.d(TAG, "Button not found, waiting 1 second before retry...") - Thread.sleep(1000) - } - } - - if (!buttonClicked) { - Assert.fail("Button not found in the embedded message after $maxAttempts attempts") } // Step 11: Verify URL handler was called Log.d(TAG, "🎯 Step 11: Verifying URL handler was called after button click...") - val urlHandlerCalled = waitForUrlHandler(timeoutSeconds = 5) + val urlHandlerCalled = waitForUrlHandler(timeoutSeconds = 3) Assert.assertTrue( "URL handler should have been called after clicking the button", urlHandlerCalled diff --git a/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt b/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt index 639b4496e..a4902ed25 100644 --- a/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt +++ b/integration-tests/src/main/java/com/iterable/integration/tests/activities/EmbeddedMessageTestActivity.kt @@ -1,7 +1,6 @@ package com.iterable.integration.tests.activities import android.os.Bundle -import android.util.Log import android.widget.Button import android.widget.Switch import android.widget.TextView @@ -9,7 +8,6 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import com.iterable.iterableapi.IterableApi -import com.iterable.iterableapi.IterableEmbeddedMessage import com.iterable.integration.tests.R import com.iterable.iterableapi.ui.embedded.IterableEmbeddedView import com.iterable.iterableapi.ui.embedded.IterableEmbeddedViewType @@ -17,27 +15,18 @@ import org.json.JSONObject class EmbeddedMessageTestActivity : AppCompatActivity() { - companion object { - private const val TAG = "EmbeddedMessageTest" - } - private lateinit var statusTextView: TextView private lateinit var checkIsPremiumButton: Button private lateinit var isPremiumSwitch: Switch private lateinit var syncMessagesButton: Button - // Track current isPremium state locally (since SDK doesn't store it) - private var currentIsPremium = false - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_embedded_message_test) - Log.d(TAG, "Embedded Message Test Activity started") - initializeViews() setupClickListeners() - updateStatus("Activity initialized - Ready to test embedded messages") + updateStatus("Ready to test embedded messages") } private fun initializeViews() { @@ -45,9 +34,6 @@ class EmbeddedMessageTestActivity : AppCompatActivity() { checkIsPremiumButton = findViewById(R.id.btnCheckIsPremium) isPremiumSwitch = findViewById(R.id.switchIsPremium) syncMessagesButton = findViewById(R.id.btnSyncMessages) - - // Initialize switch to false - isPremiumSwitch.isChecked = currentIsPremium } private fun setupClickListeners() { @@ -65,60 +51,40 @@ class EmbeddedMessageTestActivity : AppCompatActivity() { } private fun checkIsPremiumStatus() { - updateStatus("Checking isPremium status...") - Log.d(TAG, "Checking isPremium status") - - // Note: SDK doesn't store user data fields locally, so we show an info dialog AlertDialog.Builder(this) .setTitle("isPremium Status") .setMessage("User data fields are stored on the server, not in the SDK.\n\n" + - "To check isPremium status, you can:\n" + + "To check isPremium status:\n" + "1. Check server logs/dashboard\n" + "2. Call server API to get user profile\n" + "3. Check logcat for updateUser calls") .setPositiveButton("OK", null) .show() - - Toast.makeText(this, "Check logcat or server dashboard for user data fields", Toast.LENGTH_LONG).show() - updateStatus("Status check: See dialog for details") } private fun updateUserIsPremium(isPremium: Boolean) { - currentIsPremium = isPremium val statusText = if (isPremium) "true" else "false" - updateStatus("Updating user (isPremium = $statusText)...") - Log.d(TAG, "Updating user with isPremium = $statusText") val dataFields = JSONObject().apply { put("isPremium", isPremium) } - // Disable switch during update isPremiumSwitch.isEnabled = false - - // Note: updateUser doesn't have callbacks in the current SDK API - // But we can track success/failure by monitoring logs or adding listeners IterableApi.getInstance().updateUser(dataFields) - // Show toast - Toast.makeText(this, "updateUser called (isPremium = $statusText)\nWait 5 seconds then sync messages to verify", Toast.LENGTH_LONG).show() - Log.d(TAG, "βœ… updateUser called with isPremium = $statusText") + Toast.makeText(this, "updateUser called (isPremium = $statusText)\nWait 5 seconds then sync messages", Toast.LENGTH_LONG).show() - // Re-enable switch after delay isPremiumSwitch.postDelayed({ isPremiumSwitch.isEnabled = true - updateStatus("User update request sent (isPremium = $statusText) - Sync messages to verify") + updateStatus("User updated (isPremium = $statusText) - Sync messages to verify") }, 1000) } private fun syncEmbeddedMessages() { updateStatus("Syncing embedded messages...") - Log.d(TAG, "Syncing embedded messages") - IterableApi.getInstance().embeddedManager.syncMessages() - // Wait a bit for sync to complete, then show status and display messages Thread { Thread.sleep(2000) @@ -129,20 +95,17 @@ class EmbeddedMessageTestActivity : AppCompatActivity() { } val statusMessage = if (messageCount > 0) { - "βœ… Sync complete: Found $messageCount message(s) in ${placementIds.size} placement(s)" + "βœ… Found $messageCount message(s) in ${placementIds.size} placement(s)" } else { - "⚠️ Sync complete: No messages found. Check user eligibility and campaign configuration" + "⚠️ No messages found. Check user eligibility and campaign configuration" } updateStatus(statusMessage) Toast.makeText(this, statusMessage, Toast.LENGTH_LONG).show() - Log.d(TAG, statusMessage) - // Display messages if found if (messageCount > 0) { displayEmbeddedMessages(placementIds) } else { - // Clear any existing fragments clearEmbeddedMessages() } } @@ -150,41 +113,30 @@ class EmbeddedMessageTestActivity : AppCompatActivity() { } private fun displayEmbeddedMessages(placementIds: List) { - Log.d(TAG, "Displaying embedded messages for ${placementIds.size} placement(s)") + val firstPlacementId = placementIds.firstOrNull() ?: return + val messages = IterableApi.getInstance().embeddedManager.getMessages(firstPlacementId) ?: return - // For now, display the first message from the first placement - // In a real scenario, you might want to display all messages or have a placement selector - val firstPlacementId = placementIds.firstOrNull() - if (firstPlacementId != null) { - val messages = IterableApi.getInstance().embeddedManager.getMessages(firstPlacementId) - if (messages != null && messages.isNotEmpty()) { - val firstMessage = messages.first() - Log.d(TAG, "Displaying message: ${firstMessage.metadata.messageId} from placement: $firstPlacementId") - - // Create and add fragment - val fragment = IterableEmbeddedView(IterableEmbeddedViewType.BANNER, firstMessage, null) - supportFragmentManager.beginTransaction() - .replace(R.id.embedded_message_container, fragment) - .commitNowAllowingStateLoss() - - updateStatus("βœ… Message displayed: ${firstMessage.metadata.messageId}") - Log.d(TAG, "βœ… Embedded message fragment added") - } + if (messages.isNotEmpty()) { + val firstMessage = messages.first() + val fragment = IterableEmbeddedView(IterableEmbeddedViewType.BANNER, firstMessage, null) + supportFragmentManager.beginTransaction() + .replace(R.id.embedded_message_container, fragment) + .commitNowAllowingStateLoss() + + updateStatus("βœ… Message displayed: ${firstMessage.metadata.messageId}") } } private fun clearEmbeddedMessages() { val fragment = supportFragmentManager.findFragmentById(R.id.embedded_message_container) - if (fragment != null) { + fragment?.let { supportFragmentManager.beginTransaction() - .remove(fragment) + .remove(it) .commitNowAllowingStateLoss() - Log.d(TAG, "Cleared embedded message fragment") } } private fun updateStatus(status: String) { statusTextView.text = "Status: $status" - Log.d(TAG, "Status: $status") } } \ No newline at end of file