Skip to content

Commit 7bbef09

Browse files
authored
Merge pull request #8067 from woocommerce/issue/8057-iap-store-tests
[In-app Purchases] Add unit tests for InAppPurchaseStore
2 parents da009cc + 7407d46 commit 7bbef09

File tree

7 files changed

+275
-6
lines changed

7 files changed

+275
-6
lines changed

Networking/NetworkingTests/Mapper/InAppPurchasesProductsMapperTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ final class InAppPurchasesProductsMapperTests: XCTestCase {
66
// Given
77
let jsonData = try XCTUnwrap(Loader.contentsOf("iap-products"))
88
let expectedProductIdentifiers = [
9-
"woocommerce_entry_monthly",
10-
"woocommerce_entry_yearly"]
9+
"debug.woocommerce.ecommerce.monthly"
10+
]
1111

1212
// When
1313
let products = try InAppPurchasesProductMapper().map(response: jsonData)

Networking/NetworkingTests/Remote/InAppPurchasesRemoteTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class InAppPurchasesRemoteTests: XCTestCase {
4444

4545
// Then
4646
let identifiers = try XCTUnwrap(result?.get())
47-
XCTAssert(identifiers.count == 2)
47+
XCTAssert(identifiers.count == 1)
4848
}
4949

5050
func test_purchase_product_returns_created_order() throws {
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
[
2-
"woocommerce_entry_monthly",
3-
"woocommerce_entry_yearly"
2+
"debug.woocommerce.ecommerce.monthly"
43
]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"identifier" : "28D2E099",
3+
"nonRenewingSubscriptions" : [
4+
5+
],
6+
"products" : [
7+
8+
],
9+
"settings" : {
10+
11+
},
12+
"subscriptionGroups" : [
13+
{
14+
"id" : "21032762",
15+
"localizations" : [
16+
17+
],
18+
"name" : "test_subscription_group",
19+
"subscriptions" : [
20+
{
21+
"adHocOffers" : [
22+
23+
],
24+
"codeOffers" : [
25+
26+
],
27+
"displayPrice" : "69.99",
28+
"familyShareable" : false,
29+
"groupNumber" : 1,
30+
"internalID" : "1650562345",
31+
"introductoryOffer" : null,
32+
"localizations" : [
33+
{
34+
"description" : "1 Month of Debug Woo",
35+
"displayName" : "Debug Monthly",
36+
"locale" : "en_US"
37+
}
38+
],
39+
"productID" : "debug.woocommerce.ecommerce.monthly",
40+
"recurringSubscriptionPeriod" : "P1M",
41+
"referenceName" : "Debug Monthly",
42+
"subscriptionGroupID" : "21032762",
43+
"type" : "RecurringSubscription"
44+
}
45+
]
46+
}
47+
],
48+
"version" : {
49+
"major" : 2,
50+
"minor" : 0
51+
}
52+
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,8 +1830,10 @@
18301830
E16715CD2666543000326230 /* CardPresentModalSuccessWithoutEmailTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16715CC2666543000326230 /* CardPresentModalSuccessWithoutEmailTests.swift */; };
18311831
E17E3BF9266917C10009D977 /* CardPresentModalScanningFailedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17E3BF8266917C10009D977 /* CardPresentModalScanningFailedTests.swift */; };
18321832
E17E3BFB266917E20009D977 /* CardPresentModalBluetoothRequiredTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E17E3BFA266917E20009D977 /* CardPresentModalBluetoothRequiredTests.swift */; };
1833+
E181CDCC291BB2E1002DA3C6 /* InAppPurchaseStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E181CDCB291BB2E1002DA3C6 /* InAppPurchaseStoreTests.swift */; };
18331834
E1906E9A26C4126300CA6819 /* InPersonPaymentsMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1906E9926C4126300CA6819 /* InPersonPaymentsMenuViewController.swift */; };
18341835
E1ABAEF728479E0300F40BB2 /* InPersonPaymentsSelectPluginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1ABAEF628479E0300F40BB2 /* InPersonPaymentsSelectPluginView.swift */; };
1836+
E1B0839B291BC5E3001D99C8 /* WooCommerceTest.storekit in Resources */ = {isa = PBXBuildFile; fileRef = E1B0839A291BC5DD001D99C8 /* WooCommerceTest.storekit */; };
18351837
E1BAAEA026BBECEF00F2C037 /* ButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1BAAE9F26BBECEF00F2C037 /* ButtonStyles.swift */; };
18361838
E1BE703A265E6F47006CA4D9 /* CardPresentModalScanningFailed.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1BE7039265E6F47006CA4D9 /* CardPresentModalScanningFailed.swift */; };
18371839
E1C47209267A1ECC00D06DA1 /* CrashLoggingStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1C47208267A1ECC00D06DA1 /* CrashLoggingStack.swift */; };
@@ -3803,8 +3805,10 @@
38033805
E16715CC2666543000326230 /* CardPresentModalSuccessWithoutEmailTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalSuccessWithoutEmailTests.swift; sourceTree = "<group>"; };
38043806
E17E3BF8266917C10009D977 /* CardPresentModalScanningFailedTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalScanningFailedTests.swift; sourceTree = "<group>"; };
38053807
E17E3BFA266917E20009D977 /* CardPresentModalBluetoothRequiredTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalBluetoothRequiredTests.swift; sourceTree = "<group>"; };
3808+
E181CDCB291BB2E1002DA3C6 /* InAppPurchaseStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchaseStoreTests.swift; sourceTree = "<group>"; };
38063809
E1906E9926C4126300CA6819 /* InPersonPaymentsMenuViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsMenuViewController.swift; sourceTree = "<group>"; };
38073810
E1ABAEF628479E0300F40BB2 /* InPersonPaymentsSelectPluginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InPersonPaymentsSelectPluginView.swift; sourceTree = "<group>"; };
3811+
E1B0839A291BC5DD001D99C8 /* WooCommerceTest.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = WooCommerceTest.storekit; sourceTree = "<group>"; };
38083812
E1BAAE9F26BBECEF00F2C037 /* ButtonStyles.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonStyles.swift; sourceTree = "<group>"; };
38093813
E1BE7039265E6F47006CA4D9 /* CardPresentModalScanningFailed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalScanningFailed.swift; sourceTree = "<group>"; };
38103814
E1C47208267A1ECC00D06DA1 /* CrashLoggingStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CrashLoggingStack.swift; sourceTree = "<group>"; };
@@ -7014,6 +7018,7 @@
70147018
B573B19D219DC2690081C78C /* Localizable.strings */,
70157019
3F587028281B9C19004F7556 /* InfoPlist.strings */,
70167020
B56DB3D82049BFAA00D4AA8E /* Info.plist */,
7021+
E1B0839A291BC5DD001D99C8 /* WooCommerceTest.storekit */,
70177022
B56C721921B5F65E00E5E85B /* Woo-Debug.entitlements */,
70187023
03180BE72763AA9000B938A8 /* Woo-Debug-macOS.entitlements */,
70197024
B56C721A21B5F65E00E5E85B /* Woo-Release.entitlements */,
@@ -7249,6 +7254,7 @@
72497254
isa = PBXGroup;
72507255
children = (
72517256
B5DBF3C220E1484400B53AED /* StoresManagerTests.swift */,
7257+
E181CDCB291BB2E1002DA3C6 /* InAppPurchaseStoreTests.swift */,
72527258
);
72537259
path = Yosemite;
72547260
sourceTree = "<group>";
@@ -9343,6 +9349,7 @@
93439349
9379E1A5225536AD006A6BE4 /* TestAssets.xcassets in Resources */,
93449350
9379E1A32255365F006A6BE4 /* TestingMode.storyboard in Resources */,
93459351
B5F571A921BEECA50010D1B8 /* Responses in Resources */,
9352+
E1B0839B291BC5E3001D99C8 /* WooCommerceTest.storekit in Resources */,
93469353
);
93479354
runOnlyForDeploymentPostprocessing = 0;
93489355
};
@@ -10879,6 +10886,7 @@
1087910886
0271125D2887D4E900FCD13C /* LoggedOutAppSettingsTests.swift in Sources */,
1088010887
74F3015A2200EC0800931B9E /* NSDecimalNumberWooTests.swift in Sources */,
1088110888
D85136CD231E15B800DD0539 /* MockReviews.swift in Sources */,
10889+
E181CDCC291BB2E1002DA3C6 /* InAppPurchaseStoreTests.swift in Sources */,
1088210890
2655905B27863D1300BB8457 /* MockCollectOrderPaymentUseCase.swift in Sources */,
1088310891
D8053BCE231F98DA00CE60C2 /* ReviewAgeTests.swift in Sources */,
1088410892
A650BE862578E76600C655E0 /* MockStorageManager+Sample.swift in Sources */,

