IDPROD-2739 part 2: Screen routing tests (#457)
Added test coverage to `VerificationSheetFlowControllerTest` and `VerificationSheetControllerTest`
This commit is contained in:
parent
503486a3a9
commit
6cc01e7a98
|
@ -41,6 +41,7 @@
|
|||
E6548EE62728AFB400F399B2 /* TestConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548EE52728AFB400F399B2 /* TestConstants.swift */; };
|
||||
E6548EE82728D39600F399B2 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548EE72728D39500F399B2 /* Async.swift */; };
|
||||
E6548EF22729BDD700F399B2 /* MockData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548EF12729BDD700F399B2 /* MockData.swift */; };
|
||||
E6548F7F27339FB100F399B2 /* XCTestCase+Stripe.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F7E27339FB100F399B2 /* XCTestCase+Stripe.swift */; };
|
||||
E6598C8126952BC500278740 /* STPLocalizationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6598C7E26952BC500278740 /* STPLocalizationUtils.swift */; };
|
||||
E6598C8F269615E000278740 /* STPLocalizedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6598C8E269615E000278740 /* STPLocalizedString.swift */; };
|
||||
E6598CAC2696177B00278740 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E6598C922696177B00278740 /* Localizable.strings */; };
|
||||
|
@ -145,6 +146,7 @@
|
|||
E6548EE52728AFB400F399B2 /* TestConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConstants.swift; sourceTree = "<group>"; };
|
||||
E6548EE72728D39500F399B2 /* Async.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Async.swift; sourceTree = "<group>"; };
|
||||
E6548EF12729BDD700F399B2 /* MockData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockData.swift; sourceTree = "<group>"; };
|
||||
E6548F7E27339FB100F399B2 /* XCTestCase+Stripe.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTestCase+Stripe.swift"; sourceTree = "<group>"; };
|
||||
E6598C7E26952BC500278740 /* STPLocalizationUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = STPLocalizationUtils.swift; sourceTree = "<group>"; };
|
||||
E6598C8E269615E000278740 /* STPLocalizedString.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = STPLocalizedString.swift; sourceTree = "<group>"; };
|
||||
E6598C932696177B00278740 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
|
@ -459,6 +461,7 @@
|
|||
E6FB9BBB268EA95F000FDB4F /* Info.plist */,
|
||||
E6FB9BBA268EA95F000FDB4F /* StripeCoreTestUtils.h */,
|
||||
E6548EE52728AFB400F399B2 /* TestConstants.swift */,
|
||||
E6548F7E27339FB100F399B2 /* XCTestCase+Stripe.swift */,
|
||||
);
|
||||
path = StripeCoreTestUtils;
|
||||
sourceTree = "<group>";
|
||||
|
@ -746,6 +749,7 @@
|
|||
E61ADAA6270B92BD004ED998 /* UIView+StripeCoreTestingUtils.swift in Sources */,
|
||||
E6CDC469269E81CD0020A962 /* MockAnalyticsClient.swift in Sources */,
|
||||
E6548EDF2728AEBE00F399B2 /* APIStubbedTestCase.swift in Sources */,
|
||||
E6548F7F27339FB100F399B2 /* XCTestCase+Stripe.swift in Sources */,
|
||||
E6548EE62728AFB400F399B2 /* TestConstants.swift in Sources */,
|
||||
E6548EF22729BDD700F399B2 /* MockData.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
//
|
||||
// XCTestCase+Stripe.swift
|
||||
// StripeCoreTestUtils
|
||||
//
|
||||
// Created by Mel Ludowise on 11/3/21.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
public extension XCTestCase {
|
||||
func XCTAssertIs<T>(
|
||||
_ item: Any,
|
||||
_ t: T.Type,
|
||||
file: StaticString = #filePath,
|
||||
line: UInt = #line
|
||||
) {
|
||||
XCTAssert(item is T, "\(type(of: item)) is not type \(T.self)", file: file, line: line)
|
||||
}
|
||||
}
|
|
@ -54,6 +54,7 @@
|
|||
E6548F702731ED9800F399B2 /* VerificationSessionDataIDNumber.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F6F2731ED9800F399B2 /* VerificationSessionDataIDNumber.swift */; };
|
||||
E6548F722731EE0E00F399B2 /* VerificationSessionDataName.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F712731EE0E00F399B2 /* VerificationSessionDataName.swift */; };
|
||||
E6548F792733628100F399B2 /* VerificationSheetAPIContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F782733628100F399B2 /* VerificationSheetAPIContent.swift */; };
|
||||
E6548F7D27339AC000F399B2 /* VerificationSheetFlowControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F7C27339AC000F399B2 /* VerificationSheetFlowControllerTest.swift */; };
|
||||
E6548F882734906600F399B2 /* MockIdentityAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F872734906600F399B2 /* MockIdentityAPIClient.swift */; };
|
||||
E6548F8A2734AEBA00F399B2 /* IdentityAPIClientTestMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6548F892734AEBA00F399B2 /* IdentityAPIClientTestMock.swift */; };
|
||||
E6AF1ECA269FD7990091BE99 /* VerificationSheetAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6AF1EC5269FD7980091BE99 /* VerificationSheetAnalytics.swift */; };
|
||||
|
@ -184,6 +185,7 @@
|
|||
E6548F6F2731ED9800F399B2 /* VerificationSessionDataIDNumber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationSessionDataIDNumber.swift; sourceTree = "<group>"; };
|
||||
E6548F712731EE0E00F399B2 /* VerificationSessionDataName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationSessionDataName.swift; sourceTree = "<group>"; };
|
||||
E6548F782733628100F399B2 /* VerificationSheetAPIContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationSheetAPIContent.swift; sourceTree = "<group>"; };
|
||||
E6548F7C27339AC000F399B2 /* VerificationSheetFlowControllerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerificationSheetFlowControllerTest.swift; sourceTree = "<group>"; };
|
||||
E6548F872734906600F399B2 /* MockIdentityAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockIdentityAPIClient.swift; sourceTree = "<group>"; };
|
||||
E6548F892734AEBA00F399B2 /* IdentityAPIClientTestMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityAPIClientTestMock.swift; sourceTree = "<group>"; };
|
||||
E6AF1EC5269FD7980091BE99 /* VerificationSheetAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationSheetAnalytics.swift; sourceTree = "<group>"; };
|
||||
|
@ -449,6 +451,7 @@
|
|||
E6AF1EDE269FD9970091BE99 /* VerificationFlowWebViewTest.swift */,
|
||||
E6AF1EDF269FD9970091BE99 /* VerificationSheetAnalyticsTest.swift */,
|
||||
E6548EF72729BFC500F399B2 /* VerificationSheetControllerTest.swift */,
|
||||
E6548F7C27339AC000F399B2 /* VerificationSheetFlowControllerTest.swift */,
|
||||
);
|
||||
path = Unit;
|
||||
sourceTree = "<group>";
|
||||
|
@ -781,6 +784,7 @@
|
|||
E6AF1EE3269FD9970091BE99 /* IdentityVerificationSheetTest.swift in Sources */,
|
||||
E6AF1EE2269FD9970091BE99 /* VerificationFlowWebViewControllerTest.swift in Sources */,
|
||||
E6AF1EE4269FD9970091BE99 /* VerificationFlowWebViewTest.swift in Sources */,
|
||||
E6548F7D27339AC000F399B2 /* VerificationSheetFlowControllerTest.swift in Sources */,
|
||||
E6548EF82729BFC500F399B2 /* VerificationSheetControllerTest.swift in Sources */,
|
||||
E6AF1EE1269FD9970091BE99 /* VerificationClientSecretTest.swift in Sources */,
|
||||
E606937A270435FF00742859 /* IdentityElementsFactoryTest.swift in Sources */,
|
||||
|
|
|
@ -25,7 +25,7 @@ struct VerificationSheetAPIContent {
|
|||
private(set) var staticContent: VerificationPage? = nil
|
||||
|
||||
/// Server response from the last time the user's data was saved
|
||||
private var sessionData: VerificationSessionData? = nil
|
||||
private(set) var sessionData: VerificationSessionData? = nil
|
||||
|
||||
private(set) var lastError: Error? = nil
|
||||
|
||||
|
|
|
@ -16,8 +16,14 @@ final class VerificationSheetController {
|
|||
let flowController = VerificationSheetFlowController()
|
||||
let dataStore = VerificationSessionDataStore()
|
||||
|
||||
#if DEBUG
|
||||
// Make apiContent settable from tests
|
||||
|
||||
/// Content returned from the API
|
||||
var apiContent = VerificationSheetAPIContent()
|
||||
#else
|
||||
private(set) var apiContent = VerificationSheetAPIContent()
|
||||
#endif
|
||||
|
||||
init(apiClient: IdentityAPIClient = STPAPIClient.shared,
|
||||
addressSpecProvider: AddressSpecProvider = .shared) {
|
||||
|
|
|
@ -30,26 +30,40 @@ final class VerificationSheetFlowController {
|
|||
let nextScreen = nextViewController(apiContent: apiContent, sheetController: sheetController)
|
||||
navigationController.pushViewController(nextScreen, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
private extension VerificationSheetFlowController {
|
||||
/// Instantiates and returns the next view controller to display in the flow.
|
||||
func nextViewController(
|
||||
apiContent: VerificationSheetAPIContent,
|
||||
sheetController: VerificationSheetController
|
||||
) -> UIViewController {
|
||||
if apiContent.lastError != nil {
|
||||
nextViewController(
|
||||
missingRequirements: apiContent.missingRequirements,
|
||||
staticContent: apiContent.staticContent,
|
||||
requiredDataErrors: apiContent.requiredDataErrors,
|
||||
lastError: apiContent.lastError,
|
||||
sheetController: sheetController
|
||||
)
|
||||
}
|
||||
|
||||
func nextViewController(
|
||||
missingRequirements: Set<VerificationPageRequirements.Missing>?,
|
||||
staticContent: VerificationPage?,
|
||||
requiredDataErrors: [VerificationSessionDataRequirementError],
|
||||
lastError: Error?,
|
||||
sheetController: VerificationSheetController
|
||||
) -> UIViewController {
|
||||
if lastError != nil {
|
||||
// TODO(IDPROD-2749): return error screen
|
||||
return LoadingViewController()
|
||||
}
|
||||
|
||||
guard let missingRequirements = apiContent.missingRequirements,
|
||||
let staticContent = apiContent.staticContent else {
|
||||
guard let missingRequirements = missingRequirements,
|
||||
let staticContent = staticContent else {
|
||||
// TODO(IDPROD-2749): return error screen
|
||||
return LoadingViewController()
|
||||
}
|
||||
|
||||
guard apiContent.requiredDataErrors.isEmpty else {
|
||||
guard requiredDataErrors.isEmpty else {
|
||||
// TODO(IDPROD-2749): return error screen
|
||||
return LoadingViewController()
|
||||
}
|
||||
|
|
|
@ -11,10 +11,11 @@ import XCTest
|
|||
final class VerificationSheetControllerTest: XCTestCase {
|
||||
|
||||
let mockSecret = "secret_123"
|
||||
let mockStaticContent = try! VerificationPageMock.response200.make()
|
||||
|
||||
private var controller: VerificationSheetController!
|
||||
private var mockAPIClient: IdentityAPIClientTestMock!
|
||||
private var loadedExp: XCTestExpectation!
|
||||
private var exp: XCTestExpectation!
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
|
@ -22,7 +23,7 @@ final class VerificationSheetControllerTest: XCTestCase {
|
|||
// Mock the api client
|
||||
mockAPIClient = IdentityAPIClientTestMock()
|
||||
controller = VerificationSheetController(apiClient: mockAPIClient)
|
||||
loadedExp = expectation(description: "Controller finished loading")
|
||||
exp = expectation(description: "Finished API call")
|
||||
}
|
||||
|
||||
func testValidVerificationPageResponse() throws {
|
||||
|
@ -30,7 +31,7 @@ final class VerificationSheetControllerTest: XCTestCase {
|
|||
|
||||
// Load
|
||||
controller.load(clientSecret: mockSecret) {
|
||||
self.loadedExp.fulfill()
|
||||
self.exp.fulfill()
|
||||
}
|
||||
|
||||
// Verify 1 request made with secret
|
||||
|
@ -45,7 +46,7 @@ final class VerificationSheetControllerTest: XCTestCase {
|
|||
mockAPIClient.verificationPage.respondToRequests(with: .success(mockResponse))
|
||||
|
||||
// Verify completion block is called
|
||||
wait(for: [loadedExp], timeout: 1)
|
||||
wait(for: [exp], timeout: 1)
|
||||
|
||||
// Verify response updated on controller
|
||||
XCTAssertEqual(controller.apiContent.staticContent, mockResponse)
|
||||
|
@ -57,17 +58,81 @@ final class VerificationSheetControllerTest: XCTestCase {
|
|||
|
||||
// Load
|
||||
controller.load(clientSecret: mockSecret) {
|
||||
self.loadedExp.fulfill()
|
||||
self.exp.fulfill()
|
||||
}
|
||||
|
||||
// Respond to request with error
|
||||
mockAPIClient.verificationPage.respondToRequests(with: .failure(mockError))
|
||||
|
||||
// Verify completion block is called
|
||||
wait(for: [loadedExp], timeout: 1)
|
||||
wait(for: [exp], timeout: 1)
|
||||
|
||||
// Verify error updated on controller
|
||||
XCTAssertNil(controller.apiContent.staticContent)
|
||||
XCTAssertNotNil(controller.apiContent.lastError)
|
||||
}
|
||||
|
||||
func testValidVerificationSessionDataResponse() throws {
|
||||
let mockResponse = try VerificationSessionDataMock.response200.make()
|
||||
|
||||
// Mock that a VerificationPage response has already been received
|
||||
controller.apiContent.setStaticContent(result: .success(mockStaticContent))
|
||||
|
||||
// Mock that the user has entered data
|
||||
controller.dataStore.biometricConsent = true
|
||||
|
||||
// Save data
|
||||
controller.saveData { mutatedApiContent in
|
||||
XCTAssertEqual(mutatedApiContent.sessionData, mockResponse)
|
||||
XCTAssertNil(mutatedApiContent.lastError)
|
||||
self.exp.fulfill()
|
||||
}
|
||||
|
||||
// Verify 1 request made with Id, EAK, and collected data
|
||||
XCTAssertEqual(mockAPIClient.verificationSessionData.requestHistory.count, 1)
|
||||
XCTAssertEqual(mockAPIClient.verificationSessionData.requestHistory.first?.id, mockStaticContent.id)
|
||||
XCTAssertEqual(mockAPIClient.verificationSessionData.requestHistory.first?.ephemeralKey, mockStaticContent.ephemeralApiKey)
|
||||
XCTAssertEqual(mockAPIClient.verificationSessionData.requestHistory.first?.data, controller.dataStore.toAPIModel)
|
||||
|
||||
// Verify response & error are nil until API responds to request
|
||||
XCTAssertNil(controller.apiContent.sessionData)
|
||||
XCTAssertNil(controller.apiContent.lastError)
|
||||
|
||||
// Respond to request with success
|
||||
mockAPIClient.verificationSessionData.respondToRequests(with: .success(mockResponse))
|
||||
|
||||
// Verify completion block is called
|
||||
wait(for: [exp], timeout: 1)
|
||||
|
||||
// Verify response updated on controller
|
||||
XCTAssertEqual(controller.apiContent.sessionData, mockResponse)
|
||||
XCTAssertNil(controller.apiContent.lastError)
|
||||
}
|
||||
|
||||
func testErrorVerificationSessionDataResponse() throws {
|
||||
let mockError = NSError(domain: "", code: 0, userInfo: nil)
|
||||
|
||||
// Mock that a VerificationPage response has already been received
|
||||
controller.apiContent.setStaticContent(result: .success(mockStaticContent))
|
||||
|
||||
// Mock that the user has entered data
|
||||
controller.dataStore.biometricConsent = true
|
||||
|
||||
// Save data
|
||||
controller.saveData { mutatedApiContent in
|
||||
XCTAssertNil(mutatedApiContent.sessionData)
|
||||
XCTAssertNotNil(mutatedApiContent.lastError)
|
||||
self.exp.fulfill()
|
||||
}
|
||||
|
||||
// Respond to request with success
|
||||
mockAPIClient.verificationSessionData.respondToRequests(with: .failure(mockError))
|
||||
|
||||
// Verify completion block is called
|
||||
wait(for: [exp], timeout: 1)
|
||||
|
||||
// Verify response updated on controller
|
||||
XCTAssertNil(controller.apiContent.sessionData)
|
||||
XCTAssertNotNil(controller.apiContent.lastError)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// VerificationSheetFlowControllerTest.swift
|
||||
// StripeIdentityTests
|
||||
//
|
||||
// Created by Mel Ludowise on 11/3/21.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
import StripeCoreTestUtils
|
||||
@testable import StripeIdentity
|
||||
|
||||
final class VerificationSheetFlowControllerTest: XCTestCase {
|
||||
|
||||
let flowController = VerificationSheetFlowController()
|
||||
var mockVerificationPage = try! VerificationPageMock.response200.make()
|
||||
let mockSheetController = VerificationSheetController()
|
||||
|
||||
func testNextViewControllerError() {
|
||||
// TODO(IDPROD-2749): Test against an Error VC instead of Loading
|
||||
|
||||
// API error
|
||||
XCTAssertIs(flowController.nextViewController(
|
||||
missingRequirements: [.biometricConsent],
|
||||
staticContent: mockVerificationPage,
|
||||
requiredDataErrors: [],
|
||||
lastError: NSError(domain: "", code: 0, userInfo: nil),
|
||||
sheetController: mockSheetController
|
||||
), LoadingViewController.self)
|
||||
|
||||
// No requirements
|
||||
XCTAssertIs(flowController.nextViewController(
|
||||
missingRequirements: nil,
|
||||
staticContent: mockVerificationPage,
|
||||
requiredDataErrors: [],
|
||||
lastError: nil,
|
||||
sheetController: mockSheetController
|
||||
), LoadingViewController.self)
|
||||
|
||||
// No staticContent
|
||||
XCTAssertIs(flowController.nextViewController(
|
||||
missingRequirements: [.biometricConsent],
|
||||
staticContent: nil,
|
||||
requiredDataErrors: [],
|
||||
lastError: nil,
|
||||
sheetController: mockSheetController
|
||||
), LoadingViewController.self)
|
||||
|
||||
// requiredDataErrors
|
||||
XCTAssertIs(flowController.nextViewController(
|
||||
missingRequirements: [.biometricConsent],
|
||||
staticContent: mockVerificationPage,
|
||||
requiredDataErrors: [.init(
|
||||
code: .consentDeclined,
|
||||
requirement: .biometricConsent,
|
||||
title: "",
|
||||
body: "",
|
||||
buttonText: "",
|
||||
_allResponseFieldsStorage: nil
|
||||
)],
|
||||
lastError: nil,
|
||||
sheetController: mockSheetController
|
||||
), LoadingViewController.self)
|
||||
}
|
||||
|
||||
func testNextViewControllerSuccess() {
|
||||
// TODO(IDPROD-2759): Test against Success VC instead of Loading
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: []
|
||||
), LoadingViewController.self)
|
||||
}
|
||||
|
||||
func testNextViewControllerBiometricConsent() {
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.biometricConsent]
|
||||
), BiometricConsentViewController.self)
|
||||
}
|
||||
|
||||
func testNextViewControllerIndividualFields() {
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.address]
|
||||
), IndividualViewController.self)
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.dob]
|
||||
), IndividualViewController.self)
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.email]
|
||||
), IndividualViewController.self)
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.idNumber]
|
||||
), IndividualViewController.self)
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.name]
|
||||
), IndividualViewController.self)
|
||||
XCTAssertIs(nextViewController(
|
||||
missingRequirements: [.phoneNumber]
|
||||
), IndividualViewController.self)
|
||||
}
|
||||
}
|
||||
|
||||
private extension VerificationSheetFlowControllerTest {
|
||||
func nextViewController(missingRequirements: Set<VerificationPageRequirements.Missing>) -> UIViewController {
|
||||
return flowController.nextViewController(
|
||||
missingRequirements: missingRequirements,
|
||||
staticContent: mockVerificationPage,
|
||||
requiredDataErrors: [],
|
||||
lastError: nil,
|
||||
sheetController: mockSheetController
|
||||
)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue