From 9c1406f85ddde167a3d3ca4e38ae9201f606141f Mon Sep 17 00:00:00 2001 From: Nathan Schott Date: Wed, 31 Jan 2024 10:44:44 -0500 Subject: [PATCH] test: expose view model flush method for consistent tests (#24) --- .../PayPalMessageModalViewModel.swift | 23 ++++--- .../PayPalMessageViewModel.swift | 19 ++++-- .../PayPalMessageModalViewModelTests.swift | 8 +-- .../PayPalMessageViewModelTests.swift | 63 +++++++++++-------- 4 files changed, 66 insertions(+), 47 deletions(-) diff --git a/Sources/PayPalMessages/PayPalMessageModalViewModel.swift b/Sources/PayPalMessages/PayPalMessageModalViewModel.swift index eddf9c1..040f517 100644 --- a/Sources/PayPalMessages/PayPalMessageModalViewModel.swift +++ b/Sources/PayPalMessages/PayPalMessageModalViewModel.swift @@ -198,17 +198,24 @@ class PayPalMessageModalViewModel: NSObject, WKNavigationDelegate, WKScriptMessa withTimeInterval: queueTimeInterval, repeats: false ) { _ in - guard let jsonData = try? JSONEncoder().encode(self.makeConfig()), - let jsonString = String(data: jsonData, encoding: .utf8) else { return } + self.flushUpdates() + } + } - log(.debug, "Update props: \(jsonString)") + // Exposed internally for tests + func flushUpdates() { + guard let jsonData = try? JSONEncoder().encode(self.makeConfig()), + let jsonString = String(data: jsonData, encoding: .utf8) else { return } - self.webView.evaluateJavaScript( - "window.actions.updateProps(\(jsonString))" - ) { _, _ in - // TODO: Does the JS error text get returned here? - } + log(.debug, "Update props: \(jsonString)") + + self.webView.evaluateJavaScript( + "window.actions.updateProps(\(jsonString))" + ) { _, _ in + // TODO: Does the JS error text get returned here? } + + queuedTimer?.invalidate() } func userContentController( diff --git a/Sources/PayPalMessages/PayPalMessageViewModel.swift b/Sources/PayPalMessages/PayPalMessageViewModel.swift index 1d4e821..fa91736 100644 --- a/Sources/PayPalMessages/PayPalMessageViewModel.swift +++ b/Sources/PayPalMessages/PayPalMessageViewModel.swift @@ -194,12 +194,7 @@ class PayPalMessageViewModel: PayPalMessageModalEventDelegate { withTimeInterval: queueTimeInterval, repeats: false ) { _ in - if self.fetchMessageContentPending { - self.fetchMessageContent() - self.fetchMessageContentPending = false - } else { - self.delegate?.refreshContent(messageParameters: self.messageParameters) - } + self.flushUpdates() } if fireImmediately { @@ -207,6 +202,18 @@ class PayPalMessageViewModel: PayPalMessageModalEventDelegate { } } + // Exposed internally for tests + func flushUpdates() { + if fetchMessageContentPending { + fetchMessageContent() + fetchMessageContentPending = false + } else { + delegate?.refreshContent(messageParameters: self.messageParameters) + } + + queuedTimer?.invalidate() + } + /// Refreshes the Message content only if there's a new amount or logo type set private func fetchMessageContent() { if let stateDelegate, let messageView { diff --git a/Tests/PayPalMessagesTests/PayPalMessageModalViewModelTests.swift b/Tests/PayPalMessagesTests/PayPalMessageModalViewModelTests.swift index cd69b29..1d439d5 100644 --- a/Tests/PayPalMessagesTests/PayPalMessageModalViewModelTests.swift +++ b/Tests/PayPalMessagesTests/PayPalMessageModalViewModelTests.swift @@ -71,9 +71,7 @@ final class PayPalMessageModalViewModelTests: XCTestCase { } func testUpdateConfig() { - let expectation = expectation(description: "Evaluate JavaScript Callback") let (viewModel, webView, _, _) = makePayPalMessageModalViewModel() - webView.evaluateJavaScriptCallback = { _ in expectation.fulfill() } XCTAssertNil(viewModel.amount) XCTAssertNil(viewModel.offerType) @@ -92,7 +90,7 @@ final class PayPalMessageModalViewModelTests: XCTestCase { XCTAssertFalse(webView.evaluateJavaScriptCalled) - waitForExpectations(timeout: 0.5) + viewModel.flushUpdates() XCTAssertTrue(webView.evaluateJavaScriptCalled) @@ -122,9 +120,7 @@ final class PayPalMessageModalViewModelTests: XCTestCase { } func testUpdateIndividualProperties() { - let expectation = expectation(description: "Evaluate JavaScript Callback") let (viewModel, webView, _, _) = makePayPalMessageModalViewModel() - webView.evaluateJavaScriptCallback = { _ in expectation.fulfill() } XCTAssertNil(viewModel.amount) XCTAssertNil(viewModel.offerType) @@ -137,7 +133,7 @@ final class PayPalMessageModalViewModelTests: XCTestCase { XCTAssertFalse(webView.evaluateJavaScriptCalled) - waitForExpectations(timeout: 0.5) + viewModel.flushUpdates() let expectedJSONString = "{\"client_id\":\"testclientid\",\"amount\":300,\"offer\":\"PAYPAL_CREDIT_NO_INTEREST\"}" diff --git a/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift b/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift index bf86093..4856aaf 100644 --- a/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift +++ b/Tests/PayPalMessagesTests/PayPalMessageViewModelTests.swift @@ -99,8 +99,9 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.amount = newAmount XCTAssertEqual(viewModel.amount, newAmount) - // verify a request has been performed - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } func testSimplePlacementUpdate() { @@ -119,8 +120,9 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.placement = newValue XCTAssertEqual(viewModel.placement, newValue) - // verify a request has been performed - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } func testSimpleOfferTypeUpdate() { @@ -139,8 +141,9 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.offerType = newValue XCTAssertEqual(viewModel.offerType, newValue) - // verify a request has been performed - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } func testBuyerCountryTypeUpdate() { @@ -159,8 +162,9 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.buyerCountry = newValue XCTAssertEqual(viewModel.buyerCountry, newValue) - // verify a request has been performed - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } func testSimpleLogoTypeUpdate() { @@ -181,8 +185,9 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.logoType = newValue XCTAssertEqual(viewModel.logoType, newValue) - // verify a request has been performed - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } func testSimpleColorUpdate() { @@ -203,8 +208,10 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.color = newValue XCTAssertEqual(viewModel.color, newValue) + viewModel.flushUpdates() + // verify a request has NOT been performed as color changes shouldn't trigger them - assert(mockedRequest, calledTimes: 1) + XCTAssertEqual(mockedRequest.requestsPerformed, 1) } func testSimpleAlignmentUpdate() { @@ -225,8 +232,10 @@ final class PayPalMessageViewModelTests: XCTestCase { viewModel.alignment = newValue XCTAssertEqual(viewModel.alignment, newValue) + viewModel.flushUpdates() + // verify a request has NOT been performed as alignment changes shouldn't trigger them - assert(mockedRequest, calledTimes: 1) + XCTAssertEqual(mockedRequest.requestsPerformed, 1) } // MARK: - Test Duplicated Value Updates @@ -246,12 +255,16 @@ final class PayPalMessageViewModelTests: XCTestCase { let newAmount = Double.random(in: 0...1000) viewModel.amount = newAmount - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) // set the same amount again, verify another redundant request hasn't been performed viewModel.amount = newAmount - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } // MARK: - Test Update In Progress Cases @@ -309,7 +322,9 @@ final class PayPalMessageViewModelTests: XCTestCase { let newerAmount = newAmount - 1 viewModel.amount = newerAmount - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) } func testUpdateInProgressFromConfig() { @@ -333,13 +348,17 @@ final class PayPalMessageViewModelTests: XCTestCase { let newAmount = Double.random(in: 1...1000) viewModel.amount = newAmount - assert(mockedRequest, calledTimes: 2) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 2) // test a new config being set overrides the update in progress flag and triggers and update let newConfig = PayPalMessageConfig(data: .init(clientID: "testclientid", environment: .live)) viewModel.config = newConfig - assert(mockedRequest, calledTimes: 3) + viewModel.flushUpdates() + + XCTAssertEqual(mockedRequest.requestsPerformed, 3) } // MARK: - Test Merchant Provider @@ -364,16 +383,6 @@ final class PayPalMessageViewModelTests: XCTestCase { XCTAssertNotNil(viewModel.messageParameters) } - // MARK: - Helpers - - private func assert(_ mockRequest: PayPalMessageRequestMock, calledTimes count: Int) { - let predicate = NSPredicate { _, _ in - return mockRequest.requestsPerformed == count - } - let expectation = XCTNSPredicateExpectation(predicate: predicate, object: mockRequest) - wait(for: [expectation], timeout: 2) - } - private func makePayPalMessageViewModel( mockedView: PayPalMessageViewMock = PayPalMessageViewMock(), mockedDelegate: PayPalMessageViewDelegateMock = PayPalMessageViewDelegateMock(),