Skip to content

Commit

Permalink
fix: trigger text change events when setting text programmatically (#67)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: when setting text programmatically on an element, you should now expect a `textChange` event and subsequently update of the `valid`, `complete`, `empty` and other change flags.
  • Loading branch information
lcschy authored Oct 19, 2023
1 parent ab7ac93 commit 7fa76e1
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 103 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ public class TextElementUITextField: UITextField, InternalElementProtocol, Eleme
if let validation = validation {
self.isComplete = self.isComplete ?? true && validation(transform(text: newValue))
}

textFieldDidChange()
}
get { nil }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ final class CardExpirationDateUITextFieldTests: XCTestCase {
let invalidDateYearInThePastExpectation = self.expectation(description: "Invalid expiration date with year in the past")
let invalidDateMonthInThePastExpectation = self.expectation(description: "Invalid expiration date with month in the past")

var fieldCleared = false
var incompleteDateExpectationHasBeenFulfilled = false
var invalidDateYearInThePastExpectationHasBeenFulfilled = false

Expand All @@ -58,29 +59,38 @@ final class CardExpirationDateUITextFieldTests: XCTestCase {
print(completion)
} receiveValue: { message in
XCTAssertEqual(message.type, "textChange")
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.valid, false)

if (!incompleteDateExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, false)
incompleteDateExpectation.fulfill()
incompleteDateExpectationHasBeenFulfilled = true
} else if (!invalidDateYearInThePastExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, false)
invalidDateYearInThePastExpectation.fulfill()
invalidDateYearInThePastExpectationHasBeenFulfilled = true
if (fieldCleared) {
XCTAssertEqual(message.empty, true)
fieldCleared = false
} else {
XCTAssertEqual(message.complete, false)
invalidDateMonthInThePastExpectation.fulfill()
XCTAssertEqual(message.empty, false)

if (!incompleteDateExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, false)
incompleteDateExpectation.fulfill()
incompleteDateExpectationHasBeenFulfilled = true
} else if (!invalidDateYearInThePastExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, false)
invalidDateYearInThePastExpectation.fulfill()
invalidDateYearInThePastExpectationHasBeenFulfilled = true
} else {
XCTAssertEqual(message.complete, false)
invalidDateMonthInThePastExpectation.fulfill()
}

}
}.store(in: &cancellables)

let pastYear = getCurrentYear() - 1
let pastMonth = getCurrentMonth() - 1

expirationDateTextField.insertText("1") // incomplete
fieldCleared = true
expirationDateTextField.text = ""
expirationDateTextField.insertText("12/" + String(pastYear)) // year in the past
fieldCleared = true
expirationDateTextField.text = ""
expirationDateTextField.insertText(formatMonth(month: pastMonth) + "/" + String(getCurrentYear())) // month in past

Expand All @@ -94,6 +104,7 @@ final class CardExpirationDateUITextFieldTests: XCTestCase {
let validExpDateFutureMonthExpectation = self.expectation(description: "Valid expiration date - future month")
let validExpDateCurrentMonthAndYear = self.expectation(description: "Valid expiration date - current month and year")

var fieldCleared = false
var futureYearExpectationHasBeenFulfilled = false
var futureMonthExpectationHasBeenFulfilled = false

Expand All @@ -102,29 +113,38 @@ final class CardExpirationDateUITextFieldTests: XCTestCase {
print(completion)
} receiveValue: { message in
XCTAssertEqual(message.type, "textChange")
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.valid, true)

if (!futureYearExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
validExpDateFutureYearExpectation.fulfill()
futureYearExpectationHasBeenFulfilled = true
} else if (!futureMonthExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
validExpDateFutureMonthExpectation.fulfill()
futureMonthExpectationHasBeenFulfilled = true
if (fieldCleared) {
XCTAssertEqual(message.empty, true)
XCTAssertEqual(message.valid, false)
fieldCleared = false
} else {
XCTAssertEqual(message.complete, true)
validExpDateCurrentMonthAndYear.fulfill()
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.valid, true)

if (!futureYearExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
validExpDateFutureYearExpectation.fulfill()
futureYearExpectationHasBeenFulfilled = true
} else if (!futureMonthExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
validExpDateFutureMonthExpectation.fulfill()
futureMonthExpectationHasBeenFulfilled = true
} else {
XCTAssertEqual(message.complete, true)
validExpDateCurrentMonthAndYear.fulfill()
}
}
}.store(in: &cancellables)

let futureYear = getCurrentYear() + 1
let futureMonth = getCurrentMonth() + 1

expirationDateTextField.insertText(formatMonth(month: getCurrentMonth()) + "/" + String(futureYear)) // future year
fieldCleared = true
expirationDateTextField.text = ""
expirationDateTextField.insertText(formatMonth(month: futureMonth) + "/" + String(getCurrentYear())) // future month
fieldCleared = true
expirationDateTextField.text = ""
expirationDateTextField.insertText(formatMonth(month: getCurrentMonth()) + "/" + String(getCurrentYear())) // current month/year

