diff --git a/bitrise.yml b/bitrise.yml
index 74d25340..5456cdbb 100644
--- a/bitrise.yml
+++ b/bitrise.yml
@@ -260,12 +260,14 @@ workflows:
- complete_all
unit-ios:
steps:
- - xcode-test@5:
- inputs:
- - project_path: dev-app/ios/StripeTerminalReactNativeDevApp.xcworkspace
- - scheme: UnitTests
- - destination: platform=iOS Simulator,name=iPhone 14,OS=latest
- title: xcodebuild iOS Unit Tests
+ - xcode-test@5:
+ inputs:
+ - project_path: dev-app/ios/StripeTerminalReactNativeDevApp.xcworkspace
+ - scheme: UnitTests
+ - destination: platform=iOS Simulator,name=iPhone 14,OS=latest
+ - command: yarn unit-test:ios
+ - cache_local_deps: 'yes'
+ title: xcodebuild iOS Unit Tests
before_run:
- prep_all
- setup_cocoapods
diff --git a/ios/Protocols.swift b/ios/Protocols.swift
new file mode 100644
index 00000000..8307f132
--- /dev/null
+++ b/ios/Protocols.swift
@@ -0,0 +1,65 @@
+import StripeTerminal
+
+/**
+ This file includes a collection of protocols that mirror a selection of the StripeTerminal iOS public classes.
+ This enables writing swift unit tests against these protocols since the Terminal iOS SDK
+ classes prevent instantiating them (init and new are annotated as unavailable).
+
+ Note the naming here is exactly as they are named in the native SDK so we can use them interchangeably. We will
+ need to keep them in sync.
+ */
+
+protocol CollectInputsResult {
+ var skipped: Bool { get }
+}
+
+protocol TextResult : CollectInputsResult {
+ var text: String? { get }
+ var toggles: [NSNumber] { get }
+}
+
+protocol NumericResult : CollectInputsResult {
+ var numericString: String? { get }
+ var toggles: [NSNumber] { get }
+}
+
+protocol PhoneResult : CollectInputsResult {
+ var phone: String? { get }
+ var toggles: [NSNumber] { get }
+}
+
+protocol EmailResult : CollectInputsResult {
+ var email: String? { get }
+ var toggles: [NSNumber] { get }
+}
+
+protocol SignatureResult : CollectInputsResult {
+ var signatureSvg: String? { get }
+ var toggles: [NSNumber] { get }
+}
+
+protocol SelectionResult : CollectInputsResult {
+ var selection: String? { get }
+ var toggles: [NSNumber] { get }
+}
+
+extension StripeTerminal.TextResult : TextResult {
+}
+
+extension StripeTerminal.NumericResult : NumericResult {
+}
+
+extension StripeTerminal.PhoneResult : PhoneResult {
+}
+
+extension StripeTerminal.EmailResult : EmailResult {
+}
+
+extension StripeTerminal.SignatureResult : SignatureResult {
+}
+
+extension StripeTerminal.SelectionResult : SelectionResult {
+}
+
+extension StripeTerminal.CollectInputsResult : CollectInputsResult {
+}
diff --git a/ios/Tests/MappersTests.swift b/ios/Tests/MappersTests.swift
index 562e82ce..a8e9339c 100644
--- a/ios/Tests/MappersTests.swift
+++ b/ios/Tests/MappersTests.swift
@@ -19,4 +19,184 @@ final class MappersTests: XCTestCase {
XCTAssertEqual(Mappers.mapFromLocationStatus(.notSet), "notSet")
}
+ func testCollectInputsReturnsMapper() {
+ let textResult = TestableTextResult(skipped: false, text: "Written text from the reader", toggles: [
+ ToggleResult.enabled.rawValue as NSNumber,
+ ToggleResult.skipped.rawValue as NSNumber,
+ ])
+ let numericResult = TestableNumericResult(skipped: false, numericString: "123456", toggles: [
+ ToggleResult.enabled.rawValue as NSNumber,
+ ToggleResult.skipped.rawValue as NSNumber,
+ ])
+ let phoneResult = TestablePhoneResult(skipped: false, phone: "+1 425-555-1234", toggles: [
+ ToggleResult.enabled.rawValue as NSNumber,
+ ToggleResult.skipped.rawValue as NSNumber,
+ ])
+ let emailResult = TestableEmailResult(skipped: false, email: "unit.test@abc.com", toggles: [
+ ToggleResult.enabled.rawValue as NSNumber,
+ ToggleResult.skipped.rawValue as NSNumber,
+ ])
+ let selectionResult = TestableSelectionResult(skipped: false, selection: "Yes", toggles: [
+ ToggleResult.enabled.rawValue as NSNumber,
+ ToggleResult.skipped.rawValue as NSNumber,
+ ])
+ let signatureResult = TestableSignatureResult(skipped: false, signatureSvg: "", toggles: [
+ ToggleResult.enabled.rawValue as NSNumber,
+ ToggleResult.skipped.rawValue as NSNumber,
+ ])
+ var output: NSDictionary = Mappers.mapFromCollectInputsResults([
+ textResult
+ ])
+ XCTAssertNotNil(output.object(forKey: "collectInputResults"))
+ XCTAssertTrue(output["collectInputResults"] is [NSDictionary])
+
+ var results: [NSDictionary] = output["collectInputResults"] as! [NSDictionary]
+
+ XCTAssertEqual(results.count, 1)
+ guard let result = results.first else {
+ XCTFail("CollectInput result should have had a result")
+ return
+ }
+ guard
+ let skipped = result["skipped"] as? Bool,
+ let toggles = result["toggles"] as? [String],
+ let text = result["text"] as? String else {
+ XCTFail("CollectInput TestResult should have had text, skipped and toggles")
+ return
+ }
+ XCTAssertFalse(skipped)
+ XCTAssertEqual(toggles.count, 2)
+ XCTAssertEqual(toggles[0], "enabled")
+ XCTAssertEqual(toggles[1], "skipped")
+ XCTAssertEqual(text, "Written text from the reader")
+
+ output = Mappers.mapFromCollectInputsResults([
+ numericResult, phoneResult, emailResult, selectionResult, signatureResult
+ ])
+ XCTAssertNotNil(output.object(forKey: "collectInputResults"))
+ XCTAssertTrue(output["collectInputResults"] is [NSDictionary])
+
+ results = output["collectInputResults"] as! [NSDictionary]
+
+ let testNumericString = "numericString"
+ let testPhone = "phone"
+ let testEmail = "email"
+ let testSelection = "selection"
+ let testSignatureSvg = "signatureSvg"
+ XCTAssertEqual(results.count, 5)
+ XCTAssertTrue(results[0][testNumericString] != nil)
+ XCTAssertTrue(results[1][testPhone] != nil)
+ XCTAssertTrue(results[2][testEmail] != nil)
+ XCTAssertTrue(results[3][testSelection] != nil)
+ XCTAssertTrue(results[4][testSignatureSvg] != nil)
+
+ for result in results {
+ if ((result.object(forKey: testNumericString)) != nil) {
+ guard
+ let skipped = result["skipped"] as? Bool,
+ let toggles = result["toggles"] as? [String],
+ let numericString = result[testNumericString] as? String else {
+ XCTFail("CollectInput NumericResult should have had numericString, skipped and toggles")
+ return
+ }
+ XCTAssertFalse(skipped)
+ XCTAssertEqual(toggles.count, 2)
+ XCTAssertEqual(toggles[0], "enabled")
+ XCTAssertEqual(toggles[1], "skipped")
+ XCTAssertEqual(numericString, "123456")
+ }
+ if ((result.object(forKey: testPhone)) != nil) {
+ guard
+ let skipped = result["skipped"] as? Bool,
+ let toggles = result["toggles"] as? [String],
+ let phone = result[testPhone] as? String else {
+ XCTFail("CollectInput PhoneResult should have had phone, skipped and toggles")
+ return
+ }
+ XCTAssertFalse(skipped)
+ XCTAssertEqual(toggles.count, 2)
+ XCTAssertEqual(toggles[0], "enabled")
+ XCTAssertEqual(toggles[1], "skipped")
+ XCTAssertEqual(phone, "+1 425-555-1234")
+ }
+ if ((result.object(forKey: testEmail)) != nil) {
+ guard
+ let skipped = result["skipped"] as? Bool,
+ let toggles = result["toggles"] as? [String],
+ let email = result[testEmail] as? String else {
+ XCTFail("CollectInput EmailResult should have had email, skipped and toggles")
+ return
+ }
+ XCTAssertFalse(skipped)
+ XCTAssertEqual(toggles.count, 2)
+ XCTAssertEqual(toggles[0], "enabled")
+ XCTAssertEqual(toggles[1], "skipped")
+ XCTAssertEqual(email, "unit.test@abc.com")
+ }
+ if ((result.object(forKey: testSelection)) != nil) {
+ guard
+ let skipped = result["skipped"] as? Bool,
+ let toggles = result["toggles"] as? [String],
+ let selection = result[testSelection] as? String else {
+ XCTFail("CollectInput SelectionResult should have had selection, skipped and toggles")
+ return
+ }
+ XCTAssertFalse(skipped)
+ XCTAssertEqual(toggles.count, 2)
+ XCTAssertEqual(toggles[0], "enabled")
+ XCTAssertEqual(toggles[1], "skipped")
+ XCTAssertEqual(selection, "Yes")
+ }
+ if ((result.object(forKey: testSignatureSvg)) != nil) {
+ guard
+ let skipped = result["skipped"] as? Bool,
+ let toggles = result["toggles"] as? [String],
+ let signatureSvg = result[testSignatureSvg] as? String else {
+ XCTFail("CollectInput SignatureResult should have had signatureSvg, skipped and toggles")
+ return
+ }
+ XCTAssertFalse(skipped)
+ XCTAssertEqual(toggles.count, 2)
+ XCTAssertEqual(toggles[0], "enabled")
+ XCTAssertEqual(toggles[1], "skipped")
+ XCTAssertEqual(signatureSvg, "")
+ }
+ }
+ }
+}
+
+struct TestableTextResult : stripe_terminal_react_native.TextResult {
+ var skipped: Bool
+ var text: String?
+ var toggles: [NSNumber]
+}
+
+struct TestableNumericResult : stripe_terminal_react_native.NumericResult {
+ var skipped: Bool
+ var numericString: String?
+ var toggles: [NSNumber]
+}
+
+struct TestablePhoneResult : stripe_terminal_react_native.PhoneResult {
+ var skipped: Bool
+ var phone: String?
+ var toggles: [NSNumber]
+}
+
+struct TestableEmailResult : stripe_terminal_react_native.EmailResult {
+ var skipped: Bool
+ var email: String?
+ var toggles: [NSNumber]
+}
+
+struct TestableSelectionResult : stripe_terminal_react_native.SelectionResult {
+ var skipped: Bool
+ var selection: String?
+ var toggles: [NSNumber]
+}
+
+struct TestableSignatureResult : stripe_terminal_react_native.SignatureResult {
+ var skipped: Bool
+ var signatureSvg: String?
+ var toggles: [NSNumber]
}
diff --git a/package.json b/package.json
index 2dae66ff..9f5f8f7f 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"get:testbutler": "curl -f -o ./test-butler-app.apk https://repo1.maven.org/maven2/com/linkedin/testbutler/test-butler-app/2.2.1/test-butler-app-2.2.1.apk",
"docs": "npx typedoc ./src/index.tsx --out ./docs/api-reference --tsconfig ./tsconfig.json --readme none",
"unit-test:android": "cd android && ./gradlew testDebugUnitTest",
+ "unit-test:ios": "xcodebuild test -workspace dev-app/ios/StripeTerminalReactNativeDevApp.xcworkspace -destination 'platform=iOS Simulator,name=iPhone 15' -scheme UnitTests",
"unit-test:js": "jest"
},
"keywords": [