WooCommerce/WooCommerceTests/Tools/DefaultConnectivityObserver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ final class MockNetworkMonitor: NetworkMonitoring {
9797
}
9898
}
9999

100-
struct MockNetwork: NetworkMonitorable {
100+
private struct MockNetwork: NetworkMonitorable {
101101
let status: NWPath.Status
102102
private let currentInterface: NWInterface.InterfaceType
103103

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
import XCTest
2+
import TestKit
3+
import StoreKitTest
4+
5+
@testable import Yosemite
6+
@testable import Networking
7+
8+
final class InAppPurchaseStoreTests: XCTestCase {
9+
10+
/// Mock Network: Allows us to inject predefined responses!
11+
///
12+
private var network: MockNetwork!
13+
14+
/// Mock Storage: InMemory
15+
///
16+
private var storageManager: MockStorageManager!
17+
18+
private var storeKitSession = try! SKTestSession(configurationFileNamed: "WooCommerceTest")
19+
20+
/// Testing SiteID
21+
///
22+
private let sampleSiteID: Int64 = 123
23+
24+
/// Testing Product ID
25+
/// Should match the product ID in WooCommerce.storekit
26+
///
27+
private let sampleProductID: String = "debug.woocommerce.ecommerce.monthly"
28+
29+
/// Testing Order ID
30+
/// Should match the order ID in iap-order-create.json
31+
///
32+
private let sampleOrderID: Int64 = 12345
33+
34+
var store: InAppPurchaseStore!
35+
36+
37+
override func setUp() {
38+
network = MockNetwork(useResponseQueue: true)
39+
storageManager = MockStorageManager()
40+
store = InAppPurchaseStore(dispatcher: Dispatcher(), storageManager: storageManager, network: network)
41+
storeKitSession.disableDialogs = true
42+
}
43+
44+
override func tearDown() {
45+
storeKitSession.resetToDefaultState()
46+
storeKitSession.clearTransactions()
47+
}
48+
49+
func test_iap_supported_in_us() throws {
50+
// Given
51+
storeKitSession.storefront = "USA"
52+
53+
// When
54+
let result = waitFor { promise in
55+
let action = InAppPurchaseAction.inAppPurchasesAreSupported { result in
56+
promise(result)
57+
}
58+
self.store.onAction(action)
59+
}
60+
61+
// Then
62+
XCTAssertTrue(result)
63+
}
64+
65+
func test_iap_supported_in_canada() throws {
66+
// Given
67+
storeKitSession.storefront = "CAN"
68+
69+
// When
70+
let result = waitFor { promise in
71+
let action = InAppPurchaseAction.inAppPurchasesAreSupported { result in
72+
promise(result)
73+
}
74+
self.store.onAction(action)
75+
}
76+
77+
// Then
78+
XCTAssertFalse(result)
79+
}
80+
81+
func test_load_products_loads_products_response() throws {
82+
// Given
83+
network.simulateResponse(requestUrlSuffix: "iap/products", filename: "iap-products")
84+
85+
// When
86+
let result = waitFor { promise in
87+
let action = InAppPurchaseAction.loadProducts { result in
88+
promise(result)
89+
}
90+
self.store.onAction(action)
91+
}
92+
93+
// Then
94+
let products = try XCTUnwrap(result.get())
95+
XCTAssertFalse(products.isEmpty)
96+
XCTAssertEqual(products.first?.id, sampleProductID)
97+
}
98+
99+
func test_load_products_fails_if_iap_unsupported() throws {
100+
// Given
101+
storeKitSession.storefront = "CAN"
102+
network.simulateResponse(requestUrlSuffix: "iap/products", filename: "iap-products")
103+
104+
// When
105+
let result = waitFor { promise in
106+
let action = InAppPurchaseAction.loadProducts { result in
107+
promise(result)
108+
}
109+
self.store.onAction(action)
110+
}
111+
112+
// Then
113+
XCTAssert(result.isFailure)
114+
}
115+
116+
func test_purchase_product_completes_purchase() throws {
117+
// Given
118+
network.simulateResponse(requestUrlSuffix: "iap/orders", filename: "iap-order-create")
119+
120+
// When
121+
let result = waitFor { promise in
122+
let action = InAppPurchaseAction.purchaseProduct(siteID: self.sampleSiteID, productID: self.sampleProductID) { result in
123+
promise(result)
124+
}
125+
self.store.onAction(action)
126+
}
127+
128+
// Then
129+
let purchaseResult = try XCTUnwrap(result.get())
130+
guard case let .success(verificationResult) = purchaseResult,
131+
case let .verified(transaction) = verificationResult else {
132+
return XCTFail()
133+
}
134+
XCTAssertEqual(transaction.productID, sampleProductID)
135+
XCTAssertNotNil(transaction.appAccountToken)
136+
}
137+
138+
@available(iOS 16.0, *)
139+
func test_purchase_product_ensure_xcode_environment() throws {
140+
// Given
141+
network.simulateResponse(requestUrlSuffix: "iap/orders", filename: "iap-order-create")
142+
143+
// When
144+
let result = waitFor { promise in
145+
let action = InAppPurchaseAction.purchaseProduct(siteID: self.sampleSiteID, productID: self.sampleProductID) { result in
146+
promise(result)
147+
}
148+
self.store.onAction(action)
149+
}
150+
151+
// Then
152+
let purchaseResult = try XCTUnwrap(result.get())
153+
guard case let .success(verificationResult) = purchaseResult,
154+
case let .verified(transaction) = verificationResult else {
155+
return XCTFail()
156+
}
157+
XCTAssertEqual(transaction.environment, .xcode)
158+
}
159+
160+
func test_purchase_product_handles_api_errors() throws {
161+
// Given
162+
network.simulateResponse(requestUrlSuffix: "iap/orders", filename: "error-wp-rest-forbidden")
163+
164+
// When
165+
let result = waitFor { promise in
166+
let action = InAppPurchaseAction.purchaseProduct(siteID: self.sampleSiteID, productID: self.sampleProductID) { result in
167+
promise(result)
168+
}
169+
self.store.onAction(action)
170+
}
171+
172+
// Then
173+
XCTAssert(result.isFailure)
174+
let error = try XCTUnwrap(result.failure)
175+
XCTAssert(error is WordPressApiError)
176+
}
177+
178+
func test_user_is_entitled_to_product_returns_false_when_not_entitled() throws {
179+
// Given
180+
181+
// When
182+
let result = waitFor { promise in
183+
let action = InAppPurchaseAction.userIsEntitledToProduct(productID: self.sampleProductID) { result in
184+
promise(result)
185+
}
186+
self.store.onAction(action)
187+
}
188+
189+
// Then
190+
let isEntitled = try XCTUnwrap(result.get())
191+
XCTAssertFalse(isEntitled)
192+
}
193+
194+
func test_user_is_entitled_to_product_returns_true_when_entitled() throws {
195+
// Given
196+
try storeKitSession.buyProduct(productIdentifier: sampleProductID)
197+
198+
// When
199+
let result = waitFor { promise in
200+
let action = InAppPurchaseAction.userIsEntitledToProduct(productID: self.sampleProductID) { result in
201+
promise(result)
202+
}
203+
self.store.onAction(action)
204+
}
205+
206+
// Then
207+
let isEntitled = try XCTUnwrap(result.get())
208+
XCTAssertTrue(isEntitled)
209+
}
210+
}

0 commit comments

Comments
 (0)