Expand Down Expand Up @@ -248,13 +268,13 @@ final class CardExpirationDateUITextFieldTests: XCTestCase {
TokensAPI.getByIdWithRequestBuilder(id: createdToken["id"] as! String).addHeader(name: "BT-API-KEY", value: privateApiKey).execute { result in
do {
let token = try result.get().body.data!.value as! [String: Any]
XCTAssertEqual(token["fullYear"] as! String, expectedYear)
XCTAssertEqual(token["singleDigitMonth"] as! String, "\(expectedMonth.suffix(1))")
XCTAssertEqual(token["dubleDigitMonth"] as! String, expectedMonth)
XCTAssertEqual(token["fullYearMonth"] as! String, "\(expectedYear)\(expectedMonth)")
XCTAssertEqual(token["monthDashFullYear"] as! String, "\(expectedMonth)-\(expectedYear)")
XCTAssertEqual(token["monthForwardSlashTwoDigitYear"] as! String, "\(expectedMonth)/\(expectedYear.suffix(2))")
XCTAssertEqual(token["fullYearDashMonth"] as! String, "\(expectedYear)-\(expectedMonth)")
XCTAssertEqual(token["fullYear"] as! String, expectedYear)
XCTAssertEqual(token["singleDigitMonth"] as! String, "\(expectedMonth.suffix(1))")
XCTAssertEqual(token["dubleDigitMonth"] as! String, expectedMonth)
XCTAssertEqual(token["fullYearMonth"] as! String, "\(expectedYear)\(expectedMonth)")
XCTAssertEqual(token["monthDashFullYear"] as! String, "\(expectedMonth)-\(expectedYear)")
XCTAssertEqual(token["monthForwardSlashTwoDigitYear"] as! String, "\(expectedMonth)/\(expectedYear.suffix(2))")
XCTAssertEqual(token["fullYearDashMonth"] as! String, "\(expectedYear)-\(expectedMonth)")


idQueryExpectation.fulfill()
Expand Down
159 changes: 90 additions & 69 deletions IntegrationTester/UnitTests/CardNumberUITextFieldTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,43 +20,53 @@ final class CardNumberUITextFieldTests: XCTestCase {
let incompleteNumberExpectation = self.expectation(description: "Incomplete card number")
let luhnInvalidNumberExpectation = self.expectation(description: "Luhn invalid card")

var fieldCleared = false
var incompleteNumberExpectationHasBeenFulfilled = false

var cancellables = Set<AnyCancellable>()
cardNumberTextField.subject.sink { completion in
print(completion)
} receiveValue: { message in
XCTAssertEqual(message.type, "textChange")
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.valid, false)

let eventDetails = message.details as [ElementEventDetails]
let brandDetails = eventDetails[0]

XCTAssertEqual(brandDetails.type, "cardBrand")
XCTAssertEqual(brandDetails.message, "visa")

// assert metadta
XCTAssertEqual(cardNumberTextField.metadata.empty, false)
XCTAssertEqual(cardNumberTextField.metadata.valid, false)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "visa")

if (!incompleteNumberExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, false) // mask incomplete and number is invalid
XCTAssertEqual(eventDetails.count, 1)
incompleteNumberExpectation.fulfill()
incompleteNumberExpectationHasBeenFulfilled = true
if (fieldCleared) {
XCTAssertEqual(message.empty, true)
fieldCleared = false
} else {
XCTAssertEqual(message.complete, false) // mask completed but number invalid
XCTAssertEqual(eventDetails.count, 1)
let eventDetails = message.details as [ElementEventDetails]
let brandDetails = eventDetails[0]

XCTAssertEqual(brandDetails.type, "cardBrand")
XCTAssertEqual(brandDetails.message, "visa")

// assert metadta
XCTAssertEqual(cardNumberTextField.metadata.empty, false)
XCTAssertEqual(cardNumberTextField.metadata.valid, false)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "visa")

luhnInvalidNumberExpectation.fulfill()
if (!incompleteNumberExpectationHasBeenFulfilled) {
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.complete, false) // mask incomplete and number is invalid
XCTAssertEqual(eventDetails.count, 1)
incompleteNumberExpectation.fulfill()
incompleteNumberExpectationHasBeenFulfilled = true
} else {
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.complete, false) // mask completed but number invalid
XCTAssertEqual(eventDetails.count, 1)

luhnInvalidNumberExpectation.fulfill()
}
}

}.store(in: &cancellables)

cardNumberTextField.insertText("4129")
fieldCleared = true
cardNumberTextField.text = ""
cardNumberTextField.insertText("4129939187355598") // luhn invalid
fieldCleared = true
cardNumberTextField.text = ""

