Skip to content

Commit 095974b

Browse files
authored
Add swift tests (#24)
* Add swift tests * Add command to run swift tests * Add license header * Fix lint problems * Add comments * Add test for configure method * Run tests in CI * Remove build step in favour of test, which also builds * Supress warnings from dependencies on build * Update test command * Rename CI step
1 parent b1d8584 commit 095974b

File tree

10 files changed

+488
-150
lines changed

10 files changed

+488
-150
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: Setup
2+
description: Setup Node.js and install dependencies
3+
4+
runs:
5+
using: composite
6+
steps:
7+
- name: Setup iOS Simulator
8+
shell: bash
9+
run: |
10+
xcode-select -p
11+
xcrun xcodebuild -version
12+
xcrun simctl list runtimes
13+
xcrun simctl list devicetypes
14+
xcrun simctl delete all
15+
CURRENT_SIMULATOR_UUID=$(xcrun simctl create TestDevice "iPhone 14 Pro Max")
16+
echo "CURRENT_SIMULATOR_UUID=$CURRENT_SIMULATOR_UUID" >> $GITHUB_ENV

.github/workflows/ci.yml

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,22 +95,15 @@ jobs:
9595
run: |
9696
yarn turbo run test:android --cache-dir=".turbo"
9797
98-
build-ios:
99-
name: Build iOS Sample App
98+
test-ios:
99+
name: Run iOS Tests
100100
runs-on: macos-latest
101101
steps:
102102
- name: Checkout
103103
uses: actions/checkout@v3
104104

105105
- name: Setup iOS Simulator
106-
run: |
107-
xcode-select -p
108-
xcrun xcodebuild -version
109-
xcrun simctl list runtimes
110-
xcrun simctl list devicetypes
111-
xcrun simctl delete all
112-
CURRENT_SIMULATOR_UUID=$(xcrun simctl create TestDevice "iPhone 14 Pro Max")
113-
echo "CURRENT_SIMULATOR_UUID=$CURRENT_SIMULATOR_UUID" >> $GITHUB_ENV
106+
uses: ./.github/actions/setup-simulator
114107

115108
- name: Setup
116109
uses: ./.github/actions/setup
@@ -120,7 +113,7 @@ jobs:
120113

121114
- name: Check build cache
122115
run: |
123-
CACHE_STATUS=$(./scripts/check_cache sample build:ios)
116+
CACHE_STATUS=$(./scripts/check_cache sample test:ios)
124117
echo "[sample] build:ios - $CACHE_STATUS"
125118
echo "turbo_cache_hit=$CACHE_STATUS" >> $GITHUB_ENV
126119
@@ -130,4 +123,4 @@ jobs:
130123
- name: Build sample for iOS
131124
# If turbo has already cached the build it will return instantly here
132125
run: |
133-
yarn turbo run build:ios --cache-dir=".turbo"
126+
yarn turbo run test:ios --cache-dir=".turbo"

sample/ios/Podfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@ require Pod::Executable.execute_command('node', ['-p',
66
)', __dir__]).strip
77

88
platform :ios, 13
9+
910
prepare_react_native_project!
1011

12+
# Suppress warnings in dependencies when building the sample app
13+
inhibit_all_warnings!
14+
1115
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
1216
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
1317
#

sample/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,6 @@ SPEC CHECKSUMS:
682682
SwiftLint: c1de071d9d08c8aba837545f6254315bc900e211
683683
Yoga: b76f1acfda8212aa16b7e26bcce3983230c82603
684684

685-
PODFILE CHECKSUM: 168602e6106e5ed80179cff5e652b12ee4bce5a9
685+
PODFILE CHECKSUM: d1459fa6022f1a476e673f0b68fc07c26f0058ed
686686

687687
COCOAPODS: 1.14.3

sample/ios/ReactNative.xcodeproj/project.pbxproj

Lines changed: 250 additions & 68 deletions
Large diffs are not rendered by default.

sample/ios/ReactNativeTests/ReactNativeTests.m

Lines changed: 0 additions & 66 deletions
This file was deleted.
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
MIT License
3+
4+
Copyright 2023 - Present, Shopify Inc.
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22+
*/
23+
24+
import Foundation
25+
import XCTest
26+
@testable import ShopifyCheckoutKit
27+
@testable import react_native_shopify_checkout_kit
28+
29+
class ShopifyCheckoutKitTests: XCTestCase {
30+
private var shopifyCheckoutKit: RCTShopifyCheckoutKit!
31+
32+
override func setUp() {
33+
super.setUp()
34+
shopifyCheckoutKit = getShopifyCheckoutKit()
35+
resetShopifyCheckoutKitDefaults()
36+
}
37+
38+
override func tearDown() {
39+
shopifyCheckoutKit = nil
40+
super.tearDown()
41+
}
42+
43+
private func resetShopifyCheckoutKitDefaults() {
44+
ShopifyCheckoutKit.configuration.preloading = Configuration.Preloading(enabled: true)
45+
ShopifyCheckoutKit.configuration.colorScheme = .automatic
46+
}
47+
48+
private func getShopifyCheckoutKit() -> RCTShopifyCheckoutKit {
49+
return RCTShopifyCheckoutKit()
50+
}
51+
52+
/// getConfig
53+
func testReturnsDefaultConfig() {
54+
// Call getConfig and capture the result
55+
var result: [String: Any]?
56+
shopifyCheckoutKit.getConfig({ config in result = config as? [String: Any] }, reject: { _, _, _ in })
57+
58+
// Verify that getConfig returned the expected result
59+
XCTAssertEqual(result?["preloading"] as? Bool, true)
60+
XCTAssertEqual(result?["colorScheme"] as? String, "automatic")
61+
}
62+
63+
/// configure
64+
func testConfigure() {
65+
let configuration: [AnyHashable: Any] = [
66+
"preloading": true,
67+
"colorScheme": "dark",
68+
"colors": [
69+
"ios": [
70+
"spinnerColor": "#FF0000",
71+
"backgroundColor": "#0000FF"
72+
]
73+
]
74+
]
75+
76+
shopifyCheckoutKit.configure(configuration)
77+
78+
XCTAssertTrue(ShopifyCheckoutKit.configuration.preloading.enabled)
79+
XCTAssertEqual(ShopifyCheckoutKit.configuration.colorScheme, .dark)
80+
XCTAssertEqual(ShopifyCheckoutKit.configuration.spinnerColor, UIColor(hex: "#FF0000"))
81+
XCTAssertEqual(ShopifyCheckoutKit.configuration.backgroundColor, UIColor(hex: "#0000FF"))
82+
}
83+
84+
func testConfigureWithPartialConfig() {
85+
let configuration: [AnyHashable: Any] = [
86+
"preloading": false
87+
]
88+
89+
shopifyCheckoutKit.configure(configuration)
90+
91+
XCTAssertFalse(ShopifyCheckoutKit.configuration.preloading.enabled)
92+
}
93+
94+
func testConfigureWithInvalidColors() {
95+
let configuration: [AnyHashable: Any] = [
96+
"colors": [
97+
"ios": [
98+
"spinnerColor": "invalid"
99+
]
100+
]
101+
]
102+
103+
let defaultColorFallback = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
104+
shopifyCheckoutKit.configure(configuration)
105+
106+
XCTAssertEqual(ShopifyCheckoutKit.configuration.spinnerColor, defaultColorFallback)
107+
}
108+
109+
/// checkoutDidComplete
110+
func testCheckoutDidCompleteSendsEvent() {
111+
let mock = mockSendEvent(eventName: "completed")
112+
113+
mock.startObserving()
114+
mock.checkoutDidComplete()
115+
116+
XCTAssertTrue(mock.didSendEvent)
117+
}
118+
119+
/// checkoutDidCancel
120+
func testCheckoutDidCancelSendsEvent() {
121+
let mock = mockAsyncSendEvent(eventName: "close")
122+
123+
let expectation = self.expectation(description: "CheckoutDidCancel")
124+
125+
mock.sendEventImplementation = { name, _ in
126+
if name == "close" {
127+
mock.didSendEvent = true
128+
expectation.fulfill()
129+
}
130+
}
131+
132+
mock.startObserving()
133+
mock.checkoutDidCancel()
134+
135+
// Wait for the expectation to be fulfilled
136+
waitForExpectations(timeout: 1, handler: nil)
137+
138+
XCTAssertTrue(mock.didSendEvent)
139+
}
140+
141+
/// checkoutDidFail
142+
func testCheckoutDidFailSendsEvent() {
143+
let mock = mockSendEvent(eventName: "error")
144+
145+
mock.startObserving()
146+
let error = CheckoutError.checkoutExpired(message: "Checkout expired")
147+
mock.checkoutDidFail(error: error)
148+
149+
XCTAssertTrue(mock.didSendEvent)
150+
if let eventBody = mock.eventBody as? [String: Any], let message = eventBody["message"] as? String {
151+
XCTAssertEqual(message, error.localizedDescription)
152+
} else {
153+
XCTFail("Failed to get the message from eventBody")
154+
}
155+
}
156+
157+
private func mockSendEvent(eventName: String) -> RCTShopifyCheckoutKitMock {
158+
let mock = RCTShopifyCheckoutKitMock()
159+
mock.eventName = eventName
160+
return mock
161+
}
162+
163+
private func mockAsyncSendEvent(eventName: String) -> AsyncRCTShopifyCheckoutKitMock {
164+
let mock = AsyncRCTShopifyCheckoutKitMock()
165+
mock.eventName = eventName
166+
return mock
167+
}
168+
}
169+
170+
class RCTShopifyCheckoutKitMock: RCTShopifyCheckoutKit {
171+
var didSendEvent = false
172+
var eventName: String?
173+
var eventBody: Any!
174+
175+
override func sendEvent(withName name: String!, body: Any!) {
176+
if name == self.eventName {
177+
didSendEvent = true
178+
eventBody = body
179+
}
180+
}
181+
}
182+
183+
class AsyncRCTShopifyCheckoutKitMock: RCTShopifyCheckoutKit {
184+
var didSendEvent = false
185+
var eventName: String?
186+
var sendEventImplementation: ((String?, Any?) -> Void)?
187+
188+
override func sendEvent(withName name: String!, body: Any!) {
189+
sendEventImplementation?(name, body)
190+
}
191+
}

sample/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"ios": "react-native run-ios --simulator 'iPhone 14 Pro'",
1313
"start": "react-native start",
1414
"typecheck": "tsc --noEmit",
15+
"test:ios": "sh ./scripts/test_ios",
1516
"test:android": "sh ./scripts/test_android"
1617
},
1718
"dependencies": {

sample/scripts/test_ios

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
3+
set -ex
4+
set -eo pipefail
5+
6+
if [[ -n $CURRENT_SIMULATOR_UUID ]]; then
7+
dest="id=$CURRENT_SIMULATOR_UUID"
8+
else
9+
dest="platform=iOS Simulator,name=iPhone 14 Pro Max"
10+
fi
11+
12+
cd ios
13+
14+
xcodebuild build-for-testing test -workspace ReactNative.xcworkspace -scheme ReactNative -destination "$dest" -skipPackagePluginValidation -sdk iphonesimulator ASSETCATALOG_COMPILER_OPTIMIZATION=time COMPILER_INDEX_STORE_ENABLE=NO | xcpretty -c

turbo.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"$schema": "https://turbo.build/schema.json",
33
"pipeline": {
4-
"lint": {
5-
"dependsOn": ["build"]
6-
},
74
"build": {
85
"outputs": ["modules/react-native-shopify-checkout-kit/lib/**/*"]
96
},
7+
"lint": {
8+
"dependsOn": ["build"]
9+
},
1010
"build:android": {
1111
"dependsOn": ["build", "lint"],
1212
"outputs": ["sample/android/app/build"]
@@ -15,6 +15,9 @@
1515
"dependsOn": ["build", "lint"],
1616
"outputs": ["sample/ios/build"]
1717
},
18+
"test:ios": {
19+
"dependsOn": ["build", "lint"]
20+
},
1821
"test:android": {
1922
"dependsOn": ["build", "lint"]
2023
}

0 commit comments

Comments
 (0)