Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.personalization.demo

import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.scrollTo
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

/**
* On-device E2E for the loyalty demo buttons. Launches [MainActivity] (which performs the real
* production Firebase + SDK initialization) and taps the loyalty buttons, exercising
* `loyaltyManager.join` / `loyaltyManager.getStatus` against the live REES46 API on the emulator.
*
* Mirrors [OrdersDemoE2ETest]: asserts the real on-device code path runs without crashing.
*/
@RunWith(AndroidJUnit4::class)
class LoyaltyIntegrationTest {

@get:Rule
val activityRule = ActivityScenarioRule(MainActivity::class.java)

@Test
fun loyaltyJoin_tapButton_noCrash() {
onView(withId(R.id.btnLoyaltyJoin)).perform(scrollTo(), click())
Thread.sleep(3000)
}

@Test
fun loyaltyStatus_tapButton_noCrash() {
onView(withId(R.id.btnLoyaltyStatus)).perform(scrollTo(), click())
Thread.sleep(3000)
}
}
71 changes: 71 additions & 0 deletions demo-app/src/main/java/com/personalization/demo/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ class MainActivity : AppCompatActivity() {
val SHOP_SECRET = BuildConfig.SHOP_SECRET
}

private object DemoLoyaltyConstants {
const val PHONE = "79991234567"
const val EMAIL = "demo@rees46.ru"
const val FIRST_NAME = "Demo"
const val LAST_NAME = "User"
}