waitForExpectations(timeout: 1, handler: nil)
Expand All @@ -69,6 +79,7 @@ final class CardNumberUITextFieldTests: XCTestCase {
let validMasterCardNumberExpectation = self.expectation(description: "Valid mastercard card number")
let validAmexCardNumberExpectation = self.expectation(description: "Valid amex card number")

var fieldCleared = false
var visaExpectationHasBeenFulfilled = false
var mastercardExpectationHasBeenFulfilled = false

Expand All @@ -77,67 +88,77 @@ final class CardNumberUITextFieldTests: XCTestCase {
print(completion)
} receiveValue: { message in
XCTAssertEqual(message.type, "textChange")
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.valid, true)

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.empty, false)
XCTAssertEqual(cardNumberTextField.metadata.valid, true)

let eventDetails = message.details as [ElementEventDetails]
let brandDetails = eventDetails[0]
let last4Details = eventDetails[1]
let binDetails = eventDetails[2]

XCTAssertEqual(brandDetails.type, "cardBrand")
XCTAssertEqual(last4Details.type, "cardLast4")
XCTAssertEqual(binDetails.type, "cardBin")

if (!visaExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
XCTAssertEqual(brandDetails.message, "visa")
XCTAssertEqual(last4Details.message, "4242")
XCTAssertEqual(binDetails.message, "42424242")
if (fieldCleared) {
XCTAssertEqual(message.empty, true)
XCTAssertEqual(message.valid, false)
fieldCleared = false
} else {
XCTAssertEqual(message.empty, false)
XCTAssertEqual(message.valid, true)

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.complete, true)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "visa")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardLast4, "4242")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBin, "42424242")
XCTAssertEqual(cardNumberTextField.metadata.empty, false)
XCTAssertEqual(cardNumberTextField.metadata.valid, true)

validVisaCardNumberExpectation.fulfill()
visaExpectationHasBeenFulfilled = true
} else if (!mastercardExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
XCTAssertEqual(brandDetails.message, "mastercard")
XCTAssertEqual(last4Details.message, "5717")
XCTAssertEqual(binDetails.message, "54544229")
let eventDetails = message.details as [ElementEventDetails]
let brandDetails = eventDetails[0]
let last4Details = eventDetails[1]
let binDetails = eventDetails[2]

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.complete, true)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "mastercard")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardLast4, "5717")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBin, "54544229")
validMasterCardNumberExpectation.fulfill()
mastercardExpectationHasBeenFulfilled = true
} else {
XCTAssertEqual(message.complete, true)
XCTAssertEqual(brandDetails.message, "americanExpress")
XCTAssertEqual(last4Details.message, "8868")
XCTAssertEqual(binDetails.message, "348570")
XCTAssertEqual(brandDetails.type, "cardBrand")
XCTAssertEqual(last4Details.type, "cardLast4")
XCTAssertEqual(binDetails.type, "cardBin")

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.complete, true)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "americanExpress")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardLast4, "8868")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBin, "348570")
validAmexCardNumberExpectation.fulfill()
if (!visaExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
XCTAssertEqual(brandDetails.message, "visa")
XCTAssertEqual(last4Details.message, "4242")
XCTAssertEqual(binDetails.message, "42424242")

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.complete, true)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "visa")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardLast4, "4242")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBin, "42424242")

validVisaCardNumberExpectation.fulfill()
visaExpectationHasBeenFulfilled = true
} else if (!mastercardExpectationHasBeenFulfilled) {
XCTAssertEqual(message.complete, true)
XCTAssertEqual(brandDetails.message, "mastercard")
XCTAssertEqual(last4Details.message, "5717")
XCTAssertEqual(binDetails.message, "54544229")

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.complete, true)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "mastercard")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardLast4, "5717")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBin, "54544229")
validMasterCardNumberExpectation.fulfill()
mastercardExpectationHasBeenFulfilled = true
} else {
XCTAssertEqual(message.complete, true)
XCTAssertEqual(brandDetails.message, "americanExpress")
XCTAssertEqual(last4Details.message, "8868")
XCTAssertEqual(binDetails.message, "348570")

// assert metadata
XCTAssertEqual(cardNumberTextField.metadata.complete, true)
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBrand, "americanExpress")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardLast4, "8868")
XCTAssertEqual(cardNumberTextField.cardMetadata.cardBin, "348570")
validAmexCardNumberExpectation.fulfill()
}
}
}.store(in: &cancellables)

cardNumberTextField.insertText("4242424242424242")
fieldCleared = true
cardNumberTextField.text = ""
cardNumberTextField.insertText("5454422955385717")
fieldCleared = true
cardNumberTextField.text = ""
cardNumberTextField.insertText("348570250878868")

Expand Down Expand Up @@ -211,7 +232,7 @@ final class CardNumberUITextFieldTests: XCTestCase {
let proxyKey = Configuration.getConfiguration().proxyKey!
let proxyExpectation = self.expectation(description: "Throws before proxy")
let proxyHttpRequest = ProxyHttpRequest(method: .post, body: body)

BasisTheoryElements.proxy(
apiKey: privateBtApiKey,
proxyKey: proxyKey,
Expand Down
Loading

0 comments on commit 7fa76e1

Please sign in to comment.