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
4 changes: 4 additions & 0 deletions DemoApp/REES46.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
A1B2C3D40000000000000001 /* GetLastOrderProductsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000002 /* GetLastOrderProductsTest.swift */; };
A1B2C3D40000000000000003 /* GetUserOrdersTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000004 /* GetUserOrdersTest.swift */; };
A1B2C3D40000000000000005 /* LoyaltyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000006 /* LoyaltyTests.swift */; };
A1B2C3D40000000000000007 /* CatalogReadTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1B2C3D40000000000000008 /* CatalogReadTests.swift */; };
75E237932C334AD200119197 /* DeviceIdTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 75E237912C334AD200119197 /* DeviceIdTest.swift */; };
D1C0FFEF2F0A1B2C3D4E5F60 /* TrackEventCustomFieldsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1C0FFEE2F0A1B2C3D4E5F60 /* TrackEventCustomFieldsTests.swift */; };
9A7A95DF25BC40E900FFA40A /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9ADC31DE25A1C6F700D9D1AA /* UserNotifications.framework */; };
Expand Down Expand Up @@ -113,6 +114,7 @@
A1B2C3D40000000000000002 /* GetLastOrderProductsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetLastOrderProductsTest.swift; sourceTree = "<group>"; };
A1B2C3D40000000000000004 /* GetUserOrdersTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetUserOrdersTest.swift; sourceTree = "<group>"; };
A1B2C3D40000000000000006 /* LoyaltyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoyaltyTests.swift; sourceTree = "<group>"; };
A1B2C3D40000000000000008 /* CatalogReadTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogReadTests.swift; sourceTree = "<group>"; };
75E237912C334AD200119197 /* DeviceIdTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceIdTest.swift; sourceTree = "<group>"; };
D1C0FFEE2F0A1B2C3D4E5F60 /* TrackEventCustomFieldsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackEventCustomFieldsTests.swift; sourceTree = "<group>"; };
9A58AAB128E80C33004EA84C /* REESNotificationServiceExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = REESNotificationServiceExtension.entitlements; sourceTree = "<group>"; };
Expand Down Expand Up @@ -323,6 +325,7 @@
A1B2C3D40000000000000002 /* GetLastOrderProductsTest.swift */,
A1B2C3D40000000000000004 /* GetUserOrdersTest.swift */,
A1B2C3D40000000000000006 /* LoyaltyTests.swift */,
A1B2C3D40000000000000008 /* CatalogReadTests.swift */,
D1C0FFEE2F0A1B2C3D4E5F60 /* TrackEventCustomFieldsTests.swift */,
);
name = sdk;
Expand Down Expand Up @@ -694,6 +697,7 @@
A1B2C3D40000000000000001 /* GetLastOrderProductsTest.swift in Sources */,
A1B2C3D40000000000000003 /* GetUserOrdersTest.swift in Sources */,
A1B2C3D40000000000000005 /* LoyaltyTests.swift in Sources */,
A1B2C3D40000000000000007 /* CatalogReadTests.swift in Sources */,
D1C0FFEF2F0A1B2C3D4E5F60 /* TrackEventCustomFieldsTests.swift in Sources */,
D0B70C172D522DAF0001782A /* SearchServiceTests.swift in Sources */,
D0B99F9F2DB169D80012E4F0 /* UnsubscribeBackInStock.swift in Sources */,
Expand Down
164 changes: 164 additions & 0 deletions DemoApp/REES46Demo/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
private var getUserOrdersButton: UIButton!
private var loyaltyJoinButton: UIButton!
private var loyaltyStatusButton: UIButton!
private var getProfileButton: UIButton!
private var getProductCountersButton: UIButton!
private var getCategoryButton: UIButton!
private var getCollectionButton: UIButton!

public var waitIndicator: SdkActivityIndicator!

Expand Down Expand Up @@ -287,6 +291,10 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
setupGetUserOrdersButton()
setupLoyaltyJoinButton()
setupLoyaltyStatusButton()
setupGetProfileButton()
setupGetProductCountersButton()
setupGetCategoryButton()
setupGetCollectionButton()

fontInterPreload()
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
Expand Down Expand Up @@ -623,6 +631,162 @@ class MainViewController: UIViewController, UIScrollViewDelegate {
}
}