private object DemoProductViewConstants {
const val PRODUCT_ID = "demo-product-view-001"
const val DEMO_PRICE = 2499.99
Expand Down Expand Up @@ -137,6 +144,70 @@ class MainActivity : AppCompatActivity() {
findViewById<Button>(R.id.btnGetUserOrders).setOnClickListener {
getUserOrders()
}

findViewById<Button>(R.id.btnLoyaltyJoin).setOnClickListener {
loyaltyJoin()
}

findViewById<Button>(R.id.btnLoyaltyStatus).setOnClickListener {
loyaltyStatus()
}
}

private fun loyaltyJoin() {
sdk.loyaltyManager.join(
phone = DemoLoyaltyConstants.PHONE,
email = DemoLoyaltyConstants.EMAIL,
firstName = DemoLoyaltyConstants.FIRST_NAME,
lastName = DemoLoyaltyConstants.LAST_NAME,
onSuccess = { response ->
runOnUiThread {
Toast.makeText(
this,
getString(R.string.loyalty_join_ok, response.status ?: "—"),
Toast.LENGTH_LONG,
).show()
}
},
onError = { code, msg ->
runOnUiThread {
Toast.makeText(
this,
"${getString(R.string.loyalty_join_fail)}: $code $msg",
Toast.LENGTH_LONG,
).show()
}
},
)
}

private fun loyaltyStatus() {
sdk.loyaltyManager.getStatus(
identifier = DemoLoyaltyConstants.PHONE,
onSuccess = { response ->
runOnUiThread {
val member = response.payload?.member
val level = response.payload?.level?.name
Toast.makeText(
this,
getString(
R.string.loyalty_status_ok,
"${response.status ?: "—"} (member=$member, level=$level)",
),
Toast.LENGTH_LONG,
).show()
}
},
onError = { code, msg ->
runOnUiThread {
Toast.makeText(
this,
"${getString(R.string.loyalty_status_fail)}: $code $msg",
Toast.LENGTH_LONG,
).show()
}
},
)
}

private fun getUserOrders() {
Expand Down
31 changes: 26 additions & 5 deletions demo-app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">
android:fillViewport="true">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp"
android:gravity="center">

<Button
android:id="@+id/btnShowTestPopup"
Expand Down Expand Up @@ -83,6 +88,22 @@
android:text="@string/get_user_orders"
android:layout_marginTop="16dp" />

</LinearLayout>
<Button
android:id="@+id/btnLoyaltyJoin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loyalty_join"
android:layout_marginTop="24dp" />

<Button
android:id="@+id/btnLoyaltyStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/loyalty_status"
android:layout_marginTop="16dp" />

</LinearLayout>

</ScrollView>


6 changes: 6 additions & 0 deletions demo-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@
<string name="get_user_orders">Get user orders</string>
<string name="get_user_orders_ok">getUserOrders: %1$d order(s)</string>
<string name="get_user_orders_fail">getUserOrders failed</string>
<string name="loyalty_join">Loyalty: join</string>
<string name="loyalty_join_ok">loyalty join: %1$s</string>
<string name="loyalty_join_fail">loyalty join failed</string>
<string name="loyalty_status">Loyalty: status</string>
<string name="loyalty_status_ok">loyalty status: %1$s</string>
<string name="loyalty_status_fail">loyalty status failed</string>
</resources>


Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.personalization.api.OnApiCallbackListener
import com.personalization.api.models.purchase.PurchaseTrackingRequest
import com.personalization.api.managers.CartManager
import com.personalization.api.managers.InAppNotificationManager
import com.personalization.api.managers.LoyaltyManager
import com.personalization.api.managers.OrdersManager
import com.personalization.api.managers.ProductsManager
import com.personalization.api.managers.PredictManager
Expand Down Expand Up @@ -88,6 +89,9 @@ open class SDK {
@Inject
lateinit var ordersManager: OrdersManager

@Inject
lateinit var loyaltyManager: LoyaltyManager

@Inject
lateinit var inAppNotificationManager: InAppNotificationManager

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.personalization.api.managers

import com.personalization.api.responses.loyalty.LoyaltyJoinResponse
import com.personalization.api.responses.loyalty.LoyaltyStatusResponse

interface LoyaltyManager {

/**
* Join the loyalty program (`loyalty/members/join`).
*
* The shop is identified automatically by the SDK's configured `shop_id`; only the member
* fields are passed here. [phone] is required by the endpoint.
*
* @param phone Member phone number (required)
* @param email Member email (optional)
* @param firstName Member first name (optional)
* @param lastName Member last name (optional)
* @param onSuccess Callback with the parsed join response
* @param onError Callback for error
*/
fun join(
phone: String,
email: String? = null,
firstName: String? = null,
lastName: String? = null,
onSuccess: (LoyaltyJoinResponse) -> Unit,
onError: (Int, String?) -> Unit = { _: Int, _: String? -> }
)

/**
* Request the loyalty membership status (`loyalty/members/status`).
*
* The shop is identified automatically by the SDK's configured `shop_id`.
*
* @param identifier Member identifier (phone)
* @param onSuccess Callback with the parsed status response
* @param onError Callback for error
*/
fun getStatus(
identifier: String,
onSuccess: (LoyaltyStatusResponse) -> Unit,
onError: (Int, String?) -> Unit = { _: Int, _: String? -> }
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.personalization.api.responses.loyalty

import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName

/**
* Response for the `loyalty/members/join` request.
*
* The endpoint returns an envelope `{ "status": "success" | "fail", "payload": { ... } }`.
* [payload] is intentionally kept as a raw [JsonObject] because its shape differs between
* success and failure responses.
*/
data class LoyaltyJoinResponse(
@SerializedName("status")
val status: String? = null,
@SerializedName("payload")
val payload: JsonObject? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.personalization.api.responses.loyalty

import com.google.gson.annotations.SerializedName

/**
* Response for the `loyalty/members/status` request.
*
* Envelope: `{ "status": "success" | "fail", "payload": { "member": ..., "level": { ... } } }`.
*/
data class LoyaltyStatusResponse(
@SerializedName("status")
val status: String? = null,
@SerializedName("payload")
val payload: LoyaltyStatusPayload? = null
)

data class LoyaltyStatusPayload(
@SerializedName("member")
val member: Boolean? = null,
@SerializedName("level")
val level: LoyaltyLevel? = null
)

data class LoyaltyLevel(
@SerializedName("name")
val name: String? = null,
@SerializedName("code")
val code: String? = null,
@SerializedName("expiration_date")
val expirationDate: String? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import com.personalization.RegisterManager
import com.personalization.api.managers.CartManager
import com.personalization.api.managers.InAppNotificationManager
import com.personalization.api.managers.LoyaltyManager
import com.personalization.api.managers.OrdersManager
import com.personalization.api.managers.ProductsManager
import com.personalization.api.managers.RecommendationManager
Expand All @@ -13,6 +14,7 @@ import com.personalization.api.managers.TrackEventManager
import com.personalization.features.cart.CartManagerImpl
import com.personalization.features.inAppNotification.impl.InAppNotificationManagerImpl
import com.personalization.features.notification.domain.data.NotificationDataExtractor
import com.personalization.features.loyalty.impl.LoyaltyManagerImpl
import com.personalization.features.orders.impl.OrdersManagerImpl
import com.personalization.features.predict.impl.PredictManagerImpl
import com.personalization.features.products.impl.ProductsManagerImpl
Expand Down Expand Up @@ -129,6 +131,14 @@ class SdkModule {
sendNetworkMethodUseCase = sendNetworkMethodUseCase
)

@Singleton
@Provides
fun provideLoyaltyManager(
sendNetworkMethodUseCase: SendNetworkMethodUseCase
): LoyaltyManager = LoyaltyManagerImpl(
sendNetworkMethodUseCase = sendNetworkMethodUseCase
)

@Singleton
@Provides
fun provideInAppNotificationManager(
Expand Down
Loading
Loading