diff --git a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift index 4f13e0aefe7..8378f68bee4 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSaleAggregateModel.swift @@ -111,7 +111,7 @@ protocol PointOfSaleAggregateModelProtocol { popularPurchasableItemsController: PointOfSaleItemsControllerProtocol, barcodeScanService: PointOfSaleBarcodeScanServiceProtocol, soundPlayer: PointOfSaleSoundPlayerProtocol = PointOfSaleSoundPlayer(), - paymentState: PointOfSalePaymentState = .card(.idle)) { + paymentState: PointOfSalePaymentState = .idle) { self.purchasableItemsController = itemsController self.purchasableItemsSearchController = purchasableItemsSearchController self.couponsController = couponsController @@ -177,7 +177,7 @@ extension PointOfSaleAggregateModel { private func setStateForEditing() { orderStage = .building - paymentState = .card(.idle) + paymentState = .idle cardPresentPaymentInlineMessage = nil } } @@ -337,20 +337,20 @@ extension PointOfSaleAggregateModel { func startCashPayment() async { analytics.track(.pointOfSaleCashPaymentTapped) try? await cardPresentPaymentService.cancelPayment() - paymentState = .cash(.collectingCash) + paymentState.cash = .collectingCash } @MainActor func cancelCashPayment() async { analytics.track(.pointOfSaleBackToCheckoutFromCashTapped) - paymentState = .card(.idle) + paymentState.cash = .idle if case .connected = cardReaderConnectionStatus { await collectCardPayment() } } private func cashPaymentSuccess() { - paymentState = .cash(.paymentSuccess) + paymentState.cash = .paymentSuccess collectOrderPaymentAnalyticsTracker.trackSuccessfulCashPayment() } @@ -475,24 +475,24 @@ private extension PointOfSaleAggregateModel { .store(in: &cancellables) cardPresentPaymentService.paymentEventPublisher - .compactMap { [weak self] paymentEvent -> PointOfSalePaymentState? in + .compactMap { [weak self] paymentEvent -> PointOfSaleCardPaymentState? in guard let self else { return nil } - let newPaymentState = PointOfSalePaymentState(from: paymentEvent, - using: presentationStyleDeterminerDependencies) + let newCardPaymentState = PointOfSaleCardPaymentState(from: paymentEvent, + using: presentationStyleDeterminerDependencies) - if case .card(.acceptingCard) = newPaymentState { + if case .acceptingCard = newCardPaymentState { collectOrderPaymentAnalyticsTracker.trackCardReaderReady() } - if case .card(.processingPayment) = newPaymentState { + if case .processingPayment = newCardPaymentState { collectOrderPaymentAnalyticsTracker.trackCardReaderTapped() } - return newPaymentState + return newCardPaymentState } - .sink(receiveValue: { [weak self] paymentState in - self?.paymentState = paymentState + .sink(receiveValue: { [weak self] cardPaymentState in + self?.paymentState.card = cardPaymentState }) .store(in: &cancellables) diff --git a/WooCommerce/Classes/POS/Models/PointOfSalePaymentState.swift b/WooCommerce/Classes/POS/Models/PointOfSalePaymentState.swift index ff25b76859f..240813966b1 100644 --- a/WooCommerce/Classes/POS/Models/PointOfSalePaymentState.swift +++ b/WooCommerce/Classes/POS/Models/PointOfSalePaymentState.swift @@ -1,8 +1,33 @@ import Foundation -enum PointOfSalePaymentState: Equatable { - case card(PointOfSaleCardPaymentState) - case cash(PointOfSaleCashPaymentState) +struct PointOfSalePaymentState: Equatable { + var card: PointOfSaleCardPaymentState + var cash: PointOfSaleCashPaymentState + + init(card: PointOfSaleCardPaymentState, cash: PointOfSaleCashPaymentState) { + self.card = card + self.cash = cash + } + + static var idle: PointOfSalePaymentState { + .init(card: .idle, cash: .idle) + } + + var activePaymentMethod: PointOfSalePaymentMethod { + if cash != .idle { + return .cash + } + return .card + } + + var shownFullScreen: Bool { + switch activePaymentMethod { + case .cash: + return cash.shownFullScreen + case .card: + return card.shownFullScreen + } + } } enum PointOfSaleCardPaymentState: Equatable { @@ -19,43 +44,44 @@ enum PointOfSaleCardPaymentState: Equatable { } enum PointOfSaleCashPaymentState: Equatable { + case idle case collectingCash case paymentSuccess } -extension PointOfSalePaymentState { +extension PointOfSaleCardPaymentState { init?(from cardPaymentEvent: CardPresentPaymentEvent, using paymentEventPresentationStyleDependencies: PointOfSaleCardPresentPaymentEventPresentationStyle.Dependencies) { switch cardPaymentEvent { case .idle: - self = .card(.idle) + self = .idle case .show(.validatingOrder): - self = .card(.validatingOrder) + self = .validatingOrder case .show(.preparingForPayment): - self = .card(.preparingReader) + self = .preparingReader case .show(.tapSwipeOrInsertCard): - self = .card(.acceptingCard) + self = .acceptingCard case .show(.cardInserted): - self = .card(.cardInserted) + self = .cardInserted case .show(.processing), .show(.displayReaderMessage): - self = .card(.processingPayment) + self = .processingPayment case .show(.paymentError): if case let .show(eventDetails) = cardPaymentEvent, case let .message(messageType) = PointOfSaleCardPresentPaymentEventPresentationStyle( for: eventDetails, dependencies: paymentEventPresentationStyleDependencies), case .validatingOrderError = messageType { - self = .card(.validatingOrderError) + self = .validatingOrderError } else { - self = .card(.paymentError) + self = .paymentError } case .show(.paymentCaptureError): - self = .card(.paymentError) + self = .paymentError case .show(.paymentSuccess): - self = .card(.cardPaymentSuccessful) + self = .cardPaymentSuccessful case .show(.paymentIntentCreationError): - self = .card(.paymentIntentCreationError) + self = .paymentIntentCreationError default: return nil } @@ -63,19 +89,28 @@ extension PointOfSalePaymentState { var shownFullScreen: Bool { switch self { - case .card(.processingPayment), - .card(.paymentError), - .card(.cardPaymentSuccessful): + case .processingPayment, + .paymentError, + .cardPaymentSuccessful: return true - case .card(.idle), - .card(.validatingOrder), - .card(.validatingOrderError), - .card(.paymentIntentCreationError), - .card(.preparingReader), - .card(.acceptingCard), - .card(.cardInserted): + case .idle, + .validatingOrder, + .validatingOrderError, + .paymentIntentCreationError, + .preparingReader, + .acceptingCard, + .cardInserted: return false - case .cash: + } + } +} + +extension PointOfSaleCashPaymentState { + var shownFullScreen: Bool { + switch self { + case .idle: + return false + case .collectingCash, .paymentSuccess: return true } } diff --git a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift index b57a9201708..abdf5f457ea 100644 --- a/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift +++ b/WooCommerce/Classes/POS/Presentation/POSFloatingControlView.swift @@ -79,7 +79,7 @@ struct POSFloatingControlView: View { } .background(backgroundColor) .cornerRadius(Constants.cornerRadius) - .disabled(posModel.paymentState == .card(.processingPayment)) + .disabled(posModel.paymentState.card == .processingPayment) CardReaderConnectionStatusView() .foregroundStyle(fontColor) diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift index 253719fe677..f5e70d5ff5c 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleDashboardView.swift @@ -80,7 +80,7 @@ struct PointOfSaleDashboardView: View { .environment(\.floatingControlAreaSize, CGSizeMake(floatingSize.width + Constants.floatingControlHorizontalOffset, floatingSize.height + Constants.floatingControlVerticalOffset)) - .environment(\.posBackgroundAppearance, posModel.paymentState != .card(.processingPayment) ? .primary : .secondary) + .environment(\.posBackgroundAppearance, backgroundAppearance) .animation(.easeInOut, value: itemsViewState.containerState == .loading) .background(Color.posSurface) .navigationBarBackButtonHidden(true) @@ -143,6 +143,10 @@ struct PointOfSaleDashboardView: View { .animation(.default, value: posModel.paymentState.shownFullScreen) } } + + private var backgroundAppearance: POSBackgroundAppearanceKey.Appearance { + posModel.paymentState.card != .processingPayment ? .primary : .secondary + } } @available(iOS 17.0, *) diff --git a/WooCommerce/Classes/POS/Presentation/TotalsView.swift b/WooCommerce/Classes/POS/Presentation/TotalsView.swift index b331d43c582..cc43faf7f98 100644 --- a/WooCommerce/Classes/POS/Presentation/TotalsView.swift +++ b/WooCommerce/Classes/POS/Presentation/TotalsView.swift @@ -93,13 +93,21 @@ struct TotalsView: View { } private var backgroundColor: Color { - switch posModel.paymentState { - case .card(.processingPayment): - .posPrimary - case .cash(.collectingCash): - .posSurfaceBright - default: - .clear + switch posModel.paymentState.activePaymentMethod { + case .cash: + switch posModel.paymentState.cash { + case .collectingCash: + return .posSurfaceBright + default: + return .clear + } + case .card: + switch posModel.paymentState.card { + case .processingPayment: + return .posPrimary + default: + return .clear + } } } } @@ -214,7 +222,7 @@ private extension TotalsView { /// Hide totals fields with animation after a delay when starting to processing a payment /// - Parameter isShowing private func hideTotalsFieldsWithDelay(_ isShowing: Bool) { - guard !isShowing && posModel.paymentState == .card(.processingPayment) else { + guard !isShowing && posModel.paymentState.card == .processingPayment else { self.isShowingTotalsFields = isShowing return } @@ -229,10 +237,29 @@ private extension TotalsView { private extension TotalsView { @ViewBuilder private var paymentView: some View { - switch posModel.paymentState { - case .card(let cardPaymentState): + switch posModel.paymentState.activePaymentMethod { + case .cash: + // Show cash payment UI when cash payment is active + switch posModel.paymentState.cash { + case .collectingCash: + if case .loaded(let total) = posModel.orderState { + PointOfSaleCollectCashView(orderTotal: total.orderTotal) + .transition(.move(edge: .trailing)) + } + case .paymentSuccess: + if case .loaded(let total) = posModel.orderState { + PointOfSaleCardPresentPaymentInLineMessage( + messageType: .paymentSuccess( + viewModel: .init(formattedOrderTotal: total.orderTotal, + paymentMethod: PointOfSalePaymentMethod.cash))) + } + case .idle: + EmptyView() + } + case .card: + // Show card payment UI when card is the active payment method if TotalsViewHelper().shouldShowDisconnectedMessage(readerConnectionStatus: posModel.cardReaderConnectionStatus, - paymentState: cardPaymentState) { + paymentState: posModel.paymentState) { PointOfSaleCardPresentPaymentReaderDisconnectedMessageView { posModel.connectCardReader() } @@ -248,21 +275,6 @@ private extension TotalsView { } } } - case .cash(let cashPaymentState): - switch cashPaymentState { - case .collectingCash: - if case .loaded(let total) = posModel.orderState { - PointOfSaleCollectCashView(orderTotal: total.orderTotal) - .transition(.move(edge: .trailing)) - } - case .paymentSuccess: - if case .loaded(let total) = posModel.orderState { - PointOfSaleCardPresentPaymentInLineMessage( - messageType: .paymentSuccess( - viewModel: .init(formattedOrderTotal: total.orderTotal, - paymentMethod: .cash))) - } - } } } } @@ -305,11 +317,12 @@ private extension TotalsView { switch posModel.cardReaderConnectionStatus { case .connected, .disconnecting, .cancellingConnection: - switch posModel.paymentState { - case .card: - return posModel.cardPresentPaymentInlineMessage != nil + // Show card payment UI if there's a message, or cash payment UI when not idle + switch posModel.paymentState.activePaymentMethod { case .cash: return true + case .card: + return posModel.cardPresentPaymentInlineMessage != nil } case .disconnected: // Since the reader is disconnected, this will show the "Connect your reader" CTA button view. @@ -322,9 +335,14 @@ private extension TotalsView { return .primary } - switch posModel.paymentState { - case .card(let cardPaymentState): - switch cardPaymentState { + switch posModel.paymentState.activePaymentMethod { + case .cash: + return PaymentViewLayout(backgroundColor: backgroundColor, + topPadding: POSPadding.none, + bottomPadding: posModel.paymentState.cash == .collectingCash ? nil : POSPadding.none, + sidePadding: POSPadding.none) + case .card: + switch posModel.paymentState.card { case .validatingOrderError, .paymentIntentCreationError: return .outlined @@ -345,26 +363,13 @@ private extension TotalsView { .preparingReader, .processingPayment: if TotalsViewHelper().shouldShowDisconnectedMessage(readerConnectionStatus: posModel.cardReaderConnectionStatus, - paymentState: cardPaymentState) { + paymentState: posModel.paymentState) { return .outlined - } else { - return .primary } } - case .cash(let cashPaymentState): - switch cashPaymentState { - case .collectingCash: - return PaymentViewLayout(backgroundColor: backgroundColor, - topPadding: POSPadding.none, - bottomPadding: nil, - sidePadding: POSPadding.none) - case .paymentSuccess: - return PaymentViewLayout(backgroundColor: backgroundColor, - topPadding: POSPadding.none, - bottomPadding: POSPadding.none, - sidePadding: POSPadding.none) - } } + + return .primary } } diff --git a/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift b/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift index d3d216ddd9a..dcca305c839 100644 --- a/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift +++ b/WooCommerce/Classes/POS/ViewHelpers/CartViewHelper.swift @@ -12,7 +12,7 @@ struct CartViewHelper { func shouldPreventCartEditing(orderState: PointOfSaleOrderState, paymentState: PointOfSalePaymentState) -> Bool { - guard paymentState.allowsCartEditing else { + guard paymentState.card.allowsCartEditing else { return true } return orderState.isSyncing @@ -40,26 +40,21 @@ struct CartViewHelper { } } -private extension PointOfSalePaymentState { +private extension PointOfSaleCardPaymentState { var allowsCartEditing: Bool { switch self { - case .card(let cardPaymentState): - switch cardPaymentState { - case .processingPayment, - .paymentError, - .cardPaymentSuccessful, - .validatingOrder, - .preparingReader, - .cardInserted: - return false - case .idle, - .validatingOrderError, - .paymentIntentCreationError, - .acceptingCard: - return true - } - case .cash: + case .processingPayment, + .paymentError, + .cardPaymentSuccessful, + .validatingOrder, + .preparingReader, + .cardInserted: return false + case .idle, + .validatingOrderError, + .paymentIntentCreationError, + .acceptingCard: + return true } } } diff --git a/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift b/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift index f3fdc5c3c0e..9f1d5f896e7 100644 --- a/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift +++ b/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift @@ -2,9 +2,11 @@ import Foundation struct TotalsViewHelper { func shouldShowTotalsFields(for paymentState: PointOfSalePaymentState) -> Bool { - switch paymentState { - case .card(let cardPaymentState): - switch cardPaymentState { + switch paymentState.activePaymentMethod { + case .cash: + return false + case .card: + switch paymentState.card { case .idle, .acceptingCard, .cardInserted, @@ -18,64 +20,90 @@ struct TotalsViewHelper { .cardPaymentSuccessful: return false } - case .cash: - return false } } func shouldShowDisconnectedMessage(readerConnectionStatus: CardPresentPaymentReaderConnectionStatus, - paymentState: PointOfSaleCardPaymentState) -> Bool { + paymentState: PointOfSalePaymentState) -> Bool { guard readerConnectionStatus == .disconnected else { return false } - switch paymentState { - case .idle, - .acceptingCard, - .preparingReader: - return true - case .validatingOrder, - .validatingOrderError, - .paymentIntentCreationError, - .processingPayment, - .cardInserted, - .paymentError, - .cardPaymentSuccessful: + + switch paymentState.activePaymentMethod { + case .cash: return false + case .card: + switch paymentState.card { + case .idle, + .acceptingCard, + .preparingReader: + return true + case .validatingOrder, + .validatingOrderError, + .paymentIntentCreationError, + .processingPayment, + .cardInserted, + .paymentError, + .cardPaymentSuccessful: + return false + } } } func shouldShowCollectCashPaymentButton(orderState: PointOfSaleOrderState, paymentState: PointOfSalePaymentState, cardReaderConnectionStatus: CardPresentPaymentReaderConnectionStatus) -> Bool { - guard orderState != .syncing, - case .card(let cardState) = paymentState else { + guard orderState != .syncing else { return false } - if cardReaderConnectionStatus == .disconnected { - return true - } + switch paymentState.activePaymentMethod { + case .cash: + return false + case .card: + if cardReaderConnectionStatus == .disconnected { + return true + } - if case let .loaded(totals) = orderState, totals.orderTotalDecimal.isZero { - return true - } + if case let .loaded(totals) = orderState, totals.orderTotalDecimal.isZero { + return true + } - switch cardState { - case .validatingOrderError, - .paymentIntentCreationError, - .acceptingCard: - return true - default: - return false + switch paymentState.card { + case .validatingOrderError, + .paymentIntentCreationError, + .acceptingCard: + return true + case .idle, + .cardInserted, + .validatingOrder, + .preparingReader, + .processingPayment, + .paymentError, + .cardPaymentSuccessful: + return false + } } } func shouldApplyPadding(paymentState: PointOfSalePaymentState) -> Bool { - switch paymentState { - case .card(.cardPaymentSuccessful), .cash(.paymentSuccess), .cash(.collectingCash), .card(.paymentError): + switch paymentState.activePaymentMethod { + case .cash: return false - default: - return true + case .card: + switch paymentState.card { + case .cardPaymentSuccessful, .paymentError: + return false + case .idle, + .acceptingCard, + .cardInserted, + .validatingOrder, + .validatingOrderError, + .paymentIntentCreationError, + .preparingReader, + .processingPayment: + return true + } } } diff --git a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift index aa47705fd2d..9ec59821e65 100644 --- a/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift +++ b/WooCommerce/WooCommerceTests/POS/Mocks/MockPointOfSaleAggregateModel.swift @@ -45,7 +45,7 @@ final class MockPointOfSaleAggregateModel: PointOfSaleAggregateModelProtocol { couponsSearchController: PointOfSaleSearchingItemsControllerProtocol = MockPointOfSaleCouponsController(), orderStage: PointOfSaleOrderStage = .building, orderState: PointOfSaleOrderState = .idle, - paymentState: PointOfSalePaymentState = .card(.idle)) { + paymentState: PointOfSalePaymentState = .idle) { self.cardReaderConnectionStatus = cardReaderConnectionStatus self.purchasableItemsController = purchasableItemsController self.purchasableItemsSearchController = purchasableItemsSearchController diff --git a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift index b78debf52d3..681a1f3e27b 100644 --- a/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift +++ b/WooCommerce/WooCommerceTests/POS/Models/PointOfSaleAggregateModelTests.swift @@ -539,7 +539,35 @@ struct PointOfSaleAggregateModelTests { soundPlayer: MockPointOfSaleSoundPlayer()) // Then - #expect(sut.paymentState == .card(.idle)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .idle)) + #expect(sut.paymentState.activePaymentMethod == .card) + } + + @available(iOS 17.0, *) + @Test func activePaymentMethod_returns_card_when_cash_is_idle() async throws { + // Given + let paymentState = PointOfSalePaymentState(card: .acceptingCard, cash: .idle) + + // Then + #expect(paymentState.activePaymentMethod == .card) + } + + @available(iOS 17.0, *) + @Test func activePaymentMethod_returns_cash_when_cash_is_not_idle() async throws { + // Given + let paymentState = PointOfSalePaymentState(card: .acceptingCard, cash: .collectingCash) + + // Then + #expect(paymentState.activePaymentMethod == .cash) + } + + @available(iOS 17.0, *) + @Test func activePaymentMethod_returns_cash_when_cash_is_paymentSuccess() async throws { + // Given + let paymentState = PointOfSalePaymentState(card: .idle, cash: .paymentSuccess) + + // Then + #expect(paymentState.activePaymentMethod == .cash) } @available(iOS 17.0, *) @@ -558,13 +586,13 @@ struct PointOfSaleAggregateModelTests { popularPurchasableItemsController: MockPointOfSaleItemsController(), barcodeScanService: MockPointOfSaleBarcodeScanService(), soundPlayer: MockPointOfSaleSoundPlayer(), - paymentState: .card(.cardPaymentSuccessful)) + paymentState: PointOfSalePaymentState(card: .cardPaymentSuccessful, cash: .idle)) // When sut.startNewCart() // Then - #expect(sut.paymentState == .card(.idle)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .idle)) } @available(iOS 17.0, *) @@ -611,13 +639,13 @@ struct PointOfSaleAggregateModelTests { popularPurchasableItemsController: MockPointOfSaleItemsController(), barcodeScanService: MockPointOfSaleBarcodeScanService(), soundPlayer: MockPointOfSaleSoundPlayer(), - paymentState: .card(.cardPaymentSuccessful)) + paymentState: PointOfSalePaymentState(card: .cardPaymentSuccessful, cash: .idle)) // When sut.addMoreToCart() // Then - #expect(sut.paymentState == .card(.idle)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .idle)) } @available(iOS 17.0, *) @@ -675,7 +703,7 @@ struct PointOfSaleAggregateModelTests { // Then #expect(cardPresentPaymentService.cancelPaymentCalled == true) - #expect(sut.paymentState == .cash(.collectingCash)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .collectingCash)) } @available(iOS 17.0, *) @@ -700,7 +728,7 @@ struct PointOfSaleAggregateModelTests { await sut.startCashPayment() // Then - #expect(sut.paymentState == .cash(.collectingCash)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .collectingCash)) } @available(iOS 17.0, *) @@ -721,13 +749,13 @@ struct PointOfSaleAggregateModelTests { barcodeScanService: MockPointOfSaleBarcodeScanService(), soundPlayer: MockPointOfSaleSoundPlayer()) await sut.startCashPayment() - #expect(sut.paymentState == .cash(.collectingCash)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .collectingCash)) // When await sut.cancelCashPayment() // Then - #expect(sut.paymentState == .card(.idle)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .idle)) } @available(iOS 17.0, *) @@ -753,7 +781,7 @@ struct PointOfSaleAggregateModelTests { #expect(sut.orderStage == .finalizing) await sut.startCashPayment() - #expect(sut.paymentState == .cash(.collectingCash)) + #expect(sut.paymentState == PointOfSalePaymentState(card: .idle, cash: .collectingCash)) // When await sut.cancelCashPayment() diff --git a/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift b/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift index d02533a3a3e..dc632eddb40 100644 --- a/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift +++ b/WooCommerce/WooCommerceTests/POS/ViewHelpers/CartViewHelperTests.swift @@ -10,7 +10,7 @@ struct CartViewHelperTests { // When, Then #expect(sut.shouldPreventCartEditing(orderState: .syncing, - paymentState: .card(.idle)) == true) + paymentState: PointOfSalePaymentState(card: .idle, cash: .idle)) == true) } @Test func shouldPreventCartEditing_when_card_paymentState_cardPaymentSuccessful() async throws { @@ -23,7 +23,7 @@ struct CartViewHelperTests { // When, Then #expect(sut.shouldPreventCartEditing(orderState: orderLoaded, - paymentState: .card(.cardPaymentSuccessful)) == true) + paymentState: PointOfSalePaymentState(card: .cardPaymentSuccessful, cash: .idle)) == true) } @Test func shouldPreventCartEditing_when_card_paymentState_processingPayment() async throws { @@ -36,7 +36,7 @@ struct CartViewHelperTests { // When, Then #expect(sut.shouldPreventCartEditing(orderState: orderLoaded, - paymentState: .card(.processingPayment)) == true) + paymentState: PointOfSalePaymentState(card: .processingPayment, cash: .idle)) == true) } @Test func shouldPreventCartEditing_false_when_card_paymentState_acceptingCard() async throws { @@ -49,7 +49,7 @@ struct CartViewHelperTests { // When, Then #expect(sut.shouldPreventCartEditing(orderState: orderLoaded, - paymentState: .card(.acceptingCard)) == false) + paymentState: PointOfSalePaymentState(card: .acceptingCard, cash: .idle)) == false) } @Test func shouldPreventCartEditing_false_when_card_paymentState_validatingOrderError() async throws { @@ -62,7 +62,7 @@ struct CartViewHelperTests { // When, Then #expect(sut.shouldPreventCartEditing(orderState: orderLoaded, - paymentState: .card(.validatingOrderError)) == false) + paymentState: PointOfSalePaymentState(card: .validatingOrderError, cash: .idle)) == false) } @Test func shouldPreventCartEditing_when_card_paymentState_cardInserted() async throws { @@ -75,7 +75,7 @@ struct CartViewHelperTests { // When, Then #expect(sut.shouldPreventCartEditing(orderState: orderLoaded, - paymentState: .card(.cardInserted)) == true) + paymentState: PointOfSalePaymentState(card: .cardInserted, cash: .idle)) == true) } @Test func shouldPreventCartEditing_false_when_card_paymentState_paymentIntentCreationError() async throws { @@ -88,7 +88,7 @@ struct CartViewHelperTests { // Then #expect(sut.shouldPreventCartEditing(orderState: orderLoaded, - paymentState: .card(.paymentIntentCreationError)) == false) + paymentState: PointOfSalePaymentState(card: .paymentIntentCreationError, cash: .idle)) == false) } @Test(arguments: zip([0, 1, 2, 3], [nil, "1 item", "2 items", "3 items"])) diff --git a/WooCommerce/WooCommerceTests/POS/ViewHelpers/TotalsViewHelperTests.swift b/WooCommerce/WooCommerceTests/POS/ViewHelpers/TotalsViewHelperTests.swift index 95b7bf13e62..95b8e621460 100644 --- a/WooCommerce/WooCommerceTests/POS/ViewHelpers/TotalsViewHelperTests.swift +++ b/WooCommerce/WooCommerceTests/POS/ViewHelpers/TotalsViewHelperTests.swift @@ -12,7 +12,7 @@ struct TotalsViewHelperTests { readerConnectionStatus: CardPresentPaymentReaderConnectionStatus, paymentState: PointOfSaleCardPaymentState) { #expect(TotalsViewHelper().shouldShowDisconnectedMessage(readerConnectionStatus: readerConnectionStatus, - paymentState: paymentState)) + paymentState: PointOfSalePaymentState(card: paymentState, cash: .idle))) } @Test(arguments: [ @@ -26,7 +26,7 @@ struct TotalsViewHelperTests { readerConnectionStatus: CardPresentPaymentReaderConnectionStatus, paymentState: PointOfSaleCardPaymentState) { #expect(TotalsViewHelper().shouldShowDisconnectedMessage(readerConnectionStatus: readerConnectionStatus, - paymentState: paymentState) == false) + paymentState: PointOfSalePaymentState(card: paymentState, cash: .idle)) == false) } @Test(arguments: [ @@ -45,22 +45,22 @@ struct TotalsViewHelperTests { readerConnectionStatus: CardPresentPaymentReaderConnectionStatus, paymentState: PointOfSaleCardPaymentState) { #expect(TotalsViewHelper().shouldShowDisconnectedMessage(readerConnectionStatus: readerConnectionStatus, - paymentState: paymentState) == false) + paymentState: PointOfSalePaymentState(card: paymentState, cash: .idle)) == false) } @Test(arguments: [ - (PointOfSalePaymentState.card(.validatingOrderError)), - (PointOfSalePaymentState.card(.acceptingCard)), - (PointOfSalePaymentState.card(.cardInserted)), - (PointOfSalePaymentState.card(.paymentIntentCreationError)) + (PointOfSaleCardPaymentState.validatingOrderError), + (PointOfSaleCardPaymentState.acceptingCard), + (PointOfSaleCardPaymentState.cardInserted), + (PointOfSaleCardPaymentState.paymentIntentCreationError) ]) func test_shouldShowCollectCashPaymentButton_returns_true_for_supported_states( - paymentState: PointOfSalePaymentState) { + cardPaymentState: PointOfSaleCardPaymentState) { #expect(TotalsViewHelper().shouldShowCollectCashPaymentButton(orderState: .loaded(.init(cartTotal: "10", orderTotal: "10", taxTotal: "10", orderTotalDecimal: 0)), - paymentState: paymentState, + paymentState: PointOfSalePaymentState(card: cardPaymentState, cash: .idle), cardReaderConnectionStatus: .connected(.init(name: "", batteryLevel: nil)))) } @@ -70,7 +70,7 @@ struct TotalsViewHelperTests { orderTotal: "10", taxTotal: "10", orderTotalDecimal: 0)), - paymentState: .card(.idle), + paymentState: PointOfSalePaymentState(card: .idle, cash: .idle), cardReaderConnectionStatus: .disconnected)) } @@ -80,7 +80,7 @@ struct TotalsViewHelperTests { orderTotal: "0", taxTotal: "0", orderTotalDecimal: 0)), - paymentState: .card(.idle), + paymentState: PointOfSalePaymentState(card: .idle, cash: .idle), cardReaderConnectionStatus: .connected(.init(name: "", batteryLevel: nil)))) } @@ -90,19 +90,37 @@ struct TotalsViewHelperTests { orderTotal: "10", taxTotal: "10", orderTotalDecimal: 10)), - paymentState: .card(.idle), + paymentState: PointOfSalePaymentState(card: .idle, cash: .idle), cardReaderConnectionStatus: .connected(.init(name: "", batteryLevel: nil))) == false) } @Test(arguments: [ - (PointOfSalePaymentState.card(.validatingOrderError)), - (PointOfSalePaymentState.card(.acceptingCard)), - (PointOfSalePaymentState.card(.cardInserted)) + (PointOfSaleCardPaymentState.validatingOrderError), + (PointOfSaleCardPaymentState.acceptingCard), + (PointOfSaleCardPaymentState.cardInserted) ]) func test_shouldShowCollectCashPaymentButton_returns_false_when_order_syncing( - paymentState: PointOfSalePaymentState) { + cardPaymentState: PointOfSaleCardPaymentState) { #expect(TotalsViewHelper().shouldShowCollectCashPaymentButton(orderState: .syncing, - paymentState: paymentState, + paymentState: PointOfSalePaymentState(card: cardPaymentState, cash: .idle), + cardReaderConnectionStatus: .connected(.init(name: "", batteryLevel: nil))) == false) + } + + @Test func test_shouldShowCollectCashPaymentButton_returns_false_when_cash_collecting() { + #expect(TotalsViewHelper().shouldShowCollectCashPaymentButton(orderState: .loaded(.init(cartTotal: "10", + orderTotal: "10", + taxTotal: "10", + orderTotalDecimal: 0)), + paymentState: PointOfSalePaymentState(card: .idle, cash: .collectingCash), + cardReaderConnectionStatus: .connected(.init(name: "", batteryLevel: nil))) == false) + } + + @Test func test_shouldShowCollectCashPaymentButton_returns_false_when_cash_payment_success() { + #expect(TotalsViewHelper().shouldShowCollectCashPaymentButton(orderState: .loaded(.init(cartTotal: "10", + orderTotal: "10", + taxTotal: "10", + orderTotalDecimal: 0)), + paymentState: PointOfSalePaymentState(card: .idle, cash: .paymentSuccess), cardReaderConnectionStatus: .connected(.init(name: "", batteryLevel: nil))) == false) }