func setupGetProfileButton() {
getProfileButton = DemoShopButton(type: .system)
getProfileButton.setTitle("Profile: get", for: .normal)
getProfileButton.setTitleColor(.white, for: .normal)
getProfileButton.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(getProfileButton)

NSLayoutConstraint.activate([
getProfileButton.topAnchor.constraint(equalTo: loyaltyStatusButton.bottomAnchor, constant: 24),
getProfileButton.leadingAnchor.constraint(equalTo: showStoriesButton.leadingAnchor),
getProfileButton.widthAnchor.constraint(equalTo: showStoriesButton.widthAnchor),
getProfileButton.heightAnchor.constraint(equalTo: showStoriesButton.heightAnchor),
])

getProfileButton.addTarget(self, action: #selector(didTapGetProfile), for: .touchUpInside)
}

func setupGetProductCountersButton() {
getProductCountersButton = DemoShopButton(type: .system)
getProductCountersButton.setTitle("Products: counters", for: .normal)
getProductCountersButton.setTitleColor(.white, for: .normal)
getProductCountersButton.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(getProductCountersButton)

NSLayoutConstraint.activate([
getProductCountersButton.topAnchor.constraint(equalTo: getProfileButton.bottomAnchor, constant: 10),
getProductCountersButton.leadingAnchor.constraint(equalTo: showStoriesButton.leadingAnchor),
getProductCountersButton.widthAnchor.constraint(equalTo: showStoriesButton.widthAnchor),
getProductCountersButton.heightAnchor.constraint(equalTo: showStoriesButton.heightAnchor),
])

getProductCountersButton.addTarget(self, action: #selector(didTapGetProductCounters), for: .touchUpInside)
}

func setupGetCategoryButton() {
getCategoryButton = DemoShopButton(type: .system)
getCategoryButton.setTitle("Category: list", for: .normal)
getCategoryButton.setTitleColor(.white, for: .normal)
getCategoryButton.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(getCategoryButton)

NSLayoutConstraint.activate([
getCategoryButton.topAnchor.constraint(equalTo: getProductCountersButton.bottomAnchor, constant: 10),
getCategoryButton.leadingAnchor.constraint(equalTo: showStoriesButton.leadingAnchor),
getCategoryButton.widthAnchor.constraint(equalTo: showStoriesButton.widthAnchor),
getCategoryButton.heightAnchor.constraint(equalTo: showStoriesButton.heightAnchor),
])

getCategoryButton.addTarget(self, action: #selector(didTapGetCategory), for: .touchUpInside)
}

@objc
private func didTapGetProfile() {
guard let sdk = globalSDK else {
presentTrackEventDemoAlert(title: "SDK", message: "globalSDK is not initialized.")
return
}
sdk.getProfile { result in
switch result {
case .success(let response):
self.presentTrackEventDemoAlert(
title: "Profile",
message: "id: \(response.id ?? "—"), hasEmail: \(response.hasEmail.map(String.init) ?? "—"), gender: \(response.gender ?? "—")"
)
case .failure(let error):
self.presentTrackEventDemoAlert(
title: "getProfile failed",
message: Self.sdkErrorDescription(error)
)
}
}
}

@objc
private func didTapGetProductCounters() {
guard let sdk = globalSDK else {
presentTrackEventDemoAlert(title: "SDK", message: "globalSDK is not initialized.")
return
}
sdk.getProductCounters(item: "300275") { result in
switch result {
case .success(let response):
self.presentTrackEventDemoAlert(
title: "Product counters",
message: "now.view: \(response.now?.view ?? 0), price_drop: \(response.triggers?.priceDrop ?? 0)"
)
case .failure(let error):
self.presentTrackEventDemoAlert(
title: "getProductCounters failed",
message: Self.sdkErrorDescription(error)
)
}
}
}

@objc
private func didTapGetCategory() {
guard let sdk = globalSDK else {
presentTrackEventDemoAlert(title: "SDK", message: "globalSDK is not initialized.")
return
}
sdk.getCategory(category: "smartfony-i-gadzhety", limit: 5) { result in
switch result {
case .success(let response):
self.presentTrackEventDemoAlert(
title: "Category",
message: "total: \(response.productsTotal), products: \(response.products.count)"
)
case .failure(let error):
self.presentTrackEventDemoAlert(
title: "getCategory failed",
message: Self.sdkErrorDescription(error)
)
}
}
}

func setupGetCollectionButton() {
getCollectionButton = DemoShopButton(type: .system)
getCollectionButton.setTitle("Collection: get", for: .normal)
getCollectionButton.setTitleColor(.white, for: .normal)
getCollectionButton.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(getCollectionButton)

NSLayoutConstraint.activate([
getCollectionButton.topAnchor.constraint(equalTo: getCategoryButton.bottomAnchor, constant: 10),
getCollectionButton.leadingAnchor.constraint(equalTo: showStoriesButton.leadingAnchor),
getCollectionButton.widthAnchor.constraint(equalTo: showStoriesButton.widthAnchor),
getCollectionButton.heightAnchor.constraint(equalTo: showStoriesButton.heightAnchor),
])

getCollectionButton.addTarget(self, action: #selector(didTapGetCollection), for: .touchUpInside)
}

@objc
private func didTapGetCollection() {
guard let sdk = globalSDK else {
presentTrackEventDemoAlert(title: "SDK", message: "globalSDK is not initialized.")
return
}
sdk.getCollection(collectionId: "1") { result in
switch result {
case .success(let response):
self.presentTrackEventDemoAlert(
title: "Collection",
message: "products: \(response.products.count)"
)
case .failure(let error):
self.presentTrackEventDemoAlert(
title: "getCollection failed",
message: Self.sdkErrorDescription(error)
)
}
}
}

private func presentTrackEventDemoAlert(title: String, message: String) {
DispatchQueue.main.async {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
Expand Down
107 changes: 107 additions & 0 deletions DemoApp/Rees46DemoTests/CatalogReadTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import XCTest
@testable import REES46

/// Integration tests for the catalog/profile read methods
/// (`GET /profile`, `GET /products/counters`, `GET /category/{slug}`).
///
/// Mirrors `LoyaltyTests`: the shop is taken from the `TEST_SHOP_ID` / `TEST_API_URL`
/// environment (injected by CI) with a fallback to the shared constants.
class CatalogReadTests: XCTestCase {

var sdk: PersonalizationSDK!

var shopId: String {
return ProcessInfo.processInfo.environment[Constants.testShopIdKey] ?? Constants.testShopId
}

var apiDomain: String {
return ProcessInfo.processInfo.environment[Constants.testApiUrlKey] ?? Constants.testApiDomain
}

/// Category slug present in the integration test shop's catalog.
private let categorySlug = "smartfony-i-gadzhety"
/// Item id present in the integration test shop's catalog.
private let itemId = "300275"
/// Collection id configured in the integration test shop's dashboard.
private let collectionId = "1"

override func setUp() {
super.setUp()
sdk = createPersonalizationSDK(
shopId: shopId,
apiDomain: apiDomain,
parentViewController: nil
)
}

override func tearDown() {
sdk = nil
super.tearDown()
}

func testGetProfile_returnsSuccess() {
let expectation = self.expectation(description: "getProfile completion")

sdk?.getProfile { result in
switch result {
case .success(let response):
// Profile is freeform; custom_properties always parses (at least to [:]).
XCTAssertNotNil(response.customProperties)
case .failure(let error):
XCTFail("getProfile failed with error: \(error)")
}
expectation.fulfill()
}

waitForExpectations(timeout: 10, handler: nil)
}

func testGetProductCounters_returnsSuccess() {
let expectation = self.expectation(description: "getProductCounters completion")

sdk?.getProductCounters(item: itemId) { result in
switch result {
case .success(let response):
XCTAssertNotNil(response.triggers)
case .failure(let error):
XCTFail("getProductCounters failed with error: \(error)")
}
expectation.fulfill()
}

waitForExpectations(timeout: 10, handler: nil)
}

func testGetCategory_returnsProducts() {
let expectation = self.expectation(description: "getCategory completion")

sdk?.getCategory(category: categorySlug, limit: 5) { result in
switch result {
case .success(let response):
XCTAssertGreaterThan(response.productsTotal, 0)
XCTAssertFalse(response.products.isEmpty)
case .failure(let error):
XCTFail("getCategory failed with error: \(error)")
}
expectation.fulfill()
}

waitForExpectations(timeout: 10, handler: nil)
}

func testGetCollection_returnsProducts() {
let expectation = self.expectation(description: "getCollection completion")

sdk?.getCollection(collectionId: collectionId) { result in
switch result {
case .success(let response):
XCTAssertFalse(response.products.isEmpty)
case .failure(let error):
XCTFail("getCollection failed with error: \(error)")
}
expectation.fulfill()
}

waitForExpectations(timeout: 10, handler: nil)
}
}
16 changes: 16 additions & 0 deletions DemoApp/Rees46DemoTests/TrackEventCustomFieldsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,22 @@ private final class MockPersonalizationSDK: PersonalizationSDK {
fatalError("Not needed for these tests")
}

func getProfile(completion: @escaping (Result<GetProfileResponse, SdkError>) -> Void) {
fatalError("Not needed for these tests")
}

func getProductCounters(item: String, completion: @escaping (Result<ProductCountersResponse, SdkError>) -> Void) {
fatalError("Not needed for these tests")
}

func getCategory(category: String, limit: Int?, page: Int?, brands: String?, locations: String?, filters: [String: Any]?, completion: @escaping (Result<CategoryResponse, SdkError>) -> Void) {
fatalError("Not needed for these tests")
}

func getCollection(collectionId: String, location: String?, email: String?, phone: String?, externalId: String?, loyaltyId: String?, completion: @escaping (Result<CollectionResponse, SdkError>) -> Void) {
fatalError("Not needed for these tests")
}

func getDeviceId() -> String { deviceId }

func setParentViewController(controller: UIViewController, completion: @escaping () -> Void) {
Expand Down
Loading
Loading